Skip to content

Commit 54f891a

Browse files
committed
Add config providers; always load .vscode/launch.json
- Introduces a dap.providers.configs table which plugins can extend to add additional sources for dynamic configuration generation - Implements the two built-in configuration sources as providers - `.vscode/launch.json` files are now loaded automatically by one of these providers. The entries from the file always display even if the type doesn't match the current filetype. This gets rid of the `type_to_filetypes` mapping requirements.
1 parent 5e2895a commit 54f891a

File tree

3 files changed

+155
-33
lines changed

3 files changed

+155
-33
lines changed

doc/dap.txt

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1178,10 +1178,18 @@ You could also customize the buffer and window creation using a low-level builde
11781178
.build()
11791179
<
11801180

1181-
11821181
==============================================================================
11831182
EXTENSIONS API *dap-extensions*
11841183

1184+
nvim-dap provides extension points for plugins:
1185+
1186+
- |dap-listeners|
1187+
- |dap-providers|
1188+
1189+
1190+
==============================================================================
1191+
LISTENERS EXTENSIONS API *dap-listeners*
1192+
11851193

11861194
nvim-dap supports subscribing and listening to all responses or events that a
11871195
debug adapter might send to nvim-dap.
@@ -1232,6 +1240,59 @@ For events, the listeners are called with two arguments:
12321240
1. The session
12331241
2. The event payload
12341242

1243+
1244+
==============================================================================
1245+
PROVIDERS EXTENSIONS API *dap-providers*
1246+
1247+
==============================================================================
1248+
CONFIG PROVIDERS EXTENSIONS API *dap-providers-configs*
1249+
1250+
If a user starts a debug session via |dap.continue()|, nvim-dap looks for
1251+
a suitable configuration (|dap-configuration|) to use.
1252+
1253+
To do so it uses so called configuration providers registered in a
1254+
`dap.providers.configs` table.
1255+
1256+
Plugins can extend this table with additional config providers.
1257+
1258+
There are two providers built-in:
1259+
1260+
- `dap.global` - looks for configuration entries in
1261+
`dap.configurations.<filetype>`
1262+
1263+
- `dap.launch.json` - looks for configuration entries in a
1264+
`.vscode/launch.json` file.
1265+
1266+
1267+
The key for the table is a `plugin-id`. Plugins should use their plugin name.
1268+
Do _not_ use the `dap.` namespace. It is reserved for nvim-dap itself.
1269+
1270+
The value for the table is a function that takes a buffer number as parameter
1271+
and must return |dap-configuration| entries as list.
1272+
1273+
An example:
1274+
1275+
>lua
1276+
local dap = require("dap")
1277+
dap.providers.configs["mydummy_provider"] = function(bufnr)
1278+
return {
1279+
{
1280+
name = "This config always shows up",
1281+
type = "gdb",
1282+
request = "launch",
1283+
program = "/usr/bin/zig",
1284+
args = {"run", "${file}"},
1285+
cwd = "${workspaceFolder}",
1286+
},
1287+
}
1288+
end
1289+
<
1290+
1291+
To support async operations, the config providers functions are called
1292+
within a coroutine and they can also return a `thread` which must be suspended
1293+
and which will be resumed at most once and must then yield the configurations
1294+
as list.
1295+
12351296
==============================================================================
12361297
UTILS API *dap-utils*
12371298

lua/dap.lua

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ M.adapters = {}
238238
---@field request "launch"|"attach"
239239
---@field name string
240240

241+
241242
--- Configurations per adapter. See `:help dap-configuration` for more help.
242243
---
243244
--- An example:
@@ -255,6 +256,40 @@ M.adapters = {}
255256
---@type table<string, Configuration[]>
256257
M.configurations = {}
257258

259+
local providers_mt = {
260+
__newindex = function()
261+
error("Cannot add item to dap.providers")
262+
end,
263+
}
264+
M.providers = setmetatable({
265+
266+
---@type table<string, fun(bufnr: integer): thread|Configuration[]>
267+
configs = {
268+
},
269+
}, providers_mt)
270+
271+
272+
M.providers.configs["dap.global"] = function(bufnr)
273+
local filetype = vim.bo[bufnr].filetype
274+
local configurations = M.configurations[filetype] or {}
275+
assert(
276+
islist(configurations),
277+
string.format(
278+
'`dap.configurations.%s` must be a list of configurations, got %s',
279+
filetype,
280+
vim.inspect(configurations)
281+
)
282+
)
283+
return configurations
284+
end
285+
286+
287+
M.providers.configs["dap.launch.json"] = function()
288+
local ok, configs = pcall(require("dap.ext.vscode").getconfigs)
289+
return ok and configs or {}
290+
end
291+
292+
258293
local signs = {
259294
DapBreakpoint = { text = "B", texthl = "", linehl = "", numhl = "" },
260295
DapBreakpointCondition = { text = "C", texthl = "", linehl = "", numhl = "" },
@@ -421,35 +456,55 @@ end
421456

422457

423458
local function select_config_and_run(opts)
424-
local filetype = vim.bo.filetype
425-
local configurations = M.configurations[filetype] or {}
426-
assert(
427-
islist(configurations),
428-
string.format(
429-
'`dap.configurations.%s` must be a list of configurations, got %s',
430-
filetype,
431-
vim.inspect(configurations)
432-
)
433-
)
434-
if #configurations == 0 then
435-
local msg = 'No configuration found for `%s`. You need to add configs to `dap.configurations.%s` (See `:h dap-configuration`)'
436-
notify(string.format(msg, filetype, filetype), vim.log.levels.INFO)
437-
return
438-
end
439-
opts = opts or {}
440-
opts.filetype = opts.filetype or filetype
441-
lazy.ui.pick_if_many(
442-
configurations,
443-
"Configuration: ",
444-
function(i) return i.name end,
445-
function(configuration)
446-
if configuration then
447-
M.run(configuration, opts)
459+
local bufnr = api.nvim_get_current_buf()
460+
local filetype = vim.bo[bufnr].filetype
461+
lazy.async.run(function()
462+
local all_configs = {}
463+
local co = coroutine.running()
464+
local providers = vim.tbl_keys(M.providers.configs)
465+
table.sort(providers)
466+
for _, provider in ipairs(providers) do
467+
local config_provider = M.providers.configs[provider]
468+
local configs = config_provider(bufnr)
469+
if type(configs) == "thread" then
470+
assert(
471+
coroutine.status(configs) == "suspended",
472+
"If configs provider returns a thread it must be suspended"
473+
)
474+
vim.schedule(function()
475+
coroutine.resume(configs, co)
476+
end)
477+
configs = coroutine.yield()
478+
end
479+
if islist(configs) then
480+
vim.list_extend(all_configs, configs)
448481
else
449-
notify('No configuration selected', vim.log.levels.INFO)
482+
local msg = "Configuration provider %s must return a list of configurations. Got: %s"
483+
notify(msg:format(provider, vim.inspect(configs)), vim.log.levels.WARN)
450484
end
451485
end
452-
)
486+
487+
if #all_configs == 0 then
488+
local msg = 'No configuration found for `%s`. You need to add configs to `dap.configurations.%s` (See `:h dap-configuration`)'
489+
notify(string.format(msg, filetype, filetype), vim.log.levels.INFO)
490+
return
491+
end
492+
493+
opts = opts or {}
494+
opts.filetype = opts.filetype or filetype
495+
lazy.ui.pick_if_many(
496+
all_configs,
497+
"Configuration: ",
498+
function(i) return i.name end,
499+
function(configuration)
500+
if configuration then
501+
M.run(configuration, opts)
502+
else
503+
notify('No configuration selected', vim.log.levels.INFO)
504+
end
505+
end
506+
)
507+
end)
453508
end
454509

455510

lua/dap/ext/vscode.lua

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,12 @@ function M._load_json(jsonstr)
174174
return configs
175175
end
176176

177-
178-
--- Extends dap.configurations with entries read from .vscode/launch.json
179-
function M.load_launchjs(path, type_to_filetypes)
180-
type_to_filetypes = vim.tbl_extend('keep', type_to_filetypes or {}, M.type_to_filetypes)
177+
---@param path string?
178+
---@return Configuration[]
179+
function M.getconfigs(path)
181180
local resolved_path = path or (vim.fn.getcwd() .. '/.vscode/launch.json')
182181
if not vim.loop.fs_stat(resolved_path) then
183-
return
182+
return {}
184183
end
185184
local lines = {}
186185
for line in io.lines(resolved_path) do
@@ -189,7 +188,14 @@ function M.load_launchjs(path, type_to_filetypes)
189188
end
190189
end
191190
local contents = table.concat(lines, '\n')
192-
local configurations = M._load_json(contents)
191+
return M._load_json(contents)
192+
end
193+
194+
195+
--- Extends dap.configurations with entries read from .vscode/launch.json
196+
function M.load_launchjs(path, type_to_filetypes)
197+
type_to_filetypes = vim.tbl_extend('keep', type_to_filetypes or {}, M.type_to_filetypes)
198+
local configurations = M.getconfigs(path)
193199

194200
assert(configurations, "launch.json must have a 'configurations' key")
195201
for _, config in ipairs(configurations) do

0 commit comments

Comments
 (0)