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 和代码动作。
扩展阅读