nvim 调试环境

好的调试环境事半功倍!利用 nvim-dap 打造一个 nvim 的调试环境。

调试环境的选择

命令行调试在交互场景下没办法发挥优势出来,特别是断点设置和调试程序的启停。由于编辑器已切回 用 nvim,故配一个好用的调试环境很重要。目前存在 2 个选择 vimspector 或 nvim-dap,都没用过, 对比了一番,决定还是用 nvim-dap 吧。主要出发点就是它单一,多折腾一番好。摘录总体结构如下:

1
2
3
4
5
6
7
DAP-Client ----- Debug Adapter ------- Debugger ------ Debugee
(nvim-dap) | (per language) | (per language) (your app)
| |
| Implementation specific communication
| Debug adapter and debugger could be the same process
|
Communication via the Debug Adapter Protocol

安装 nvim-dap

在参考文章中一上来就几个插件一起,看的有点蒙。其实这些并不是必要的,而是锦上添花的,一起上 有点摸不着头脑,还是先搞完单单一个 nvim-dap 的情况。

按照官方介绍,用 vim-plug 安装完后,需要根据目标调试语言进行配置后才能用。第一个还是用比较 熟悉的 C 语言来配置。由于目前 gdb 不在苹果 M4 芯片支持,只能选择用调试器 lldb。结果还是出 问题了,lldb 貌似不支持 dap,要用 lldb-vscode(现名 lldb-dap, 更接地气了)。

配置文件是需要新建一个的,在编辑器启动时能加载就可以了。故在init.vim中添加 require("mydap"), 并在 $XDG_CONFIG_HOME/nvim/lua 目录下创建一个 mydap.lua 文件, 内容 如下(现在只是想尽快看看效果,至于如何配好后面再处理吧):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
local dap = require("dap")

dap.adapters.lldb = {
type = 'executable',
command = 'lldb-dap', -- Need support dap
name = 'lldb'
}

dap.configurations.cpp = {
{
name = 'Launch',
type = 'lldb',
request = 'launch',
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
cwd = '${workspaceFolder}',
stopOnEntry = false,
args = {},
},
}

dap.configurations.c = dap.configurations.cpp

-- Set keymap
local dap = require("dap")
vim.keymap.set('n', '<F5>', function() dap.continue() end)
vim.keymap.set('n', '<leader>dapb', function() dap.toggle_breakpoint() end)

保存好配置后,打开c文件,就可以用<leader>dapb来设置断点,通过<f5>来启动调试软件。

REPL

nvim-dap默认不弹出调试窗口出来。需在启动脚本里加入如下事件响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- Set event
local dap = require("dap")
dap.listeners.before.attach.dapui_config = function()
dap.repl.open()
end
dap.listeners.before.launch.dapui_config = function()
dap.repl.open()
end
dap.listeners.before.event_terminated.dapui_config = function()
dap.repl.close()
end
dap.listeners.before.event_exited.dapui_config = function()
dap.repl.close()
end

这样一启动调试,就可以新建一个命令交互的Buffer。就可以达到跟终端调试差不多的效果。但用 ctrl+c不能暂停,可以用API dap.pause()

记录一个问题

报错如下:

1
2
3
Debug adapter didn't respond. Either the adapter is slow (then wait and ignore this) or
there is a problem with your adapter or `lldb` configuration. Check the log for error
(:help dap.set_log_level)

通过stdpath("cache") 找到log所在的路径,发现如下错误:

1
2
3
4
5
[ ERROR ] 2024-07-28T22:43:52Z+0800 ] ...t/.local/share/nvim/plugged/nvim-dap/lua/dap/session.lua:1466 ]	"stderr"	{
command = "lldb",
name = "lldb",
type = "executable"
} "error: 'Content-Length' is not a valid command.\n"

得知Mac xcode一起安装的lldb不支持dap, 需要安装lldb-dap.

后记

当前配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
local dap = require("dap")

dap.adapters.lldb = {
type = 'executable',
command = 'lldb-dap', -- Need support dap
name = 'lldb'
}

dap.configurations.cpp = {
{
name = 'Launch',
type = 'lldb',
request = 'launch',
program = function()
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
end,
cwd = '${workspaceFolder}',
stopOnEntry = false,
args = {},
},
}

dap.configurations.c = dap.configurations.cpp

-- keymap
vim.keymap.set('n', '<leader>dapc', function() dap.continue() end)
vim.keymap.set('n', '<F4>', function() dap.step_over() end)
vim.keymap.set('n', '<F16>', function() dap.step_into() end)
vim.keymap.set('n', '<leader>dapso', function() dap.step_out() end)
vim.keymap.set('n', '<F5>', function() dap.toggle_breakpoint() end)
-- vim.keymap.set('n', '<leader>dapsb', function() dap.set_breakpoint() end)
-- vim.keymap.set('n', '<leader>dapSb', function() dap.set_breakpoint(nil, nil, vim.fn.input('Log point message: ')) end)
vim.keymap.set('n', '<leader>dapbl', function() vim.cmd('copen') vim.cmd('exe \"normal \\<c-w>p\"') dap.list_breakpoints() end)
vim.keymap.set('n', '<leader>dapr', function() dap.repl.open() end)
vim.keymap.set('n', '<leader>daprl', function() dap.run_last() end)
vim.keymap.set({'n', 'v'}, '<leader>dapho', function() require('dap.ui.widgets').hover() end)
vim.keymap.set({'n', 'v'}, '<leader>dappv', function() require('dap.ui.widgets').preview() end)
vim.keymap.set('n', '<leader>dapcff', function()
local widgets = require('dap.ui.widgets')
widgets.centered_float(widgets.frames)
end)
vim.keymap.set('n', '<leader>dapcfs', function()
local widgets = require('dap.ui.widgets')
widgets.centered_float(widgets.scopes)
end)

-- event listener
dap.listeners.before.attach.dapui_config = function() dap.repl.open() end
dap.listeners.before.launch.dapui_config = function() dap.repl.open() end
dap.listeners.before.event_terminated.dapui_config = function() dap.repl.close() end
dap.listeners.before.event_exited.dapui_config = function() dap.repl.close() end

搭配其他插件一起, 慢慢来。参考链接[1] [2] [3]


  1. https://davelage.com/posts/nvim-dap-getting-started/ ↩︎

  2. https://github.com/theHamsta/nvim-dap-virtual-text ↩︎

  3. https://github.com/rcarriga/nvim-dap-ui ↩︎