This configuration uses Neovim 0.11+ native LSP support with nvim-lspconfig. LSP server configurations can be overridden using files in the after/lsp/ directory.
How LSP configuration works
The LSP system has three layers:
Base configuration - nvim-lspconfig defaults
Global overrides - Settings in lua/plugins/lsp/init.lua
Server-specific overrides - Files in after/lsp/*.lua
Server-specific overrides in after/lsp/ are automatically merged with base configurations. Create a file named after/lsp/servername.lua to customize any LSP server.
Enabled language servers
The following LSP servers are enabled by default:
vim . lsp . enable ({
"lua_ls" , -- Lua
"gopls" , -- Go
"tsgo" , -- TypeScript/JavaScript
"angularls" , -- Angular
"biome" , -- Biome (JS/TS/JSON)
"bashls" , -- Bash/Shell
"cssls" , -- CSS/SCSS/Less
"html" , -- HTML
"jsonls" , -- JSON
"yamlls" , -- YAML
"dockerls" , -- Docker
"clangd" , -- C/C++
"tailwindcss" , -- Tailwind CSS
"emmet_language_server" , -- Emmet
"harper_ls" , -- Grammar/spell checking
})
Creating LSP overrides
Create override file
Create a file in after/lsp/ matching the server name: touch after/lsp/lua_ls.lua
Add configuration
Return a table with your custom settings: return {
settings = {
Lua = {
diagnostics = {
globals = { "vim" },
},
workspace = {
library = { vim . env . VIMRUNTIME },
},
},
},
}
Restart Neovim
Restart Neovim for changes to take effect.
Example configurations
Lua language server
Complete configuration for Lua development with Neovim API support:
return {
settings = {
Lua = {
diagnostics = {
globals = { "vim" },
disable = { "inject-field" , "undefined-field" , "missing-fields" },
},
runtime = { version = "LuaJIT" },
workspace = {
library = { vim . env . VIMRUNTIME },
checkThirdParty = false ,
},
telemetry = { enable = false },
},
},
}
Go language server (gopls)
Optimized gopls configuration with performance tuning:
return {
cmd = { "gopls" },
settings = {
gopls = {
codelenses = {
generate = false ,
gc_details = false , -- Very CPU intensive
test = false ,
tidy = false ,
vendor = false ,
regenerate_cgo = false ,
upgrade_dependency = false ,
run_govulncheck = false , -- Very CPU intensive, run manually
},
hints = {
assignVariableTypes = true ,
compositeLiteralFields = true ,
compositeLiteralTypes = true ,
constantValues = true ,
functionTypeParameters = true ,
parameterNames = true ,
rangeVariableTypes = true ,
},
analyses = {
nilness = true ,
unusedparams = true ,
unusedwrite = true ,
ST1003 = false , -- Disable naming convention checks
undeclaredname = true ,
fillreturns = true ,
nonewvars = true ,
},
usePlaceholders = false ,
completeUnimported = true ,
staticcheck = false , -- Save CPU/memory
matcher = "Fuzzy" ,
diagnosticsDelay = "1000ms" ,
symbolMatcher = "fuzzy" ,
directoryFilters = {
"-.git" ,
"-.vscode" ,
"-.idea" ,
"-node_modules" ,
},
semanticTokens = true ,
gofumpt = true ,
},
},
}
Some gopls analyzers and codelenses are disabled for performance reasons. Enable them selectively based on your needs.
TypeScript language server (tsgo)
Comprehensive TypeScript configuration with inlay hints and code lens:
return {
capabilities = {
workspace = {
didChangeWatchedFiles = {
dynamicRegistration = true ,
},
},
},
init_options = {
maxTsServerMemory = 8192 ,
},
settings = {
typescript = {
suggest = {
autoImports = true ,
includeAutomaticOptionalChainCompletions = true ,
includeCompletionsForImportStatements = true ,
classMemberSnippets = { enabled = true },
objectLiteralMethodSnippets = { enabled = true },
},
preferences = {
importModuleSpecifierPreference = "shortest" ,
importModuleSpecifierEnding = "auto" ,
includePackageJsonAutoImports = "auto" ,
preferTypeOnlyAutoImports = false ,
quoteStyle = "auto" ,
organizeImports = {
typeOrder = "last" ,
caseSensitivity = "auto" ,
},
},
inlayHints = {
parameterNames = {
enabled = "all" ,
suppressWhenArgumentMatchesName = false ,
},
parameterTypes = { enabled = true },
variableTypes = {
enabled = true ,
suppressWhenTypeMatchesName = false ,
},
propertyDeclarationTypes = { enabled = true },
functionLikeReturnTypes = { enabled = true },
enumMemberValues = { enabled = true },
},
implementationsCodeLens = {
enabled = true ,
showOnInterfaceMethods = true ,
showOnAllClassMethods = true ,
},
referencesCodeLens = {
enabled = true ,
showOnAllFunctions = true ,
},
},
},
}
Configuration options
Available override fields
Settings
Capabilities
Command and initialization
Root directory detection
return {
settings = {
-- Server-specific settings object
},
}
Global LSP settings
Global settings apply to all language servers:
local capabilities = require ( "blink.cmp" ). get_lsp_capabilities ({
textDocument = {
foldingRange = {
dynamicRegistration = false ,
lineFoldingOnly = true ,
},
},
})
vim . lsp . config ( "*" , {
capabilities = capabilities ,
root_markers = { ".git" },
})
Diagnostic configuration
Customize diagnostic display globally:
vim . diagnostic . config ({
severity_sort = true ,
float = { border = "rounded" , source = true },
underline = true ,
update_in_insert = false ,
signs = {
text = {
[ vim . diagnostic . severity . ERROR ] = " " ,
[ vim . diagnostic . severity . WARN ] = " " ,
[ vim . diagnostic . severity . INFO ] = " " ,
[ vim . diagnostic . severity . HINT ] = " " ,
},
},
virtual_text = false , -- Using tiny-diagnostic plugin
})
Inlay hints
Inlay hints are disabled by default but can be toggled with <leader>ih:
vim . api . nvim_create_autocmd ( "LspAttach" , {
callback = function ( args )
local bufnr = args . buf
local client = vim . lsp . get_client_by_id ( args . data . client_id )
if client . server_capabilities . inlayHintProvider then
vim . lsp . inlay_hint . enable ( false , { bufnr = bufnr })
end
end ,
})
Inlay hints can impact performance in large files. They’re disabled by default and can be enabled per-buffer with <leader>ih.
Adding a new language server
Install the server
Install the language server globally: npm install -g typescript-language-server
Enable in configuration
Add the server to the enabled list: vim . lsp . enable ({
"lua_ls" ,
"gopls" ,
"typescript-language-server" , -- Add your server
-- ... other servers
})
Create override file (optional)
Create after/lsp/typescript-language-server.lua if you need custom settings.
Troubleshooting
Check LSP status
View logs
:lua vim.cmd.edit ( vim.lsp.get_log_path ())
Restart LSP server
Check which servers are attached
: lua print ( vim . inspect ( vim . lsp . get_clients ()))