由于Neovim现在已实现了对LSP的本地支持,LSP的配置函数发生了变化,现在不再使用 require("lspconfig")[lsp].setup(config) 这个函数进行配置并启动。而是使用 vim.lsp.config[lsp] = config 或 vim.lsp.config(lsp, config) 进行设置,再使用 vim.lsp.enable(lsp) 使配置生效。文章已更改以适配该更新。
上个有关Neovim的文章中,我们已经知道如何配置Neovim的插件系统,现在我们将运用上一章的内容来学习如何将Neovim变成一个合格的代码编辑器,给他添加补全功能。Neovim的补全功能依赖外部插件提供,我们要使用的是作为补全前端的 blink 和作为补全引擎管理工具的 mason 。mason 所管理的是 lsp 也就是language-server-protocol,这是一个由微软提出的,将编程语言工具分割成前后端的协议,使用 lsp 我们就能够通过下载各个语言的language-servers来获取辅助工具。这一章我们先讲 mason 。
mason 本体的配置
mason 本身的配置相当简单易懂,因为它就只是个管理器而已:
"mason-org/mason.nvim",-- event = "VeryLazy",dependencies = { "neovim/nvim-lspconfig", "mason-org/mason-lspconfig.nvim",},opts = { ui = { icons = { package_installed = "✓", package_pending = "➜", package_uninstalled = "✗" } }},language-server 的配置
接下来我就要为 mason 配置 lsp ,这才是真正的难点。
首先我们要考虑language-server的自动化安装与配置,我们希望 mason 在每次Neovim启动前都检查一下我们所需的language-server是否安装,并对其进行配置。那么我们可以定义这样一个函数:
local function setup(name, config) local success, package = pcall(registry.get_package, name ) if success and not package:is_installed() then package:install() end
local lsp = require("mason-lspconfig").get_mappings().package_to_lspconfig[name] config.capabilities = require("blink.cmp").get_lsp_capabilities() vim.lsp.config[lsp] = config vim.lsp.enable(lsp)end这段代码要求提供一个 lsp 的 name 及其 config 作为输入,pcall 会检查提供的 name 是否已经在 mason 中注册,如果没有,则安装它们。随后便为已注册的 lsp 采用 config 进行配置,由于 lsp 在 mason 内的名称和 lsp 中有所不同,我们在配置前对其进行转换。接下来,我们可以创建一个表来储存我们对 lsp 的配置,并使用循环其中的元素来应用我们的配置。
local servers = { ["language-server"] = {config},}for server, config in pairs(servers) do setup(server, config)end我们在上面使用的 setup 函数就是我们在第二段中所创建的配置函数,通过 servers 列表我们可以轻松地为 language-server 进行配置。一般的 language-server 使用默认配置就能得到良好体验,config 留空就行,但如果我们希望使用 lua 为我们的Neovim编写配置时也能提供正确的 lsp 体验的话,我们需要为 lua-language-server 单独编写配置。
["lua-language-server"] = { on_init = function(client) if client.workspace_folders then local path = client.workspace_folders[1].name if path ~= vim.fn.stdpath('config') and vim.loop.fs_stat(path..'/.luarc.json') or vim.loop.fs_stat(path..'/.luarc.jsonc') then return end end
client.config.settings.Lua = vim.tbl_deep_extend('force', client.config.settings.Lua, { runtime = { -- Tell the language server which version of Lua you're using -- (most likely LuaJIT in the case of Neovim) version = 'LuaJIT' }, -- Make the server aware of Neovim runtime files workspace = { checkThirdParty = false, library = { vim.env.VIMRUNTIME -- Depending on the usage, you might want to add additional paths here. -- "${3rd}/luv/library" -- "${3rd}/busted/library", } -- or pull in all of 'runtimepath'. NOTE: this is a lot slower and will cause issues when working on your own configuration (see https://github.com/neovim/nvim-lspconfig/issues/3189) -- library = vim.api.nvim_get_runtime_file("", true) } }) end, settings = { Lua = {} }},配置完 language-server 后,我们打开一个相应的文件应该就能看见 language-server 提供的诊断信息以及 language-server 提供的着色。但现在诊断信息有个问题,它只会在 normal 模式下才会更新诊断,而且我们只知道哪儿错了,不知道为什么。这是因为Neovim的 lsp 接口还没有配置好,虽然这并不属于 mason 的内容,但既然我们使用了 mason 来管理 lsp ,这条内容也可以写在这里:
vim.diagnostic.config({ update_in_insert = true, virtual_text = true,})这下我们就能在 insert 下实时更新诊断信息,并从错误/警告旁的文字查看诊断内容,但当诊断内容过长或一行字数过多时我们不太方便通过 vitrual_text 来查看诊断信息。这时,我们可以通过Neovim内置的函数在悬浮窗口显示诊断信息,为了方便使用,我们将快捷键绑定在这个命令上。
maps('n', '<leader>e', vim.diagnostic.open_float, opt)一部分 lsp 还具有格式化代码、变量重命名的功能,我们同样将快捷键绑定在相应的命令上。
maps("n", "<leader>f", function() vim.lsp.buf.format({ async = true }) end, opt)maps('n', '<leader>r', vim.lsp.buf.rename, opt)Neovim其实还内置了几个 lsp 相关的快捷键,像是:<]d> 、<[d> 分别表示跳转到下一个、上一个错误提示。活用这些快捷键能显著提高编写代码的速度。
结语
通过上述配置我们可以得到一个用于诊断代码结构与错误的 lsp ,但 lsp 的功能远不于此,像是自动补全、格式化代码等功能我们会在接下来的文章中一一讲解。