Problem: filetype: some html files are wrongly recognized as htmlangular
Solution: Use the \< atom to anchor ng-template and ng-content to start
of word (truffle)
Prevent false-positive htmlangular detection on words containing
'ng-template' or 'ng-content' as a substring (e.g. 'song-template',
'sing-content'). Anchor both branches with \< to require a word start,
matching the \<DTD\s\+XHTML\s idiom used five lines below.
related: neovim/neovim#39778.
closes: vim/vim#20246354ab1a69e
Co-authored-by: truffle <truffleagent@gmail.com>
(cherry picked from commit f3bb21e71d)
Problem:
Visual selection could end up in the wrong place after
nvim_buf_set_text or nvim_buf_set_lines. In some delete cases,
Visual.lnum was already clamped before the line shift happened, so the
adjustment got skipped.
Solution:
Split fix_cursor_cols into reusable fix_pos_col logic and reuse it
for Visual updates. Also adjust Visual.lnum before changed_lines so
the shift uses the original position before final clamping.
(cherry picked from commit 450ba41436)
Problem:
During startup, we manually trigger a useless and misleading `OptionSet`
event, which doesn't set `v:option_*` values (this is a limitation of
`nvim_exec_autocmds`).
ad4bc2d90c/runtime/lua/vim/_core/defaults.lua (L939).
Solution:
The `nvim_exec_autocmds('OptionSet',…)` call does not serve any purpose
since 5cbb9d613b, so just drop it.
Problem:
After closing and reopening Neovim, ]' and [' fail with E92: Buffer 0
not found for marks restored from ShaDa. Direct jumps like 'a work
because mark_get_local() rewrites fnum before returning, but ]' uses
getnextmark() which does not, leaving fnum = 0.
Solution:
Set .fnum = buf->b_fnum when restoring local marks from ShaDa.
(cherry picked from commit 0b96b3cd52)
Problem:
On "arm clang unittest" CI job, `make unittest` sometimes fails with
lots of these messages:
FAILED test/unit/testutil.lua @ 773: ...
test/unit/testutil.lua:773:
test/unit/testutil.lua:297: declaration specifier expected near '_Static_assert' at line 429
exit code: 256
stack traceback:
test/unit/testutil.lua:773: in function 'itp_parent'
test/unit/testutil.lua:811: in function <test/unit/testutil.lua:801>
Solution:
Update filter_complex_blocks.
(cherry picked from commit adb5d8a646)
Problem:
Using the `DiffTool` plugin (e.g. through `nvim -d ...` or `:DiffTool
<file1> <file2>` fails if a space is in one of the paths. This occurs
because the `diff` wraps the paths with quotes (`'`) if space
characters are present, which the line diff regex fails to parse.
Solution:
Update regex to handle quoted paths by matching the string within the
quotes, if it exists.
(cherry picked from commit 1e09b020e5)
Problem:
When using prompt_appendbuf with multi-element list,
the first item is concated and rest replace the prompt instead of
inserting the lines before the prompt.
Solution:
Concat first element with replace_buf and insert the rest of the list
with set_buffer_lines.
Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
(cherry picked from commit a977e1077b)
Problem: 'title' is updated when changing the name of a non-current
buffer with nvim_buf_set_name().
Solution: Set RedrawingDisabled when renaming the buffer.
(cherry picked from commit 96fc7c150f)
Problem: Message redirection column for captured output is not reset
after :echon since (4260f73, e63346df).
Solution: Ensure msg_ext_append is set before the kind with :echon.
(cherry picked from commit ce9f4f0369)
Problem: The four pointer-resolution loops in u_read_undo() lack
an i != j guard, so a header whose uh_next.seq equals
its own uh_seq resolves uh_next.ptr to itself. On
buffer close, u_freeheader() sees uhp->uh_next.ptr !=
NULL and skips updating b_u_oldhead, so u_blockfree()
dereferences the freed header on the next iteration.
The same pattern applies to uh_prev, uh_alt_next and
uh_alt_prev. A crafted .un~ file in the same directory
as a text file can trigger the use-after-free and
subsequent double-free when the buffer is closed.
(Daniel Cervera)
Solution: Add an i != j guard to each of the four resolution
loops, matching the guard already present in the
duplicate-detection loop above.
closes: vim/vim#20168
Supported by AI
4f610f07b7
Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit 2d5f56c0aa)
Problem: Crash with invalid shellredir/shellpipe value
(bfredl)
Solution: Validate the option and allow only a single "%s".
fixes: vim/vim#20157closes: vim/vim#2015984ae09dd79
Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit ffe87d91f7)
Problem:
- Empty ranges have different `<`, `<=`, `has` and `intersect` semantics compared to regular ranges.
- `to_inclusive_pos` assumes that the end position of a range is exclusive, which is not true for empty ranges
Solution:
Special case empty ranges in these operations.
(cherry picked from commit 416f3482e7)
Problem:
When `:!` writes shell output to a buffer, write_output() splits on `\r`, `\n`,
and `\r\n`, replacing the terminator byte with NUL. For a binary-mode buffer
this is wrong: `\r` should be preserved verbatim, not treated as a line
terminator. This wrong behavior causes a file like `\r\n` round-trips through
`:%!cat` to `\n`.
This was masked when 'shelltemp' was enabled, because output went through a temp
file and the regular file I/O path handled binary-mode correctly. Switching the
default to 'noshelltemp' exposed the bug, since output is now piped directly
into write_output().
Solution:
In `write_output()`, skip the `\r` and `\r\n` splits for a binary-mode buffer;
only split on `\n`.
(cherry picked from commit 832a68835b)
Problem: select_spec tests a public function, but uses private enums.
Solution: Replace private enums with public enums.
(cherry picked from commit a61c8f3580)
feat(tui): restore 'ttyfast' to control tty requests
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.
Co-authored-by: Kyle <50718101+kylesower@users.noreply.github.com>
Problem: read_compound() in spellfile.c computes the size of the regex
pattern buffer using signed-int arithmetic on the attacker
controlled SN_COMPOUND sectionlen. With sectionlen=0x40000008
and UTF-8 encoding active the multiplication wraps to 27 while
the per-byte loop writes up to ~1B bytes, overflowing the heap.
Reachable when loading a crafted .spl file (e.g. via 'set spell'
after a modeline sets 'spelllang'). The cp/ap/crp allocations
have the same int + 1 overflow class (Daniel Cervera)
Solution: Use type size_t as buffer size and reject values larger than
COMPOUND_MAX_LEN (100000). Apply the same size_t treatment to
the cp/ap/crp allocations.
Github Advisory:
https://github.com/vim/vim/security/advisories/GHSA-q4jv-r9gj-6cwv9299332917
Co-authored-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit 0976ce255b)
Problem:
parser_gc() calls ts_parser_delete() but leaves the userdata pointer
pointing to freed memory. If the GC finalizer runs at an unexpected time
(e.g. inside nvim_buf_get_lines #39411), a stale pointer could cause a crash.
Solution:
- NULL out `*ud` after ts_parser_delete() in parser_gc()
- Update parser_check() to handle NULL with a clear error message,
guarding all parser methods against UAF
Co-authored-by: Lewis Russell <lewis6991@gmail.com>
Signed-off-by: Szymon Wilczek <swilczek.lx@gmail.com>
(cherry picked from commit 0c3e6e1b0e)
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 })
```
Co-authored-by: Yi Ming <ofseed@foxmail.com>
Problem: Entering the pager fails if <ESC> is remapped to :fclose by user.
Solution: Avoid executing mappings with nvim_feedkeys() that closes expanded cmdline.
(cherry picked from commit 2b7a00746d)
Problem: When closing gvim with an unsaved unnamed buffer, choosing
"Yes" in the "Save changes?" dialog and then "Cancel" in the
file selection dialog either silently writes the buffer to a
file named "Untitled" (overwriting any existing file with
that name) or discards the buffer altogether
(vibs29, after v9.1.0265).
Solution: In dialog_changed(), if browse_save_fname() leaves the buffer
without a file name, treat it as a cancel and return without
saving. Also stop clearing the modified flag in the restore
path on write failure, so the unsaved changes are kept and
the caller (e.g. gui_shell_closed()) can also cancel the
close. Pre-fill the file dialog with "Untitled" to match
the preceding "Save changes to ..." prompt. Add a test for
the write-failure path (Hirohito Higashi).
fixes: vim/vim#20132closes: vim/vim#20143cf947e7ef0
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
(cherry picked from commit 2bb426ce4a)
Problem: Cannot set 'path' option via modeline (zeertzjq, after v9.2.0435)
Solution: Revert the part that disallows setting 'path' via modeline.
closes: vim/vim#2013788fb739918
Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit d1c3d6fbaa)
Problem: [security]: Backticks enclosed shell commands in the 'path'
option value are executed during completion (q1uf3ng).
Solution: Skip path entries containing backticks, add P_SECURE to 'path'
option, so that it cannot be set from a modeline (for symmetry with
the 'cdpath' option)
Github Advisory:
https://github.com/vim/vim/security/advisories/GHSA-hwg5-3cxw-wvvg
Supported by AI.
190cb3c2b9
Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit b06f8b174f)
Problem:
The `nvim_get_chan_info()` terminal channel test used `shell-test INTERACT` to verify that `jobstop()` reports an unhandled `SIGHUP` as exit code `129`.
`INTERACT` reads from stdin with `fgets()`, so closing the PTY could race with `SIGHUP` delivery. If `fgets()` observed EOF first, `shell-test` exited normally with code `0`, causing intermittent failures on slower sanitizer builds.
Solution:
Add a `shell-test HOLD` mode that prints a readiness prompt and then waits without reading stdin. Use it for the `SIGHUP` assertion so PTY EOF cannot make the helper exit normally before the signal path is observed.
(cherry picked from commit 78111e5371)
Problem:
`EBUSY` during cleanup:
Windows CI can intermittently fail `pack_spec.lua` with `EBUSY` while removing
`site/pack/core/opt/plugindirs`.
This can happen because:
- the test Nvim session may still be alive when `after_each()` removes the pack
directory
- Windows does not allow removing a directory while another process still has an
open handle below it
- startup-time `vim.pack.add()` performs a real `git clone`, so process and file
handle release timing can vary on slower runners
Startup timeout:
The startup tests can also fail before cleanup because they wait for `_G.done`
with a fixed timeout. That timeout includes the time needed for startup to run
`vim.pack.add()` and finish the local clone.
Solution:
Close before cleanup:
Capture the pack, lockfile, and log paths while the test Nvim session is still
available, then call `n.check_close()` before removing the pack directory.
Extend Windows startup wait:
Increase the `_G.done` retry budget only on Windows so startup-time
`vim.pack.add()` has more time to finish on slower CI runners.
(cherry picked from commit 19a2ef5afa)
Problem:
This test would sometimes fail to match lines starting with `.` (indicating throttling) due to a race condition, likely because throttling completed before the test could properly assert.
Solution:
I 6x'd the amount of test data we were pushing into `nvim` in an attempt to trigger throttling consistently.
I don't _love_ this solution as it is still non-deterministic and might not hold up over time.
A good solution would be: create a deterministic way to pause neovim in a functional test, assert on the temporarily throttle state, then unpause neovim. However, it's likely this is not possible today and will take too much effort.
Before test time (30000 lines): ~0.40sec/run
After test time (150000 lines): ~1.7sec/run
This increases test runtime, but if it removes flakes I think it's worth it.
(cherry picked from commit cbedd537ac)
Problem: When an error line in a file passed to :cfile / :cgetfile is
longer than IOSIZE, qf_parse_file_pfx() copies the tail
into the fixed-size IObuff with STRMOVE(), overflowing the heap buffer.
The same code path can also loop indefinitely because
qf_parse_file_pfx() always returns QF_MULTISCAN when a
tail is present, and qf_init_ext() unconditionally goes
to "restofline" without bounding the tail length (Nabih).
Solution: Remove the STRMOVE() into IObuff. In the QF_MULTISCAN
branch, alias linebuf into the tail directly and update
linelen, requiring strict progress (new length less than
the previous length) before retrying; otherwise ignore
the line.
closes: vim/vim#20126
Supported by AI
77677c33de
Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit 0e69a38026)
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.
(cherry picked from commit 7ed5609439)
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
(cherry picked from commit 344d984ed2)
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
(-).
(cherry picked from commit 84ae70c172)
fix(lsp): send didClose, didOpen when languageId changes
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
Co-authored-by: phanium <91544758+phanen@users.noreply.github.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`.
(cherry picked from commit 1799aaebda)
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.
(cherry picked from commit 46c83ce321)
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.
(cherry picked from commit 4ed2e66d2e)
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.
(cherry picked from commit f83d0b9653)
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:`.
(cherry picked from commit 393f687503)
Co-authored-by: Barrett Ruth <62671086+barrettruth@users.noreply.github.com>
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.
(cherry picked from commit 58aad59e1c)
Co-authored-by: Barrett Ruth <62671086+barrettruth@users.noreply.github.com>