Easy Guide on Setting Native Neovim LSP with Lua
- Node.js & npm
- package manager for nvim (recommended: Packer ) you can also use lazy or Vim-Plug
use 'neovim/nvim-lspconfig'
use 'williamboman/mason.nvim'
use 'williamboman/mason-lspconfig.nvim'
use 'hrsh7th/nvim-cmp'
use 'hrsh7th/cmp-nvim-lsp'
use 'hrsh7th/cmp-path'
use 'L3MON4D3/LuaSnip'
use 'saadparwaiz1/cmp_luasnip'
use 'ray-x/lsp_signature.nvim'
use 'RRethy/vim-illuminate'
local M = {}
local status_cmp_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
if not status_cmp_ok then
vim.notify "cmp_nvim_lsp plugin not found"
return
end
local signature_status, lsp_signature = pcall(require, "lsp_signature")
if not signature_status then
vim.notify "lsp_signature plugin not found"
return
end
local illuminate_status, illuminate = pcall(require, "illuminate")
if not illuminate_status then
vim.notify "illuminate plugin not found"
return
end
local opts = { noremap = true, silent = true }
local keymap = vim.api.nvim_buf_set_keymap
local lsp_keymaps = function(bufnr)
keymap(bufnr, "n", "<leader>gd", "<cmd>lua vim.lsp.buf.declaration()<CR>", opts)
keymap(bufnr, "n", "<leader>gD", "<cmd>lua vim.lsp.buf.definition()<CR>", opts)
keymap(bufnr, "n", "<leader>gim", "<cmd>lua vim.lsp.buf.implementation()<CR>", opts)
keymap(bufnr, "n", "<leader>gr", "<cmd>lua vim.lsp.buf.references()<CR>", opts)
keymap(bufnr, "n", "<leader>ca", "<cmd>lua vim.lsp.buf.code_action()<CR>", opts)
keymap(bufnr, "n", "<leader>r", "<cmd>lua vim.lsp.buf.rename()<CR>", opts)
keymap(bufnr, "n", "<C-h>", "<cmd>lua vim.lsp.buf.signature_help()<CR>", opts)
keymap(bufnr, "n", "K", "<cmd>lua vim.lsp.buf.hover()<CR>", opts)
keymap(bufnr, "n", "<leader>e", "<cmd>lua vim.diagnostic.open_float()<CR>", opts)
keymap(bufnr, "n", "<C-n>", "<cmd>lua vim.diagnostic.goto_next({buffer=0})<CR>", opts)
keymap(bufnr, "n", "<C-p>", "<cmd>lua vim.diagnostic.goto_prev({buffer=0})<CR>", opts)
end
local signature_cfg = {
hint_enable = false,
floating_window = true,
check_completion_visible = true,
toggle_key = '<M-t>',
select_signature_key = '<M-s>',
}
M.capabilities = cmp_nvim_lsp.default_capabilities()
M.on_attach = function(client, bufnr)
if client.name == "tsserver" then
client.server_capabilities.documentFormattingProvider = false -- comment this if you are not using null-ls for formatting;
end
client.server_capabilities.semanticTokensProvider = nil
lsp_keymaps(bufnr)
illuminate.on_attach(client)
lsp_signature.on_attach(signature_cfg, bufnr)
end
return M
- We could have put these inside our lsp file but this way we can reuse this file in language specific config also
- Change the keymaps according to your liking
- I have excluded formatting bind as i use format on save i.e.
vim.cmd [[autocmd BufWritePre * lua vim.lsp.buf.format()]] -- put this on your init.lua file
local mason_status, mason = pcall(require, "mason")
if not mason_status then
return
end
local mason_lsp_status, masonlsp = pcall(require, "mason-lspconfig")
if not mason_lsp_status then
return
end
local lsp_status, lspconf = pcall(require, "lspconfig")
if not lsp_status then
return
end
local servers = {
'sumneko_lua',
'tsserver',
-- add other lsp server name here
}
mason.setup {
ui = {
border = "none",
icons = {
package_installed = "✓",
package_pending = "➜",
package_uninstalled = "✗"
},
},
log_level = vim.log.levels.INFO,
max_concurrent_installers = 4,
}
masonlsp.setup {
ensure_installed = servers,
automatic_installation = true,
}
for _, lsp in pairs(servers) do
lspconf[lsp].setup {
on_attach = require("user.lsp-handlers").on_attach,
capabilities = require("user.lsp-handlers").capabilities,
flags = {
debounce_text_changes = 150,
},
settings = {
Lua = {
diagnostics = {
globals = { 'vim' }
}
}
}
}
end
- You can see available language servers from here and add that inside servers table above
local luaship_status, luasnip = pcall(require, "luasnip")
if not luaship_status then
return
end
local cmp_status, cmp = pcall(require, "cmp")
if not cmp_status then
return
end
local snippet_status, csnip = pcall(require, "luasnip/loaders/from_vscode")
if not snippet_status then
return
end
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 borderstyle = {
border = { "╭", "─", "╮", "│", "╯", "─", "╰", "│" },
winhighlight = "Normal:CmpPmenu,CursorLine:PmenuSel,Search:None",
}
cmp.setup {
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
["<M-k>"] = cmp.mapping.select_prev_item(), -- select suggestion up
["<M-j>"] = cmp.mapping.select_next_item(), -- select suggestion down
["<M-i>"] = cmp.mapping(cmp.mapping.scroll_docs(-1), { "i", "c" }), -- scroll docs up
["<M-o>"] = cmp.mapping(cmp.mapping.scroll_docs(1), { "i", "c" }), -- scroll docs down
["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
["<C-y>"] = cmp.config.disable,
["<C-e>"] = cmp.mapping {
i = cmp.mapping.abort(),
c = cmp.mapping.close(),
},
["<CR>"] = cmp.mapping.confirm { select = true },
}),
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]",
luasnip = "[Snippet]",
buffer = "[Buffer]",
path = "[Path]",
})[entry.source.name]
return vim_item
end,
},
sources = {
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "buffer" },
{ name = "path" },
{ name = "nvim_lua" },
},
confirm_opts = {
behavior = cmp.ConfirmBehavior.Replace,
select = false,
},
window = {
completion = borderstyle,
documentation = borderstyle,
},
experimental = {
ghost_text = false,
native_menu = false,
},
}
csnip.lazy_load { paths = vim.fn.stdpath "config" .. "/snippets" }
require('user.lsp.nvim-lsp')
require('user.lsp.nvim-cmp')