上回说到,Neovim距离VSCode这类代码编辑器还差一个最后的工具——调试器 (Debug Adapter Protocol, dap)。这回我们在Neovim中安装调试器,所需的工具就是之前所用到的 mason,它除了管理 lsp 还能管理 dap。不像 lsp 的安装可以直接写进插件配置文件中,dap 直接在 mason 的管理界面中安装就行了。
在Neovim中使用调试器需要一项插件——nvim-dap ,其仓库在这里:
nvim-dap 为了自身的轻量化与灵活性仅仅能提供调试功能,不提供调试信息的显示,这里给出了三种方法来显示调试信息:
- 使用内置的REPL界面。
- 使用 widget UI。
- 使用UI插件。
为了配置方便,我们直接使用UI插件就好,这里给出了三种典型UI插件:nvim-dap-ui、nvim-dap-view、nvim-dap-virtual-text。其中 nvim-dap-view 是比较类似VSCode的调试界面,也是我的选择,但如果你喜欢全屏运行Neovim的话,nvim-dap-ui 的界面会更舒服一些。
安装 nvim-dap
我们直接把 dap-view 作为 nvim-dap 的一个依赖来安装,因为它们总是一起使用、一起配置的。
return { "mfussenegger/nvim-dap", dependencies = { "igorlfs/nvim-dap-view", "nvim-neotest/nvim-nio" }, config = function() local dap_breakpoint = { error = { text = "", texthl = "DapBreakpoint", linehl = "DapBreakpoint", numhl = "DapBreakpoint", }, condition = { text = '', texthl = 'DapBreakpoint', linehl = 'DapBreakpoint', numhl = 'DapBreakpoint', }, rejected = { text = "", texthl = "DapBreakpint", linehl = "DapBreakpoint", numhl = "DapBreakpoint", }, logpoint = { text = '', texthl = 'DapLogPoint', linehl = 'DapLogPoint', numhl = 'DapLogPoint', }, stopped = { text = '', texthl = 'DapStopped', linehl = 'DapStopped', numhl = 'DapStopped', }, }
vim.fn.sign_define('DapBreakpoint', dap_breakpoint.error) vim.fn.sign_define('DapBreakpointCondition', dap_breakpoint.condition) vim.fn.sign_define('DapBreakpointRejected', dap_breakpoint.rejected) vim.fn.sign_define('DapLogPoint', dap_breakpoint.logpoint) vim.fn.sign_define('DapStopped', dap_breakpoint.stopped) end}这里我们仅仅是安装了 nvim-dap 插件,并配置了Neovim自身的调试调试图标,而没有对 nvim-dap 进行配置,这是因为我们希望 nvim-dap 只在我们所设定的文件类型中加载,不要让它拖慢Neovim的速度。
配置 nvim-dap
我们在 nvim/lua 中新建一个 dap 目录来存放我们的 dap 配置,并在 nvim/ftplugin/filetype.lua 中引用它。以 debugpy 这个Python调试器为例,由于Python调试器应该在python文件中启用,我们在 nvim/ftplugin/ 中应该新建一个 python.lua 文件,并填入引用 dap 中调试器配置的代码—— require(dap.debugpy)。dap/debugpy.lua 中才是我们对 debugpy 的配置,如下所示。你也能在这里找到其他调试器的配置说明。
local dap = require('dap')local dapui = require('dap-view')dap.adapters.python = function(cb, config) if config.request == "attach" then local port = (config.connect or config).port local host = (config.connect or config).host or "127.0.0.1" cb({ type = "server", port = assert(port, '`connect.port` is required for a python `attach` configuration'), host = host, }) else cb({ type = 'executable', command = 'python path under debugpy venv', args = { '-m', 'debugpy.adapter' }, }) endenddap.configurations.python = { { type = 'python', request = 'launch', name = 'launch file', program = '${file}', pythonPath = function() if os.getenv("VIRTUAL_ENV") then return os.getenv("VIRTUAL_ENV") .. "/Scripts/python" else return 'python' end end, },}
dapui.setup({ winbar = { controls = { enabled = true } }})
dap.listeners.after.event_initialized['dapui_config'] = function() dapui.open({})end
dap.listeners.before.event_terminated['dapui_config'] = function() dapui.close({})end
dap.listeners.before.event_exited['dapui_config'] = function() dapui.close({})end-- 设置DAP keymap。local map = vim.api.nvim_set_keymaplocal opts = { noremap = true, silent = true, buffer = bufnr }map('n', '<F5>', "<cmd>lua require'dap'.continue()<CR>", opts)map('n', '<F8>', "<cmd>lua require'da'.disconnect()<CR>", opts)map('n', '<F10>', "<cmd>lua require'dap'.step_over()<CR>", opts)map('n', '<F12>', "<cmd>lua require'dap'.step_into()<CR>", opts)map('n', '<F9>', "<cmd>lua require'dap'.toggle_breakpoint()<CR>", opts)需要注意的是 python path under debugpy venv,在Windows下一般是 ~/AppData/Local/nvim-data/mason/packages/debugpy/venv/Scripts/pythonw.exe ,在Linux下一般是 ~/.local/share/nvim/mason/packages/debugpy/venv/bin/python。并且在Windows下,上述所有设计 python 路径的地方都应改为 pythonw.exe,这能让Python脚本调试时不弹出命令提示符。
如果你想安装其他调试器,按照官方说明即可。
结语
Neovim的配置记录到这里应该就结束了,从一开始一步步走下来Neovim应该已经成为了一个功能上和VSCode颉颃胜负的代码编辑器,同时也可以作为一个快速的的文本查看器来使用。但是这一切都是在终端中才成立的,由于Windows上 powershell 的加载速度一言难尽,如果将Neovim作为记事本的替代还是有一些难度的。下一篇文章应该会着重介绍Neovide——一个 rust 写的Neovim外壳,它能够提供较为统一的配色和极为丝滑的动画效果。使用Neovide在图形界面打开文件可以显得没有那么割裂,让使用Neovim替代记事本的体验更进一步。