在前面幾個章節,我們逐漸為
Vim
配置了文法高亮、代碼的跳轉和自動補全功能。現在的
Vim
已經可以作為代碼編輯器來使用了。但是想将它作為日常發開的主力編輯器來用還需要很長一段路要走,其中一個就是要為它配置代碼的一鍵編譯與運作功能。這裡我們仍然以
C
和
Python
為例。一個是需要編譯運作的一個是直接就可以運作的,這兩個語言應該能代表大多數語言的情況。
自動運作
C 語言的配置
在之前
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", "<F7>", "<cmd>make<CR>", {silent = true, noremap = true})
-- 編譯運作
vim.api.nvim_buf_set_keymap(bufnr, "n", "<F5>", "<cmd>make run<CR>", {silent = true, noremap = true})
end
到此我們關于
C/C++
的配置就完成了。可能顯的有些簡單但是已經初步可用了,小夥伴可以根據自己的需求來進一步修改這個配置。使用這個配置的前提是
C/C++
的工程中有已經定義好的
Makefile
檔案
Python的配置
之前我們在講解指令的模式的提到過可以使用
%
來代表目前
buffer
所對應的檔案。是以
python
的配置就比較簡單了。因為
Python
不需要編譯,是以這裡直接綁定
<F5>
來運作
vim.api.nvim_buf_set_keymap(bufnr, "n", "<F5>", "<cmd>!python %<CR>", {silent = true, noremap = true})
dap 配置
我們經常看到有人配置
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"}, "<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})
這個函數是 0.7 以後的版本引進的,如果你的是0.7之前的版本,還是老老實實的多寫幾遍。另外我們這裡綁定了
<F5>
快捷鍵,是以之前我們在
Python
中,綁定的直接運作的
<F5>
鍵的代碼需要注釋一下。
我們想要真正實作調試,還需要配合調試器使用。前面說
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
},
}
各參數的含義如下:
-
: 是一個字元串它表示目前配置的名稱,你可以了解為一個idname
-
: 使用哪個調試器,跟我們之前配置的 type
相關dap.adapters
-
:調試的方式,支援 request
附加到一個已有的程序或者 attach
啟動一個新程序。由于在上一步我們指定由用戶端來啟動調試器,是以這裡應該選擇 launch
來啟動一個新調試程序launch
-
: 需要調試的代碼, program
表示目前 ${file}
所對應檔案buffer
-
: 執行該檔案需要使用的 pythonPath
解析器路徑python
這樣我們在某一個打開的檔案上按下
<F5>
的時候,它會通過
pythonPath
指定的解析器來執行腳本,并且會按照配置中
request
指定的方式來打開一個新的調試器程序。并且将對應的調試指令發送到調試器完成調試工作。