nvim-dap-virtual-text
nvim-dap-virtual-text copied to clipboard
virtual text does not hide while run lua require'dap'.stop()
text does not hide call lua require'dap'.stop() in debugging
@mfussenegger at the moment virtual text get's hidden on
event_terminated
event_exited
event_continued
exited/terminated don't get triggered with all debug adapters. Any recommendations how I could reliably clear when the debug session stops?
I think the problem here is that stop() is only closing the session, without telling the debug adapters about it.
Users have to call dap.disconnect(); dap.stop() to properly end the session. I'll try to improve the docs in nvim-dap to clarify the behavior.
See https://github.com/mfussenegger/nvim-dap/pull/251
dap.disconnect() or dap.close() no trigger event_terminated or event_exited
That would also mean that the debug adapter and possibly the application you debug keep running. With which debug adapter does that happen, and how does your configuration look like?
It would be possible to generate some kind of client_closed_session event within nvim-dap, but it is possible that this would only hide problems and not fix the root cause.
local dap = require('dap')
dap.adapters.go= function(cb)
local stdout = vim.loop.new_pipe(false)
local handle
local pid_or_err
local port = 38697
local root_dir = require 'lspconfig/configs'.gopls.get_root_dir(vim.fn.expand '%:p:h')
local opts = {
stdio = {nil, stdout},
args = {"dap", "-l", "127.0.0.1:" .. port},
cwd = root_dir,
detached = true
}
handle, pid_or_err = vim.loop.spawn("dlv", opts, function(code)
stdout:close()
handle:close()
if code ~= 0 then
print('dlv exited with code', code)
end
end)
assert(handle, 'Error running dlv: ' .. tostring(pid_or_err))
stdout:read_start(function(err, chunk)
assert(not err, err)
if chunk then
vim.schedule(function()
--- You could adapt this and send `chunk` to somewhere else
require('dap.repl').append(chunk)
end)
end
end)
-- Wait for delve to start
vim.defer_fn(
function()
cb({type = "server", host = "127.0.0.1", port = port})
end,
100)
end
dap.configurations.go = {
{
type = "go",
name = "Debug",
request = "launch",
-- program = "${workspaceFolder}",
program=function()
return require 'lspconfig/configs'.gopls.get_root_dir(vim.fn.expand '%:p:h')
end,
showLog = "true"
},
{
type = "go",
name = "Debug file",
request = "launch",
-- program = "${file}",
program = function()
return vim.fn.fnamemodify(vim.fn.bufname(), ':p')
end
},
{
type = "go",
name = "Debug test", -- configuration for debugging test files
request = "launch",
mode = "test",
args = function()
local lines = vim.fn.readfile(vim.fn.fnamemodify(vim.fn.bufname(), ':p'))
local fns = {}
for _, l in pairs(lines) do
local name = string.match(l,'^%s*func.*Test(.+)%(')
if name then
table.insert(fns,name)
end
end
if #fns < 1 then
error("no test case found")
elseif #fns == 1 then
return {
"--test.run",
"Test"..fns[1]
}
else
local option_strings = {"\n\nselect test case: "}
for i, case in ipairs(fns) do
table.insert(option_strings, string.format("%d: %s", i, case))
end
local choice = vim.fn.inputlist(option_strings)
if choice < 1 or choice > #fns then
error('out of index')
end
return {
"--test.run",
"Test"..fns[choice]
}
end
end,
--program = "${file}"
program = function()
return vim.fn.fnamemodify(vim.fn.bufname(), ':p:h')
end
},
}
local getExeFile=function()
local cmd = "cargo metadata --no-deps --format-version 1"
local cargo_metadata = vim.fn.system(cmd)
local cargo_workspace_dir = nil
if vim.v.shell_error == 0 then
cargo_workspace_dir = vim.fn.json_decode(cargo_metadata)["workspace_root"]
end
local fp = cargo_workspace_dir .. '/target/debug/'
local files = vim.fn.readdir(fp);
local ss = {}
for _, v in pairs(files) do
local p = fp ..v
if vim.fn.executable(p) == 1 then
table.insert(ss, v)
end
end
if #ss == 0 then
return
end
if #ss == 1 then
return fp .. ss[1]
end
local option_strings = {"select crate: "}
for i, runnable in ipairs(ss) do
table.insert(option_strings, string.format("%d: %s", i, runnable))
end
local choice = vim.fn.inputlist(option_strings)
if choice < 1 or choice > #ss then
return
end
return fp .. ss[choice]
end
dap.adapters.lldb = {
type = 'executable',
command = '/usr/bin/lldb-vscode-11', -- adjust as needed
name = "lldb"
}
dap.configurations.rust = {
{
name = "Launch",
type = "lldb",
request = "launch",
program = function()
vim.fn.system('cargo build')
-- return getExeFile(vim.fn.getcwd() .. '/target/debug/')
return getExeFile()
end,
cwd = '${workspaceFolder}',
stopOnEntry = false,
args = {},
runInTerminal = false,
env = function()
local variables = {}
for k, v in pairs(vim.fn.environ()) do
table.insert(variables, string.format("%s=%s", k, v))
end
return variables
end,
},
}
--dap.listeners.after['event_terminated']['my-plugin'] = function(session, body)
dap.listeners.after['event_exited']['my-plugin'] = function(session, body)
dap.repl.close()
end
dap.listeners.after['output']['my-plugin'] = function(session, body)
dap.repl.open()
end
require('dap.ext.vscode').load_launchjs()
Maybe I should also react to the disconnect/terminate request to clear the text
Users have to call dap.disconnect(); dap.stop() to properly end the session. I'll try to improve the docs in nvim-dap to clarify the behavior.
@mfussenegger I tried it, but the text still exists.
@theHamsta any chance that you could expose command virtual_text.toggle or disable ?
I wanted to refactor the plugin to allow more options and initialization with a proper setup call. When I started the plugin I wanted to have minimal setup and I couldn't think other option than activated and deactivated. I can also add a toggle command
I guess toggling should be something like
vim.g.dap_virtual_text = not vim.g.dap_virtual_text
dap.listeners.after.variables["nvim-dap-virtual-text"](require'dap'.session())
I'm seeing the same issue using cpptools and virtual text remaining after calling disconnect then stop.
There is now :DapVirualTextForceRefresh as a mitigation, for a proper fix I would need to get notified when one of the termination methods get call in nvim-dap. Right now I'm just reacting to events in the DAP protocol. When a debug adapter fails to terminate properly won't receive a notification that it is not there anymore.
@JoseConseco there are now :DapVirualTextEnable, :DapVirualTextDisable, :DapVirualTextToggle
There is now also a terminate function in nvim-dap which helps terminate some debug adapters. See https://github.com/mfussenegger/nvim-dap/pull/345
@theHamsta thanks. It works great now. I can combine now: dap.close()| DapVirtualTextDisable
@JoseConseco you probably want a dap.close() | DapVirtualTextForceRefresh. Or you else you'd need to enable the virtual text again when you start your next debug session DapVirtualTextEnable
DapVirtualTextForceRefresh
I have E5108: Error executing lua [string ":lua"]:1: attempt to call field 'refresh' (a nil value).
Sorry, I wanted refresh originally to be private. Should be exported now.
Sorry, I wanted refresh originally to be private. Should be exported now.
Now I have another error: E5108: Error executing lua ...tart/nvim-dap-virtual-text/lua/nvim-dap-virtual-text.lua:46: attempt to index local 'session' (a nil value)
I'm really stupid today :sweat: Refresh should clear virtual text now only when there's no session (https://github.com/theHamsta/nvim-dap-virtual-text/commit/bcea8a48eaf9228025dfdcfeec108920db727a58)
Now works, thank you!
DapVirtualTextForceRefresh works indeed better than DapVirualTextDisable. Btw I think this issue can be closed.
You're sure? I feel this should happen automatically and without you needing to invoke a command.
I think upstream has done a lot to trigger the event even if the debug adapter hang up .
Even :DapVirualTextDisable doesn't clear the virt text for me: no new text is produced, but the old text does not hide.
I've figured it out. The virt text hides when the debugee executes to the end and exits, and when I call dap.terminate. But if I call dap.close, then the virt text persists, and nothing I do afterwards can ever get them hidden (DapVirtualTextDisable, dap.terminate and DapVirtualTextRefresh, etc.).
So I can just use dap.terminate, though I still wonder why dap.close() | DapVirtualTextForceRefresh doesn't work for me.
I still wonder why dap.close() | DapVirtualTextForceRefresh doesn't work for me.
dap.close() closes the session object on the client, it doesn't go through the proper termination routine specified by the debug adapter protocol.
See :h dap.close():
Closes the current session.
This does NOT terminate the debug adapter or debugee. You usually want to use either |dap.terminate()| or |dap.disconnect()| instead.
I've figured it out. The virt text hides when the debugee executes to the end and exits, and when I call
dap.terminate. But if I calldap.close, then the virt text persists, and nothing I do afterwards can ever get them hidden (DapVirtualTextDisable,dap.terminateandDapVirtualTextRefresh, etc.).So I can just use
dap.terminate, though I still wonder whydap.close() | DapVirtualTextForceRefreshdoesn't work for me.
Can confirm. Looks like closing the session is not the correct approach, for me dap.terminate();dap.disconnect() seems to do the trick.