Vim / Neovim 完全指南 / 12 - LSP 配置
“LSP turns Neovim from a text editor into a true IDE.”
12.1 什么是 LSP
12.1.1 Language Server Protocol
LSP(Language Server Protocol)是微软提出的协议,定义了编辑器与语言服务器之间的通信标准。
编辑器(客户端) 语言服务器
┌──────────┐ JSON-RPC ┌──────────┐
│ Neovim │ ←────────────→ │ pyright │
│ │ stdio │ ts_ls │
│ Client │ │ gopls │
└──────────┘ └──────────┘
12.1.2 LSP 提供的功能
| 功能 |
说明 |
| 代码补全 |
智能补全建议 |
| 悬停文档 |
K 查看函数签名 |
| 跳转定义 |
gd 跳转到定义 |
| 查找引用 |
gr 查找所有引用 |
| 重命名 |
批量重命名符号 |
| 代码格式化 |
统一代码风格 |
| 诊断信息 |
错误、警告实时提示 |
| 代码操作 |
自动导入、快速修复 |
| 签名帮助 |
函数参数提示 |
| 文档符号 |
大纲视图 |
12.2 Mason — 语言服务器安装器
12.2.1 安装 Mason
-- ~/.config/nvim/lua/plugins/lsp.lua
return {
{ "williamboman/mason.nvim",
cmd = "Mason",
keys = {
{ "<leader>cm", "<cmd>Mason<cr>", desc = "Mason" },
},
opts = {
ensure_installed = {
"lua-language-server",
"pyright",
"ts_ls",
"gopls",
"rust-analyzer",
},
},
config = function(_, opts)
require("mason").setup(opts)
local mr = require("mason-registry")
for _, tool in ipairs(opts.ensure_installed) do
local p = mr.get_package(tool)
if not p:is_installed() then
p:install()
end
end
end,
},
{ "williamboman/mason-lspconfig.nvim",
dependencies = { "williamboman/mason.nvim" },
opts = {
ensure_installed = {
"lua_ls",
"pyright",
"ts_ls",
"gopls",
"rust_analyzer",
},
automatic_installation = true,
},
},
}
12.2.2 Mason 命令
:Mason " 打开 Mason UI
:MasonInstall xxx " 安装包
:MasonUninstall xxx " 卸载包
:MasonUpdate " 更新所有包
:MasonLog " 查看日志
12.3 nvim-lspconfig
12.3.1 基本配置
-- ~/.config/nvim/lua/plugins/lsp.lua
return {
{ "neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
},
config = function()
local lspconfig = require("lspconfig")
-- Lua
lspconfig.lua_ls.setup({
settings = {
Lua = {
runtime = { version = "LuaJIT" },
workspace = {
checkThirdParty = false,
library = { vim.env.VIMRUNTIME },
},
telemetry = { enable = false },
diagnostics = {
globals = { "vim" },
},
},
},
})
-- Python
lspconfig.pyright.setup({})
-- Go
lspconfig.gopls.setup({
settings = {
gopls = {
analyses = {
unusedparams = true,
},
staticcheck = true,
},
},
})
-- TypeScript/JavaScript
lspconfig.ts_ls.setup({})
-- Rust
lspconfig.rust_analyzer.setup({
settings = {
["rust-analyzer"] = {
checkOnSave = { command = "clippy" },
},
},
})
end,
},
}
12.3.2 LSP 快捷键
-- 在 LspAttach 事件中设置
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("UserLspConfig", {}),
callback = function(ev)
local opts = function(desc)
return { buffer = ev.buf, desc = desc }
end
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts("跳转定义"))
vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts("跳转声明"))
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts("查找引用"))
vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts("跳转实现"))
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts("悬停文档"))
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts("重命名"))
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts("代码操作"))
vim.keymap.set("n", "<leader>D", vim.lsp.buf.type_definition, opts("类型定义"))
vim.keymap.set("n", "<leader>ds", vim.lsp.buf.document_symbol, opts("文档符号"))
vim.keymap.set("n", "<leader>ws", vim.lsp.buf.workspace_symbol, opts("工作区符号"))
vim.keymap.set("n", "<leader>f", function()
vim.lsp.buf.format({ async = true })
end, opts("格式化"))
-- 诊断快捷键
vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts("上一个诊断"))
vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts("下一个诊断"))
vim.keymap.set("n", "<leader>e", vim.diagnostic.open_float, opts("诊断浮窗"))
vim.keymap.set("n", "<leader>dl", vim.diagnostic.setloclist, opts("诊断列表"))
end,
})
12.4 诊断(Diagnostic)
12.4.1 诊断级别
| 级别 |
图标 |
说明 |
| ERROR |
✗ |
错误 |
| WARN |
▲ |
警告 |
| INFO |
● |
信息 |
| HINT |
★ |
提示 |
12.4.2 诊断配置
vim.diagnostic.config({
virtual_text = {
prefix = "●", -- 或 "▎", "■", "●"
source = "if_many",
},
float = {
style = "minimal",
border = "rounded",
source = "always",
header = "",
prefix = "",
},
signs = true,
underline = true,
update_in_insert = false,
severity_sort = true,
})
-- 自定义诊断图标
local signs = { Error = "✗", Warn = "▲", Info = "●", Hint = "★" }
for type, icon in pairs(signs) do
local hl = "DiagnosticSign" .. type
vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = hl })
end
12.4.3 诊断命令
:lua vim.diagnostic.open_float() " 浮动窗口显示诊断
:lua vim.diagnostic.goto_next() " 下一个诊断
:lua vim.diagnostic.goto_prev() " 上一个诊断
:Telescope diagnostics " Telescope 浏览诊断
:TroubleToggle " Trouble 列表
12.5.1 内置格式化
-- LSP 格式化
vim.keymap.set("n", "<leader>f", function()
vim.lsp.buf.format({ async = true })
end)
-- 保存时自动格式化
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = { "*.lua", "*.py", "*.go", "*.rs" },
callback = function()
vim.lsp.buf.format({ async = false })
end,
})
{ "stevearc/conform.nvim",
event = { "BufWritePre" },
cmd = { "ConformInfo" },
opts = {
formatters_by_ft = {
lua = { "stylua" },
python = { "black", "isort" },
javascript = { "prettier" },
typescript = { "prettier" },
go = { "gofmt", "goimports" },
rust = { "rustfmt" },
sh = { "shfmt" },
},
format_on_save = {
timeout_ms = 500,
lsp_format = "fallback",
},
},
}
12.6 代码检查(Linting)
{ "mfussenegger/nvim-lint",
event = { "BufWritePost", "BufReadPost", "InsertLeave" },
config = function()
local lint = require("lint")
lint.linters_by_ft = {
python = { "ruff" },
javascript = { "eslint_d" },
typescript = { "eslint_d" },
go = { "golangcilint" },
sh = { "shellcheck" },
}
vim.api.nvim_create_autocmd({ "BufWritePost", "InsertLeave" }, {
callback = function()
lint.try_lint()
end,
})
end,
}
12.7 常见语言服务器
| 语言 |
服务器 |
安装命令 |
| Lua |
lua_ls |
:MasonInstall lua-language-server |
| Python |
pyright / ruff |
:MasonInstall pyright |
| TypeScript |
ts_ls |
:MasonInstall typescript-language-server |
| Go |
gopls |
:MasonInstall gopls |
| Rust |
rust_analyzer |
:MasonInstall rust-analyzer |
| C/C++ |
clangd |
:MasonInstall clangd |
| Java |
jdtls |
:MasonInstall jdtls |
| Bash |
bashls |
:MasonInstall bash-language-server |
| JSON |
jsonls |
:MasonInstall json-lsp |
| YAML |
yamlls |
:MasonInstall yaml-language-server |
| HTML |
html |
:MasonInstall html-lsp |
| CSS |
cssls |
:MasonInstall css-lsp |
| Docker |
dockerls |
:MasonInstall dockerfile-language-server |
| SQL |
sqlls |
:MasonInstall sql-language-server |
12.8 业务场景
| 场景 |
LSP 功能 |
| 代码导航 |
gd 定义, gr 引用, <C-o> 返回 |
| 重命名重构 |
<leader>rn 全项目重命名 |
| 错误修复 |
<leader>ca 代码操作(自动导入等) |
| 文档查阅 |
K 悬停查看签名和文档 |
| 代码格式化 |
<leader>f 格式化 |
| 错误导航 |
]d [d 在诊断间跳转 |
12.9 总结
| 组件 |
用途 |
| mason.nvim |
语言服务器安装管理 |
| mason-lspconfig.nvim |
Mason 与 lspconfig 桥接 |
| nvim-lspconfig |
语言服务器配置 |
| nvim-lint |
代码检查 |
| conform.nvim |
代码格式化 |
下一步:第 13 章 - 代码补全 → 配置智能代码补全、Snippet 和代码动作。
扩展阅读