在前面几个章节,我们逐渐为 Vim
配置了语法高亮、代码的跳转和自动补全功能。现在的 Vim
已经可以作为代码编辑器来使用了。但是想将它作为日常发开的主力编辑器来用还需要很长一段路要走,其中一个就是要为它配置代码的一键编译与运行功能。这里我们仍然以 C
和 Python
为例。一个是需要编译运行的一个是直接就可以运行的,这两个语言应该能代表大多数语言的情况。
在之前 vim
入门的一系列教程中我们介绍过 vim
自带 make
命令的运行机制以及如何进行自定义。对于其他语言要实现这个自动编译运行的效果我们核心的操作就是在修改 make
命令。而 C/C++
本身采用 make
命令来进行编译和运行,所以这里 C/C++
我们直接采用 vim 自带的 :make
命令
我们先创建一个 C
的工程。让后使用上一节的生成 hello world 的代码片段生成一个基本的程序。然后提供一个供 :make
命令使用的 Makefile
文件
main.out: main.o
gcc main.o -o main.out
main.o: main.c
gcc -c main.c
clean:
rm -rf *.o *.out
run:
./main.out
然后我们执行 make
用来编译。如果出错了,可以使用 quickfix
相关命令跳转到对应位置。
我们一般的流程是 :make
进行编译,然后使用 :make run
来进行运行。把命令搞清楚了,下面就考虑如何加快这个流程,做到一键编译运行。我们的思路还是绑定快捷键。每种语言虽然定义相同的快捷键但是运行的命令不同,我们需要根据不同的语言类型绑定对应的命令。这个时候最好的办法就是在 filetype
的机制上完成绑定的操作。
我们在 lua/lsp/cpp.lua
中绑定快捷键。
local on_attach = function(client, bufnr)
lsp_set_keymap.set_keymap(bufnr)
-- 编译
vim.api.nvim_buf_set_keymap(bufnr, "n", "" , "make" , {silent = true, noremap = true})
-- 编译运行
vim.api.nvim_buf_set_keymap(bufnr, "n", "" , "make run" , {silent = true, noremap = true})
end
到此我们关于 C/C++
的配置就完成了。可能显的有些简单但是已经初步可用了,小伙伴可以根据自己的需求来进一步修改这个配置。使用这个配置的前提是 C/C++
的工程中有已经定义好的 Makefile
文件
之前我们在讲解命令的模式的提到过可以使用 %
来代表当前 buffer
所对应的文件。所以 python
的配置就比较简单了。因为 Python
不需要编译,所以这里直接绑定
来运行
vim.api.nvim_buf_set_keymap(bufnr, "n", "" , "!python %" , {silent = true, noremap = true})
我们经常看到有人配置 neovim
或者 vim
的时候会介绍到 dap
,那么什么是 dap
呢? dap
的全称是 Debug Adapter Protocol
从名称上看它又是一个协议。它为多种调试器提供了一层统一的适配抽象层。有点类似于前面的介绍的 lsp
。只要在适配层提供接口的实现,那么在客户端,也就是代码编辑器这端可以不做任何修改的集成不同调试
联想到 lsp
的配置,我们配置dap
首先需要的是有一个 dap
的客户端,用来向调试器发送各种命令,例如下断点、显示变量名等等。另外想要能够调试也需要有具体的调试器,用来接收处理这些命令。现在思路有了,我们 这里先以 Python
为例来介绍 dap
的基本配置。
首先是需要一个客户端,用于通过 neovim
下发各种调试命令并实时显示调试信息。
截止到 0.7
版本 NeoVim
并没有在内部集成 dap
客户端的功能,需要我们单独安装相关插件来实现这部分的功能。这里我们使用的客户端是 nvim-dap
插件。
我们先使用 use {'mfussenegger/nvim-dap'}
来安装它。
接着我们来定义一下相关的快捷键,这里我喜欢使用 Visual Studio
的快捷键。各位小伙伴可以自行选择自己喜欢的快捷键。这里我希望在插入模式和选择中也可以使用这些快捷键,由于 vim.api.nvim_set_keymap
函数第一个参数只能有一个模式字符串,如果采用这个函数来定义快捷键,这里同样的代码我要写三次,为了简化代码,这里介绍一个新的函数 vim.keymap.set
。它与 vim.api.nvim_set_keymap
函数支持的参数相同,只是它第一个表示模式的参数可以支持用字典来一次绑定到多个模式中。这样就简化了绑定快捷键的代码量。
vim.keymap.set({"i", "n", "v"}, "" , "lua require'dap'.continue()" , {silent = true, noremap = true, buffer = bufnr})
vim.keymap.set({"i", "n", "v"}, "" , "lua require'dap'.step_over()" , {silent = true, noremap = true, buffer = bufnr})
vim.keymap.set({"i", "n", "v"}, "" , "lua require'dap'.step_into()" , {silent = true, noremap = true, buffer = bufnr})
vim.keymap.set({"i", "n", "v"}, "" , "lua require'dap'.step_over()" , {silent = true, noremap = true, buffer = bufnr})
vim.keymap.set({"i", "n", "v"}, "" , "lua require'dap'.toggle_breakpoint()" , {silent = true, noremap = true, buffer = bufnr})
这个函数是 0.7 以后的版本引进的,如果你的是0.7之前的版本,还是老老实实的多写几遍。另外我们这里绑定了
快捷键,因此之前我们在 Python
中,绑定的直接运行的
键的代码需要注释一下。
我们想要真正实现调试,还需要配合调试器使用。前面说 dap
只是一层协议,需要客户端服务器按照这一层协议来实现相关功能,某些调试器可能自身支持这个协议,而某些可能不支持,这样就需要额外的配置来使调试器也能支持该协议。下面我们以 Python
为例先把整个调试环境搭建起来,先跑起来再说
Lsp
在安装 Server
的时候有 nvim-lsp-installer
这样的插件来专门安装 LSP server
的,那么 dap
有没有类似的插件来安装 dap
调试器相关的服务呢?有!我们使用 mason
来管理 dap
的调试器。
use { "williamboman/mason.nvim" }
当初我推荐过 nvim-lsp-installer
插件作为下载、管理 lsp server
的工具。后来只是知道作者发布了新的管理工具,因为比较新怕出问题就没怎么关注,后来有好多小伙伴在评论区推荐,我仔细看了一下发现它已经支持 dap
服务的管理了。那还是使用它吧 ^_^
。
我们可以使用 Mason
打开一个带界面的 Lsp
和 DAP
的服务管理窗口,可以使用 数字键在上面进行跳转,找到想要的服务之后直接使用 i
来安装
也可以使用 MasonInstall
来安装想要的服务。
我们先在插件配置中删除与 nvim-lsp-installer
相关的配置,包括 packer
中对它的引用和 plugins-config
目录中的配置。
下一步就是配置 dap
的客户端与 服务端的联动,这需要配置 nvim-dap
插件,根据官方的描述我们主要配置两个部分,第一个部分叫做适配器,主要配置我们加载哪个调试器,以及如何加载调试器。这一步需要提供如下的配置框架
local dap = require('dap')
dap.adapters.language = {
}
language
是具体的调试器例如 debugpy
这里的 language
就是 python
了。既然与语言相关,我们自然的想到要用 ftplugin
目录。为了方便管理,这里与 lsp
配置的组织形式类似,我们将所有关于 dap
的配置都放到 lua/dap
目录中。并且按语言名称来命名,例如关于 python
的 dap
配置我们放到 lua/dap/python.lua
中。然后在 ftplugin/python.lua
中加载这个配置文件即。
require("dap/python")
然后在 python.lua
文件中写入以下配置
local dap = require('dap')
dap.adapters.python = {
type = 'executable';
command = '/usr/bin/python3.8';
args = { '-m', 'debugpy.adapter' };
}
其中各个参数的含义如下:
type
: 表示启动调试器的方式, executable
表示由客户端自行启动调试器; server
表示 调试器已经单独启动了,后续客户端只需要将调试请求发送到服务器即可。command
: 表示启动调试器的命令args
: 表示启动调试器的命令行参数由于 python
调试工具 debugpy
是一个 Python
的第三方模块,因此这里我们使用 python -m debugpy.adapter
来启动这个调试器。
接着我们需要针对语言来配置如何进行调试。它的配置都放在一个名为 dap.configurations.language
的 字典中。language
代表的是当前文件的文件类型名, 所以针对 Python
来说这里需要填写的是 dap.configurations.python
。它的配置如下所示:
dap.configurations.python = {
{
type = "python";
request = "launch";
name = "launch file";
program = "${file}";
pythonPath = function ()
return "/usr/bin/python3.8"
end
},
}
各参数的含义如下:
name
: 是一个字符串它表示当前配置的名称,你可以理解为一个idtype
: 使用哪个调试器,跟我们之前配置的 dap.adapters
相关request
:调试的方式,支持 attach
附加到一个已有的进程或者 launch
启动一个新进程。由于在上一步我们指定由客户端来启动调试器,因此这里应该选择 launch
来启动一个新调试进程program
: 需要调试的代码, ${file}
表示当前 buffer
所对应文件pythonPath
: 执行该文件需要使用的 python
解析器路径这样我们在某一个打开的文件上按下
的时候,它会通过 pythonPath
指定的解析器来执行脚本,并且会按照配置中 request
指定的方式来打开一个新的调试器进程。并且将对应的调试命令发送到调试器完成调试工作。
好了,到此为止我们配置了最基本、最简单的dap
调试。现在只是有一个勉强能用的调试工具,距离好用还差的很远,下一篇里面我们首先会对 dap
功能进行增强,美化,并讨论如何针对 C/C++
这种编译型的语言进行调试。最后感谢各位给我提意见的小伙伴,谢谢大家!