如何在 neovim 中設(shè)置 lsp
在本教程中,我們將學(xué)習(xí)如何設(shè)置 lsp 以進(jìn)行自動(dòng)完成并了解每個(gè)包的作用。
我們還將設(shè)置自動(dòng)格式化。
Neovim 支持 lsp,這意味著它充當(dāng) lsp 的客戶端。
Neovim 有內(nèi)置的 lsp 支持。但neovim對(duì)于如何使用它沒有任何意見。Neovim 把這個(gè)留給了用戶。
了解這一切是什么
當(dāng)您在 neovim 中打開文件時(shí),lsp 客戶端將附加到緩沖區(qū)。
附加的客戶端依賴于 lsp 中的文件類型和配置。
然后客戶端將開始與安裝在您計(jì)算機(jī)上的語(yǔ)言服務(wù)器對(duì)話。
lsp 通過代碼完成、錯(cuò)誤診斷和更多功能進(jìn)行響應(yīng)。
讓我們啟動(dòng) neovim 并將 lsp 客戶端附加到緩沖區(qū)
我們將嘗試啟動(dòng) lsp 客戶端并將客戶端附加到緩沖區(qū)。
初始化lua
vim.lsp.start_client({ name = 'my-server-name', cmd = {'lua-language-server'}, root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]), })
參數(shù):
name是用戶指定的服務(wù)器名稱,必須是唯一的
cmd在您的計(jì)算機(jī)上啟動(dòng)語(yǔ)言服務(wù)器
root_dir檢查模式
upward = true就是從上往下開始尋找
將 setup.py (或 root_dir 中提到的任何類型的文件)放在init.lua 旁邊,然后 lsp 客戶端將識(shí)別該模式并附加到緩沖區(qū)(在本例中為 init.lua )。
運(yùn)行 :lua =vim.lsp.get_active_clients() 會(huì)給你一個(gè)lua表。
如果表為空,則 lsp 不會(huì)附加并且運(yùn)行 :lua =vim.lsp.get_active_clients() 將給出空表 {} 。
如果一切順利的話你可以看到這一點(diǎn)。
lsp 已連接并與語(yǔ)言服務(wù)器通信。
您可以看到警告彈出init.lua。
或者您可以打開任何 lua 文件,只需放置 setup.py 或 pyproject.toml,lsp 將附加到緩沖區(qū)。
您還可以僅輸入代碼即可完成代碼<C-x><C-o>,出于某種原因,這對(duì)我不起作用,所以我使用<C-o><C-n><C-p>(下一個(gè)和上一個(gè)完成項(xiàng))。
鍵入vim.將光標(biāo)放在 后面。并在插入模式下按<C-o><C-n><C-p>。
如果一切正常,您將看到一個(gè)彈出窗口打開。
像這樣:
我將介紹每個(gè)插件及其在 LSP 設(shè)置中各自的作用。
這將使您全面了解 LSP 設(shè)置中每個(gè)插件的功能和用途。
neovim/nvim-lspconfig
這是 neovim 的官方插件,其中包含服務(wù)器配置和更多功能,可幫助您設(shè)置 lsp 客戶端并啟動(dòng)特定語(yǔ)言服務(wù)器。
刪除所有以前的代碼。
安裝lspconfig,我在這里使用lazy.vim,你可以使用你最喜歡的包管理器
初始化lua
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not vim.loop.fs_stat(lazypath) then vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", "--branch=stable", -- latest stable release lazypath, }) end vim.opt.rtp:prepend(lazypath) require("lazy").setup({ -- All of the packages goes here "neovim/nvim-lspconfig" } })
現(xiàn)在導(dǎo)入 lspconfig
初始化lua
{...} -- Setup language servers. local lspconfig = require('lspconfig')
現(xiàn)在 lspconfig 直接給出所有可用 lsps 的默認(rèn)設(shè)置的配置。查看適用于您的編程語(yǔ)言的配置(https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md)。
初始化lua
local lspconfig = require('lspconfig') -- Python lspconfig.pyright.setup {} -- Typescript , Javascript lspconfig.tsserver.setup {} -- Lua lspconfig.lua_ls.setup {}
現(xiàn)在您可以為每個(gè) lsp 設(shè)置提供自定義選項(xiàng)。請(qǐng)參閱您的編程語(yǔ)言的配置(https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md)。
初始化lua
lspconfig.rust_analyzer.setup { -- Server-specific settings. See `:help lspconfig-setup` settings = { ['rust-analyzer'] = {}, }, }
現(xiàn)在我們想要在 lsp 客戶端附加到緩沖區(qū)時(shí)設(shè)置一些鍵盤映射。
這里創(chuàng)建了一個(gè)autocmd
-- Use LspAttach autocommand to only map the following keys -- after the language server attaches to the current buffer vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('UserLspConfig', {}), callback = function(ev) -- Enable completion triggered by <c-x><c-o> vim.bo[ev.buf].omnifunc = 'v:lua.vim.lsp.omnifunc' -- Buffer local mappings. -- See `:help vim.lsp.*` for documentation on any of the below functions local opts = { buffer = ev.buf } vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts) vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts) vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts) vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts) vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, opts) vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, opts) vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, opts) vim.keymap.set('n', '<space>wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts) vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, opts) vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, opts) vim.keymap.set({ 'n', 'v' }, '<space>ca', vim.lsp.buf.code_action, opts) vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts) vim.keymap.set('n', '<space>f', function() vim.lsp.buf.format { async = true } end, opts) end, })
這里定義了一些有用的鍵盤映射,例如
這里定義了一些有用的鍵盤映射,例如gdfor going to defination。
再次,如果我們按下,<C-o><C-n><C-p> 我們將像以前一樣彈出完成窗口。
我們希望自動(dòng)觸發(fā)完成彈出窗口,類似于 IDE。
所以我們必須使用另一個(gè)名為nvim_cmp的包(https://github.com/hrsh7th/nvim-cmp)
刪除自動(dòng)命令
hrsh7th/nvim-cmp
安裝 nvim_cmp 和一些源。
require("lazy").setup({ 'hrsh7th/nvim-cmp', -- Autocompletion plugin, 'hrsh7th/cmp-nvim-lsp', -- LSP source for nvim-cmp, 'saadparwaiz1/cmp_luasnip', -- Snippets source for nvim-cmp 'L3MON4D3/LuaSnip', -- Snippets plugin "neovim/nvim-lspconfig", } })
這里的
這里的來源是什么意思
Nvim_cmp 從許多來源獲取完成信息,例如
cmp-nvim-lsp從語(yǔ)言服務(wù)器獲取數(shù)據(jù)
luasnip從片段中獲取數(shù)據(jù)并展開 nvim_cmp 的片段
buffersNeovim 中從緩沖區(qū)獲取數(shù)據(jù)
在這里,我們使用 cmp 的功能擴(kuò)展 lsp 的功能,以利用 cmp 的功能。
并創(chuàng)建了一個(gè) lua 表,其中包含我想要設(shè)置的所有 lsp 服務(wù)器。
local capabilities = require("cmp_nvim_lsp").default_capabilities() local luasnip = require 'luasnip' local servers = { "lua_ls" "tsserver" } for _, lsp in ipairs(servers) do lspconfig[lsp].setup { -- on_attach = my_custom_on_attach, capabilities = capabilities, } end
設(shè)置 Nvim-cmp
導(dǎo)入 Nvim-cmp
local cmp = require 'cmp' cmp.setup { }
片段
Nvim-cmp 不“知道”如何擴(kuò)展片段,這就是我們需要 luasnip 的原因。
這個(gè)回調(diào)函數(shù)從片段中獲取數(shù)據(jù),這里 cmp 需要擴(kuò)展它。
所以我們使用 luasnip 來擴(kuò)展代碼片段。
cmp.setup { snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, },}
來源
Nvim-cmp 需要來源向我們展示建議
來源包括來自語(yǔ)言服務(wù)器、緩沖區(qū)等的響應(yīng)。
cmp.setup { snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, sources = { { name = 'nvim_lsp' }, {name = "buffer"}, },}
為 Nvim-cmp 定義鍵盤映射
cmp.setup { snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, sources = { { name = 'nvim_lsp' }, {name = "buffer"}, }, mapping = cmp.mapping.preset.insert({ ['<C-u>'] = cmp.mapping.scroll_docs(-4), -- Up ['<C-d>'] = cmp.mapping.scroll_docs(4), -- Down ['<C-j>'] = cmp.mapping.select_next_item(), ['<C-k>'] = cmp.mapping.select_prev_item(), ['<C-Space>'] = cmp.mapping.complete(), ['<CR>'] = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = true, }, }),}
最后,我們?cè)诖蜃謺r(shí)得到了自動(dòng)完成功能。
保存時(shí)格式化
現(xiàn)在我們有了自動(dòng)完成功能。我們應(yīng)該設(shè)置一種方法來格式化我們的代碼。
使用 lsp 格式化緩沖區(qū)的一種方法是調(diào)用 vim.lsp.buf.format()
像這樣調(diào)用 :lua vim.lsp.buf.format() 應(yīng)該格式化緩沖區(qū)。
為此,我們必須定義一個(gè)自動(dòng)命令,它將 vim.lsp.buf.format() 在保存緩沖區(qū)之前運(yùn)行。
async選項(xiàng)告訴如何格式化,如果 true 則格式是 非阻塞的(當(dāng) vim 理想時(shí),將會(huì)發(fā)生緩沖區(qū)的格式化)。
我這樣做是 async = false 因?yàn)闊o論如何我想在保存緩沖區(qū)之前格式化緩沖區(qū)。
vim.api.nvim_create_autocmd('BufWritePre', { callback = function() vim.lsp.buf.format { async = false, } end, })
這將在保存任何緩沖區(qū)時(shí)運(yùn)行,這是我們不希望的,因?yàn)槿绻?lsp_client 未附加到緩沖區(qū),那么它將拋出錯(cuò)誤。
因此,只有當(dāng) lsp 附加到緩沖區(qū)時(shí),我們才必須定義上述自動(dòng)命令。
我們必須創(chuàng)建另一個(gè)自動(dòng)命令,該命令僅在連接 lsp 時(shí)運(yùn)行。
vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('lsp-attach-format', { clear = true }), -- This is where we attach the autoformatting for reasonable clients callback = function(args) local client_id = args.data.client_id local client = vim.lsp.get_client_by_id(client_id) local bufnr = args.buf if not client.server_capabilities.documentFormattingProvider then return end vim.api.nvim_create_autocmd('BufWritePre', { group = get_augroup(client), buffer = bufnr, callback = function() if not format_is_enabled then return end vim.lsp.buf.format { async = false, filter = function(c) return c.id == client.id end, } end, }) end,})
lsp.buf.format 中的過濾器接收客戶端并且必須返回布爾值。false 將停止請(qǐng)求格式化緩沖區(qū)。
例如:
這不會(huì)格式化附加了 tsserver 的緩沖區(qū)。
-- Never request typescript-language-server for formattingvim.lsp.buf.format { filter = function(client) return client.name ~= "tsserver" end}
就是這樣。
詳細(xì)配置示例:https://github.com/rishavmngo/dot-files
參考:
nvim-lua/kickstart.nvim(https://github.com/nvim-lua/kickstart.nvim)
lsp 的 Neovim 文檔(https://neovim.io/doc/user/lsp.html)
文章來源地址http://www.zghlxwxcb.cn/article/342.html文章來源:http://www.zghlxwxcb.cn/article/342.html
到此這篇關(guān)于如何在 neovim 中設(shè)置 lsp的文章就介紹到這了,更多相關(guān)內(nèi)容可以在右上角搜索或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!