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:
1. `vim_getenv` is followed by `TO_SLASH` when getting
path-related variables.
2. cmd exits when launched with forward slash.
Solution:
1. try calling `TO_SLASH` in `vim_getenv`.
2. pass fullpath via `lpApplicationName`, only include `cmd.exe`
in cmdline.
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
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: Cursor is not adjusted when 'cmdheight' is changed to cover
the cursor with 'splitkeep' ~= "cursor".
Solution: Handle window resize for 'splitkeep' after changing 'cmdheight'.
Ensure previous window height is set when changing 'splitkeep'
(Luuk van Baal).
closes: vim/vim#20043bd0f3e6da5
Co-authored-by: Luuk van Baal <luukvbaal@gmail.com>
Problem: A <Cmd> command in Insert mode can edit the current buffer,
e.g., with setline(). That edit appends to the current undo
block, but Insert mode does not know that the cursor line may
need to be saved again before the next typed edit. If the next
typed edit is a <BS> at the start of a line, it can join away
the line that was changed by the <Cmd> command before Insert
mode saves that updated line. The newest undo entry can then
still refer to the joined-away line, so undo sees a range past
the end of the buffer and fails with E438.
Solution: If a <Cmd> command in Insert mode changes the buffer, set
ins_need_undo so stop_arrow() refreshes Insstart. This lets
the next edit properly decide whether a new undo entry is
needed (Jaehwang Jung)
closes: vim/vim#20087
AI-assisted: Codex
e47daed442
Co-authored-by: Jaehwang Jung <tomtomjhj@gmail.com>
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: No message kind and multiple events for :recover and
(non-prompt) swapfile attention messages.
Solution: Assign these the "list_cmd" and "wmsg" kind.
Problem: when jumping to tags, will open URLs
(Srinivas Piskala Ganesh Babu)
Solution: Disallow trying to open remote files.
closes: vim/vim#20068
Supported by AI
ae196b2d58
Co-authored-by: Christian Brabandt <cb@256bit.org>
Problem: Modeline-tainted 'complete' values can invoke completion
callbacks outside the sandbox.
Solution: Enter the sandbox for both 'complete' callback phases and add
a regression test (Barrett Ruth)
closes: vim/vim#20078dd9b31fb62
Spell decorations from other lines aren't relevant to the current line.
Also, decor_redraw_col() can only go forward, while spell navigation
needs to go both forward and backward.
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.
Problem: There is no way to do something on CTRL-Z.
Solution: Add VimSuspend and VimResume autocommand events. (closesvim/vim#7450)
100118c73a
----
Nvim implemented these events first and has enough tests.
test_suspend.vim relies on Vim 'terminal' feature.
Treat it as N/A even if all tests could be ported as Lua functional
screen/terminal tests.
----
Co-authored-by: Bram Moolenaar <Bram@vim.org>
Problem: With `vim.g.health = { style = 'float' }`, running
`:checkhealth` from a `:help` buffer placed the float in the
top-left corner instead of centered.
make_floating_popup_options() picks the NW/NE/SW/SE anchor
and the available height from cursor-relative metrics
(winline(), wincol(), winheight()). When the caller passes
relative='editor', those metrics are meaningless, so the
function could flip to an 'E' anchor and clamp the float
off-screen.
Solution: When relative='editor', treat the whole editor area as
available space (lines_above=0, lines_below=&lines,
wincol=0). This makes the NW anchor the natural choice and
keeps the float position stable regardless of where the
cursor is in the current window.
AI-assisted: Claude Code
Problem: tests: Test_backupskip() may read from $HOME
Solution: Set $HOME to an empty value, use --clean
(D Ben Knoble)
Even though we unset HOME, we can see via scriptnames that user files
are still sourced! One of my installed plugins warns when not compiled
with +python3, so this test has a "press Enter" prompt.
Use `--clean` like most other GetVimProg()'s do to fix it. Some tests
use `system()` instead, but that turns this test into a failure rather
than passing; I'm not sure why other tests don't suffer from this.
To prove to ourselves, we can use code like this:
diff --git i/src/testdir/test_options.vim w/src/testdir/test_options.vim
index a408e20e1..044364a54 100644
--- i/src/testdir/test_options.vim
+++ w/src/testdir/test_options.vim
@@ -1179,6 +1179,7 @@ func Test_backupskip()
" P_NODUP). Run this in a separate instance and write v:errors in a file,
" so that we see what happens on startup.
let after =<< trim [CODE]
+ call writefile([execute('scriptnames')], 'foo')
let bsklist = split(&backupskip, ',')
call assert_equal(uniq(copy(bsklist)), bsklist)
call writefile(['errors:'] + v:errors, 'Xtestout')
@@ -1196,7 +1197,7 @@ func Test_backupskip()
" unset $HOME, so that it won't try to read init files
let saveenv['HOME'] = getenv("HOME")
call setenv('HOME', v:null)
- exe 'silent !' . cmd
+ exe 'silent !' . cmd .. ' --cmd "echo &rtp"'
call assert_equal(['errors:'], readfile('Xtestout'))
" restore environment variables
Here, that causes "foo" to include a bunch of files under ~/.vim. I'm
not sure why this happens, but lets paper over it for the test.
We can also tell that (orthogonal to --clean) setting HOME='' works too.
Let's do that in addition since unsetting HOME isn't quite enough.
closes: vim/vim#200518d9c383aaf
Co-authored-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Problem: On slow systems tests can be flaky.
Solution: Use TermWait() instead of term-wait(). (Yegappan Lakshmanan,
closesvim/vim#6756)
733d259a83
Co-authored-by: Bram Moolenaar <Bram@vim.org>
* build(vim-patch): test_clientserver.vim is N/A
Requires 'job' and 'clientserver' features.
Both N/A for Neovim.
* build(vim-patch): test_gui_init.vim is N/A
Starts with command 'CheckCanRuiGui' .
Nvim doesn't support ':gui' (yet).
* build(vim-patch): test_plugin_vimball.vim is N/A
Run ':h vimball'.
* build(vim-patch): test_remote.vim is N/A
Needs 'clientserver', 'terminal' features.
* build(vim-patch): test_short_sleep.py is for test_terminal.vim. Both N/A
* build(vim-patch): mark N/A files from test_crypt.vim
* build(vim-patch): mark N/A file for test_terminal3.vim
* build(vim-patch): mark N/A files channel/terminal/vim9 tests
Problem: There are many Git forges each with a different way of
constructing permanent links to like commits and tags.
Solution: Add a private utility function that computes these special
links on the best effort basis.
Problem: In `vim.pack.update()` confirmation buffer it might be useful
to be able to use `gx` (open link at cursor) when cursor is on
something like commit or tag.
Solution: Add `textDocument/documentLink` method support for the
in-process LSP. This may be used by LSP clients and makes `gx`
automatically work.
The shortcoming is that this requires tracking how to construct a URL
from source and commit/tag. Currently only GitHub hosted repositories
are supported.
Problem:
Cannot remove a `@conceal` highlight when defined in highlights.scm.
Solution:
Support a `@noconceal` highlight that works similarly to `@nospell` where it
overrides the conceal set on the range to remove it. Additionally, can
set the conceal metadata field to false for the same behavior.
Problem:
When ASAN detects an error in a child process, the process may be killed
by test teardown before ASAN finishes writing its log file. This results
in truncated logs with no stack trace.
Also, the sanitizer log content was only written to stdout (which the
test runner may not display) and not included in the test_assert error
message.
Solution:
- Poll (up to 2s) for the ASAN "SUMMARY" line before reading, so the
crashing process has time to finish writing.
- Include full log content in the test_assert error message, so it
appears in CI output regardless of stdout handling.
- Warn when the log appears truncated (no SUMMARY line found).
Problem: When Nvim exits while connecting to a socket it leads to
stack-buffer-overflow.
Solution: Associate the handle with the Stream and use the Stream's
internal_close_cb to update the "closed" status.
Problem:
The LSP client incorrectly checks for server capabilities when determining
support for self-mapped methods (e.g., 'shutdown'), which do not have
corresponding capabilities in the server's response. This leads to false
negatives when checking if such methods are supported.
This was handled correctly for dynamic registrations, but not for static.
Methods such as 'shutdown', do not have a related server capability and should
be assumed to be supported.
Solution:
Update the `supports_method` logic to always return true for self-mapped
methods.
Problem:
When running nvim on a remote machine over SSH, if there is high ping,
then bg detection may not complete in time. This results in a warning
every time nvim is started. #38648
Solution:
Restore 'ttyfast' option and allow it to control whether or not bg
detection is performed. Because this is during startup and before any
user config or commands, we use the environment variable
`NVIM_NOTTYFAST` to allow disabling `ttyfast` during initialization.
Problem:
Invalid `nvim_create_user_command` calls can leak the
`preview` callback reference after Neovim has taken ownership of it.
1. build with {a,l}san
2. run:
```sh
<path/to/nvim> --headless -u NONE --clean +'lua
for i = 1, 100 do
pcall(vim.api.nvim_create_user_command,
"some very epic stuff" .. i,
{}, -- NOTE: this is INVALID (not a function or string)
{ preview = function() end })
end
vim.cmd("qa!")
' +qa
```
3. see:
```
100 lua references were leaked!
```
Solution:
Clear `preview_luaref` in `err:`.
Problem: `nvim_set_keymap` leaks the `callback` `LuaRef` when the
LHS is too long.
Solution: Make `set_maparg_lhs_rhs` transfer `rhs_lua` to
`MapArguments` up front so the caller always owns the ref.
Problem:
On Windows, path separators may become inconsistent for various reasons,
which makes normalization quite painful.
Solution:
Normalize paths to `/` at the entry boundaries and always use it
internally, converting back only in rare cases where `\` is really
needed (e.g. cmd.exe/bat scripts?).
This is the first commit in a series of incremental steps.
Note:
* some funcs won't respect shellslash. e.g. `expand/fnamemodify`
* some funcs still respect shellslash, but will be updated in a follow
PR. e.g. `ex_pwd/f_chdir/f_getcwd`
* uv's built-in funcs always return `\`. e.g. `uv.cwd/uv.exepath`
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Problem:
We should not use "\" (backslashes) except where absolutely required.
See references in https://github.com/neovim/neovim/pull/37729
Solution:
There is no reason to use "\" slashes in the trust db, so don't.
Problem: filetype: some Beancount files are not recognized
Solution: Detect *.bean files as beancount filetype
(Bruno Belanyi)
closes: vim/vim#20037521eac1877
Co-authored-by: Bruno Belanyi <bruno@belanyi.fr>
Problem: `set all&` resets option values directly and leaves UI-derived state stale for `guicursor`, `laststatus`, and `showtabline`.
Solution: Repair some of the stale UI state in the bulk reset path by reparsing `guicursor`, refreshing statusline state, and recomputing tabline/window rows.
Problem:
LSP spec allows response message to have a null request-id.
This may happen when for example client sends unparseable request.
https://github.com/microsoft/language-server-protocol/issues/196
Solution:
Guard the server response branches against id=vim.NIL (json null),
and handle error responses with null id by logging a warning
and dispatching on error.
Problem:
CI (ubuntu asan, ubuntu tsan, windows) reports `uv_loop_close()
hang?` from the two new null-id response tests. The leaked
handle is the server-side accepted TCP socket created inside
`server:listen` callback. The tests closed only the listener
but not the accepted socket, so libuv could not finish shutting
down the loop and each test session took ~2s extra to exit.
Solution:
Hoist the accepted socket to the outer `exec_lua` scope and
close it at teardown before closing the listener. The close
runs synchronously inside `exec_lua`, so the loop has time to
dispose the handle before the session exits.
* test(lsp): close accepted socket on read-loop exit/error
Match the precedent in the handler test ("handler can return
false as response") and the shared `_create_tcp_server` helper
in `test/functional/plugin/lsp/testutil.lua`: close the
accepted socket from inside the `create_read_loop` exit/error
callbacks. The teardown close added in the previous commit
remains as belt-and-suspenders, so the socket is disposed
whether the server goes away first or the client does.
Problem:
`vim.secure.trust()` hashes an unchanged empty buffer as
a newline, so trusting an empty file by buffer never works.
Solution:
Hash unchanged empty-buffers `''` so buffer-based
trust matches the on-disk empty file.
Problem: Using `version=vim.version.range(...)` in plugin specification
is meant to use semver-like tags. Whether a tag is semver-like was
decided by a plain `vim.version.parse` which is not strict by default.
This allowed treating tags like `nvim-0.6` (which is usually reserved
for the latest revision compatible with Nvim<=0.6 version) like semver
tags and resulted in confusing behavior (preferring `nvim-0.6` tag
over `v0.2.2`, for example).
Solution: Use `vim.version.range(x, { strict = true })` to decide if the
tag name is semver-like or not. This allows tags like both `v1.2.3`
and `1.2.3` while being consistent in what Nvim thinks is a semver
string.
This is technically not a breaking change since it was documented that
only tags like `v<major>.<minor>.<patch>` will be recognized as
semver.