Problem: `vim.lsp.enable()` skips all buffers with non-empty `buftype`,
including `help` buffers. LSPs targeting `filetype='help'` never
auto-attach despite help buffers being real files.
Solution: Expand the `buftype` guard in `lsp_enable_callback` to allow
`help` alongside `""`.
The `buffer` option remains functional but is now undocumented.
Providing both will raise an error. Since providing `buf` was disallowed
before, there is no code that will break due to using `buffer` alongside
`buf`.
Problem:
Checking the extension of a file is done often, e.g. in Nvim's codebase
for differentiating Lua and Vimscript files in the runtime. The current
way to do this in Lua is (1) a Lua pattern match, which has pitfalls
such as not considering filenames starting with a dot, or (2)
fnamemodify() which is both hard to discover and hard to use / read if
not very familiar with the possible modifiers.
vim.fs.ext() returns the file extension including the leading dot of
the extension. Similar to the "file extension" implementation of many
other stdlibs (including fnamemodify(file, ":e")), a leading dot
doesn't indicate the start of the extension. E.g.: the .git folder in a
repository doesn't have the extension .git, but it simply has no
extension, similar to a folder named git or any other filename without
dot(s).
Problem:
No way to iterate configs. Users need to reach
for `vim.lsp.config._configs`, an internal interface.
Solution:
Provide vim.lsp.get_configs().
Also indirectly improves :lsp enable/disable completion
by discarding invalid configs from completion.
refactor(lua): add integer coercion helpers
Add vim._tointeger() and vim._ensure_integer(), including optional base
support, and switch integer-only tonumber()/assert call sites in the Lua
runtime to use them.
This also cleans up related integer parsing in LSP, health, loader, URI,
tohtml, and Treesitter code.
supported by AI
Problem:
In autocmd examples, using "args" as the event-object name is vague and
may be confused with a user-command.
Solution:
Use "ev" as the conventional event-object name.
`lsp.config[]` resolves an LSP config the first time it is called, and
returns the cached result on subsequent calls.
The change in #37571 added an extra call to `lsp.config[]` which will
resolve the config *before* the server is added to `_enabled_configs`,
meaning the result is discarded. That means configs will be needlessly
resolved again once `lsp_enable_callback` fires for the first time. That
includes an additional `loadfile()` call which is relatively expensive
and can have unexpected side effects.
Avoid this by storing the result of the initial call to `lsp.config[]`
in `_enabled_configs` so the config is not resolved a second time once
`lsp_enable_callback` is called for the first time.
Problem:
When quitting Nvim, LSP servers will not be force-stopped, even if
ClientConfig.exit_timeout is set to an integer.
pkill emmylua_ls; VIMRUNTIME=runtime/ nvim --clean -u repro.lua repro.lua ; waitpid $(pgrep emmylua_ls)
vim.lsp.config('foo', { cmd = { 'emmylua_ls' }, exit_timeout = 1000 })
vim.lsp.enable('foo')
vim.defer_fn(vim.cmd.quit, 500)
Solution:
On VimExit, wait up to `exit_timeout:integer` milliseconds for servers
to exit. Do this with an explicit `vim.wait()`, because the actual
force-stop is deferred, and won't be attempted if Nvim exits before
then.
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Problem:
Two cases lsp.enable() won't work in the first FileType event
1. lsp.enable luals inside FileType or ftplugin/lua.lua, then:
```
nvim a.lua
```
2. lsp.enable luals inside FileType or ftplugin/lua.lua, then:
```
nvim -> :edit a.lua -> :mksession! | restart +qa! so Session.vim
```
Solution:
Currently `v:vim_did_enter` is used to detected two cases:
1. "maunally enabled" (lsp.enable() or `:lsp enable`)
2. "inside FileType event"
To detect 2. correctly we use did_filetype().
Problem:
If `vim.lsp.enable()` fails with an error, either because `'*'` is one
of the provided names or because there is an error in a config,
`vim.lsp.enable()` will still have side-effects:
- All names before the one that encountered an error will still be added
to `lsp._enabled_configs`, but the autocommand will not get added or
run.
- Any name which makes `vim.lsp.config[name]` error will be added to
`lsp._enabled_configs`, causing all future calls to `vim.lsp.enable()`
to fail. This will also break `:che vim.lsp`.
Solution:
- Check all names for errors before modifying `lsp._enabled_configs`.
- Check `vim.lsp.config[name]` does not raise an error before enabling
the name.
Work on #37166
- Dynamic Registration Tracking via Provider
- Supports_Method
- Multiple Registrations
- RegistrationOptions may dictate support for a method
Problem:
- Despite [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig)
claims to be a "data-only" plugin, in fact it still provides some
user-facing commands because they haven't been upstreamed to Nvim.
Solution:
- Upstream `:LspRestart`, `:LspStart` and `:LspStop` commands as `:lsp
restart`, `:lsp start` and `:lsp stop` respectively.
Co-authored-by: glepnir <glephunter@gmail.com>
Problem:
The `flags` field calls its sub-fields "experimental".
But `exit_timeout` is now used for multiple purposes.
Solution:
Graduate `exit_timeout` to a top-level ClientConfig field.
Problem:
LSP server may not exit even after the client was stopped/disabled via enable(false).
Solution:
Automatically force-stop after a timeout, unless `client.flags.exit_timeout = false`.
This warning doesn't really make sense, since the `enable()` call is
meant to be run before the `lsp.config` calls. It will be logged many
times (once for each enabled LSP) at startup.
This is especially annoying because calling `enable()` after
configuration causes the first opened buffer not to have its filetype
set in some situations. This is a separate bug which really needs to be
fixed, and makes this superfluous logging more likely.
Problem:
If a client doesn't have a config then an error may be thrown.
Probably caused by: 2f78ff816b
Lua callback: …/lsp.lua:442: attempt to index local 'config' (a nil value)
stack traceback:
…/lsp.lua:442: in function 'can_start'
…/lsp.lua:479: in function 'lsp_enable_callback'
…/lsp.lua:566: in function <…/lsp.lua:565>
Solution:
Not all clients necessarily have configs.
- Handle `config=nil` in `can_start`.
- If user "enables" an invalid name that happens to match a *client*
name, don't auto-detach the client.
* fix(lsp): type of root_dir should be annotated with string|fun|nil
* feat(lsp): support root_dir as function in _get_workspace_folders
* feat(lsp): let checkhealth support root_dir() function
Examples:
vim.lsp: Active Clients ~
- lua_ls (id: 1)
- Version: <Unknown>
- Root directories:
~/foo/bar
~/dev/neovim
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Problem: The registerCapability handler re-enables document_color,
making it impossible to disable it in LspAttach.
Solution: Enable it once on initialization and avoid re-enabling
on registerCapability.
Problem:
LSP logs show misleading "cannot start" messages when editing a filetype
NOT listed in the `config.filetypes` field.
[ERROR][2025-09-13 18:55:56] …/runtime//lua/vim/lsp/log.lua:151
"cannot start cssls due to config error: …/runtime//lua/vim/lsp.lua:423:
cmd: expected expected function or table with executable command,
got table: 0x0104701b18. Info: vscode-css-language-server is not executable"
Solution:
- `can_start`: check `config.filetypes` before checking the rest of the
config.