LSP Configuration
Language Server Protocol configuration, keybindings, and diagnostic workflows.
Basic LSP Setup
Minimal lspconfig configuration
local lspconfig = require('lspconfig')
lspconfig.lua_ls.setup({})
lspconfig.pyright.setup({})
lspconfig.bashls.setup({})
Common Language Servers
Servers for daily use
lspconfig.lua_ls.setup({}) -- Lua (sumneko)
lspconfig.pyright.setup({}) -- Python
lspconfig.bashls.setup({}) -- Bash/Zsh
lspconfig.jsonls.setup({}) -- JSON
lspconfig.yamlls.setup({}) -- YAML
lspconfig.gopls.setup({}) -- Go
lspconfig.rust_analyzer.setup({}) -- Rust
lspconfig.clangd.setup({}) -- C/C++
lspconfig.ts_ls.setup({}) -- TypeScript/JavaScript
The on_attach Pattern
Define keymaps when LSP attaches to a buffer
local on_attach = function(client, bufnr)
local opts = { buffer = bufnr, noremap = true, silent = true }
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, opts)
vim.keymap.set('n', '<leader>f', function()
vim.lsp.buf.format({ async = true })
end, opts)
end
Apply on_attach to all servers
lspconfig.lua_ls.setup({ on_attach = on_attach })
lspconfig.pyright.setup({ on_attach = on_attach })
lspconfig.bashls.setup({ on_attach = on_attach })
Diagnostic Navigation
Navigate and display diagnostics
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev) -- previous diagnostic
vim.keymap.set('n', ']d', vim.diagnostic.goto_next) -- next diagnostic
vim.keymap.set('n', '<leader>e', vim.diagnostic.open_float) -- show in float
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist) -- send to loclist
Configure diagnostic display
vim.diagnostic.config({
virtual_text = true, -- inline diagnostic text
signs = true, -- signs in the gutter
underline = true, -- underline problematic code
update_in_insert = false, -- don't update while typing
severity_sort = true, -- errors before warnings
float = {
border = 'rounded',
source = true, -- show which LSP produced the diagnostic
},
})
Formatting
Format on save with autocommand
vim.api.nvim_create_autocmd('BufWritePre', {
callback = function()
vim.lsp.buf.format({ async = false })
end,
})
Format only with specific LSP client
vim.lsp.buf.format({
filter = function(client)
return client.name == 'null-ls' -- use only null-ls for formatting
end,
})
Server-Specific Configuration
lua_ls with Neovim runtime awareness
lspconfig.lua_ls.setup({
on_attach = on_attach,
settings = {
Lua = {
runtime = { version = 'LuaJIT' },
diagnostics = {
globals = { 'vim' }, -- recognize vim global
},
workspace = {
library = vim.api.nvim_get_runtime_file('', true),
checkThirdParty = false,
},
telemetry = { enable = false },
},
},
})
pyright with venv detection
lspconfig.pyright.setup({
on_attach = on_attach,
settings = {
python = {
analysis = {
typeCheckingMode = 'basic',
autoSearchPaths = true,
useLibraryCodeForTypes = true,
},
},
},
})
Capabilities (for nvim-cmp integration)
Advertise completion capabilities to LSP servers
local capabilities = require('cmp_nvim_lsp').default_capabilities()
lspconfig.lua_ls.setup({
on_attach = on_attach,
capabilities = capabilities,
})
Inspecting LSP Status
Check active clients and server status
:LspInfo " show attached LSP clients and their status
:LspLog " open LSP log file
:LspRestart " restart LSP clients for current buffer
:LspStop " stop all LSP clients
:LspStart " start LSP clients for current buffer
LSP Keybindings Summary
Standard LSP keymaps at a glance
gd " go to definition
gD " go to declaration
gr " find references
gi " go to implementation
K " hover documentation
<leader>rn " rename symbol
<leader>ca " code action
<leader>f " format buffer
[d " previous diagnostic
]d " next diagnostic
<leader>e " diagnostic float
<leader>q " diagnostics to location list