Add on_config hooks to dap.providers

Should solve https://github.com/mfussenegger/nvim-dap/issues/720
This commit is contained in:
Mathias Fussenegger 2024-05-30 18:25:34 +02:00 committed by Mathias Fußenegger
parent d8d53cb80b
commit ea8f6311b1
2 changed files with 75 additions and 32 deletions

View file

@ -1289,9 +1289,40 @@ An example:
<
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.
within a coroutine.
==============================================================================
ON_CONFIG EXTENSIONS API *dap-providers-on_config*
Plugins can pre-process the |dap-configuration| whenever a debug session
starts.
To do so, register a `on_config` hook in the `dap.providers.on_config` table.
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 |dap-configuration| as
parameter and must return a |dap-configuration|.
Before making modifications to the config you should copy it, to ensure you
don't make permanent changes to a configuration stored within
`dap.configrations.<filetype>` via mutations.
Example:
>lua
local dap = require("dap")
dap.providers.on_config["dummy-noop"] = function(config)
return vim.deepcopy(config)
end
<
To support async operations, the on_config functions are called within a
coroutine.
This functionality should only be used by plugins that implement generic
functionality applicable for all or most configurations.
For configuration pre-processing that is specific to a individual adapter you
should instead use the `enrich_config` function available in |dap-adapter|.
==============================================================================
UTILS API *dap-utils*

View file

@ -265,20 +265,24 @@ M.adapters = {}
---@type table<string, dap.Configuration[]>
M.configurations = {}
local providers_mt = {
__newindex = function()
error("Cannot add item to dap.providers")
end,
}
M.providers = setmetatable({
local providers = {
---@type table<string, fun(bufnr: integer): thread|dap.Configuration[]>
configs = {
},
}, providers_mt)
configs = {},
---@type table<string, fun(config: dap.Configuration):dap.Configuration>
on_config = {},
}
do
local providers_mt = {
__newindex = function()
error("Cannot add item to dap.providers")
end,
}
M.providers = setmetatable(providers, providers_mt)
end
M.providers.configs["dap.global"] = function(bufnr)
providers.configs["dap.global"] = function(bufnr)
local filetype = vim.bo[bufnr].filetype
local configurations = M.configurations[filetype] or {}
assert(
@ -292,8 +296,7 @@ M.providers.configs["dap.global"] = function(bufnr)
return configurations
end
M.providers.configs["dap.launch.json"] = function()
providers.configs["dap.launch.json"] = function()
local ok, configs = pcall(require("dap.ext.vscode").getconfigs)
return ok and configs or {}
end
@ -375,6 +378,7 @@ local var_placeholders = {
end,
}
local function expand_config_variables(option)
option = eval_option(option)
if option == M.ABORT then
@ -404,6 +408,7 @@ local function expand_config_variables(option)
return ret
end
---@param lsession dap.Session
local function add_reset_session_hook(lsession)
lsession.on_close['dap.session'] = function(s)
@ -470,10 +475,10 @@ local function select_config_and_run(opts)
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 provider_keys = vim.tbl_keys(providers.configs)
table.sort(provider_keys)
for _, provider in ipairs(provider_keys) do
local config_provider = providers.configs[provider]
local configs = config_provider(bufnr)
if type(configs) == "thread" then
assert(
@ -537,6 +542,23 @@ local function first_stopped_session()
end
---@param config dap.Configuration
---@result dap.Configuration
local function prepare_config(config)
local co, is_main = coroutine.running()
assert(co and not is_main, "prepare_config must be running in coroutine")
local mt = getmetatable(config)
if mt and type(mt.__call) == "function" then
config = config()
assert(config and type(config) == "table", "config metatable __call must return a config table")
end
for _, on_config in pairs(providers.on_config) do
config = on_config(config)
end
return vim.tbl_map(expand_config_variables, config)
end
---@class dap.run.opts
---@field new? boolean force new session
---@field before? fun(config: dap.Configuration): dap.Configuration pre-process config
@ -565,12 +587,7 @@ function M.run(config, opts)
config = opts.before(config)
end
local trigger_run = function()
local mt = getmetatable(config)
if mt and type(mt.__call) == "function" then
config = config()
assert(config and type(config) == "table", "config metatable __call must return a config table")
end
config = vim.tbl_map(expand_config_variables, config)
config = prepare_config(config)
for _, val in pairs(config) do
if val == M.ABORT then
notify("Run aborted", vim.log.levels.INFO)
@ -832,12 +849,7 @@ function M.restart(config, opts)
config = config or lsession.config
if lsession.capabilities.supportsRestartRequest then
lazy.async.run(function()
local mt = getmetatable(config)
if mt and type(mt.__call) == "function" then
config = config()
assert(config and type(config) == "table", "config metatable __call must return a config table")
end
config = vim.tbl_map(expand_config_variables, config)
config = prepare_config(config)
lsession:request('restart', config, function(err0, _)
if err0 then
notify('Error restarting debug adapter: ' .. lazy.utils.fmt_error(err0), vim.log.levels.ERROR)