在前面幾個(gè)章節(jié),我們逐漸為 Vim
配置了語(yǔ)法高亮、代碼的跳轉(zhuǎn)和自動(dòng)補(bǔ)全功能。現(xiàn)在的 Vim
已經(jīng)可以作為代碼編輯器來(lái)使用了。但是想將它作為日常發(fā)開(kāi)的主力編輯器來(lái)用還需要很長(zhǎng)一段路要走,其中一個(gè)就是要為它配置代碼的一鍵編譯與運(yùn)行功能。這里我們?nèi)匀灰?C
和 Python
為例。一個(gè)是需要編譯運(yùn)行的一個(gè)是直接就可以運(yùn)行的,這兩個(gè)語(yǔ)言應(yīng)該能代表大多數(shù)語(yǔ)言的情況。
自動(dòng)運(yùn)行
C 語(yǔ)言的配置
在之前 vim
入門(mén)的一系列教程中我們介紹過(guò) vim
自帶 make
命令的運(yùn)行機(jī)制以及如何進(jìn)行自定義。對(duì)于其他語(yǔ)言要實(shí)現(xiàn)這個(gè)自動(dòng)編譯運(yùn)行的效果我們核心的操作就是在修改 make
命令。而 C/C++
本身采用 make
命令來(lái)進(jìn)行編譯和運(yùn)行,所以這里 C/C++
我們直接采用 vim 自帶的 :make
命令
我們先創(chuàng)建一個(gè) C
的工程。讓后使用上一節(jié)的生成 hello world 的代碼片段生成一個(gè)基本的程序。然后提供一個(gè)供 :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
然后我們執(zhí)行 make
用來(lái)編譯。如果出錯(cuò)了,可以使用 quickfix
相關(guān)命令跳轉(zhuǎn)到對(duì)應(yīng)位置。
我們一般的流程是 :make
進(jìn)行編譯,然后使用 :make run
來(lái)進(jìn)行運(yùn)行。把命令搞清楚了,下面就考慮如何加快這個(gè)流程,做到一鍵編譯運(yùn)行。我們的思路還是綁定快捷鍵。每種語(yǔ)言雖然定義相同的快捷鍵但是運(yùn)行的命令不同,我們需要根據(jù)不同的語(yǔ)言類型綁定對(duì)應(yīng)的命令。這個(gè)時(shí)候最好的辦法就是在 filetype
的機(jī)制上完成綁定的操作。
我們?cè)?lua/lsp/cpp.lua
中綁定快捷鍵。
local on_attach = function(client, bufnr)
lsp_set_keymap.set_keymap(bufnr)
-- 編譯
vim.api.nvim_buf_set_keymap(bufnr, "n", "<F7>", "<cmd>make<CR>", {silent = true, noremap = true})
-- 編譯運(yùn)行
vim.api.nvim_buf_set_keymap(bufnr, "n", "<F5>", "<cmd>make run<CR>", {silent = true, noremap = true})
end
到此我們關(guān)于 C/C++
的配置就完成了。可能顯的有些簡(jiǎn)單但是已經(jīng)初步可用了,小伙伴可以根據(jù)自己的需求來(lái)進(jìn)一步修改這個(gè)配置。使用這個(gè)配置的前提是 C/C++
的工程中有已經(jīng)定義好的 Makefile
文件
Python的配置
之前我們?cè)谥v解命令的模式的提到過(guò)可以使用 %
來(lái)代表當(dāng)前 buffer
所對(duì)應(yīng)的文件。所以 python
的配置就比較簡(jiǎn)單了。因?yàn)?Python
不需要編譯,所以這里直接綁定 <F5>
來(lái)運(yùn)行
vim.api.nvim_buf_set_keymap(bufnr, "n", "<F5>", "<cmd>!python %<CR>", {silent = true, noremap = true})
dap 配置
我們經(jīng)常看到有人配置 neovim
或者 vim
的時(shí)候會(huì)介紹到 dap
,那么什么是 dap
呢? dap
的全稱是 Debug Adapter Protocol
從名稱上看它又是一個(gè)協(xié)議。它為多種調(diào)試器提供了一層統(tǒng)一的適配抽象層。有點(diǎn)類似于前面的介紹的 lsp
。只要在適配層提供接口的實(shí)現(xiàn),那么在客戶端,也就是代碼編輯器這端可以不做任何修改的集成不同調(diào)試
聯(lián)想到 lsp
的配置,我們配置dap
首先需要的是有一個(gè) dap
的客戶端,用來(lái)向調(diào)試器發(fā)送各種命令,例如下斷點(diǎn)、顯示變量名等等。另外想要能夠調(diào)試也需要有具體的調(diào)試器,用來(lái)接收處理這些命令。現(xiàn)在思路有了,我們 這里先以 Python
為例來(lái)介紹 dap
的基本配置。
首先是需要一個(gè)客戶端,用于通過(guò) neovim
下發(fā)各種調(diào)試命令并實(shí)時(shí)顯示調(diào)試信息。
截止到 0.7
版本 NeoVim
并沒(méi)有在內(nèi)部集成 dap
客戶端的功能,需要我們單獨(dú)安裝相關(guān)插件來(lái)實(shí)現(xiàn)這部分的功能。這里我們使用的客戶端是 nvim-dap
插件。
我們先使用 use {'mfussenegger/nvim-dap'}
來(lái)安裝它。
接著我們來(lái)定義一下相關(guān)的快捷鍵,這里我喜歡使用 Visual Studio
的快捷鍵。各位小伙伴可以自行選擇自己喜歡的快捷鍵。這里我希望在插入模式和選擇中也可以使用這些快捷鍵,由于 vim.api.nvim_set_keymap
函數(shù)第一個(gè)參數(shù)只能有一個(gè)模式字符串,如果采用這個(gè)函數(shù)來(lái)定義快捷鍵,這里同樣的代碼我要寫(xiě)三次,為了簡(jiǎn)化代碼,這里介紹一個(gè)新的函數(shù) vim.keymap.set
。它與 vim.api.nvim_set_keymap
函數(shù)支持的參數(shù)相同,只是它第一個(gè)表示模式的參數(shù)可以支持用字典來(lái)一次綁定到多個(gè)模式中。這樣就簡(jiǎn)化了綁定快捷鍵的代碼量。
vim.keymap.set({"i", "n", "v"}, "<F5>", "<cmd>lua require'dap'.continue()<CR>", {silent = true, noremap = true, buffer = bufnr})
vim.keymap.set({"i", "n", "v"}, "<F10>", "<cmd>lua require'dap'.step_over()<CR>", {silent = true, noremap = true, buffer = bufnr})
vim.keymap.set({"i", "n", "v"}, "<F11>", "<cmd>lua require'dap'.step_into()<CR>", {silent = true, noremap = true, buffer = bufnr})
vim.keymap.set({"i", "n", "v"}, "<F12>", "<cmd>lua require'dap'.step_over()<CR>", {silent = true, noremap = true, buffer = bufnr})
vim.keymap.set({"i", "n", "v"}, "<F9>", "<cmd>lua require'dap'.toggle_breakpoint()<CR>", {silent = true, noremap = true, buffer = bufnr})
這個(gè)函數(shù)是 0.7 以后的版本引進(jìn)的,如果你的是0.7之前的版本,還是老老實(shí)實(shí)的多寫(xiě)幾遍。另外我們這里綁定了 <F5>
快捷鍵,因此之前我們?cè)?Python
中,綁定的直接運(yùn)行的 <F5>
鍵的代碼需要注釋一下。
我們想要真正實(shí)現(xiàn)調(diào)試,還需要配合調(diào)試器使用。前面說(shuō) dap
只是一層協(xié)議,需要客戶端服務(wù)器按照這一層協(xié)議來(lái)實(shí)現(xiàn)相關(guān)功能,某些調(diào)試器可能自身支持這個(gè)協(xié)議,而某些可能不支持,這樣就需要額外的配置來(lái)使調(diào)試器也能支持該協(xié)議。下面我們以 Python
為例先把整個(gè)調(diào)試環(huán)境搭建起來(lái),先跑起來(lái)再說(shuō)
Lsp
在安裝 Server
的時(shí)候有 nvim-lsp-installer
這樣的插件來(lái)專門(mén)安裝 LSP server
的,那么 dap
有沒(méi)有類似的插件來(lái)安裝 dap
調(diào)試器相關(guān)的服務(wù)呢?有!我們使用 mason
來(lái)管理 dap
的調(diào)試器。
use { "williamboman/mason.nvim" }
當(dāng)初我推薦過(guò) nvim-lsp-installer
插件作為下載、管理 lsp server
的工具。后來(lái)只是知道作者發(fā)布了新的管理工具,因?yàn)楸容^新怕出問(wèn)題就沒(méi)怎么關(guān)注,后來(lái)有好多小伙伴在評(píng)論區(qū)推薦,我仔細(xì)看了一下發(fā)現(xiàn)它已經(jīng)支持 dap
服務(wù)的管理了。那還是使用它吧 ^_^
。
我們可以使用 Mason
打開(kāi)一個(gè)帶界面的 Lsp
和 DAP
的服務(wù)管理窗口,可以使用 數(shù)字鍵在上面進(jìn)行跳轉(zhuǎn),找到想要的服務(wù)之后直接使用 i
來(lái)安裝
也可以使用 MasonInstall
來(lái)安裝想要的服務(wù)。
我們先在插件配置中刪除與 nvim-lsp-installer
相關(guān)的配置,包括 packer
中對(duì)它的引用和 plugins-config
目錄中的配置。
下一步就是配置 dap
的客戶端與 服務(wù)端的聯(lián)動(dòng),這需要配置 nvim-dap
插件,根據(jù)官方的描述我們主要配置兩個(gè)部分,第一個(gè)部分叫做適配器,主要配置我們加載哪個(gè)調(diào)試器,以及如何加載調(diào)試器。這一步需要提供如下的配置框架
local dap = require('dap')
dap.adapters.language = {
}
language
是具體的調(diào)試器例如 debugpy
這里的 language
就是 python
了。既然與語(yǔ)言相關(guān),我們自然的想到要用 ftplugin
目錄。為了方便管理,這里與 lsp
配置的組織形式類似,我們將所有關(guān)于 dap
的配置都放到 lua/dap
目錄中。并且按語(yǔ)言名稱來(lái)命名,例如關(guān)于 python
的 dap
配置我們放到 lua/dap/python.lua
中。然后在 ftplugin/python.lua
中加載這個(gè)配置文件即。
require("dap/python")
然后在 python.lua
文件中寫(xiě)入以下配置
local dap = require('dap')
dap.adapters.python = {
type = 'executable';
command = '/usr/bin/python3.8';
args = { '-m', 'debugpy.adapter' };
}
其中各個(gè)參數(shù)的含義如下:
-
type
: 表示啟動(dòng)調(diào)試器的方式,executable
表示由客戶端自行啟動(dòng)調(diào)試器;server
表示 調(diào)試器已經(jīng)單獨(dú)啟動(dòng)了,后續(xù)客戶端只需要將調(diào)試請(qǐng)求發(fā)送到服務(wù)器即可。 -
command
: 表示啟動(dòng)調(diào)試器的命令 -
args
: 表示啟動(dòng)調(diào)試器的命令行參數(shù)
由于 python
調(diào)試工具 debugpy
是一個(gè) Python
的第三方模塊,因此這里我們使用 python -m debugpy.adapter
來(lái)啟動(dòng)這個(gè)調(diào)試器。
接著我們需要針對(duì)語(yǔ)言來(lái)配置如何進(jìn)行調(diào)試。它的配置都放在一個(gè)名為 dap.configurations.language
的 字典中。language
代表的是當(dāng)前文件的文件類型名, 所以針對(duì) Python
來(lái)說(shuō)這里需要填寫(xiě)的是 dap.configurations.python
。它的配置如下所示:
dap.configurations.python = {
{
type = "python";
request = "launch";
name = "launch file";
program = "${file}";
pythonPath = function ()
return "/usr/bin/python3.8"
end
},
}
各參數(shù)的含義如下:
-
name
: 是一個(gè)字符串它表示當(dāng)前配置的名稱,你可以理解為一個(gè)id -
type
: 使用哪個(gè)調(diào)試器,跟我們之前配置的dap.adapters
相關(guān) -
request
:調(diào)試的方式,支持attach
附加到一個(gè)已有的進(jìn)程或者launch
啟動(dòng)一個(gè)新進(jìn)程。由于在上一步我們指定由客戶端來(lái)啟動(dòng)調(diào)試器,因此這里應(yīng)該選擇launch
來(lái)啟動(dòng)一個(gè)新調(diào)試進(jìn)程 -
program
: 需要調(diào)試的代碼,${file}
表示當(dāng)前buffer
所對(duì)應(yīng)文件 -
pythonPath
: 執(zhí)行該文件需要使用的python
解析器路徑
這樣我們?cè)谀骋粋€(gè)打開(kāi)的文件上按下 <F5>
的時(shí)候,它會(huì)通過(guò) pythonPath
指定的解析器來(lái)執(zhí)行腳本,并且會(huì)按照配置中 request
指定的方式來(lái)打開(kāi)一個(gè)新的調(diào)試器進(jìn)程。并且將對(duì)應(yīng)的調(diào)試命令發(fā)送到調(diào)試器完成調(diào)試工作。
好了,到此為止我們配置了最基本、最簡(jiǎn)單的dap
調(diào)試。現(xiàn)在只是有一個(gè)勉強(qiáng)能用的調(diào)試工具,距離好用還差的很遠(yuǎn),下一篇里面我們首先會(huì)對(duì) dap
功能進(jìn)行增強(qiáng),美化,并討論如何針對(duì) C/C++
這種編譯型的語(yǔ)言進(jìn)行調(diào)試。最后感謝各位給我提意見(jiàn)的小伙伴,謝謝大家!