11191 Commits

Author SHA1 Message Date
glepnir
f97bcbdf77 vim-patch:9.2.1007: fuzzy completion list wrongly sorted after complete() (#41498)
Problem:  With 'completeopt' "fuzzy", a re-sort after complete() leaves the
          last match unsorted at the end of the list.
Solution: Find the original text by its flag instead of assuming
          compl_shows_dir points at it.

closes: vim/vim#21103

7e74722999
2026-08-26 04:32:09 +00:00
zeertzjq
35ae925da9 vim-patch:9.2.1002: filetype: bazelrc files are not recognized (#41496)
Problem:  filetype: bazelrc files are not recognized
Solution: Detect *.bazelrc and tools/bazel.rc files as bazelrc filetype,
          include syntax und filetype plugins, update the menus
          (Barrett Ruth).

closes: vim/vim#21146

1afe7ad1bb

Co-authored-by: Barrett Ruth <br@barrettruth.com>
2026-08-26 11:41:17 +08:00
Justin M. Keyes
604bda445a test(harness): avoid overlong socket filename #41488
Problem:
Test sockets live under `$TMPDIR`, which the harness points at the build
dir.  On macOS/BSD `sockaddr_un.sun_path` is 104 bytes, and a CI build
path plus "nvim.<pid>.<n>" leaves little room:

    /Users/runner/work/neovim/neovim/build/Xtest_tmpdir_terminal/nvim.runner/aBcDeF/nvim.12345.0

Solution:
Point XDG_RUNTIME_DIR (`stdpath('run')`) at "/tmp/nvim_<pid>".  28 bytes:

    /tmp/nvim_19916/nvim.19919.1

TODO?:
- `TEMP_DIR_NAMES` prefers `$TMPDIR` over `/tmp`, so on macOS
  `stdpath('run')` defaults to the long `/var/folders/<xx>/<…>/T/` path
  instead of the short `/tmp` alias.

Note:
- The 104-byte limit applies to the `bind()` arg, not its "realpath",
  to, so `/tmp/…` symlinks can be used to workaround the limit.
2026-08-25 14:15:48 -04:00
Justin M. Keyes
aae43789d7 revert: "fix(lsp): do not expand $VAR in tagfunc filenames" #41489
revert c5cb0350a6
2026-08-25 18:09:02 +00:00
Justin M. Keyes
8adf6e769f Merge #41484 from justinmk/retrytest
test(harness): support `{retries=…}`
2026-08-25 11:05:16 -04:00
Justin M. Keyes
3aa323584e test(shell): unreliable "throttles shell-command output…" 2026-08-25 16:37:32 +02:00
Justin M. Keyes
03dfbe333c test(channel): unreliable "chansend sends lines…" 2026-08-25 16:34:21 +02:00
Justin M. Keyes
8ca36b9783 test(harness): support {retries=…}
Problem:
Cannot retry a test with its full `after_each`/`before_each` lifecycle.

Solution:
- Overload `it()` to accept an `opts` param:
  ```
  it('flaky', { retries = 2 }, function(ctx) end) -- 3 attempts.
  ```
- Pass `ctx` to test functions.
- Fix a bug in `t.read_file_list()`.
2026-08-25 16:34:21 +02:00
Justin M. Keyes
fb5d466a35 fix(help): :helptags on CRLF helpfiles
Problem:
The vimdoc parser does not treat "\r" as whitespace, so in a CRLF
helpfile the codeblock rule fails and "*" pairs inside examples become
tags. On Windows this generates duplicate "." and "/" tags:

    D:/a/neovim/neovim/build/bin/nvim.exe -u NONE -i NONE -e --headless -c "helptags ++t doc" -c "exe 'cquit' !empty(v:errmsg)""
    Error in command line:
    E154: Duplicate tag "." in gui.txt and repeat.txt
    E154: Duplicate tag "/" in pattern.txt and usr_08.txt
    E154: Duplicate tag "/" in usr_08.txt and pattern.txt

Note: The old C parser was unaffected because it read helpfiles in text
mode (`os_fopen(…, "r")`).

Solution:
Strip "\r" before parsing.
2026-08-25 13:36:01 +02:00
Justin M. Keyes
1ef030f162 fix(help): :helptags regressions
Problem:
Parent commit regressed some behavior of the old C helptags-gen impl:
- helpfiles in sub-directories are named by basename, so :help fails
- "help-tags" always names "tags", never "tags-nl"
- E150 E151 E152 E153 are never reported
- existing tags file is not overwritten if no tags were found
- a duplicate tag aborts the run, skipping the remaining directories
- `*.TXT` and `*.FRX` (uppercase) are not recognized as helpfiles
- tree-sitter-vimdoc accepts tags that the C parser rejected: `*a|b*`
  (breaks |links|) and unterminated `*tag`
- PUC Lua "<" compares with (localized) `strcoll()`, so the tags file is
  not sorted by byte value (E432)
- requiring `vim.treesitter` at load time breaks :help itself, not just
  :helptags, where the module is unavailable
- `vim.pack` runs :helptags for plugins that have no "doc/" directory,
  so every install/update emits E150

Solution:
- Fail the build if generating helptags reports any `v:errmsg`.
- Restore old behavior: tags generated for runtime/doc are now identical
  to those from the C implementation. Errors are non-fatal messages
  instead of exceptions, so all directories are still processed.
- Load treesitter lazily, report a plain error if parser is missing.
2026-08-25 13:32:25 +02:00
Yochem van Rosmalen
b36b3d7f3a feat(help): generate :helptags using Treesitter
Problem:
Tags are manually parsed in C which is not flexible and prone to errors.
Extending the help system to allow for other formats (e.g. Markdown)
would require a large rewrite in the C core, while with Treesitter it
only needs a query update.

Solution:
Use the power of treesitter to extract the tags from helpfiles.

- build: set `$VIMRUNTIME` when generating helptags, like
  `cmake/Util.cmake` already does for other generators.
- fix(help): only accept tags delimited by whitespace. The old C parser
  only accepted a `*tag*` preceded by start-of-line or whitespace and
  followed by whitespace or end-of-line. The vimdoc parser also captures
  tags followed by other text, e.g. `*$XDG_STATE_HOME*/.../logs` in
  starting.txt, which caused an E154 duplicate tag error for docs that
  were previously fine.
2026-08-25 10:52:12 +02:00
Justin M. Keyes
a1de07418b feat(ui2): lift ui2 options into 'messagesopt' (part 1) #41474
Problem:
In order for ui2 to graduate to the main "messages ui" its configuration
needs to graduate into actual options.

Solution:
Migrate some of its config to 'messagesopt':
- "maxheight" (note: currently this is an integer treated as
  a "percentage"; if we want to support a row count we could allow
  values with units, like `"42%"`)
- "pager"
- "timeout"

Also improves error messages:

    messagesopt=hit-enter,history:500,bogus       E474: Unknown item 'bogus'
    messagesopt=hit-enter,history:500,progress:x  E474: 'progress' must be one of: , c
    messagesopt=hit-enter,history:abc             E474: 'history' requires a number
2026-08-25 03:58:17 -04:00
Justin M. Keyes
510f61555b fix(ui2): pager_char does not match keycode aliases #41468
Problem:
`pager_char` compares the `keytrans()` result exactly. E.g. `pager_char
= "<cr>"` does not work, it expects `<CR>` (uppercase).

Solution:
Normalize `pager_char` in enable().
2026-08-24 15:01:50 -04:00
Étienne Robert
d1b811b6c2 fix(lsp): advance inline completion range over matching input #41434
Problem:
`Completor:accept()` applies the item against the range the server
answered with. Characters typed after the request but before accepting
lie inside neither that range nor the replacement, so they are left
behind by the insertion: typing `a` after `foob` and accepting `foobar`
within the 200 ms debounce yields `foobara`.

Solution:
Grow the item's range over the characters typed since the request, so
that `accept()` replaces them. Only while the candidate still matches,
decided by the longest common prefix `show()` already computes, so input
that contradicts it is left alone. Snippet items are unaffected, since
`accept()` ignores the range for them.
2026-08-24 10:51:39 -04:00
Justin M. Keyes
8c440c469b fix(ui2): ui2 misinterprets getchar() input as pager_char (CR) #41465
Problem:
`vim.on_key()` callbacks are invoked for keys that `getchar()` consumes,
which never reach the main loop, and there is no way to tell such a key
apart.  With ui2 the `on_key` handler activated after interactive
cmdline, handles it as `pager_char` (thus enters the ui2 pager).

Solution:
Skip `vim.on_key()` callbacks during `getchar()`/`getcharstr()`.
2026-08-24 10:18:47 -04:00
Étienne Robert
143bbfbb12 fix(lsp): clamp inline completion range to the line on accept #41435
Problem:
`Completor:accept()` hands the item's range straight to
`nvim_buf_set_text()`. That range is resolved when the response arrives,
so text deleted before accepting (backspacing within the 200 ms
debounce) leaves the range end past what is left of the line, and the
accept aborts with `Invalid 'end_col': out of range`.

18d6436 fixed the same staleness in `Completor:show()`, which drops an
item once its range *start* falls outside the buffer. That guard does
not cover the range end, and `accept()` was never given one, so this is
a hole left by that fix rather than a regression of it.

Solution:
Clamp the end of the range to the end of the line before writing. The
start needs no clamp: `show()` dropped the item one event loop tick
earlier if it had gone out of range.
2026-08-24 08:41:57 -04:00
glepnir
2a382ffb61 fix(lsp): completion overwrites text before the word boundary #41463
Problem:
One item with an edit range before the word boundary changes the start
column for all items. For tsserver `str.`, completing `str.char` can
become `strcharAt` and break filtering.

Solution:
Prepend the missing text and filter on it.
2026-08-24 06:14:49 -04:00
glepnir
92d5531f96 vim-patch:9.2.1001: complete_info() does not report the item highlight groups (#41461)
Problem:  complete_info() omits "abbr_hlgroup" and "kind_hlgroup".
Solution: Keep the highlight group ID with the match instead of the
          resolved attribute and add both entries to the returned items
          (glepnir).

closes: vim/vim#21105

1c32cede0a
2026-08-24 14:35:25 +08:00
zeertzjq
70958dae75 vim-patch:9.2.0996: debugger: crash when evaluating a variable in a :def function frame (#41460)
Problem:  In the debugger ">up" and ">frame" select an older function call
          frame, but get_funccal_local_ht() and the related functions check
          current_funccal while returning a dictionary of the frame that
          get_funccal() selected.  A :def function keeps its local variables
          on the vim9 stack, its funccall_T has no l: and a: dictionaries and
          is allocated cleared, so with such a frame selected the returned
          hashtab has a NULL ht_array and evaluating any variable name at the
          debug prompt crashes in hash_lookup().
Solution: Check the funccal that is actually used and return NULL when it has
          no l: variables, so that the variable is reported as undefined
          instead.

closes: vim/vim#21111

5ad47b07af

Co-authored-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 09:27:46 +08:00
Justin M. Keyes
eeefe7ca65 fix(cwd): E812 when a message opens a window during a file read #41458
Problem:
When ui2 is enabled, opening an already-open file in another Neovim
instance results in the error `E812: Autocommands changed buffer…`.

Analysis:
On E812 the file is not loaded.  The default SwapExists handler notifies
`W325: Ignoring swapfile…`, and ui2 shows that message by opening
a window, which is a temp context switch.  `ctx_dirs_restore()`
re-shortens every buffer name on the way out, and `shorten_buf_fname()`
always frees and reallocates `b_sfname`.  `readfile()` aliases `b_fname`
across `check_need_swap()` and compares the pointer to detect a rename.

Regression by b296666e41, which replaced the `cs_save_sfname` restore
(that kept curbuf's pointer) with `shorten_fnames(true)`.

Solution:
Keep the allocation in `shorten_buf_fname()` when the short name is
unchanged.  Pointer stability is what the E200/E201/E812 guards actually
assert.
2026-08-23 19:32:07 -04:00
not_compiled
716835c232 fix(ui2): dismiss msgs after mapped motions #41450
Problem:
Mapped keys can produce on_key callbacks with an empty `typed` value.
so mapped motions such as `j -> gj` are not recognized as typed input
and do not dismiss messages

Solution:
Track whether an empty `typed` callback follows typed input, allowing
mapped motions to dismiss messages like directly typed motions.
2026-08-23 19:09:26 -04:00
Justin M. Keyes
69bf8c7792 fix(cmdatom): terminal-mode keys leak into mapping lhs #41455
Problem:
A mapping that enters terminal-mode (`:FzfLua files` via `:startinsert`)
never reaches a composite-end: terminal-mode runs no "normal" CmdFrames.
The composite collects the entire terminal session (and more) into `lhs`.

E.g. if I have `<M-/>` mapped to open `:FzfLua files`, then interact
with fzf-lua UI, the emitted CmdAtom looks like:

    <M-/>… => { type='mapping', lhs='<M-/><C-N><C-N><CR>', keys=nil }

Solution:
End the composite when terminal-mode is entered, which emits a more
meaningful and repeatable atom:

    <M-/>… => { type='mapping', lhs='<M-/>', keys=nil }
2026-08-23 15:04:11 -04:00
Justin M. Keyes
bb82f9612c fix(cmdatom): "!" operator hardcodes its range #41451
Problem:
The "!" operator stuffs its cmdline continuation (`:.,.+1!`), so its
frame ends before capture (stuff pending) and the redo-prep disappears
with it.

    !ipsort<CR> => { type='excmd', lhs=':.,.+1!sort<NL>', keys=':.,.+1!sort<NL>' }

Compare to builtin "." which works bc `do_bang()` completes the redo
(`!ip` + `sort<NL>`).

Solution:
Appoint the stuffed continuation frame as the "redo-prep" frame.

    !ipsort<CR> => { type='operator', operator='!', lhs='!ipsort<NL>', keys='!ipsort<NL>' }

Notes:
- atom_cmd_end(): a frame ending with stuff pending re-points its
  redo-prep to the next frame.
- atom_cmd_start(): a stuffed continuation frame (`KeyStuffed`) keeps
  the redo-prep; flushed stuff discards it.
2026-08-23 13:58:45 -04:00
Shubh
c5cb0350a6 fix(lsp): do not expand $VAR in tagfunc filenames #41398
Problem:
CTRL-] via lsp tagfunc passes URI paths through :tag, which expands
\$VAR, so files like people_.\$personId.tsx fail with E429.

Solution:
fnameescape() the filename in tag items so \$ is treated as literal.
2026-08-23 11:30:17 -04:00
Shubh
bca9c4afa7 fix(man): allow :Man to open by absolute path #41401
Problem:
man -w echoes any existent file back unchanged. :Man rejected all such
paths, so :Man /usr/share/man/man1/bash.1 failed even for real man pages.

Solution:
Accept an echoed path when it looks like a man page (.../man1/foo.1).
2026-08-23 11:28:22 -04:00
Olivia Kinnear
8ce1aac477 feat(pos): mutable vim.Pos, vim.Range fields #41318
Allow plugins to set the human-readable fields.
2026-08-23 07:13:41 -04:00
zeertzjq
f534e06c70 test(netrw): fix failure on Windows 2026-08-23 09:33:26 +08:00
zeertzjq
9a4711dec7 vim-patch:9.2.0995: tests: no check that g:netrw_home has higher priority than $MYVIMDIR
Problem:  No check that g:netrw_home has higher priority than $MYVIMDIR.
Solution: Set $MYVIMDIR to a different directory at the start of the
          test function.

Also, s:NetrwHome() checks has('nvim') before exists('$MYVIMDIR'). This
ordering does make sense as Nvim's docs have no mention of $MYVIMDIR.
Match that in the test.

closes: vim/vim#21117

b3526a91b8
2026-08-23 09:33:26 +08:00
zeertzjq
3ab4a70338 vim-patch:9.2.0993: runtime(netrw): error when parent dir of g:netrw_home doesn't exist
Problem:  netrw: error when parent dir of g:netrw_home doesn't exist.
Solution: Use 'p' flag of mkdir() (zeertzjq).

closes: vim/vim#21113

ffce1ce58d
2026-08-23 09:33:26 +08:00
zeertzjq
bf7d308000 vim-patch:9.2.0980: runtime(netrw): prioritize g:netrw_home on Neovim
Problem:  runtime(netrw): g:netrw_home not respected on Neovim
Solution: prioritize g:netrw_home for bookmarks and history directory
          setting, add tests (J. Paulo Seibt)

related: neovim/neovim@5a78c5b
closes:  vim/vim#21091

4be03620b3

Co-authored-by: J. Paulo Seibt <jpseibt@gmail.com>
2026-08-23 09:33:25 +08:00
Justin M. Keyes
b296666e41 fix(cwd): stale buffer names after temp context-switch #41433
Problem:
1. `ctx_dirs_restore()` is the only chdir site that doesn't re-shorten
   buffer names; `post_chdir()`, `update_cwd()` and `do_autochdir()` all
   call `shorten_fnames(true)`.  So after a temp window-context switch
   that moved the CWD, every buffer's `b_fname` is still relative to the
   other directory.  ui2 renders messages in a float, and entering it is
   such a switch, so with a `:bcd` in a `BufReadPost` handler 'statusline'
   "%f" shows ".config/nvim/init.lua" while the CWD is already
   `~/.config/nvim`, and :write resolves the name against it:
   ```
   E212: Can't open file for writing: no such file or directory
   ```
2. `msg_multihl()` leaves `msg_ext_id` pointing at the caller's storage
   when nothing was emitted (e.g. 'msg_silent'): the reset only ran on
   a flush that produced chunks.  The next message then ships a dead
   stack frame as its msg_show id.  After ":silent write" the id is
   buf_write()'s `msg_id[MAXPATHL + 32]`:
   ```
   id = "\0\0\0\0\0\0\0\0\29\0(<C6>k\24R\17p<C7><C7><C7>\1\0\0\0..."
   ```

Solution:
1. `shorten_fnames(true)` after restoring the CWD.  Drop `cs_save_sfname`,
   which was a partial workaround for the same bug.
2. Release the id in `msg_multihl()`, where it is set and the caller's
   frame is still alive. Stop reading `.data.integer` out of a String
   union member.
2026-08-22 16:24:05 -04:00
Freddie Haddad
9b0bc7edef fix(plines): 'linebreak' ignores inline virtual text width #41227
Problem:
With 'linebreak', a word is moved to the next screen line when it does
not fit on the current one. The check measures only the buffer text, so
inline virtual text anchored inside the word is not counted. The word is
kept on a line it cannot fit, and the virtual text is split across the
screen line boundary.

Solution:
Count the width of inline virtual text anchored within the word, so the
check uses the width that is actually displayed.
2026-08-22 19:48:16 +00:00
Justin M. Keyes
7037e1effe fix(write): :write can target a garbage filename #41432
Problem:
`buf_write()` captures the buffer's `fname`/`sfname`/`ffname`, then emits its
progress-message before opening the file. This may run user code
synchronously: the `Progress` autocmd, and the `msg_show` handler of an
in-process UI (ui2). Either can change the CWD, and `shorten_fnames()`
then frees/reallocs every buffer's short name. The rest of `buf_write()`
reads the freed name...

    "foldtext()" [New] 41L, 997B written
    E212: Can't open file for writing: illegal byte sequence

ASAN, with ui2 enabled and a `BufEnter` handler that runs `:lcd`:

    READ  path_skip_sep <- path_tail <- match_file_list <- buf_write
    FREE  shorten_buf_fname <- shorten_fnames <- update_cwd <- set_curbuf
          <- win_set_buf <- nvim_open_win <- ui2 msg_show handler
          <- ui_call_msg_show <- msg_ext_ui_flush <- buf_write

Solution:
Copy the names after the `*Pre` autocmds.

Note: `readfile()` has the same shape, but its messages pass no
progress-id, so they skip `msg_progress()`. Safe, for now...
2026-08-22 15:07:45 -04:00
Justin M. Keyes
47cd769ed5 feat(cmdatom): mappings capture continuation
Problem:
A mapping that ends mid-operation (`nnoremap ,D d`) emits a content-free
"mapping" atom plus a `pending` field, and the "continuation" motion
arrives as a sibling atom. Consumers must stitch the two together (which
has broken cases, e.g. Insert-opening mappings (",i") lose their session
entirely).

- ",i": the session atom is dropped bc the mapping RHS is consumed
  before the session starts (typebuf_maplen()==0), so
  atom_is_user_input()=false.
- ":normal"-in-opfunc: the opfunc internal "v..y" session (a) became
  kVatomTyped just because the deferred composite was open, masking the
  real operator capture via atom_captures, and (b) its nested frames
  re-derived the outer redo.
- "Motion" based on `moved=true`, has false negatives.
- `CmdAtom.remap` is unnecessary, and clutters the docs/usage.

Solution:
- Introduce `frame_id` to identify CmdFrames.
- Classify `type=motion` better, via `NV_MOTION` flag on the `nv_cmds` table.
- Drop `CmdAtom.pending`, `CmdAtom.remap`.
- Defer atom_composite_end() at the clock edge while an operator is
  pending, Visual is active, or `restart_edit` is set: the composite
  keeps collecting, so the continuation is captured in the mapping atom.
- ",i": Now an open composite counts as user input.
- ":normal"-in-opfunc: Now handled correctly.
- `remap` is now decided by `composite.payload || 0 subatoms`.
  atom_payload_mark() records the read the resolution never captures.
- `toplevel` is now decided by `CmdFrame.parent == NULL`.

before/after:

    INPUT       BEFORE                             AFTER
    ---------------------------------------------------------------------
    ,D w        {mapping lhs=,D pending=operator}  {operator lhs=,Dw keys=dw}
                + {operator keys=dw}
    ysiw"       {mapping lhs=ys pending=operator}  {operator lhs=ysiw" keys=g@iw"}
                + {operator lhs=g@iw"}
    ,v d        {mapping pending=visual}           {visual lhs=,vd keys=viwd}
                + {visual lhs=viwd}
    ,i XY<Esc>  {normal keys=i lhs=,iXY<Esc>}      {insert keys=1iXY<Esc> text=XY}
2026-08-22 17:29:34 +02:00
Shubh
f583991553 fix(url): escape cmd.exe special chars in vim.ui.open() URL #41394
Problem:
On Windows, vim.ui.open() passes URLs to `cmd.exe /c start`. cmd.exe
treats `&` as a command separator, so query strings are truncated.

Solution:
Caret-escape `&|<>^%!` in URIs when the open handler is cmd.exe.
2026-08-22 10:49:55 -04:00
Sean Dewar
dadaa7fcad fix(ui2): offset extmarks copied to pager by srow #41425
Problem: wrong end_row when copying extmarks to the pager buffer when expanding
a message. Results in incorrect highlights or errors such as "invalid `end_col`
out of range".

Solution: don't forget to offset end_row by srow, as is already done for the
copied lines and extmark rows.
2026-08-22 10:19:16 -04:00
glepnir
2c5f37f6af fix(lsp): empty filterText/sortText/insertText not treated as unset #41421
Problem:
Empty string is falsy, so the spec's label fallback applies, but there only checks
for nil. An empty filterText drops the item, an empty sortText sorts it
first, and an empty insertText leaves the word empty.

Solution:
Treat empty string as unset.
2026-08-22 07:40:17 -04:00
zeertzjq
f9ea56f959 test: fix $NVIM_LOG_FILE from environment being overwritten (#41420)
Instead of setting it in the test runner (which runs in an Nvim instance
that already has a log file), make run_tests.zig set a $NVIM_LOG_FILE
fallback path like what RunTests.cmake does.
2026-08-22 15:58:47 +08:00
Justin M. Keyes
6423657352 feat(cmdatom)!: eliminate the need for vim-repeat #41414
Problem:
- `lhs` is not fully realized. E.g. for a "payload" mapping
  `lhs` omits the `getchar()` payload during a mapping (vim-surround
  `ds'` reports `lhs="ds"`). This means plugins like vim-repeat are
  still needed...
- The "delta" fields of a CmdAtom are calculated too late.
  - `<abuf>` and `changed` check whatever (wrong) buffer a command
    (":bnext") might land in.
  - CTRL-W_w between two windows on the same buffer reports type="motion".

Solution:
- `CmdOrigin` samples (buf/win/cursor/changedtick) at each "scope" entry
  (CmdFrame, composite, Visual session, insert session).
- `dd<C-w>l` reports `changed=true` for the buffer it edited, regardless
  of where the cursor ends up.
- New fields:
  - `pos`: cursor position at command start.
  - `moved`: indicates whether the cursor moved (in same buffer).
  - `undoseq`: undo state at settlement.
- lhs now includes the payload: "ds)" reports lhs="ds)" instead of "ds".
  - Easy for users to "replay" any atom.
- Rename: type "command" => "normal", "ex" => "excmd"; `arg` => `cmdarg`
- Drop `cascade` field (no reason to expose it)
2026-08-21 13:42:17 -04:00
zeertzjq
eaff5417be vim-patch:9.2.0987: heap-buffer-overflow in spell_suggest() (#41406)
Problem:  Heap-buffer-overflow in spell_suggest() when the cursor is
          beyond the end of the line, because a SpellFileMissing
          autocommand changed the buffer (dvaave2025).
Solution: parse_spelllang() may run autocommands, so validate the cursor
          position and re-take the saved position afterwards.

fixes:  vim/vim#21097
closes: vim/vim#21100

Supported by AI.

6073903cda

Co-authored-by: Christian Brabandt <cb@256bit.org>
2026-08-21 08:51:56 +08:00
zeertzjq
af37d459a6 vim-patch:9.2.0985: Multiline messages not visible when mapping starts cmdline (#41405)
Problem:  Multiline messages exceeding 'cmdheight' not visible when a
          mapping starts cmdline immediately after it (after 9.2.0967).
Solution: Revert patch 9.2.0967 and use a different solution (zeertzjq).

fixes:  vim/vim#21098
closes: vim/vim#21101

fb4866a2dd
2026-08-21 07:46:34 +08:00
Justin M. Keyes
31de0d69fc fix(cmdatom): lhs not always reported in CmdAtom #41386
Problem:
An unreplayable Visual operation does not emit a CmdAtom event. That's
maybe not super important, but it hints at a flaw in how `vatom`
"voiding" is plumbed: `vatom.state=kVatomVoid` replaces the "kind", so
that info is lost to later parts in the lifecycle.

Solution:
- Define `VatomState` as "flags", so `kVatomVoid` can "poison"
  `vatom.state` without losing its kind flag.
- Unify `lhs`: always report the original user input in `CmdAtom.lhs`,
  for all kinds of user actions: visual, "translated"/"stuffed" cmds,
  and dot-repeat (".") itself.
- Unreplayable Visual atom emits CmdAtom with non-empty `lhs` and empty
  `keys`, like a mapping/macro composite.
2026-08-20 05:18:36 -04:00
zeertzjq
40740f49f8 vim-patch:9.2.0982: tests: test_substitute leaves swapfiles behind (#41390)
Problem:  tests: test_substitute leaves swapfiles behind
Solution: Close open buffer using :bw!

be66c98572

Co-authored-by: Christian Brabandt <cb@256bit.org>
2026-08-20 01:51:34 +00:00
Justin M. Keyes
2fea699b96 revert: "fix(lifecycle): on Windows, CTRL_CLOSE kills Nvim mid-teardown"
Revert commit 5a71131282

That change seems good in theory, but it consistently causes 2 failures

    FAILED   …/api/vim_spec.lua @ 3213: API nvim_list_chans, nvim_get_chan_info stream=job :terminal channel
    Expected values to be equal.
    Expected:
    {
      argv = { "D:/a/neovim/neovim/build/bin/nvim.exe", "-u", "NONE", "-i", "NONE" },
      exitcode = 129,
      id = 4,
      mode = "terminal",
      pty = "?",
      stream = "job"
      ...
    }
    Actual:
    {
      argv = { "D:/a/neovim/neovim/build/bin/nvim.exe", "-u", "NONE", "-i", "NONE" },
      exitcode = 143,
      id = 4,
      mode = "terminal",
      pty = "?",
      stream = "job",
      ...
    }
    stack traceback:
            …/api/vim_spec.lua:3257: in function <…/api/vim_spec.lua:3213>

    FAILED   …/terminal/tui_spec.lua @ 3669: TUI exits immediately when stdin is closed
    …/terminal/tui_spec.lua:3669: retry() attempts: 69
    Expected values to be equal.
    Expected:
    vim.NIL
    Actual:
    {
      name = "nvim.exe",
      pid = 2256,
      ppid = 8200,
    }
    stack traceback:
            …/testutil.lua:98: in function 'retry'
            …/terminal/tui_spec.lua:3669: in function <…/terminal/tui_spec.lua:3656>
2026-08-19 20:36:14 +02:00
Justin M. Keyes
5a71131282 fix(lifecycle): on Windows, CTRL_CLOSE kills Nvim mid-teardown
Problem:
`signal_ignore_deadly` doesn't work for Windows, where the console still
may terminate Nvim during teardown (after `signal_teardown`), while it
is already trying to exit. Besides interrupting any housekeeping we are
doing, it results in an unpredictable exit code (flaky tests).

    [Process exited -1073741510]  // 0xC000013A STATUS_CONTROL_C_EXIT

Solution:
Register our own CTRL_CLOSE_EVENT handler which "blocks" the signal.
Note: if exit takes longer than 5s, Windows will consider the process
"hung" and kill it anyway.
2026-08-19 15:46:58 +02:00
Justin M. Keyes
5b1c21f4b8 fix(lifecycle): late signal kills Nvim mid-teardown
Problem:
A deadly signal arriving during teardown can kill Nvim while it is
preserving swapfiles. `os_exit()` ignores deadly signals via
`signal_reject_deadly()`, but `signal_teardown()` => `uv_signal_stop()`
resets them to the default behavior, so SIGHUP arriving after that
kills the process:

    [Process exited 129]      // 128 + SIGHUP

This is a race when closing a pty: kernel sends SIGHUP to foreground
process group *and* the reads return EOF, so `chanclose()` on a TUI job
prepares to exit twice. This means it is unpredictable whether Nvim
exits 1 or is terminated.

Solution:
Ignore deadly signals once the watchers are closed. Only SIGKILL
interrupts it now.
2026-08-19 15:46:58 +02:00
zeertzjq
2cd4229d01 fix(window): crash when closing new curwin during CTRL-W_x (#41378)
Problem:  Crash when closing the new current window during CTRL-W_x.
Solution: Remove a duplicate redraw of a window that should normally be
          already mark for redraw in the previous call.
2026-08-19 20:13:08 +08:00
zeertzjq
53211ade2b vim-patch:9.2.0969: runtime(shaderslang): matchit % breaks on braces (#41371)
Problem:  b:match_words groups "{" with the if/for/while/switch keywords
          and "}" with "break" which breaks % matching on braces
Solution: Drop the brace and bracket groups, matchit appends
          'matchpairs' by itself (Matthias Bruns).

matchit counts every alternative in a group instead of pairing the
alternatives with each other.  Listing `{` alongside the if, for,
while, switch, struct and class keywords therefore makes a line such
as `for (...) {` count as two openers, and listing `break` alongside
`}` lets a brace pair with a break statement.  As a result % on the
opening brace of a function does not move at all, and % on
`switch (x) {` jumps to `break;` instead of the closing brace.

Braces and brackets do not need to be listed: matchit appends
'matchpairs' to b:match_words by itself.  Drop them and leave the
preprocessor group unchanged.

closes: vim/vim#21064

08c74ce09a

Co-authored-by: Matthias Bruns <matthiasbruns35@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 07:47:24 +08:00
Justin M. Keyes
0c091cedc2 test(tui): unreliable "TUI exits immediately when stdin is closed" #41367
Problem:
Unreliable test on slow CI (ASAN/TSAN):

    FAILED  .../tui_spec.lua @ 3054: TUI exits immediately when stdin is closed
    retry() attempts: 1
    Expected: vim.NIL
    Actual: { name = "nvim", pid = 33201, ppid = -1 }

The test asserts "immediate" exit of the Nvim process, but this may be
subject to OS delays outside of our control.

Solution:
Make the "timed out waiting for DA1" log conditional on the actual
timeout, and assert the logs in the test.
2026-08-18 09:22:05 -04:00
Goldpigg
ab82c2c6b0 feat(extmark): virt_lines can highlight until EOL #41289 2026-08-18 08:35:22 -04:00