Commit Graph

37969 Commits

Author SHA1 Message Date
Lewis Russell
ce8a897f98 feat(lua): add vim.async
Problem: Nvim has many Lua APIs that start callback-driven work: timers,
jobs, libuv handles, and other event-loop tasks. Callers that need to
sequence or cancel that work have to build their own coroutine wrappers,
task bookkeeping, and cleanup rules. This makes async control flow hard
to share, test, and document.

Solution: Add `vim.async`, a structured-concurrency module vendored from
async.nvim. It provides task handles, await/pawait helpers,
sleep/timeout helpers, completion-order iteration, and semaphores on top
of Nvim's event loop.

The API follows the same broad model as Trio: async work has an owner,
tasks are awaited explicitly, and cancellation is cooperative. Include
generated vimdoc with an introductory overview and examples, a news
entry, and functional tests for the new module.

AI-assisted
2026-09-03 19:17:25 +01:00
Justin M. Keyes
da4355ab8f fix(multicursor): 'guicursor' uses Normal shape in Visual mode #41669
Problem:
'guicursor' is not respected when extra cursors exist.

Per-cursor selection toggles Visual off, and the sandbox restores it
only after restore_current_state(), whose ui_cursor_shape() caches the
Normal shape.

Solution:
Restore Visual before restore_current_state().
2026-09-03 13:22:50 -04:00
Sébastien Hoffmann
3808c00fc8 fix(statusline): default 'statusline' improvements #41597
Problem: there should be exactly one cell of padding between sections,
and exactly one cell of minimum padding between the left and the right.
- Spaces between sections waste space when a section is empty.
  According to a comment in #33036, this was the reason to avoid `%k`
  and implement the keymap section with a vim expression, but other
  sections still have this problem.
- The diagnostics section wastes space because it is not entirely empty
  when there are diagnostics in another buffer.
- The terminal exit code section can touch the right side, e.g. the
  ruler, even though it belongs to the left side.
Solution:
- Use auto-hiding item groups (`%(` without width fields) to get rid of
  unneeded spaces when a section shows no information.
  This simplifies the 'showcmd' and 'keymap' sections in particular.
- As a slight simplification, `term_exitcode` is moved into the flags
  section since it is formatted with square brackets like a flag.
- Count the diagnostics for the current buffer specifically.
- Ensure at least one cell of padding between the left and the right
  side by adding a space next to the separator `%=`.

Problem: some sections are implemented with `%{%`, even though
reevaluation of the expression result is not needed.
This leads to otherwise needless %-escaping in `progress_status`.
Solution: use `%{` instead.

Problem: `%{` and `%{%` (without items) replace spaces with fillchars.
This looks out-of-place inside the terminal exit code section, and in
contrast to all other sections, the 'busy' section is surrounded by
fillchars, which looks inconsistent, and with some terminal-font
combinations, ◐ overlaps the fillchar, e.g. Alacritty & JetBrains Mono.
Solution: use non-breaking spaces U+202F to avoid fillchar substitution.

Problem: sections that appear/disappear frequently can make otherwise
more stable sections jump around a lot.
Solution: sort the sections on the right roughly by volatility:
'showcmd' in first place, 'keymap' next to the ruler.
2026-09-03 12:44:30 -04:00
Justin M. Keyes
ba0ee081c8 fix(coverity): DEADCODE, FORWARD_NULL, UNINIT #41667
Problem:

    CID 655469:           (DEADCODE)
    /build/src/nvim/auto/lua_api_c_bindings.generated.h: 7696             in nlua_api_nvim__mcursor_cascading()
    7690
    7691     exit_0:
    7692       arena_mem_free(arena_finish(&arena));
    7693       if (ERROR_SET(&err)) {
    7694         luaL_where(lstate, 1);
    7695         if (err_param) {
    >>>     CID 655469:           (DEADCODE)
    >>>     Execution cannot reach this statement: "lua_pushstring(lstate, "Inv...".
    7696           lua_pushstring(lstate, "Invalid '");
    7697           lua_pushstring(lstate, err_param);
    7698           lua_pushstring(lstate, "': ");
    7699         }
    7700         lua_pushstring(lstate, err.msg);
    7701         api_clear_error(&err);
    /build/src/nvim/auto/lua_api_c_bindings.generated.h: 7702             in nlua_api_nvim__mcursor_cascading()
    7696           lua_pushstring(lstate, "Invalid '");
    7697           lua_pushstring(lstate, err_param);
    7698           lua_pushstring(lstate, "': ");
    7699         }
    7700         lua_pushstring(lstate, err.msg);
    7701         api_clear_error(&err);
    >>>     CID 655469:           (DEADCODE)
    >>>     Execution cannot reach the expression "5" inside this statement: "lua_concat(lstate, (err_par...".
    7702         lua_concat(lstate, err_param ? 5 : 2);
    7703         return lua_error(lstate);
    7704       }
    7705
    7706       return 1;
    7707     }

    CID 655468:         Null pointer dereferences  (FORWARD_NULL)
    /src/nvim/mcursor.c: 833             in mc_ins_cascade()
    827           mc_ins_span_push(keys.items, NULL);
    828           mc_ins_preview_rebase();
    829         }
    830       } else if (ins.data != NULL && ins.size < mc_ins_span.done_len) {
    831         // Capture shrank without a restart signal, e.g. completion surgery rewrote the pending keys.
    832         mc_ins_cascade_restart();
    >>>     CID 655468:         Null pointer dereferences  (FORWARD_NULL)
    >>>     Passing null pointer "ins.data + mc_ins_span.done_len" to "mc_ins_keys_nonliteral", which dereferences it.
    833       } else if (ins.size > mc_ins_span.done_len
    834                  && mc_ins_keys_nonliteral(ins.data + mc_ins_span.done_len,
    835                                            ins.size - mc_ins_span.done_len)) {
    836         // Non-literal keys pending (BS, CTRL-U, ...): re-execute instead of previewing.
    837         mc_ins_span_flush(&ins, false);
    838       } else {

    CID 655467:         Uninitialized variables  (UNINIT)
    /src/nvim/mcursor.c: 1041             in mc_vsel_refresh()
    1035       for (size_t i = 0; i < kv_size(mc_cursors); i++) {
    1036         Context *ctx = &kv_A(mc_cursors, i);
    1037         pos_T pos;
    1038         if (!mc_ctx_resolve(ctx, &pos)) {
    1039           continue;
    1040         }
    >>>     CID 655467:         Uninitialized variables  (UNINIT)
    >>>     Using uninitialized value "pos". Field "pos.coladd" is uninitialized.
    1041         curwin->w_cursor = pos;
    1042         check_cursor(curwin);
    1044         Visual.select = false;
    1045         nvim_feedkeys(span, cstr_as_string("nix"), false);
    1046         if (!Visual.active) {

Solution:

- CID 655467 UNINIT: real (minor). `mc_vsel_refresh()` copied
  uninitialized local into `curwin->w_cursor`, and garbage coladd
  survived into the Visual replay (`equalpos()` compares it).
  - Fix: `pos_T pos = { 0 }`
- CID 655468 FORWARD_NULL: false positive
- CID 655469 DEADCODE: generated code. `nvim__mcursor_cascading`
  is flagged bc it is new,
- TODO: teach `gen_api_dispatch.lua` to omit the branch for
  parameterless functions.
2026-09-03 12:41:40 -04:00
Justin M. Keyes
e1a28e52b2 fix(editor): shada "force" load clears v:oldfiles #41662
Problem:
`shada_read()` rebuilds `v:oldfiles` for any "forced" read, which first
*clears* the list. For an in-memory caller such as multicursor, the list
is never repopulated. So `v:oldfiles` is empty after `Q` + edit.

Solution:
Rebuild `v:oldfiles` only when `kShaDaGetOldfiles` is requested.
`shada_read_everything()` (`:rshada[!]`, startup) always requests it.

old bug from 411a06c8b6
2026-09-03 11:32:20 -04:00
Justin M. Keyes
3beb15da57 fix(multicursor): live-mirror 'autocomplete' <BS> #41660
Problem:
During 'autocomplete', a completion-ending `<BS>` restarts completion
immediately. Multicursor insert-cascade skips replay (`edit()` refuses
to nest if compl/pum is active), so the mcursors do not update until
ESC.

Solution:
Flush pending keys in `TRIGGER_AUTOCOMPLETE`, just before autocomplete
restarts again.
2026-09-03 10:28:17 -04:00
Justin M. Keyes
e1cde28ba2 fix(multicursor): live-mirror Visual-mode operator mapping #41655
Problem:
Insert entered by a Visual-mode operator mapping (`xnoremap c c`) does
not live-mirror at cursors.

Solution:
Decide `typed` from the Visual session's own provenance (`vatom`, which
spans the selection and knows whether it was user input) instead of only
negating it.
2026-09-03 08:44:22 -04:00
Volodymyr Chernetskyi
73923b0dd8 fix(ssh): compare table length in the SSH config #41637
Problem:
`parse_ssh_config()` compares tables against a freshly allocated empty
table.
- In `parse_multiple_values()`, the guard which avoids flushing an empty
  accumulator never applies. Runs of separators and trailing whitespace
  push empty strings into the results, and `is_valid()` does not filter
  them. `Host  alpha   beta ` parses as `{ 'alpha', '', 'beta' }`.
- In `parse_value()`, the condition reduces to `chr == '"' and quoted`.
  `quoted` starts false and only that branch sets it, so it can never
  become true: quotes are never recognised and are inserted literally,
  and the unterminated-quote check is unreachable.

Solution:
Compare `#val` instead. Add a test for repeated and trailing separators.

AI-assisted
2026-09-03 06:40:58 -04:00
zeertzjq
51eacf284c fix(statuscolumn): heap buffer overflow with sign/number column (#41640)
Problem:  Drawing 'statuscolumn' leads to a heap-buffer-overflow if a
          sign/number column comes after many items.
Solution: Avoid curitem >= stl_items_len when writing to stl_items.
2026-09-03 16:56:41 +08:00
Volodymyr Chernetskyi
9222ed3a4a fix(filetype): report documented parameter name #41649
Problem:
Invalid `args` parameter for `vim.filetype.match()` reports wrong
parameter name `"arg"`.

Solution:
Report correct parameter name `"args"` instead.
2026-09-03 04:10:05 -04:00
Volodymyr Chernetskyi
9a8966879a fix(iter): keep take() predicate within bounds #41635
Problem:
`IterArray:take()` iterates up to `self._tail`, but `_tail` is
exclusive. When no element fails the predicate, the loop reads one index
past the last element and calls the predicate with `nil`.

Solution:
Stop at `self._tail - inc`, which is the last in-range index for both
iteration directions. Add tests using a predicate which dereferences its
argument and matches every element, forward and reversed.

AI-assisted
2026-09-03 03:23:45 -04:00
zeertzjq
b51a0b2dcc vim-patch:9.2.1036: syntax highlighting is slower than necessary (#41644)
Problem:  While advancing the syntax highlighting, store_current_state()
          calls syn_stack_find_entry() about once per parsed line, and
          that function rescans the state-cache list from its head every
          time. With the list capped at 1000 entries this linear rescan
          dominates the cost of highlighting a large file.
Solution: Keep a cached position to the entry last located in the list
          and resume the scan from it when it is at or before the wanted
          line, advancing the finger as new states are stored
          (Julien Voisin).

The list is sorted by line number, so when the remembered entry is at or
before the wanted line the answer can only follow it, never precede it;
resuming from there returns the same entry as a scan from the head.  The
"at or before" guard keeps this correct for any lookup order: a backward
or random-access lookup whose remembered entry is past the wanted line
falls back to a full scan.  The pointer is cleared whenever an entry is
freed, the array is reallocated or the block is freed, so it cannot
dangle.

Parsing the syntax of a 20000-line C file is about a third faster, a
5000-line file about a quarter faster, benchmarked via something like
this:

```
call synID(1, 1, 1)              " warm-up
let s = reltime()
for l in range(1, line('$')) | call synID(l, 1, 1) | endfor
call writefile([reltimefloat(reltime(s))], $T)
```

closes: vim/vim#21166

f1b4549129

Co-authored-by: Julien Voisin <julien.voisin@dustri.org>
2026-09-03 11:07:44 +08:00
zeertzjq
7645d7e232 vim-patch:9.2.1035: filetype: Github citation files are not recognized (#41643)
Problem:  filetype: Github citation files are not recognized
Solution: Detect *.cff files as yaml filetype (Wu Zhenyu).

Reference:
https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-citation-files

closes: vim/vim#21163

05de894401

Co-authored-by: Wu, Zhenyu <wuzhenyu@ustc.edu>
2026-09-03 11:07:12 +08:00
zeertzjq
aff123a5d4 vim-patch:9.2.1034: NFA regexp matching is slow for ASCII text (#41642)
Problem:  NFA regexp matching is slower than necessary for ASCII text
          because two indirect function calls are made for every
          character.
Solution: Add an inline fast path for an ASCII byte that is not followed
          by a composing character (Julien Voisin).

The main loop of nfa_regmatch() fetched the current character and its
byte length with two calls through the mb_ptr2char and mb_ptr2len
function pointers on every character.  These pointers cannot be inlined,
yet for ASCII text, which is the common case, both merely return the byte
and a length of one.

Handle that case inline.  NUL is checked first so that reading the next
byte cannot go past the end of the line, and the "next byte is ASCII"
condition matches the check in utfc_ptr2len(), so a base character
followed by a composing character still falls through to the original
calls.

A "perf stat -e instructions" on a full scroll of a 60000 line C file with
syntax highlighting enabled shows an instructions count reduction of 4%.

closes: vim/vim#21179

58390ca285

Co-authored-by: Julien Voisin <julien.voisin@dustri.org>
2026-09-03 11:06:35 +08:00
zeertzjq
89837bdd72 vim-patch:9.2.1032: scrolling moves cursor up with 'scrolloffpad' (#41641)
Problem:  Cursor correction can move the cursor up at the end of the
          buffer when 'scrolloffpad' is enabled (gx089).
Solution: Allow missing context below EOF in cursor_correct().  Add
          regression tests for CTRL-D and CTRL-E (Seunghee Kim).

fixes:   vim/vim#21096
closes:  vim/vim#21188

6a3db67a52

Co-authored-by: SeungheeKim <ksh368@naver.com>
2026-09-03 11:05:33 +08:00
dependabot[bot]
4b3ab0d6bd ci: bump the github-actions group across 1 directory with 3 updates
Bumps the github-actions group with 3 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action), [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm) and [vmactions/openbsd-vm](https://github.com/vmactions/openbsd-vm).


Updates `github/codeql-action` from 4.37.8 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v4.37.8...v4.37.9)

Updates `vmactions/freebsd-vm` from 1.5.4 to 1.5.5
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](d0518f9125...f0552d3b69)

Updates `vmactions/openbsd-vm` from 1.4.6 to 1.4.7
- [Release notes](https://github.com/vmactions/openbsd-vm/releases)
- [Commits](e6c68b637a...86cdc08415)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: vmactions/freebsd-vm
  dependency-version: 1.5.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: vmactions/openbsd-vm
  dependency-version: 1.4.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 23:58:45 +02:00
Volodymyr Chernetskyi
0ecca23ee2 fix(treesitter): accept a string title in inspect_tree() #41639
Problem:
`inspect_tree()` documents `title` as
`string|fun(bufnr:integer):string|nil`, but the implementation handles
only `nil` and function values. A string title leaves `title` unset and
fails the assertion below.

Solution:
Use the string as the title.
2026-09-02 17:01:54 -04:00
Volodymyr Chernetskyi
a58cee4512 fix(lsp): respect range in linewise visual mode #41636
Problem:
An explicitly passed `range` to `vim.lsp.buf.format()` in linewise
visual mode is silently replaced by the selection.

Solution:
Parenthesise the mode check.
2026-09-02 17:01:32 -04:00
Volodymyr Chernetskyi
10fa98bb6e fix(pack): read document links from the request buffer #41638
Problem:
The `textDocument/documentLink` handler resolves the confirmation buffer
from the request URI and checks it for `nil`, then reads lines from
buffer `0`. When the confirmation buffer is not current, links are
computed from unrelated text and returned against the confirmation
buffer's line numbers, producing missing or misplaced links.

Solution:
Read lines from the resolved `bufnr`.

AI-assisted
2026-09-02 16:58:26 -04:00
Volodymyr Chernetskyi
45b645718c fix(keymap): report the documented parameter name #41633
Problem:
Invalid `modes` parameter for `vim.keymap.del()` reports wrong parameter
name `"mode"`.

Solution:
Report correct parameter name `"mode"` instead.
2026-09-02 16:27:20 -04:00
Volodymyr Chernetskyi
40258d5d05 fix(health): correct reporting messages #41632
Problems:
- Slow shell check measures time in nanoseconds, but reports seconds.
- `kdch1` check incorrectly tests `kbs_entry` instead.
- curl version is passed as an advice, so it is never reported.

Solutions:
- Scale the elapsed time to seconds before reporting.
- Test `kdch1_entry` for `kdch1` check.
- Format curl warning with `string.format()`.

AI-assisted
2026-09-02 16:04:32 -04:00
Justin M. Keyes
f1d89e874a fix(multicursor): 'operatorfunc' motion not followed #41627
Problem:
With follow-mode ("q="), an 'operatorfunc' that only moves the cursor
does not cascade.

Solution:
Update the `effect` condition to include cursor-moves if follow-mode is
active.
2026-09-02 12:00:54 -04:00
Justin M. Keyes
c75aae2bfb fix(multicursor): 'autocomplete' during insert-cascade raises E565 #41625
Problem:
With mcursors and 'autocomplete', a non-literal key (`<BS>`) during an
active completion attempts to insert-cascade, which attempts a nested
`edit()`, which refuses with E565.

Solution:
Defer the flush while completion is active; the pending keys will be
handled later.
2026-09-02 11:20:55 -04:00
Justin M. Keyes
80e9708afa fix(multicursor): atom may cascade in another buffer #41624
Problem:
An atom queued in buffer A cascades on B's cursors if the mapping ends
in B ("nnoremap X x:bnext<CR>").

Solution:
Check the atom's origin buffer (`CmdAtom.origin.buf`).

Note: This does not preclude mappings etc from doing work in temporary
throwaway buffers, as long as they return to the origin buffer.
2026-09-02 10:38:27 -04:00
Volodymyr Chernetskyi
8321941ff2 fix(vim.version): fix metatable for intersections #41615
Problem:
`vim.version.intersect()` returns tables with `VersionRange` (method
table) as their metatable. Although the calculated bounds are correct,
the results do not expose `VersionRange` methods.

Solution:
Construct intersection tables with `range_mt`, matching
`vim.version.range()`. Add assertions covering method access and
repeated intersection.
2026-09-02 10:10:18 -04:00
Volodymyr Chernetskyi
a41d008666 fix(trust): hash fileformat=mac buffers correctly #41616
Problem:
`compute_hash()` reconstructs buffer contents with `fileformat` set to
`mac` using CRLF (`dos`) line endings. The resulting hash does not match
the file's bytes.

Solution:
Define the line ending for every supported file format. Use that mapping
when reconstructing buffer contents for hashing.
2026-09-02 10:09:20 -04:00
Volodymyr Chernetskyi
5bc7dbb13e fix(loader): remove all cached loaders on disable #41613
Problem:
When disabling, the loader list is traversed forwards while entries are
removed. As a result, some entries are skipped and remain active. Thus,
disabling `vim.loader` restores `_G.loadfile`, but does not fully
restore the original package loader chain.

Solution:
Traverse `package.loaders` in reverse when removing cached loaders.
Capture the original `package.loaders` list in the test, and assert that
the list is restored properly.
2026-09-02 10:08:37 -04:00
Justin M. Keyes
350fa5ad7c fix(multicursor): act on fold contents, not the fold itself #41622
Problem:
If an edit cascades to cursor(s) that happen to be within a closed fold,
the edit acts on the fold itself (`:h fold-behavior`). E.g. "dd" deletes
the entire fold, not the cursor's line within the fold.

Solution:
Check `mc_replaying()` in `hasAnyFolding()`, so folds are ignored during
a multicursor cascade. This means multicursor always replays relative to
text *within* the fold.
2026-09-02 09:42:40 -04:00
Volodymyr Chernetskyi
3ea7bc3f60 fix(health): restore pynvim version lookup #41619
Problem:
Fallback pynvim version lookup is broken since Vimscript-to-Lua rewrite.
- `vim.fs.basename()` returns the module filename instead of the
  directory needed to discover adjacent metadata. This produces an
  empty metadata list, hiding the remaining issues.
- `table.sort()` requires a Boolean comparator, while
  `vim.version.cmp()` returns a number.
- `table.sort()` sorts in place and returns no value, so assigning its
  result discards the metadata list.

Solution:
- Use `vim.fs.dirname()` to discover adjacent package metadata.
- Use `vim.version.gt()` as the descending Boolean comparator.
- Sort the metadata list in place.
This restores fallback version detection when `neovim.VERSION` is
unavailable.
2026-09-02 08:12:07 -04:00
Justin M. Keyes
d290ef58b2 Merge #41599 from justinmk/mchammer 2026-09-02 07:46:37 -04:00
Volodymyr Chernetskyi
1c31526a90 fix(vim.hl): convert finish column independently #41618
Problem:
Coordinates-to-position conversion for the finish column in
`vim.hl.range()` checks the start column. This results in an incorrect
finish column when only one of the column coordinates is `vim.v.maxcol`.

Solution:
Check the finish column when converting the finish position. Update the
existing screen test so that a `vim.v.maxcol` finish highlights the
end-of-line marker, matching the existing `-1` behavior.
2026-09-02 07:45:52 -04:00
Justin M. Keyes
7168e0d12f fix(mcursor): drop q= "follow motion" message 2026-09-02 13:09:30 +02:00
Justin M. Keyes
a6d1882cac fix(highlight): cterm default for Cursor hlgroup
Problem:
The `Cursor` highlight is invisible if 'termguicolors' is disabled.

Solution:
Change its default.
2026-09-02 13:09:30 +02:00
Jan Edmund Lazo
47ce0d1f87 build(vim-patch): v9.1.0578 is n/a #41585
Nvim implementation of ":TOhtml" fails on test_tohtml.vim
because they compare with the sample test files.
Customizing the sample files to pass Vim's test is maintenance burden.
Nvim has lua functional tests to skip Vim's tests.

Nvim's test Makefile computes NEW_TESTS, NEW_TESTS_RES, unlike Vim.
Likely N/A.

Following runtime/ files are N/A after Lua rewrite.

- runtime/autoload/tohtml.vim
- runtime/syntax/2html.vim
- runtime/plugin/tohtml.vim
2026-09-02 04:54:35 -04:00
glepnir
cccf359851 build: disable some clang-tidy readability checks #41556
Problem:
New clang-tidy checks readability-trailing-comma and
readability-redundant-parentheses fire across the codebase.

Solution:
Disable readability-trailing-comma and readability-redundant-parentheses
checks in the clang-tidy configuration.
2026-09-02 04:54:00 -04:00
zeertzjq
17acde8471 vim-patch:9.2.1029: NFA regexp matching is slower than necessary (#41611)
Problem:  NFA regexp matching is slower than necessary because addstate()
          tracks the recursion depth in a static variable.
Solution: Pass the recursion depth as a function parameter so it stays in a
          register instead of being spilled to memory around every recursive
          call (Julien Voisin).

addstate() is called very frequently and recursively. Storing the depth
counter in a static variable forces the compiler to reload and store it in
memory around each recursive call, since it cannot prove the recursion does
not modify it. Passing it as a parameter keeps it in a register and drops
the increment/decrement bookkeeping on every return path. The recursion
limit behavior is unchanged.

This reduces the instruction count of NFA matching by roughly 4% on
state-heavy patterns. Benchmarking was done with something like:

```
set re=0
let s:word = repeat('abc123 def456 ghijk_lmnop QRSTUV wxyz0 ', 4000)
for i in range(120)
  let s = s:word | let s = substitute(s, '\%#=2\(\a\+\)\(\d\+\)', '\2\1', 'g')
  let s = s:word | let s = substitute(s, '\%#=2\a\+\d\+', 'X', 'g')
  call matchstr(s:word, '\%#=2\(\a\|\d\|_\)\{3,}')
endfor
qa!
```

and `taskset -c 7 perf stat -e instructions ./vim -u NONE -N -X -es -S /tmp/nfabench/count.vim`

closes: vim/vim#21202

0e47c9612e

Co-authored-by: Julien Voisin <julien.voisin@dustri.org>
2026-09-02 13:52:45 +08:00
zeertzjq
90fc8946e8 vim-patch:fac9e33: runtime(doc): fix example output of cosh (#41609)
closes: vim/vim#21197

fac9e333a8

Co-authored-by: Eisuke Kawashima <e-kwsm@users.noreply.github.com>
2026-09-02 13:52:12 +08:00
zeertzjq
8e34903b6a vim-patch:e9c5e56: runtime(python): improve performance of number and ellipsis matching (#41608)
Problem:  The floating-point `.d` number match and the two ellipsis
          matches are slow: each begins with a look-behind, which cannot
          be reduced to a fixed first character, so the automatic regexp
          engine selects the slower NFA backend for them.
Solution: Force the backtracking engine with \%#=1 on those three
          patterns; it evaluates the look-behind far more efficiently.
          Highlighting is unchanged.

Measured with :syntime over a 40000 line corpus: the three affected
rules drop from ~0.13s to ~0.008s (about -93%), which cuts the total
syntax parse cost by ~10% (1.85s to 1.65s).

closes: vim/vim#21194

e9c5e56081

Co-authored-by: Julien Voisin <julien.voisin@dustri.org>
2026-09-02 13:51:57 +08:00
zeertzjq
d42367570c Merge pull request #41569 from zeertzjq/vim-9.2.1024
vim-patch:9.2.{0468,1024,1031}
2026-09-02 11:06:02 +08:00
zeertzjq
749e0a06c2 vim-patch:9.2.1031: 'wildmode' list:full does not show 'wildmenu'
Problem:  With 'wildmode' set to list:full the matches are listed but the
          wildmenu is not shown, although it is "full" that starts
          wildmenu mode (zeertzjq).
Solution: List the matches and show the menu, as the two behaviors in
          the same phase ask for.  The menu is left to the phases that
          ask for it, so that "list" on its own still only lists
          (Hirohito Higashi).

fixes:  vim/vim#21196
closes: vim/vim#21205

fa1ddffcce

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
2026-09-02 09:51:47 +08:00
zeertzjq
8bbcd3e00e vim-patch:9.2.1024: 'wildmode' list:full does not complete first match
Problem:  With 'wildmode' set to list:full, matches are listed but the
          first match is not completed on the first Tab press (rendcrx).
Solution: Do not suppress selection when list and full are active in
          the same completion phase.  Add regression tests for file
          completion, comma-separated phases and noselect precedence
          (Seunghee Kim).

fixes:   vim/vim#19532
related: vim/vim#18088
closes:  vim/vim#21181

54c988c6c5

Co-authored-by: SeungheeKim <ksh368@naver.com>
2026-09-02 09:51:47 +08:00
zeertzjq
b279472393 vim-patch:9.2.0468: popups: not correctly updated from a CmdlineChanged autocommand
Problem:  popup_show() from a CmdlineChanged autocommand doesn't update
          the screen (Mao-Yining)
Solution: Refresh the screen when popups need redraw
          (Yasuhiro Matsumoto).

popup_settext()/popup_show() called from a CmdlineChanged autocommand
did not refresh the screen because cmdline mode normally skips
update_screen(), so async info-popup updates only became visible after
a manual :redraw.  Refresh the screen when popups need redrawing right
after the autocommand.

fixes:  vim/vim#20175
closes: vim/vim#20179

ef1ecc3b61

Co-authored-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
2026-09-02 09:51:41 +08:00
Justin M. Keyes
9a29622b54 feat(multicursor): MC HAMMER #41587
Other (squashed) commits:

fix(tui): emit ui_send output atomically with the frame

Problem:
tui_ui_send() writes directly to the TTY, bypassing the output buffer.
Sequences sent via nvim_ui_send() (e.g. kitty multiple-cursors, or
visual-dot-repeat) always arrive in a separate TTY write from the frame
they were computed for. This manifests as "tearing", or e.g. in the case
of multicursor the terminal renders text with stale cursor overlays.

Solution:
- tui_ui_send(): while a frame is being assembled (pending invalid
  regions or buffered output), buffer instead of writing directly.
  - Out-of-frame sends (tty queries, clear-on-disable) still write
    immediately.
- mcursor.lua: emit the terminal-cursor update at the end of the redraw
  cycle (`on_end`, when screen positions are final) instead of
  vim.schedule().
2026-09-01 15:17:22 +00:00
Evgeni Chasnovski
9ebf9b1017 fix(ui2): respect options set during startup after enabling ui2 #41591
Problem: options related to ui2 (like `fillchars` with `msgsep`) do not
  take effect if set during startup after enabling ui2.

Solution: explicitly check just after startup if relevant options were
  changed during startup.
2026-09-01 13:23:23 +00:00
Jaehwang Jung
ca992e82d4 fix(buffer): keep trailing slash significant in URI names #41577
Problem:
Literal path comparison ignored one trailing slash for every buffer name,
including URIs. Generic URI syntax does not make a non-empty path
equivalent to the same path with a trailing slash, so distinct URI
buffers collapsed into one.

Solution:
Require equal lengths when comparing URI buffer names, while retaining
trailing-separator normalization for filesystem paths.

AI-assisted
2026-09-01 01:04:57 +00:00
Justin M. Keyes
9d2a31b05e fix(cwd): flicker after temp context-switch #41561 #41582
Problem:
`shorten_fnames()` always redraws the statusline/tabline, even if no
buffer name changed. Since b296666e a temp context-switch
(`win_execute()`, `vim._with{win=}`) restores the CWD, so every such
switch flickers the message area.

Solution:
Redraw only if `shorten_buf_fname()` actually changed a name.
2026-08-31 20:49:50 -04:00
dependabot[bot]
16a1d203e9 ci: bump the github-actions group across 1 directory with 2 updates
Bumps the github-actions group with 2 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action) and [vmactions/freebsd-vm](https://github.com/vmactions/freebsd-vm).


Updates `github/codeql-action` from 4.37.6 to 4.37.8
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v4.37.6...v4.37.8)

Updates `vmactions/freebsd-vm` from 1.5.3 to 1.5.4
- [Release notes](https://github.com/vmactions/freebsd-vm/releases)
- [Commits](83b151f58c...d0518f9125)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.37.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: vmactions/freebsd-vm
  dependency-version: 1.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-31 14:48:10 +02:00
wrvsrx
c275b5de5a fix(lsp): separate adjacent nested folding ranges #41428
Problem:
Folding range markers are overwritten while ranges are evaluated. A range ending on a row can hide another range starting there, and multiple nested ranges ending together emit only the innermost ending level.

Solution:
Track starts and the number of ends per row before emitting markers. Prefer starts on shared boundary rows and use the outermost level when nested ranges end together.

AI-assisted
2026-08-31 08:41:19 -04:00
Christian Clason
e084aa6bde build(deps): bump libiconv to v1.19 2026-08-31 14:28:05 +02:00
Aryan Pandey
ad42ee1c41 fix(cmdwin): handle UTF-8 characters containing 0x80 #41566
Problem:
Confirming cmdwin with a UTF-8 character containing 0x80 does not complete the
command.

Solution:
Escape K_SPECIAL bytes while feeding the cmdwin input after confirmation.
2026-08-31 06:52:20 -04:00