diff --git a/README.md b/README.md index 2897c07..7ac8ef1 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ You must download and build a copy of [vscode-js-debug](https://github.com/micro use { "microsoft/vscode-js-debug", opt = true, - run = "npm install --legacy-peer-deps && npm run compile" + run = "npm install --legacy-peer-deps && npx gulp dapDebugServer" } ``` @@ -52,8 +52,7 @@ npm run compile ```lua require("dap-vscode-js").setup({ -- node_path = "node", -- Path of node executable. Defaults to $NODE_PATH, and then "node" - -- debugger_path = "(runtimedir)/site/pack/packer/opt/vscode-js-debug", -- Path to vscode-js-debug installation. - -- debugger_cmd = { "js-debug-adapter" }, -- Command to use to launch the debug server. Takes precedence over `node_path` and `debugger_path`. + -- debugger_path = "(runtimedir)/site/pack/packer/opt/vscode-js-debug/dist/src/dapDebugServer.js", -- Path to vscode-js-debug installation. adapters = { 'pwa-node', 'pwa-chrome', 'pwa-msedge', 'node-terminal', 'pwa-extensionHost' }, -- which adapters to register in nvim-dap -- log_file_path = "(stdpath cache)/dap_vscode_js.log" -- Path for file logging -- log_file_level = false -- Logging level for output to file. Set to false to disable file logging. diff --git a/lua/dap-vscode-js/adapter.lua b/lua/dap-vscode-js/adapter.lua index c23a03e..c9b8aed 100644 --- a/lua/dap-vscode-js/adapter.lua +++ b/lua/dap-vscode-js/adapter.lua @@ -1,78 +1,51 @@ local M = {} -local uv = vim.loop -local js_session = require("dap-vscode-js.session") local utils = require("dap-vscode-js.utils") -local logger = require("dap-vscode-js.log") +-- local logger = require("dap-vscode-js.log") local dapjs_config = require("dap-vscode-js.config") -local dap = require("dap") -local function adapter_config(port, mode, proc, start_child) - return { - type = "server", - host = "127.0.0.1", - port = port, - id = mode, - reverse_request_handlers = { - attachedChildSession = function(parent, request) - logger.debug( - string.format( - "Got attachedChildSession request from port %d to start port %s", - parent.adapter.port, - request.arguments.config.__jsDebugChildServer - ) - ) - logger.trace("attachedChildSession request, port " .. tostring(port) .. ": " .. vim.inspect(request)) - - start_child(request, mode, parent, proc) - end, - }, - } -end - -local function start_child_session(request, mode, parent, proc) - local body = request.arguments - local session = nil - local child_port = tonumber(body.config.__jsDebugChildServer) - - session = require("dap.session"):connect( - adapter_config(child_port, mode, proc, start_child_session), - {}, - function(err) - if err then - logger.log("DAP connection failed to start: " .. err, vim.log.levels.ERROR) - else - logger.debug("Initializing child session on port " .. tostring(child_port)) - - session:initialize(body.config) - - js_session.register_session(session, parent, proc) - end - end - ) -end - -function M.generate_adapter(mode, config) - config = config or dapjs_config - - return function(callback) - local proc - - proc = utils.start_debugger(config, function(port, proc) - logger.debug("Debugger process started on port " .. port) - - js_session.register_port(port) - callback(adapter_config(port, mode, proc, start_child_session)) - end, function(code, signal) - if code and code ~= 0 then - logger.error("JS Debugger exited with code " .. code .. "!") - end - end, function(err) - logger.error("Error trying to launch JS debugger: " .. err) - end, function(chunk) - -- logger.log("JS Debugger stderr: " .. chunk, vim.log.levels.ERROR) - logger.error("JS Debugger stderr: " .. chunk) - end) - end +function M.generate_adapter(_, user_config) + user_config = user_config or dapjs_config + + return function(on_config, config, parent) + local target = config["__pendingTargetId"] + if target and parent then + local adapter = parent.adapter + on_config({ + type = "server", + host = "localhost", + port = adapter.port + }) + else + local debug_executable = user_config.adapter_executable_config + + if not debug_executable then + local debugger_path = user_config.debugger_executable + + if not utils.file_exists(debugger_path) then + -- TODO: show user to README.md with directions + error("Debugger entrypoint file '" .. debugger_path .. "' does not exist. Did it build properly?") + end + + local debugger_cmd_args = { debugger_path, "${port}" } + + for _, arg in ipairs(user_config.debugger_args or {}) do + table.insert(debugger_cmd_args, arg) + end + + debug_executable = { + command = user_config.node_path, + args = debugger_cmd_args, + } + end + + on_config({ + type = "server", + host = "localhost", + port = "${port}", + executable = debug_executable + }) + end + end end return M diff --git a/lua/dap-vscode-js/config.lua b/lua/dap-vscode-js/config.lua index a68b569..b467350 100644 --- a/lua/dap-vscode-js/config.lua +++ b/lua/dap-vscode-js/config.lua @@ -2,11 +2,18 @@ local utils = require("dap-vscode-js.utils") local defaults = { node_path = os.getenv("NODE_PATH") or "node", - debugger_path = utils.join_paths(utils.get_runtime_dir(), "site/pack/packer/opt/vscode-js-debug"), - debugger_cmd = nil, + debugger_executable = utils.join_paths(utils.get_runtime_dir(), "site/pack/packer/opt/vscode-js-debug/dist/src/dapDebugServer.js"), + debugger_args = {}, + adapter_executable_config = nil, + log_file_path = utils.join_paths(utils.get_cache_dir(), "dap_vscode_js.log"), log_file_level = false, log_console_level = vim.log.levels.WARN, + + -- legacy + debugger_cmd = nil, + debugger_path = utils.join_paths(utils.get_runtime_dir(), "site/pack/packer/opt/vscode-js-debug"), + legacy_flat_debugger = false, } local config = vim.deepcopy(defaults) diff --git a/lua/dap-vscode-js/dap.lua b/lua/dap-vscode-js/dap.lua index f5075c3..32189dc 100644 --- a/lua/dap-vscode-js/dap.lua +++ b/lua/dap-vscode-js/dap.lua @@ -1,6 +1,6 @@ local M = {} local dap = require("dap") -local js_adapter = require("dap-vscode-js.adapter") +-- local js_adapter = require("dap-vscode-js.adapter") local DAP_TYPES = { "pwa-node", "pwa-chrome", "pwa-msedge", "node-terminal", "pwa-extensionHost" } @@ -10,11 +10,11 @@ local function filter_adapters(list) end, list) end -function M.attach_adapters(config) +function M.attach_adapters(config, generate_adapter) local adapter_list = filter_adapters(config.adapters or DAP_TYPES) for _, adapter in ipairs(adapter_list) do - dap.adapters[adapter] = js_adapter.generate_adapter(adapter, config) + dap.adapters[adapter] = generate_adapter(adapter, config) end end diff --git a/lua/dap-vscode-js/init.lua b/lua/dap-vscode-js/init.lua index b39e56a..16e6fb3 100644 --- a/lua/dap-vscode-js/init.lua +++ b/lua/dap-vscode-js/init.lua @@ -2,17 +2,22 @@ ---@class Settings @Plugin configuration options ---@field node_path string: Path of node executable. Defaults to $NODE_PATH, and then "node" ----@field debugger_path string: Path to vscode-js-debug. Defaults to (runtimedir)/site/pack/packer/opt/vscode-js-debug ----@field debugger_cmd string[]: The command to use to launch the debug server. This option takes precedence over both `node_path` and `debugger_path`. +---@field debugger_executable string: Path to debugger executable (dapDebugServer.js). Defaults to (runtimedir)/site/pack/packer/opt/vscode-js-debug/dist/src/dapDebugServer.js +---@field debugger_args string[]: Arguments to pass to debugger command. Defaults to {} +---@field adapter_executable_config table: Overrides adapter debugger executable configuration - see nvim-dap for more info ---@field adapters string[]: List of adapters to configure. Options are 'pwa-node', 'pwa-chrome', 'pwa-msedge', 'node-terminal', 'pwa-extensionHost'. Defaults to all. See https://github.com/microsoft/vscode-js-debug/blob/main/OPTIONS.md for configuration options. ---@field log_file_path string: Log file path. Defaults to (stdpath cache)/dap_vscode_js.log ---@field log_file_level number: Logging level for output to file. Set to false to disable file logging. Default is false. ---@field log_console_level number: Logging level for output to console. Set to false to disable console output. Default is vim.log.levels.ERROR. +---@field legacy_flat_debugger boolean: Use legacy flat debugger, and use legacy custom session manager for dealing with child/parent sessions. This is not maintained anymore. +---@field debugger_path string: (LEGACY) Path to vscode-js-debug. Defaults to (runtimedir)/site/pack/packer/opt/vscode-js-debug +---@field debugger_cmd string[]: (LEGACY) The command to use to launch the debug server. This option takes precedence over both `node_path` and `debugger_path`. local config = require("dap-vscode-js.config") -local js_session = require("dap-vscode-js.session") -local js_dap = require("dap-vscode-js.dap") local logger = require("dap-vscode-js.log") +local legacy = require("dap-vscode-js.legacy") +local js_dap = require("dap-vscode-js.dap") +local js_adapter = require("dap-vscode-js.adapter") local dapjs = {} @@ -21,8 +26,13 @@ local dapjs = {} ---@param force boolean? function dapjs.setup(settings, force) config.__set_config(settings, force or true) - js_session.setup_hooks("dap-vscode-js", config) - js_dap.attach_adapters(config) + + if legacy.use_legacy(config) then + legacy.setup(config) + else + -- use new adapter + js_dap.attach_adapters(config, js_adapter.generate_adapter) + end logger.debug("Plugin initialized!") end diff --git a/lua/dap-vscode-js/legacy/adapter.lua b/lua/dap-vscode-js/legacy/adapter.lua new file mode 100644 index 0000000..e217f26 --- /dev/null +++ b/lua/dap-vscode-js/legacy/adapter.lua @@ -0,0 +1,79 @@ +local M = {} +local uv = vim.loop +local js_session = require("dap-vscode-js.legacy.session") +local utils = require("dap-vscode-js.utils") +local logger = require("dap-vscode-js.log") +local dapjs_config = require("dap-vscode-js.config") +local dap = require("dap") + +local function adapter_config(port, mode, proc, start_child) + return { + type = "server", + host = "127.0.0.1", + port = port, + id = mode, + reverse_request_handlers = { + attachedChildSession = function(parent, request) + logger.debug( + string.format( + "Got attachedChildSession request from port %d to start port %s", + parent.adapter.port, + request.arguments.config.__jsDebugChildServer + ) + ) + logger.trace("attachedChildSession request, port " .. tostring(port) .. ": " .. vim.inspect(request)) + + start_child(request, mode, parent, proc) + end, + }, + } +end + +local function start_child_session(request, mode, parent, proc) + local body = request.arguments + local session = nil + local child_port = tonumber(body.config.__jsDebugChildServer) + + session = require("dap.session"):connect( + adapter_config(child_port, mode, proc, start_child_session), + {}, + function(err) + if err then + logger.log("DAP connection failed to start: " .. err, vim.log.levels.ERROR) + else + logger.debug("Initializing child session on port " .. tostring(child_port)) + if parent.children then + session.parent = parent + parent.children[session.id] = session + end + session:initialize(body.config) + js_session.register_session(session, parent, proc) + end + end + ) +end + +function M.generate_adapter(mode, config) + config = config or dapjs_config + + return function(callback) + local proc + + proc = utils.start_debugger(config, function(port, proc) + logger.debug("Debugger process started on port " .. port) + + js_session.register_port(port) + callback(adapter_config(port, mode, proc, start_child_session)) + end, function(code, signal) + if code and code ~= 0 then + logger.error("JS Debugger exited with code " .. code .. "!") + end + end, function(err) + logger.error("Error trying to launch JS debugger: " .. err) + end, function(chunk) + logger.error("JS Debugger stderr: " .. chunk) + end) + end +end + +return M diff --git a/lua/dap-vscode-js/legacy/init.lua b/lua/dap-vscode-js/legacy/init.lua new file mode 100644 index 0000000..2164a7b --- /dev/null +++ b/lua/dap-vscode-js/legacy/init.lua @@ -0,0 +1,20 @@ +local js_adapter = require("dap-vscode-js.legacy.adapter") +local js_session = require("dap-vscode-js.legacy.session") +local js_dap = require("dap-vscode-js.dap") + +local M = {} + +--- Whether or not to use legacy module +--- @param config Settings +--- @return boolean +function M.use_legacy(config) + -- TODO: figure out if `startDebugging` is supported + return config.legacy_flat_debugger +end + +function M.setup(config) + -- js_session.setup_hooks("dap-vscode-js", config) + -- js_dap.attach_adapters(config, js_adapter.generate_adapter) +end + +return M diff --git a/lua/dap-vscode-js/session.lua b/lua/dap-vscode-js/legacy/session.lua similarity index 100% rename from lua/dap-vscode-js/session.lua rename to lua/dap-vscode-js/legacy/session.lua diff --git a/lua/dap-vscode-js/utils.lua b/lua/dap-vscode-js/utils.lua index d84ea42..3d781a3 100644 --- a/lua/dap-vscode-js/utils.lua +++ b/lua/dap-vscode-js/utils.lua @@ -39,7 +39,7 @@ local function schedule_wrap_safe(func) return (func and vim.schedule_wrap(func)) or function(...) end end -local function file_exists(name) +function M.file_exists(name) local f = io.open(name, "r") if f ~= nil then io.close(f) @@ -60,7 +60,7 @@ local function get_spawn_cmd(config) end local entrypoint = debugger_entrypoint(config.debugger_path) - if not file_exists(entrypoint) then + if not M.file_exists(entrypoint) then error("Debugger entrypoint file '" .. entrypoint .. "' does not exist. Did it build properly?") end return config.node_path, { entrypoint } diff --git a/scripts/setup_tests b/scripts/setup_tests index 515e28d..1f2e745 100755 --- a/scripts/setup_tests +++ b/scripts/setup_tests @@ -7,5 +7,5 @@ __install_lib "mfussenegger" "nvim-dap" __install_lib "rcarriga" "nvim-dap-ui" __install_lib "microsoft" "vscode-js-debug" -(cd ./lib/vscode-js-debug && yarn install && yarn run compile) +(cd ./lib/vscode-js-debug && npm install --legacy-peer-deps && npx gulp dapDebugServer) (cd ./tests/js/jest && yarn install) diff --git a/scripts/test b/scripts/test index 53dda84..2801fe4 100755 --- a/scripts/test +++ b/scripts/test @@ -21,7 +21,7 @@ export DAP_JS_ENABLE_LOGGING=$LOG shift $(($OPTIND - 1)) -rm "./lib/dap-vscode-js.log" +rm -f "./lib/dap-vscode-js.log" tempfile=".test_output.tmp" diff --git a/tests/init.vim b/tests/init.vim index c17e2d5..401e1ed 100644 --- a/tests/init.vim +++ b/tests/init.vim @@ -6,8 +6,10 @@ set rtp+=./lib/nvim-dap set rtp+=./lib/nvim-dap-ui set rtp+=./tests +set noswapfile + " let $PLENARY_TEST_TIMEOUT=60000 runtime! plugin/plenary.vim -lua DEBUGGER_PATH="./lib/vscode-js-debug" +lua DEBUGGER_PATH="./lib/vscode-js-debug/dist/src/dapDebugServer.js" lua LOG_PATH="./lib/dap-vscode-js.log" diff --git a/tests/integration/jest_spec.lua b/tests/integration/jest_spec.lua index 0c11549..5958ea5 100644 --- a/tests/integration/jest_spec.lua +++ b/tests/integration/jest_spec.lua @@ -1,11 +1,7 @@ -local breakpoints = require("dap.breakpoints") local async = require("plenary.async.tests") local wrap = require("plenary.async.async").wrap -local dapjs = require("dap-vscode-js") -local js_session = require("dap-vscode-js.session") local dap = require("dap") local test_utils = require("__dap_js_test_util") -local config = require("dap-vscode-js.config") local workDir = test_utils.test_file("jest") @@ -59,7 +55,7 @@ describe("pwa-node jest", function() test_utils.open_test("jest/integration.test.ts") - test_utils.on_session_end(function() + test_utils.add_listener("before", "event_terminated", function () terminated = true try_exit() diff --git a/tests/integration/node_bp_spec.lua b/tests/integration/node_bp_spec.lua new file mode 100644 index 0000000..7b9ca32 --- /dev/null +++ b/tests/integration/node_bp_spec.lua @@ -0,0 +1,73 @@ +local breakpoints = require("dap.breakpoints") +local async = require("plenary.async.tests") +local wrap = require("plenary.async.async").wrap +local dap = require("dap") +local test_utils = require("__dap_js_test_util") + +local launch_config = { + type = "pwa-node", + request = "launch", + name = "Launch file", + program = "${file}", + cwd = "${workspaceFolder}", +} + +local current_session + +describe("pwa-node", function() + before_each(function() + test_utils.reset() + test_utils.setup_dapjs() + end) + + async.it( + "wont reject valid breakpoints", + wrap(function(done) + test_utils.open_test("test1.ts") + local bufexpr = vim.api.nvim_get_current_buf() + + local breakpoint_accepted = false + local breakpoint_stopped = false + + test_utils.set_breakpoint(3, bufexpr) + + test_utils.add_listener("after", "event_breakpoint", function(session, body) + local bps = breakpoints.get(bufexpr)[bufexpr] + + assert.equal(#bps, 1) + + local bp_signs = test_utils.get_breakpoint_signs(bufexpr) + + assert.equal(#bp_signs, 1) + + for _, bp in ipairs(bp_signs) do + assert.equal(#bp.signs, 1) + + for _, sign in ipairs(bp.signs) do + assert.equal("DapBreakpoint", sign.name) + end + end + + breakpoint_accepted = true + end) + + test_utils.add_listener("after", "event_stopped", function(session, body) + assert.equal(body.reason, "breakpoint") + breakpoint_stopped = true + + vim.defer_fn(function () + dap.continue() + end, 200) + end) + + test_utils.add_listener("before", "event_terminated", function () + assert.equal(true, breakpoint_accepted) + assert.equal(true, breakpoint_stopped) + + done() + end) + + dap.run(launch_config) + end, 1) + ) +end) diff --git a/tests/integration/node_spec.lua b/tests/integration/node_spec.lua index 02a2453..8d8ad21 100644 --- a/tests/integration/node_spec.lua +++ b/tests/integration/node_spec.lua @@ -1,10 +1,7 @@ -local breakpoints = require("dap.breakpoints") local async = require("plenary.async.tests") local wrap = require("plenary.async.async").wrap -local dapjs = require("dap-vscode-js") local dap = require("dap") local test_utils = require("__dap_js_test_util") -local config = require("dap-vscode-js.config") local launch_config = { type = "pwa-node", @@ -14,8 +11,6 @@ local launch_config = { cwd = "${workspaceFolder}", } -local current_session - describe("pwa-node", function() before_each(function() test_utils.reset() @@ -46,56 +41,16 @@ describe("pwa-node", function() if #console >= 3 then assert.same(console, { "4\n", "2\n", "6\n" }) - output_happened = true end try_exit() end) - test_utils.on_session_end(function() - termination_happened = true - - try_exit() - end) - - dap.run(launch_config) - end, 1) - ) - - async.it( - "wont reject valid breakpoints", - wrap(function(done) - test_utils.open_test("test1.ts") - local bufexpr = vim.api.nvim_get_current_buf() - - test_utils.set_breakpoint(3, bufexpr) - - test_utils.add_listener("after", "event_stopped", function(session, body) - assert.equal(body.reason, "breakpoint") - - local bps = breakpoints.get(bufexpr)[bufexpr] - - assert.equal(#bps, 1) - - local bp_signs = test_utils.get_breakpoint_signs(bufexpr) - - assert.equal(#bp_signs, 1) - - for _, bp in ipairs(bp_signs) do - assert.equal(#bp.signs, 1) - - for _, sign in ipairs(bp.signs) do - assert.equal(sign.name, "DapBreakpoint") - end - end - - done() - end) - - test_utils.on_session_end(function() - done() - end) + test_utils.add_listener("before", "event_terminated", function () + termination_happened = true + try_exit() + end) dap.run(launch_config) end, 1) diff --git a/tests/lua/__dap_js_test_util.lua b/tests/lua/__dap_js_test_util.lua index 6b7dc08..57d4a1d 100644 --- a/tests/lua/__dap_js_test_util.lua +++ b/tests/lua/__dap_js_test_util.lua @@ -4,7 +4,6 @@ local dap = require("dap") local dap_bps = require("dap.breakpoints") local dapjs = require("dap-vscode-js") local dapjs_utils = require("dap-vscode-js.utils") -local js_session = require("dap-vscode-js.session") local logger = require("dap-vscode-js.log") local dap_ns = "dap_breakpoints" @@ -14,9 +13,6 @@ local util_id = "___dap_js_test_utils" local enable_logging = vim.env["DAP_JS_ENABLE_LOGGING"] == "true" --- local current_session -local current_sessions = {} - function M.clear_listeners() for _, time in ipairs({ "before", "after" }) do for key, _ in pairs(dap.listeners[time]) do @@ -37,7 +33,6 @@ function M.reset() M.clear_listeners() -- M.clear_config() M.clear_breakpoints() - current_sessions = {} end function M.set_breakpoint(lnum, bufnr, opts) @@ -46,28 +41,10 @@ end function M.add_listener(time, event_or_command, callback) dap.listeners[time][event_or_command][M.id] = function(session, ...) - if not current_sessions[session] then - return - end - callback(session, ...) end end -function M.on_session_end(callback) - M.add_listener("after", "event_terminated", function(session, ...) - current_sessions[session] = nil - - local active_sessions = vim.tbl_filter(function(el) - return js_session.is_session_registered(el) - end, vim.tbl_keys(current_sessions)) - - if #active_sessions == 0 then - callback(session, ...) - end - end) -end - function M.setup_dapjs(config) local info = debug.getinfo(2, "Sl") local lineinfo = info.short_src @@ -75,15 +52,11 @@ function M.setup_dapjs(config) logger.msg_prefix = string.format("(%s) ", lineinfo) dapjs.setup(vim.tbl_extend("force", { - debugger_path = DEBUGGER_PATH, + debugger_executable = DEBUGGER_PATH, log_file_path = LOG_PATH, log_file_level = (enable_logging and vim.log.levels.TRACE) or false, log_console_level = false, }, config or {})) - - dap.listeners.before["event_initialized"][util_id] = function(session) - current_sessions[session] = true - end end function M.test_file(file) diff --git a/tests/unit/utils_spec.lua b/tests/unit/utils_spec.lua index 921851a..be83c59 100644 --- a/tests/unit/utils_spec.lua +++ b/tests/unit/utils_spec.lua @@ -5,26 +5,6 @@ local config = require("dap-vscode-js.config") local test_utils = require("__dap_js_test_util") describe("dap-vscode-js.utils", function() - describe(".start_debugger", function() - async.it( - "can start successfully", - wrap(function(done) - utils.start_debugger({ - debugger_path = DEBUGGER_PATH, - node_path = "node", - }, function() - done() - end, function(code, signal) - assert.falsy(code) - end, function(err) - assert.falsy(err) - end, function(err) - error(err) - end) - end, 1) - ) - end) - describe(".get_spawn_cmd", function() it("will use debug_cmd when provided", function() test_utils.setup_dapjs({