Problem:
For a given position, it is not easy to compare which of several other positions is closest to it.
Solution:
Add support for converting `vim.Pos` to a buffer byte offset.
This allows for sorting, e.g:
```lua
table.sort(positions, function(pos1, pos2)
return pos1:to_offset() < pos2:to_offset()
end
```
Or a binary search, e.g:
```lua
vim.list.bisect(positions, pos, { key = function(pos) return pos:to_offset() end })
```
Improve the vim.iter annotations with richer generics that track element and
tuple types through iterator pipelines, including multi-value stages and
list-specific methods.
Extend the LuaCATS parser and vimdoc generator so those richer generic classes
and overloads round-trip into the generated help. These annotations are only
supported by EmmyLua, so LuaLS still uses a broader fallback in _meta.lua.
AI-assisted: Codex
Problem: Entering the pager fails if <ESC> is remapped to :fclose by user.
Solution: Avoid executing mappings with nvim_feedkeys() that closes expanded cmdline.
Problem:
UI tools and orchestration engines need more context than just raw
socket addresses from serverlist(). Without knowing if a server belongs
to the current instance or knowing its PID, UIs cannot display
meaningful options to users.
Solution:
- Added the `info=v:true` option to `serverlist()`.
- When `info` is requested, it implies `peer=true` and returns a list of
dictionaries (defined as `vim.ServerInfo`) with `addr`, `pid` and
`own`.
- Uses an RPC request to `getpid()` across the socket to fetch the
peer's actual process ID.
Signed-off-by: Szymon Wilczek <swilczek.lx@gmail.com>
Problem:
Can't get a command's description from nvim_get_commands when
cmd is string.
Solution:
Returns "desc" field in nvim_get_commands.
`definition` is now empty when cmd is function type.
Problem:
When using `vim.list.unique` or `vim.list.bisect`, if the `key` function is
complex, it can degrade performance, because it is invoked on every comparison
Solution:
The `key` interface convention is designed specifically to address this issue;
performance can be improved by memoizing its results.
Also added the shorthand use of the field name string as the key.
Problem:
Nested workspace capabilities like workspace.fileOperations.didCreate and
workspace.textDocumentContent are not handled consistently for dynamic and
static registration provider lookup.
Solution:
Generate explicit registration-provider mappings from the LSP metadata and use
them when registering and querying capabilities. Add coverage for dynamic and
static nested workspace registrations.
Problem:
`diagnostic.status` only follows the `config.status.format` setting to determine how to display diagnostic signs. However, `signs` can actually also be configured via `config.signs.text`.
Solution:
If the user has set symbols via `config.status.format`, let that determine the content of `signs`; otherwise, use `config.signs.text` for display.
TODO: drop support `type(config.status.format) == 'table'`; users should just configure `config.signs.text` directly.
Problem:
`get_node_text()` returned inconsistent results between buffer and
string sources when a node's range ends at `end_col == 0` (i.e. the node
ends with a newline). The buffer path dropped the trailing newline; the
string path included it correctly.
Solution:
Append `'\n'` in `buf_range_get_text()` when `end_col == 0` and
`start_row ~= end_row`. The `start_row ~= end_row` guard excludes
zero-width nodes at column 0, which should return `""`.
Remove the workaround in the `#trim!` directive that manually
compensated for the missing newline.
Strip whitespace in `resolve_lang()` so injection language nodes ending
at `end_col == 0` (e.g. `">lua\n"`) still resolve correctly.
Problem:
The fallback that tokenizes `eap->arg` by unescaped whitespace (when the
parser doesn't pre-split via `EX_EXPAND` etc.) lives in `nlua_do_ucmd`,
so only user-command callbacks got `eap.fargs`. Builtin commands routed
through `nlua_call_excmd` have to re-parse the args themselves
(e.g. `M.ex_lsp`).
Solution:
- Move the tokenization into `nlua_push_eap` so every Lua handler sees
`eap.fargs`. Keep only the `EX_NOSPC` override in `nlua_do_ucmd` (the
`nargs=1`/`?` case which is genuinely user-command-specific).
- Drop the re-parse in `M.ex_lsp`.
Problem:
LSP clients previously did not handle dynamic registration for off-spec methods
Solution:
Update the client logic to assume support for dynamic registration when
the method is unknown. Adjust the registration provider fallback and
enhance tests to verify correct behaviour for unknown methods and their
registration options. This improves compatibility with servers using
custom dynamic registrations.
AI-assisted: OpenCode
Problem:
The argument to `:help` is normalized to fit the general tag format.
I.e. i^U-default, iCTRL-U-default and i_CTRL_U-default should all point
to the i_CTRL_U-default tag. Our normalization adds an underscore around
the CTRL keycode, e.g. iCTRL-GCTRL-J becomes i_CTRL-G_CTRL-J. That's not
necessary if the following part starts with a dash, like the case of
iCTRL-U-default.
Solution:
Do not insert an underscore if the following character is a dash/minus
(-).
docs: update instructions for debugging LSP
Previously, it was suggested to set:
vim.lsp.log.set_format_func(vim.inspect)
This made sense before f72c13341a, when
`format_func` was called once per argument being logged, but since that
commit it's called with the log level followed by the other args, so the
suggested setting would call `vim.inspect(log_level, ....)` which would
just print the human readable name of the current log level and no other
details, for example with this set I saw in my logs:
"DEBUG""DEBUG""DEBUG""DEBUG"
Instead just rely on the default formatter, which will:
> ... log the level, date, source and line number of the
caller, followed by the arguments.
Problem:
After 55ceb31, z= and tselect don't work if `vim.ui.select` is an async
provider (especially terminal buffers).
Solution:
Drop the `vim.wait()` approach, use an async approach.
fix#39506
Problem:
followup to 55ceb314ca#39478
`:oldfiles` and swapfile `:recover` do not delegate to `vim.ui.select`.
Solution:
- Delegate to `vim.ui.select`.
- Fix a long-standing `recover_names` bug where `concat_fnames(dir_name,
files[i], true)` produced malformed `<dir>//<dir>/<file>` paths (also
fixes `swapfilelist()`).
Problem:
- Various `TermRequest` handlers which all do similar things.
- `tty.query` is specific to `XTGETTCAP DCS`, can't be reused for other kinds of terminal queries.
Solution:
Provide `tty.request()`.
Problem: completion: no support for "noinsert" with 'wildmode' and
commandline completion
Solution: Add "noinsert" value to the 'wildmode' option, mirroring
'completeopt' "noinsert" behaviour (glepnir).
fixes: vim/vim#16551closes: vim/vim#20080af494af5ff
Problem:
If a buffer's filetype changes after the LSP client has already
attached (e.g. from json to jsonc via a modeline), but the client
supports both filetypes, it stays attached. It does not notify the
server of the new languageId, causing the server to incorrectly process
the file using the old languageId.
Solution:
Save the languageId used during textDocument/didOpen, and send
textDocument/didClose + textDocument/didOpen when buffer's languageId
changed.
Lsp spec:
0003fb53f1/_specifications/lsp/3.18/textDocument/didOpen.md (L5)
> If the language id of a document changes, the client
> needs to send a textDocument/didClose to the server followed by a
> textDocument/didOpen with the new language id if the server handles
> the new language id as well.
AI-assisted: Gemini 3.1 Pro
Problem:
The threshold to consider a color as bright is too high, and for some colors the
foreground is set to white when it should be black.
Solution:
Change the threshold to 0.179. This value is taken from pastel-textcolor.
Problem:
Similar to clearmatches(), it's always necessary to provide a fallback
that allows the user to do a "global reset" when something goes wrong.
Solution:
vim.img.del(math.huge) clears all images.
Use kitty's d=A command to clear all placements in a single
escape sequence rather than N individual deletes, also freeing stored
image data not referenced by the scrollback buffer.
Problem:
Error when querying document symbols using python-lsp-server:
lsp/util.lua:1955: attempt to concatenate field 'containerName' (a userdata value)
Solution:
Check for `vim.NIL`.
Problem:
`:tselect` and `z=` (spell suggest) have their own bespoke select menus.
Solution:
- Delegate to `vim.ui.select` instead.
- Bonus:
- `:tselect` gains mouse support. `print_tag_list` didn't suport mouseclick.
This causes some minor regressions, which are not blockers:
- `z=` no longer draws the list right-left if 'rightleft' is set.
- TODO: can/should `vim.ui.select` / `vim.fn.inputlist()` handle that?
- `:tselect`
- No "column" headings (`# pri kind tag file`).
- No highlighting: (HLF_T: tag name, HLF_D: file, HLF_CM: extra fields).
- TODO: can `vim.ui.select()` support highlighted chunks (`[[text, hl_id], ...]`) ?
fix https://github.com/neovim/neovim/issues/25814
fix https://github.com/neovim/neovim/issues/31987
After an edit, LanguageTree:_edit() updates the current trees. When the
LanguageTree manages explicit regions, _edit() also refreshes _regions
from tree:included_ranges(true), so those regions have the edited byte
offsets.
A later injection pass may call set_included_regions() with a different
number of child regions. That path discards the old trees and emits
changedtree callbacks for them. invalidate(true) does the same when a
buffer is reloaded. Before this change, both discard paths called
tree:included_ranges(true) for every old tree, even if _edit() had just
collected those exact ranges.
That duplicate range extraction is expensive with many injection trees.
Realistic shapes include generated C files with many macro bodies parsed
by the C preproc_arg injection, Markdown documents with many fenced blocks
of the same language, and template files with many embedded-language
islands. The stock highlighter registers recursive changedtree callbacks,
so this is on the normal highlighting edit path.
Track whether _regions currently came from tree:included_ranges(true)
with _regions_from_tree_ranges. _do_changedtree_callbacks() reuses
_regions only in that state; otherwise it falls back to calling
tree:included_ranges(true). Clear the marker when regions are replaced by
injection ranges, when a tree is reparsed, or when trees are discarded.
This avoids keeping a second copy of the ranges while preserving callback
precision: changedtree still receives tree:included_ranges(true) for the
old tree, not the broader managed region.
Benchmark on 100k C macro injections, one-line edit, recursive
changedtree callback:
- HEAD median: edited parse 84.2 ms, child region replacement 58.7 ms
- This change: edited parse 34.6 ms, child region replacement 8.5 ms
That is about 2.4x faster for the edit parse and 6.9x faster for child
region replacement in this workload.
Add a regression test that replacing injection regions still fires
changedtree and still reports the old tree's exact included ranges.
AI-assisted: Codex
Problem:
`v:starttime`, `:uptime` use a monotonic high-resolution timer. This
only works as long as the timer keeps running (if the computer is
suspended the timer is paused). This is somewhat unintuitive, and
doesn't match the behavior of the `uptime` shell command.
Solution:
Implement `os_realtime` to get the real time since the
epoch in nanoseconds.
Move vim.wait into runtime/lua/vim/_core/editor.lua and replace
the C entrypoint with narrow vim._core helpers for polling, UI
flushing, and interrupt checks.
Keep the existing interval semantics by retaining the dummy timer that
wakes the loop while it is otherwise idle.
Update the docs to describe the success return values correctly, and
adjust the test expectation for the new vim.validate() callback error.
AI-assisted: Codex
Problem:
BufModifiedSet autocmd only triggered for current buffer during
redraw, causing delayed events when :wa writes non-current buffers.
Solution:
- Use the aucmd_defer approach to implement `Optionset modified`.
- Drop BufModifiedSet.