简述
记录配置nvim LSP过程。nvim版本:v0.7.2 。所需插件:neovim/nvim-lspconfig,williamboman/nvim-lsp-installer
目录结构:
与LSP相关的配置全部置于~/.config/nvim/lua/lsp 文件夹中。
使用lua配置nvim,可将配置内容依据喜好放置在不同文件,只需要在~/.config/nvim/init.lua 中指明有哪些配置文件即可。
安装LSP插件
packer
use "williamboman/nvim-lsp-installer"
use 'neovim/nvim-lspconfig'
- nvim-lspconfig: 配置LSP服务器
- nvim-lsp-installer: 下载对应语言所需要的LSP服务器
配置文件
在~/.config/nvim/init.lua 中导入自定义的lsp模块
require("lsp")
当一个文件夹中存在init.lua 文件时,可在~/.config/nvim/init.lua 中直接导入该文件夹。~/.config/nvim/lua 文件夹是属于自动检索路径。
文件:~/.config/nvim/lua/lsp/init.lua
require("nvim-lsp-installer").setup{}
local opts = { noremap=true, silent=true }
vim.keymap.set('n', '<space>e', vim.diagnostic.open_float, 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', '<space>q', vim.diagnostic.setloclist, opts)
local lspconfig = require("lspconfig")
local function on_attach(client, bufnr)
local bufopts = { noremap=true, silent=true, buffer=bufnr }
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, bufopts)
vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, bufopts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts)
end
local lsp_flags = {
debounce_text_changes = 100,
}
local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())
该部分主要是设置LSP信息提示时间,按键映射。来自nvim-config 插件主页中的Suggested configuration部分。
安装LSP服务器
输入命令:LspInstallInfo 调出插件nvim-lsp-installer 界面。
按i 可安装当前光标下的LSP服务器。服务器是以zip文件形式下载的,需要确保本地环境有zip,unzip两个命令。 默认下载/安装路径为:~/.local/share/nvim/lsp_servers
启动LSP服务
在~/.config/nvim/lua/lsp/init.lua 文件尾添加启动配置。
lspconfig.sumneko_lua.setup {
on_attach = on_attach,
flags = lsp_flags,
capabilities = capabilities,
settings = {
Lua = {
runtime = {
version = 'LuaJIT',
},
diagnostics = {
globals = {"vim", "packer_bootstrap"},
},
workspace = {
library = vim.api.nvim_get_runtime_file("", true),
},
telemetry = {
enable = false,
},
},
},
}
lspconfig.pyright.setup {
on_attach = on_attach,
flags = lsp_flags,
capabilities = capabilities,
settings = {
python = {
analysis = {
autoSearchPaths = true,
diagnosticMode = "workspace",
useLibraryCodeForTypes = true,
typeCheckingMode = "off",
}
}
},
}
在这里可以查看所有nvim支持的LSP服务器默认设置。前三个参数是启动所有LSP服务器都需要填写的,就是将前面配置的内容传递给LSP服务器。后面的setting部分不一定需要修改。上面的配置启动了lua与python语言的LSP服务器。
到这里LSP已经配置完成,能够显示语法检查的错误信息等。
修改LSP提示信息风格
文件: ~/.config/nvim/lua/lsp/handlers.lua
local M = {}
M.setup = function()
local signs = {
{ name = "DiagnosticSignError", text = "?" },
{ name = "DiagnosticSignWarn", text = "?" },
{ name = "DiagnosticSignHint", text = "?" },
{ name = "DiagnosticSignInfo", text = "?" },
}
for _, sign in ipairs(signs) do
vim.fn.sign_define(sign.name, { texthl = sign.name, text = sign.text, numhl = "" })
end
local config = {
virtual_text = false,
signs = {
active = signs,
},
update_in_insert = true,
underline = true,
severity_sort = true,
float = {
focusable = false,
style = "minimal",
border = "rounded",
source = "always",
header = "",
prefix = "",
},
}
vim.diagnostic.config(config)
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
border = "rounded",
})
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, {
border = "rounded",
})
end
return M
主要是将提示标志修改成nerd font 字体符号,修改文档信息等呼出窗口为有边框模式等。其中virtual_text 这一设置控制是否在有语法错误的那一行末显示错误信息的精简内容。
在~/.config/nvim/lua/lsp/init.lua 文件末导入该文件,使配置生效。
require("lsp.handlers").setup()
效果
红色框中的内容便是最终LSP显示的信息样式。
LSP 是不怎么提供补全内容的,在nvim-lspconfig插件页面有启动默认补全的设置,这里没有使用,后续通过nvim-cmp插件来提过更加完善的补全效果。错误信息内容需要通过前面配置快捷键<space>+e 呼出。
代码补全
所需插件
这里使用的是nvim-cmp 搭配ultisnips 进行补全与代码片段实现。所用到的插件都是nvim-cmp页面中Recommended Configuration部分所罗列的设置。核心插件是nvim-cmp , cmp-nvim-lsp 提供LSP的语法补全,其他插件只是完善作用。
配置nvim-cmp
文件:~/.config/nvim/lua/lsp/cmp.lua
local kind_icons = {
Text = "?",
Method = "m",
Function = "?",
Constructor = "?",
Field = "",
Variable = "?",
Class = "?",
Interface = "?",
Module = "?",
Property = "?",
Unit = "",
Value = "?",
Enum = "?",
Keyword = "?",
Snippet = "?",
Color = "?",
File = "?",
Reference = "?",
Folder = "?",
EnumMember = "?",
Constant = "?",
Struct = "?",
Event = "?",
Operator = "?",
TypeParameter = "?",
}
local t = function(str)
return vim.api.nvim_replace_termcodes(str, true, true, true)
end
local cmp = require('cmp')
cmp.setup{
snippet = {
expand = function(args)
vim.fn["UltiSnips#Anon"](args.body)
end,
},
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
},
mapping = {
["<Tab>"] = cmp.mapping({
c = function()
if cmp.visible() then
cmp.select_next_item({ behavior = cmp.SelectBehavior.Insert })
else
cmp.complete()
end
end,
i = function(fallback)
if cmp.visible() then
cmp.select_next_item({ behavior = cmp.SelectBehavior.Insert })
else
fallback()
end
end
}),
["<S-Tab>"] = cmp.mapping({
c = function()
if cmp.visible() then
cmp.select_prev_item({ behavior = cmp.SelectBehavior.Insert })
else
cmp.complete()
end
end,
i = function(fallback)
if cmp.visible() then
cmp.select_prev_item({ behavior = cmp.SelectBehavior.Insert })
else
fallback()
end
end
}),
['<Down>'] = cmp.mapping(cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }), {'i'}),
['<Up>'] = cmp.mapping(cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }), {'i'}),
['<C-n>'] = cmp.mapping({
c = function()
if cmp.visible() then
cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
else
vim.api.nvim_feedkeys(t('<Down>'), 'n', true)
end
end,
i = function(fallback)
if cmp.visible() then
cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
else
fallback()
end
end
}),
['<C-p>'] = cmp.mapping({
c = function()
if cmp.visible() then
cmp.select_prev_item({ behavior = cmp.SelectBehavior.Select })
else
vim.api.nvim_feedkeys(t('<Up>'), 'n', true)
end
end,
i = function(fallback)
if cmp.visible() then
cmp.select_prev_item({ behavior = cmp.SelectBehavior.Select })
else
fallback()
end
end
}),
['<C-b>'] = cmp.mapping(cmp.mapping.scroll_docs(-4), {'i', 'c'}),
['<C-f>'] = cmp.mapping(cmp.mapping.scroll_docs(4), {'i', 'c'}),
['<CR>'] = cmp.mapping({
i = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = true }),
c = function(fallback)
if cmp.visible() then
cmp.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = true})
else
fallback()
end
end
}),
},
formatting = {
fields = { "kind", "abbr", "menu" },
format = function(entry, vim_item)
vim_item.kind = string.format("%s", kind_icons[vim_item.kind])
vim_item.menu = ({
nvim_lsp = "[LSP]",
ultisnips = "[Snippet]",
buffer = "[Buffer]",
path = "[Path]",
})[entry.source.name]
return vim_item
end,
},
sources = cmp.config.sources({
{ name = 'nvim_lsp' },
{ name = 'ultisnips' },
}, {
{ name = 'buffer' },
{ name = 'path' },
})
}
cmp.setup.filetype('gitcommit', {
sources = cmp.config.sources({
{ name = 'cmp_git' },
}, {
{ name = 'buffer' },
})
})
cmp.setup.cmdline('/', {
completion = { autocomplete = false },
sources = {
{ name = 'buffer' }
}
})
cmp.setup.cmdline(':', {
completion = { autocomplete = false },
sources = cmp.config.sources({
{ name = 'path' }
}, {
{ name = 'cmdline' }
})
})
配置内容主要来自插件nvim-cmp主页Recommended Configuration部分。只是添加了一些nerd font 图标,并根据Wiki部分做了部分修改。
主页中使用的代码片段插件不是ultisnips。上面提供了多种选择。
启动补全
在文件~/.config/nvim/lua/lsp/init.lua 文件末添加如下内容。
require("lsp.cmp")
最终效果
|