Different language servers need different startup commands, file type associations, and workspace detection rules. Users and plugins need a unified way to define and share LSP configurations without code duplication.
LSP Configuration in Neovim uses vim.lsp.config() to define server settings, with a merge chain that allows global defaults, plugin configs, and user overrides to coexist.
- Definition:
vim.lsp.config('server-name', {cmd = {...}, filetypes = {...}, root_markers = {...}}) - Enable:
vim.lsp.enable('server-name')activates auto-attachment for matching buffers - Merge Priority (lowest to highest):
- Global config (
vim.lsp.config('*', {...})) lsp/<name>.luafiles in runtimepathafter/lsp/<name>.luafiles- Direct config calls in init.lua
- Global config (
- Config sources: Can also use
lsp/<name>.luafiles in runtimepath
- Wildcard
*config applies to all servers - root_markers can be nested for equal-priority markers:
{{'a', 'b'}, 'c'} - Config merging uses
vim.tbl_deep_extendwith “force” behavior - Config files in runtimepath may be eagerly evaluated (performance note)
- “after/” directory follows standard Vim after-directory semantics
- Built from: vim.lsp — the framework providing config()
- Builds into: LSP Client — configs become client instances via enable/start
- Related: Root Markers — part of config that determines workspace
- Related: LSP Events — lifecycle events triggered for configured clients
- Using
vim.lsp.config['name']has side-effect of resolving config - Prefer
vim.lsp.is_enabled()to check status without resolving - Place configs in
after/lsp/to override plugin defaults