Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config providers; always load .vscode/launch.json #1237

Merged
merged 1 commit into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion doc/dap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1178,10 +1178,18 @@ You could also customize the buffer and window creation using a low-level builde
.build()
<


==============================================================================
EXTENSIONS API *dap-extensions*

nvim-dap provides extension points for plugins:

- |dap-listeners|
- |dap-providers|


==============================================================================
LISTENERS EXTENSIONS API *dap-listeners*


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


==============================================================================
PROVIDERS EXTENSIONS API *dap-providers*

==============================================================================
CONFIG PROVIDERS EXTENSIONS API *dap-providers-configs*

If a user starts a debug session via |dap.continue()|, nvim-dap looks for
a suitable configuration (|dap-configuration|) to use.

To do so it uses so called configuration providers registered in a
`dap.providers.configs` table.

Plugins can extend this table with additional config providers.

There are two providers built-in:

- `dap.global` - looks for configuration entries in
`dap.configurations.<filetype>`

- `dap.launch.json` - looks for configuration entries in a
`.vscode/launch.json` file.


The key for the table is a `plugin-id`. Plugins should use their plugin name.
Do _not_ use the `dap.` namespace. It is reserved for nvim-dap itself.

The value for the table is a function that takes a buffer number as parameter
and must return |dap-configuration| entries as list.

An example:

>lua
local dap = require("dap")
dap.providers.configs["mydummy_provider"] = function(bufnr)
return {
{
name = "This config always shows up",
type = "gdb",
request = "launch",
program = "/usr/bin/zig",
args = {"run", "${file}"},
cwd = "${workspaceFolder}",
},
}
end
<

To support async operations, the config providers functions are called
within a coroutine and they can also return a `thread` which must be suspended
and which will be resumed at most once and must then yield the configurations
as list.

==============================================================================
UTILS API *dap-utils*

Expand Down
107 changes: 81 additions & 26 deletions lua/dap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ M.adapters = {}
---@field request "launch"|"attach"
---@field name string


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

local providers_mt = {
__newindex = function()
error("Cannot add item to dap.providers")
end,
}
M.providers = setmetatable({

---@type table<string, fun(bufnr: integer): thread|Configuration[]>
configs = {
},
}, providers_mt)


M.providers.configs["dap.global"] = function(bufnr)
local filetype = vim.bo[bufnr].filetype
local configurations = M.configurations[filetype] or {}
assert(
islist(configurations),
string.format(
'`dap.configurations.%s` must be a list of configurations, got %s',
filetype,
vim.inspect(configurations)
)
)
return configurations
end


M.providers.configs["dap.launch.json"] = function()
local ok, configs = pcall(require("dap.ext.vscode").getconfigs)
return ok and configs or {}
end


local signs = {
DapBreakpoint = { text = "B", texthl = "", linehl = "", numhl = "" },
DapBreakpointCondition = { text = "C", texthl = "", linehl = "", numhl = "" },
Expand Down Expand Up @@ -421,35 +456,55 @@ end


local function select_config_and_run(opts)
local filetype = vim.bo.filetype
local configurations = M.configurations[filetype] or {}
assert(
islist(configurations),
string.format(
'`dap.configurations.%s` must be a list of configurations, got %s',
filetype,
vim.inspect(configurations)
)
)
if #configurations == 0 then
local msg = 'No configuration found for `%s`. You need to add configs to `dap.configurations.%s` (See `:h dap-configuration`)'
notify(string.format(msg, filetype, filetype), vim.log.levels.INFO)
return
end
opts = opts or {}
opts.filetype = opts.filetype or filetype
lazy.ui.pick_if_many(
configurations,
"Configuration: ",
function(i) return i.name end,
function(configuration)
if configuration then
M.run(configuration, opts)
local bufnr = api.nvim_get_current_buf()
local filetype = vim.bo[bufnr].filetype
lazy.async.run(function()
local all_configs = {}
local co = coroutine.running()
local providers = vim.tbl_keys(M.providers.configs)
table.sort(providers)
for _, provider in ipairs(providers) do
local config_provider = M.providers.configs[provider]
local configs = config_provider(bufnr)
if type(configs) == "thread" then
assert(
coroutine.status(configs) == "suspended",
"If configs provider returns a thread it must be suspended"
)
vim.schedule(function()
coroutine.resume(configs, co)
end)
configs = coroutine.yield()
end
if islist(configs) then
vim.list_extend(all_configs, configs)
else
notify('No configuration selected', vim.log.levels.INFO)
local msg = "Configuration provider %s must return a list of configurations. Got: %s"
notify(msg:format(provider, vim.inspect(configs)), vim.log.levels.WARN)
end
end
)

if #all_configs == 0 then
local msg = 'No configuration found for `%s`. You need to add configs to `dap.configurations.%s` (See `:h dap-configuration`)'
notify(string.format(msg, filetype, filetype), vim.log.levels.INFO)
return
end

opts = opts or {}
opts.filetype = opts.filetype or filetype
lazy.ui.pick_if_many(
all_configs,
"Configuration: ",
function(i) return i.name end,
function(configuration)
if configuration then
M.run(configuration, opts)
else
notify('No configuration selected', vim.log.levels.INFO)
end
end
)
end)
end


Expand Down
18 changes: 12 additions & 6 deletions lua/dap/ext/vscode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,12 @@ function M._load_json(jsonstr)
return configs
end


--- Extends dap.configurations with entries read from .vscode/launch.json
function M.load_launchjs(path, type_to_filetypes)
type_to_filetypes = vim.tbl_extend('keep', type_to_filetypes or {}, M.type_to_filetypes)
---@param path string?
---@return Configuration[]
function M.getconfigs(path)
local resolved_path = path or (vim.fn.getcwd() .. '/.vscode/launch.json')
if not vim.loop.fs_stat(resolved_path) then
return
return {}
end
local lines = {}
for line in io.lines(resolved_path) do
Expand All @@ -189,7 +188,14 @@ function M.load_launchjs(path, type_to_filetypes)
end
end
local contents = table.concat(lines, '\n')
local configurations = M._load_json(contents)
return M._load_json(contents)
end


--- Extends dap.configurations with entries read from .vscode/launch.json
function M.load_launchjs(path, type_to_filetypes)
type_to_filetypes = vim.tbl_extend('keep', type_to_filetypes or {}, M.type_to_filetypes)
local configurations = M.getconfigs(path)

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