講講主要開發相關的特性配置
LSP
診斷, 補全和高亮等特性主要依賴 lsp 實現, 現行的配置依賴 nvim >= 0.11, 因為 lsp 是使用 nvim 原生的接口進行配置的. 以前我維護 nvim-lspconfig 的另一個舊分支, 現在已經完全遷移, 那個分支不再維護.
- 安裝器
我使用 mason 安裝某些 lsp, 只有 clangd 和 tinymist 是通過系統包管理安裝的, 其他都經過 mason 安裝.
mason 並沒有提供自動安裝 lsp 的功能, 需要 mason-lspconfig 的 ensure_installed 來自動安裝某些 lsp. 我自己實現了這個函數,
避免引入了多餘的插件.
local function ensure_installed(list)
local registry = require 'mason-registry'
local function install_package(pkg_name)
local ok, pkg = pcall(registry.get_package, pkg_name)
if not ok then
vim.notify(('Package %s not found'):format(pkg_name), vim.log.levels.WARN)
return
end
if not pkg:is_installed() then
vim.notify('Installing LSP: ' .. pkg_name, vim.log.levels.INFO)
pkg:install():once('closed', function()
if pkg:is_installed() then
vim.schedule(function()
vim.notify('LSP installed: ' .. pkg_name, vim.log.levels.INFO)
end)
else
vim.schedule(function()
vim.notify('Failed to install LSP: ' .. pkg_name, vim.log.levels.ERROR)
end)
end
end)
end
end
if not registry.refresh then
-- Old Mason version fallback
for _, name in ipairs(list) do
install_package(name)
end
else
-- Newer Mason: async registry refresh
registry.refresh(function()
for _, name in ipairs(list) do
install_package(name)
end
end)
end
end
- lsp 配置
所有 lsp 的配置都在 nvim 配置根目錄下的 lsp/ 下.
local clangd = {
filetypes = { 'c', 'cpp' },
-- 識別項目根目錄的文件
root_markers = {
'.git/',
'clice.toml',
'.clang-tidy',
'.clang-format',
'compile_commands.json',
'compile_flags.txt',
'configure.ac', -- AutoTools
},
cmd = {
'clangd',
'--background-index',
'--clang-tidy',
'--header-insertion=iwyu',
'--completion-style=detailed',
'--function-arg-placeholders=true',
'-j=4',
'--fallback-style="{BasedOnStyle: LLVM, IndentWidth: 4}"',
},
capabilities = {
textDocument = {
completion = {
editsNearCursor = true,
completionItem = { snippetSupport = false },
},
},
offsetEncoding = { 'utf-8', 'utf-16' },
},
reuse_client = function(client, config)
return client.name == config.name
end,
settings = {
clangd = {
Completion = {
CodePatterns = 'NONE',
},
},
},
---@class ClangdInitializeResult: lsp.InitializeResult
---@field offsetEncoding? string
---@param init_result ClangdInitializeResult
on_init = function(client, init_result)
if init_result.offsetEncoding then
client.offset_encoding = init_result.offsetEncoding
end
end,
on_attach = function(_, buf)
vim.api.nvim_buf_create_user_command(buf, 'LspClangdSwitchSourceHeader', function()
switch_source_header(buf)
end, { desc = 'Switch between source/header' })
vim.api.nvim_buf_create_user_command(buf, 'LspClangdSymbolInfo', function()
symbol_info()
end, { desc = 'Show symbol info' })
end,
}
return clangd
大部分 lsp 的配置都可以在 nvim-lspconfig 的倉庫下找到默認的, 直接抄就行了.
Tree-sitter
tree-sitter 主要作为辅助的高亮手段, 也提供提纲(outline) 之类的功能. 使用 nvim-treesitter 插件.
代码补全和自定義片段
代码补全需要使用 blink 插件進行增強. 同時使用 blink.cmp 提供的 snippet 來自定義片段, 例如刷 leetcode 需要的代碼框架和 html 標籤等.
local CodeCompletion = {
'saghen/blink.cmp',
dependencies = {
'catppuccin/nvim',
},
event = { 'BufReadPost', 'BufNewFile' },
version = '1.*',
opts = {
keymap = {
preset = 'default',
-- Conflict with cursor move under insert mode?
-- ['<C-g>'] = { 'show', 'show_documentation', 'hide_documentation' },
['<C-k>'] = { 'show', 'show_documentation', 'hide_documentation' },
['<C-e>'] = { 'hide' },
['<Up>'] = { 'select_prev', 'fallback' },
['<Down>'] = { 'select_next', 'fallback' },
['<C-p>'] = { 'snippet_backward', 'fallback_to_mappings' },
['<C-n>'] = { 'snippet_forward', 'fallback_to_mappings' },
['<Tab>'] = { 'select_next', 'fallback' },
['<S-Tab>'] = { 'select_prev', 'fallback_to_mappings' },
['<C-b>'] = { 'scroll_documentation_up', 'fallback' },
['<C-f>'] = { 'scroll_documentation_down', 'fallback' },
['<CR>'] = { 'accept', 'fallback' },
},
appearance = {
nerd_font_variant = 'normal',
},
completion = {
menu = {
border = 'rounded',
},
documentation = {
auto_show = true,
window = {
border = 'rounded',
},
},
list = {
selection = {
preselect = false,
auto_insert = true,
},
},
},
sources = {
default = { 'snippets', 'lsp', 'path', 'buffer' },
providers = {
snippets = {
opts = {
friendly_snippets = false,
extended_filetypes = {
astro = { 'html' },
markdown = { 'blog', 'html' },
zsh = { 'sh' },
},
},
},
},
},
-- (Default) Rust fuzzy matcher for typo resistance and significantly better performance
-- You may use a lua implementation instead by using `implementation = "lua"` or fallback to the lua implementation,
-- when the Rust fuzzy matcher is not available, by using `implementation = "prefer_rust"`
--
-- See the fuzzy documentation for more information
fuzzy = { implementation = 'prefer_rust_with_warning' },
},
opts_extend = { 'sources.default' },
}
return CodeCompletion
格式化
格式化依賴 conform.nvim 插件.
local CodeFormatter = {
'stevearc/conform.nvim',
event = { 'BufReadPost', 'BufNewFile' },
opts = {},
config = function()
require('conform').setup {
formatters_by_ft = {
lua = { 'stylua' },
cpp = { 'clang-format' },
c = { 'clang-format' },
json = { 'prettier' },
jsonc = { 'prettier' },
html = { 'prettier' },
css = { 'prettier' },
astro = { 'prettier' },
typescript = { 'prettier' },
javascript = { 'prettier' },
},
}
local do_format = function()
require('conform').format { async = true, lsp_fallback = true }
end
vim.keymap.set('n', '<leader>lf', do_format, { desc = 'Format Current Buffer', noremap = true, silent = true })
vim.api.nvim_create_user_command('Format', do_format, { desc = 'Format Current Buffer' })
end,
}
return CodeFormatter
工作區補丁
vscode 這樣的編輯器在項目的根目錄下有一個 .vscode 目錄作為工作區的本地配置, 覆蓋用戶的默認配置. 這在某些時候非常有用.
我主要做 c/cpp 開發, 很多項目的縮進規則都不一樣, 就需要在工作區進行配置. 很多項目下都可以配置一些預設的命令在工作區執行,
比如代碼格式化和編譯, 單元測試. leetcode 也需要運行測試命令. 工作區補丁會被放置在項目根目錄下的 .nvim 或 .vscode/nvim 下.
設置後者的原因是大部分項目的 .gitignore 都有 .vscode 但是沒有 .nvim.
.nvim
├── ftplugin
└── init.lua
工作區補丁的結構很簡單, ftplugin 用來存放文件類型加載的代碼, init.lua是入口點.
其他
Markdown
主要使用 render-markdown 和 markdown-preview, 我不推薦 在終端內渲染 Markdown 文檔中的圖片和公式, 儘管有插件能夠做到這些, 但是這些特性的效果很大程度上是取決於終端的.