Skip to content

Commit 02de696

Browse files
committed
feat(dap): resolve debug adapters discovery-first
Resolve dap_deps through the shared resolver; adapters self-validate from $PATH (delve via dlv, codelldb, lldb, python via debugpy) with Mason loaded lazily off the :Dap tick. Client configs follow the validate-FIRST / raise-LAST contract so remote-attach adapters stay registered. debugpy resolves via a bounded probe cascade with a slow-TTL negative cache (no permanent latch). Shared attach_endpoint validates remote host/port shape at config time (config.port and opts.default_port to the same 1-65535 contract).
1 parent 1269df3 commit 02de696

6 files changed

Lines changed: 618 additions & 85 deletions

File tree

lua/modules/configs/tool/dap/clients/codelldb.lua

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ return function()
44
local utils = require("modules.utils.dap")
55
local is_windows = require("core.global").is_windows
66

7+
-- Self-validate at config time (validate FIRST — contract: tool/dap/init.lua
8+
-- resolver spec): launch AND attach both spawn the local binary, so
9+
-- unlike delve/python nothing is worth registering without it — error if missing.
10+
local command =
11+
require("modules.utils.tools").exepath_or_error("codelldb", "install it via Mason or your package manager")
712
dap.adapters.codelldb = {
813
type = "server",
914
port = "${port}",
1015
executable = {
11-
command = vim.fn.exepath("codelldb"), -- Find codelldb on $PATH
16+
command = command,
1217
args = { "--port", "${port}" },
13-
detached = is_windows and false or true,
18+
detached = not is_windows,
1419
},
1520
}
1621
dap.configurations.c = {
Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,53 @@
1-
-- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#go
1+
-- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#go-using-delve-directly
22
-- https://github.com/golang/vscode-go/blob/master/docs/debugging.md
33
return function()
44
local dap = require("dap")
55
local utils = require("modules.utils.dap")
6+
local is_windows = require("core.global").is_windows
67

7-
if not require("mason-registry").is_installed("go-debug-adapter") then
8-
vim.notify(
9-
"Automatically installing `go-debug-adapter` for go debugging",
10-
vim.log.levels.INFO,
11-
{ title = "nvim-dap" }
12-
)
8+
-- Use delve's built-in DAP server (`dlv dap`) directly, no separate go-debug-adapter.
9+
-- `dlv` resolves lazily on the spawn path: remote attach needs no local binary, so
10+
-- the adapter registers even when it's absent.
1311

14-
local go_dbg = require("mason-registry").get_package("go-debug-adapter")
15-
go_dbg:install():once(
16-
"closed",
17-
vim.schedule_wrap(function()
18-
if go_dbg:is_installed() then
19-
vim.notify("Successfully installed `go-debug-adapter`", vim.log.levels.INFO, { title = "nvim-dap" })
20-
end
21-
end)
22-
)
12+
---A function adapter (over a static table) so a remote `attach` config can
13+
---connect to an already-running `dlv dap` instead of spawning a local one.
14+
---@param callback fun(adapter: table)
15+
---@param config table
16+
local function delve_adapter(callback, config)
17+
if config.request == "attach" and config.mode == "remote" then
18+
-- Shared shape validation (utils.attach_endpoint): malformed values
19+
-- error at config time instead of surfacing as an opaque connection
20+
-- failure. Default when unset: 38697 is the conventional example
21+
-- port from the nvim-dap wiki, not a delve default.
22+
local host, port = utils.attach_endpoint(config, { label = "delve remote attach", default_port = 38697 })
23+
callback({
24+
type = "server",
25+
host = host,
26+
port = port,
27+
})
28+
else
29+
-- Resolve lazily so a dlv installed after config load is picked up without reconfiguring.
30+
local command = require("modules.utils.tools").exepath_or_error(
31+
"dlv",
32+
"install delve via Mason or your package manager"
33+
)
34+
callback({
35+
type = "server",
36+
port = "${port}",
37+
executable = {
38+
command = command,
39+
args = { "dap", "-l", "127.0.0.1:${port}" },
40+
detached = not is_windows,
41+
},
42+
})
43+
end
2344
end
2445

25-
dap.adapters.go = {
26-
type = "executable",
27-
command = "node",
28-
args = {
29-
vim.env.MASON .. "/packages/go-debug-adapter" .. "/extension/dist/debugAdapter.js",
30-
},
31-
}
46+
-- Register under both names: the configurations below use `type = "go"`, while
47+
-- mason-nvim-dap / other integrations reference the adapter as `delve`.
48+
dap.adapters.go = delve_adapter
49+
dap.adapters.delve = delve_adapter
50+
3251
dap.configurations.go = {
3352
{
3453
type = "go",
@@ -37,7 +56,6 @@ return function()
3756
cwd = "${workspaceFolder}",
3857
program = utils.input_file_path(),
3958
console = "integratedTerminal",
40-
dlvToolPath = vim.fn.exepath("dlv"),
4159
showLog = true,
4260
showRegisters = true,
4361
stopOnEntry = false,
@@ -50,7 +68,6 @@ return function()
5068
program = utils.input_file_path(),
5169
args = utils.input_args(),
5270
console = "integratedTerminal",
53-
dlvToolPath = vim.fn.exepath("dlv"),
5471
showLog = true,
5572
showRegisters = true,
5673
stopOnEntry = false,
@@ -63,7 +80,6 @@ return function()
6380
program = utils.input_exec_path(),
6481
args = utils.input_args(),
6582
console = "integratedTerminal",
66-
dlvToolPath = vim.fn.exepath("dlv"),
6783
mode = "exec",
6884
showLog = true,
6985
showRegisters = true,
@@ -76,7 +92,6 @@ return function()
7692
cwd = "${workspaceFolder}",
7793
program = utils.input_file_path(),
7894
console = "integratedTerminal",
79-
dlvToolPath = vim.fn.exepath("dlv"),
8095
mode = "test",
8196
showLog = true,
8297
showRegisters = true,
@@ -89,11 +104,17 @@ return function()
89104
cwd = "${workspaceFolder}",
90105
program = "./${relativeFileDirname}",
91106
console = "integratedTerminal",
92-
dlvToolPath = vim.fn.exepath("dlv"),
93107
mode = "test",
94108
showLog = true,
95109
showRegisters = true,
96110
stopOnEntry = false,
97111
},
98112
}
113+
114+
-- Availability check LAST (contract: tool/dap/init.lua resolver spec); the raise
115+
-- is the provisioning signal — the attach-capable adapters above stay registered.
116+
require("modules.utils.tools").exepath_or_error(
117+
"dlv",
118+
"local `dlv dap` launch is unavailable until installed (remote attach still works)"
119+
)
99120
end

lua/modules/configs/tool/dap/clients/lldb.lua

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,19 @@ return function()
33
local dap = require("dap")
44
local utils = require("modules.utils.dap")
55

6+
-- Opt-in preset (not in default dap_deps; codelldb covers C-family). Self-validate
7+
-- so the resolver surfaces `lldb` (validate FIRST — contract:
8+
-- tool/dap/init.lua resolver spec). LLVM 18 renamed `lldb-vscode` ->
9+
-- `lldb-dap`; probe the new name first, keep the old for distros still
10+
-- shipping it.
11+
local command = require("modules.utils.tools").exepath_or_error(
12+
{ "lldb-dap", "lldb-vscode" },
13+
"install it via your package manager (ships with LLVM/lldb)"
14+
)
15+
616
dap.adapters.lldb = {
717
type = "executable",
8-
command = vim.fn.exepath("lldb-vscode"), -- Find lldb-vscode on $PATH
18+
command = command,
919
}
1020
dap.configurations.c = {
1121
{

0 commit comments

Comments
 (0)