上一章,我们初步认识了lsp,并且对 nvim-treesitter插件进行了配置,为编辑器提供了代码着色、自动格式化以及增量选中功能。算是初步体验了 lsp的相关功能。从这篇开始我们通过lsp的功能,进一步提升代码编辑、查阅等功能的体验
neovim lsp
早期想通过 neovim
使用 lsp
得通过一个额外的插件 nvim-coc
。它使用nodejs
实现,而且提供 lua
的接口。但是在 neovim
中混用不同编程语言有时候会出现莫名其妙的问题,比如我之前遇到的补全失效,但是过一段时间它自己又好了。好在 neovim
社区听从了程序员们对于 lsp
的呼唤,它内置了lsp
的客户端,并且为了方便配置服务端,它提供了一个名为 nvim-lspconfig
的插件。在现在的 neovim
版本下配置不同语言的 lsp
已经很方便了,根据官方的文档,我们只需要4步即可
- 安装
nvim-lspconfig
插件 - 安装对应语言
lsp
的服务端 - 针对
xx
语言,添加语言的配置require('lspconfig').xx.setup{…}
- 检查
lsp
的服务端在该缓冲区是否正常运行print(vim.inspect(vim.lsp.buf_get_clients()))
安装 nvim-lspconfig
好了,我们按照官方的提示,我们先来配置 lua
相关的内容,以便后期在写配置时可以有更好的编程体验。
use {'neovim/nvim-lspconfig'}
我们可以去微软的官方网站查看各个语言的服务端信息 https://microsoft.github.io/language-server-protocol/implementors/servers/
安装对应服务端
针对 lua
语言我们选中的是 sumneko/lua-language-server
这个服务端。根据 wiki
页面的安装方式,我们可以使用命令行自行安装。但是现在有了更方便的方式了我们使用 nvim-lsp-installer
插件进行安装。
use {
"williamboman/nvim-lsp-installer",
"neovim/nvim-lspconfig",
}
我们还是一样,在plugin-config
目录下创建一个配置文件用来配置 nvim-lsp-installer
插件
require("nvim-lsp-installer").setup {}
我们可以使用 LspInstallInfo
命令来查看当前lsp服务的安装情况。我们使用 :LspInstall --sync [server]
来安装对应的服务端。其中 --sync
代表我们希望以同步的方式安装,也就是安装时会卡主 neovim
主体。使用下面的命令来安装 lua
的服务端
:LspInstall --sync sumneko_lua
我们可以在 这个页面 查看 nvim-lsp-installer
插件支持的各个语言对应的服务端
针对lua进行配置
安装完成之后,我们来配置 lua
相关的内容。不知道还记不记得我们之前介绍 文件类型的时候说过不同文件类型的配置都在 ~/.config/nvim/ftplugin
里面。我们在这个目录里面定义一个 lua.lua
的文件,写入以下内容
-- 是否将 tab 替换为 space
vim.bo.expandtab = true
vim.bo.shiftwidth = 4
vim.bo.tabstop = 4
vim.bo.softtabstop = 4
-- 取消自动注释,当前行是注释时,按下回车键会默认添加一行注释,这里取消这一行为
vim.opt_local.formatoptions = vim.opt_local.formatoptions - {"r", "c", "o"}
我们之前分析过 neovim
是如何实现文件类型检测的。在那篇文章中我们说它定义了自动命令,当检测到对应文件类型的时候会调用 ~/.config/nvim/ftplugin
目录中对应以文件类型命名的目录或者 lua
文件。使用这种方式有两个好处,第一个就是我们不用手动使用 require
来加载了,第二个好处就是可以根据文件类型动态的选择调用或者不调用,而且这个工作由 neovim
自动完成,不需要我们进行干预
基于这些好处,我们在 ftplugin/lua.lua
中保存 lua
相关的配置。另外再在 ~/.config/nvim/lua
目录创建一个 lsp
目录,专门用来保存语言的配置。然后再在 ftplugin/lua.lua
中加载它
require('lsp/lua')
我们在这个文件中添加 Lua
相关的lsp配置
-- 定义快捷键
-- 根据官方的提示,这里我们使用 on_attach 表示当前缓冲加载服务端完成之后调用
local on_attach = function(client, bufnr)
-- 跳转到声明
vim.api.nvim_buf_set_keymap(bufnr, "n", "gd", "lua vim.lsp.buf.declaration()", {silent = true, noremap = true})
-- 跳转到定义
vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "lua vim.lsp.buf.definition()", {silent = true, noremap = true})
-- 显示注释文档
vim.api.nvim_buf_set_keymap(bufnr, "n", "gh", "lua vim.lsp.buf.hover()", {silent = true, noremap = true})
-- 跳转到实现
vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "lua vim.lsp.buf.implementation()", {silent = true, noremap = true})
-- 跳转到引用位置
vim.api.nvim_buf_set_keymap(bufnr, "n", "gr", "lua vim.lsp.buf.references()", {silent = true, noremap = true})
-- 以浮窗形式显示错误
vim.api.nvim_buf_set_keymap(bufnr, "n", "go", "lua vim.diagnostic.open_float()", {silent = true, noremap = true})
vim.api.nvim_buf_set_keymap(bufnr, "n", "gp", "lua vim.diagnostic.goto_prev()", {silent = true, noremap = true})
vim.api.nvim_buf_set_keymap(bufnr, "n", "gn", "lua vim.diagnostic.goto_next()", {silent = true, noremap = true})
end
require'lspconfig'.sumneko_lua.setup {
settings = {
Lua = {
runtime = {
-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
version = 'LuaJIT',
},
diagnostics = {
-- Get the language server to recognize the `vim` global
globals = {'vim'},
},
workspace = {
-- Make the server aware of Neovim runtime files
library = vim.api.nvim_get_runtime_file("", true),
},
-- Do not send telemetry data containing a randomized but unique identifier
telemetry = {
enable = false,
},
},
},
on_attach = on_attach,
}
大部分的配置都是根据 sumneko_lua
官方的文档抄过来的,这里我们需要着重强调一下 on_attach
这里,根据 nvim-config
的描述,当 lsp
服务程序加载完成之后会调用我们在 on_attach
出指定的回调函数,在函数内部我们使用 vim.api.nvim_buf_set_keymap
仅仅针对当前加载了lsp
服务的缓冲区进行,而对于其他普通文件我们不需要用到这些快捷键。
看到这里不知道小伙伴是否有点头晕了,这次我们安装了好多内容,也创建了不少配置文件,下面来回顾一下
- 安装
nvim-lspconfig
来用于lsp的配置 - 安装
nvim-lsp-installer
来安装不同语言的lsp
服务端 - 安装
sumneko_lua
作为lua
语言的lsp
服务端 - 创建了一个
~/.config/nvim/ftplugin
作为加载lua
语言配置的入口 - 创建了一个
~/.config/nvim/lsp/lua.lua
保存lua
语言相关的配置
本篇只讲了如何基于 neovim
官方给出的那4步来配置一个语言的 lsp
服务端,但是还有如何自动补全没有谈到,下节我们将开始讲解如何使用自动补全。敬请期待!