强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 格式化(Formatting)

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,
})

12.5.2 conform.nvim(外部格式化器)

{ "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 常见语言服务器

语言服务器安装命令
Lualua_ls:MasonInstall lua-language-server
Pythonpyright / ruff:MasonInstall pyright
TypeScriptts_ls:MasonInstall typescript-language-server
Gogopls:MasonInstall gopls
Rustrust_analyzer:MasonInstall rust-analyzer
C/C++clangd:MasonInstall clangd
Javajdtls:MasonInstall jdtls
Bashbashls:MasonInstall bash-language-server
JSONjsonls:MasonInstall json-lsp
YAMLyamlls:MasonInstall yaml-language-server
HTMLhtml:MasonInstall html-lsp
CSScssls:MasonInstall css-lsp
Dockerdockerls:MasonInstall dockerfile-language-server
SQLsqlls: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.nvimMason 与 lspconfig 桥接
nvim-lspconfig语言服务器配置
nvim-lint代码检查
conform.nvim代码格式化

下一步第 13 章 - 代码补全 → 配置智能代码补全、Snippet 和代码动作。


扩展阅读