Neovim 簡介
neovim 從 vim 專案 fork 之後,新增了 async 功能,所有工作不會再被單一線程給擋住 (在過去 linter 檢查時,是無法寫程式碼的),大大提升了可用性和使用者體驗,朝現代編輯器躍進了一大步。neovim 並沒有因此停下腳步,在之後的版本陸續推出了
remote plugin:使得寫 plugin 不在受限於 vimscript,可以使用其他語言並且基於 client server 架構和 neovim 溝通,這樣就能好好的利用各個語言生態系的工具,製作出更多有彈性的套件。ex: deoplete.nvim 和 defx.nvim 皆是 python 所撰寫的 remote plugin
treesitter 的支援:過去 vim 的 syntax highlight 都是靠 regex,現在可以使用 treesitter ,效能更好,抓詞的時候更準確
支援 lua 寫套件:可以完全使用 lua 撰寫套件 ,脫離怪異的 vimscript,和 remote plugin 不同,remote plugin 是基於 client server 架構通訊
built-in LSP client (0.5):內建 LSP client,使用者可以透過 neovim LSP api 和 LSP server 互動
支援 init.lua:可以用 lua 撰寫 vimrc,本章也會用到此功能
2021 的 0.5 版本發佈,除了古老的文字介面,neovim 可說是不折不扣的現代編輯器。
編輯器基本功能
一個現代的編輯器應該有以下幾種基本功能:
客製化 Color scheme
Auto completion - 自動補全引擎
File manager - 可以在側邊欄看到專案的結構
Async linter - 在寫程式碼犯錯的同時,有錯誤提示
Background compile - 編譯的同時不應該卡住整個 Editor,要讓使用者能做其他事情
Code navigation - 包含跳轉到定義,跳轉到型態定義
Code outliner - 當一個檔案寫得很長的時候,怎麼綜觀全局,並且在這些函式中快速跳轉
Fuzzy Finder - 透過 fuzzy finder 找到想要的檔案或是專案裡的字串等等
本篇將會使用短小的 lua 設定檔達到基礎 go 語言的開發環境,並產出 init.lua 檔案
預備動作
安裝 neovim 0.5 stable
brew install neovim
如果不知道在 neovim 裏如何使用 lua,可參考 nanotee/nvim-lua-guide
本章使用到的 packer.nvim ,可以翻閱一下 README,知道基本用法和操作
init.lua 設置
Color scheme
選一個適合自己眼睛的主題吧,推薦 gruvbox-material
use {
'sainnhe/gruvbox-material',
setup = function()
vim.g.gruvbox_material_background = 'soft'
end,
config = function()
vim.cmd[[colorscheme gruvbox-material]]
end
}
File explorer - nerdtree
雖然在上篇我不推薦使用 nerdtree,並且端出了需要高度手動配置的 defx.nvim。經過仔細思考之後發現這是個人偏好,不應該拉讀者一起折騰,沒有什麼特殊需求的話,大部分的情況下 nerdtree 是很夠用的,使用者體驗也不錯。老牌的 nerdtree 使用上相對穩定,也會找到較多的討論。
安裝 nerdtree
use {
'preservim/nerdtree',
setup = function()
-- Read the following nerdtree section and add what you need
end
}
映射 <F4>
打開或關閉 nerdtree
vim.api.nvim_set_keymap("n", "<F4>", "<cmd>NERDTreeToggle<cr>" ,{silent = true, noremap = true})
在 nerdtree 只要會使用兩個按鍵就能存活
?
打開 quick menu 列出所有基本的案鍵映射m
對游標下的檔案或檔案夾列出基本操作選單
vim-go
強大的 vim-go 提供了各式各樣 go 語言的開發功能,每一個使用 vim 開發 go 的人一定都用過。它包括了 auto completion, code navigation, background compilation, code format…功能,我將會介紹 vim-go 如何使用這些功能
安裝 vim-go
use {
'fatih/vim-go',
run = ':GoUpdateBinaries',
ft = 'go',
setup = function()
-- Read the following section and add what you need
end
}
Auto-complete
vim-go 提供的 auto completion 使用 omnifunc 實作 (:help omnifunc
),omnifunc 使用 <c-x><c-o>
觸發並彈出候選補全選單,按起來很不順手,為了更好的體驗,我將設定成:
<tab>
觸發候補選單或是跳到下一個候選,沒有候選時就輸出 tab 字元<s-tab>
觸發候補選單或跳到上一個候選,沒有候選時就刪除 tab 字元<enter>
完成補全,沒有候選時就輸出下一行
local t = function(str)
return vim.api.nvim_replace_termcodes(str, true, true, true)
end
local check_back_space = function()
local col = vim.fn.col('.') - 1
if col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') then
return true
else
return false
end
end
_G.tab_complete = function()
if vim.fn.pumvisible() == 1 then
return t "<C-n>"
elseif check_back_space() then
return t "<Tab>"
else
return t "<C-x><C-o>"
end
end
_G.s_tab_complete = function()
if vim.fn.pumvisible() == 1 then
return t "<C-p>"
else
return t "<C-h>"
end
end
_G.enter_key = function()
if vim.fn.pumvisible() == 1 then
return t "<C-y>"
else
return t "<CR>"
end
end
vim.api.nvim_set_keymap("i", "<tab>", "<C-R>=v:lua.tab_complete()<CR>" ,{silent = true, noremap = true})
vim.api.nvim_set_keymap("i", "<s-tab>", "<C-R>=v:lua.s_tab_complete()<CR>" ,{silent = true, noremap = true})
vim.api.nvim_set_keymap('i', '<enter>', '<C-R>=v:lua.enter_key()<CR>' ,{silent = true, noremap = true})
Code navigation - vim-go
:GoDef
或是gd
跳轉到定義 (gd
映射是 vim-go 內建,不需額外設定)[[
]]
motion[[
跳到上一個 function 宣告處]]
跳到下一個 function 宣告處
使用
:GoRefer
或是映射gr
列出 referrer 到 location list
vim.api.nvim_set_keymap('n', 'gr', '<Plug>(go-referrers)')
Background compile - vim-go
vim-go 提供了編譯和執行的指令,可以輕鬆在 vim 裏編譯、執行
:GoRun
跑起 main package:GoBuild
編譯如果專案使用 Makefile,可以使用 vim-dispatch 參考我另一篇文章
Code format - vim-go
:GoFmt
排版程式碼不想手動可以在每次儲存時自動排版
vim.g.go_fmt_autosave = 1
Async linter - 使用 nvim-lspconfig
安裝 nvim-lspconfig 並且設定 gopls
use {
'neovim/nvim-lspconfig',
config = function()
require('lspconfig').gopls.setup{}
-- Read the following lspconfig section and add what you need
end
}
將 diagnostic 資訊顯示在程式碼旁
diagnostic_config = {
-- Enable underline, use default values
underline = true,
-- Enable virtual text, override spacing to 2
virtual_text = {
spacing = 2,
prefix = '<',
},
-- Use a function to dynamically turn signs off
-- and on, using buffer local variables
signs = function(bufnr, client_id)
local ok, result = pcall(vim.api.nvim_buf_get_var, bufnr, 'show_signs')
-- No buffer local variable set, so just enable by default
if not ok then
return true
end
return result
end,
-- Disable a feature
update_in_insert = false,
}
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, diagnostic_config)```
- 在 diagnostic 之間跳轉
- 映射 `[d` `]d` 跳轉到上一個和下一個 diagnostic
- ```plain text
vim.api.nvim_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>',{silent = true, noremap = true})
vim.api.nvim_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>',{silent = true, noremap = true})
Code outline - tagbar
安裝 gotags:
$ go get -u github.com/jstemmer/gotags
直接照 gotags 文件的設定
use {
'preservim/tagbar',
cmd = 'TagbarToggle',
setup = function()
-- Read the following tagbar section and add what you need
end
}
直接照 gotags 文件的設定
vim.g.tagbar_type_go = {
ctagstype = 'go',
kinds = {
'p:package',
'i:imports:1',
'c:constants',
'v:variables',
't:types',
'n:interfaces',
'w:fields',
'e:embedded',
'm:methods',
'r:constructor',
'f:functions'
},
sro = '.',
kind2scope = {
t = 'ctype',
n = 'ntype',
},
scope2kind = {
ctype = 't',
ntype = 'n'
},
ctagsbin = 'gotags',
ctagsargs = '-sort -silent'
}
映射 開啟或是關閉 tagbar
vim.api.nvim_set_keymap("n", "<F8>", "<cmd>TagbarToggle<cr>" ,{silent = true, noremap = true})
Fuzzy finder - Telescope
作為 neovim built-in LSP client 的主要開發者 tjdevries 所主導開發的 fuzzy finder,早晚會成為 neovim 界 fuzzy finder 的主流,在大部分的使用情境下可以取代 fzf.vim,並且使用 lua 開發。
Telescope 的 live_grep 和 find_files 是預設使用 ripgrep 安裝 rigrep
$ brew install ripgrep
安裝 telescope
use {
'nvim-telescope/telescope.nvim',
requires = {{'nvim-lua/popup.nvim'}, {'nvim-lua/plenary.nvim'}},
cmd = 'Telescope',
setup = function()
-- Read the following telescope section and add what you need
end
}
映射 <leader>fg
即時文字搜索
vim.api.nvim_set_keymap('n', '<leader>fg', '<cmd>lua require('telescope.builtin').live_grep()<cr>', {noremap = true})
映射 <leader>ff
尋找 file
vim.api.nvim_set_keymap('n', '<leader>ff', '<cmd>lua require('telescope.builtin').find_files()<cr>', {noremap = true})
結語
最後完成的 init.lua,貼上內容重開 neovim 後,記得使用
PackerSync
安裝套件
本篇的用意和上篇一樣,盡量用精簡的設定檔和少量的套件打造一個短小精幹的 go 語言基礎開發環境,但使用效果和體驗和上篇效果相當,大部分的 go 語言開發功能都透用 vim-go 達成。
雖然我針對了了七個從 coloscheme 到 fuzzy finder 重點功能,但這其實只是個開始,你應該把這篇所寫的設定檔當成一個骨架繼續改進,甚至只要記這七個重點功能的用意,替換成你覺得更棒的,比如說你覺得 fzf 比 telescope 更棒,或是你覺得 ycm 補全比 vim-go 內建的更優秀,那就換吧,往"打造成自己最合適的開發環境"這條道路前進。