Commit Graph

38051 Commits

Author SHA1 Message Date
Lewis Russell
efdd73c096 feat(watch): add on_error callback
Problem: Watcher failures are reported inconsistently, and callers
cannot detect when a watcher is no longer usable.

Solution: Route startup and event failures and unexpected inotifywait
exits through an optional on_error callback. Log to nvim-watch.log by
default. Ignore disappearing child directories and exits caused by
cancellation. Let LSP log failures and notify once per message, using
INFO for missing roots and ERROR otherwise, without interrupting
registration.

AI-assisted
2026-09-09 11:39:57 +01:00
zeertzjq
318ea4de21 vim-patch:9.2.1054: Virtual Replace mode: BS over multi-byte text eats the padding (#41799)
Problem:  In Virtual Replace mode, backspacing over a character that
          replaced several multi-byte characters deletes the padding
          that follows it, so the text after the cursor loses its
          alignment.
Solution: In replace_do_bs() advance by the length of the character at
          the current offset instead of always measuring the first
          restored character (Volodymyr Chernetskyi).

After the original characters are restored, replace_do_bs() adds up
their screen width so it knows how much of the alignment padding to drop
again. The loop advanced "i" by mb_ptr2len(p) - 1, which always returns
the length of the *first* restored character rather than the length of
the character at the offset being looked at.

One backspace can restore more than one character, because a wide
character may have replaced several narrow ones.  When those characters
do not all have the same byte length the index lands in the middle of a
character: chartabsize() then measures a trailing byte, counts it as an
unprintable <xx> worth four cells, and the inflated width makes the
following loop delete padding spaces that should have been kept.

    call setline(1, 'aé    xyz')
    call feedkeys("gR\u4e00\<BS>\e", 'xt')

leaves "aéxyz" instead of restoring the original line.  Going the other
way round ('éa') happens to work, since there the first character is the
longer one.

This has been wrong since the loop was added in Vim 7.0.

closes: vim/vim#21211

5d934b1bdb

Co-authored-by: Volodymyr Chernetskyi <19735328+chernetskyi@users.noreply.github.com>
2026-09-09 01:22:54 +00:00
zeertzjq
b7f9aceadd vim-patch:5867d1f: runtime(svelte): include svelte syntax script and syntax tests (#41796)
closes: vim/vim#21200

5867d1f8ee

Co-authored-by: 231tr0n <zeltronsrikar@gmail.com>
2026-09-09 00:34:53 +00:00
zeertzjq
338ce77f34 vim-patch:9.2.1041: vim_strbyte() can be improved (#41784)
Problem:  vim_strbyte() can be improved
Solution: Replace the byte-by-byte scan in vim_strbyte() with strchr(),
          preserving its byte-range and NUL behavior (Yasuhiro Matsumoto).

closes: vim/vim#21240

df4ef959d5

Co-authored-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
2026-09-08 22:58:59 +00:00
Justin M. Keyes
493a4dd571 fix(multicursor): mapping that creates initial cursors is not replayed #41788
Problem:
A Visual-mode mapping that creates cursors (`xmap I Q0i`) replays its
commands only if cursors already existed when it started.

Solution:
Always collect `composite`. Consumers are still decided per frame
(`CmdFrame.consumers`) and at emit/cascade.
2026-09-08 12:05:34 -04:00
Lewis Russell
6a54396db4 build: enable more EmmyLua checks
- call-non-callable
- missing-fields
- redefined-label
- redefined-local
- undefined-field
- unreachable-code

Promote redefined-local and unreachable-code to warnings.

Fix DOS line-ending handling for multiline semantic tokens, and support
intersection types in the help parser.

AI-assisted
2026-09-08 16:50:53 +01:00
zeertzjq
ec65796cea vim-patch:9.2.1040: matchfuzzypos() can be improved (#41786)
Problem:  matchfuzzypos() can be improved
Solution: Compute the uppercase character once per character in
          has_match(), instead of repeating the conversion while
          scanning each candidate (Yasuhiro Matsumoto).

closes: vim/vim#21241

c0c3fbd967

Co-authored-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
2026-09-08 15:45:31 +00:00
zeertzjq
e66fdc1eac vim-patch:9.2.1038: CursorLineFold/Sign highlighting depends on 'cursorlineopt' (#41785)
Problem:  CursorLineFold and CursorLineSign are only applied when
          'cursorlineopt' contains "number" or is "both"
          (Evgeni Chasnovski).
Solution: Apply those groups whenever 'cursorline' is set, independent
          of 'cursorlineopt' (Vrushali Zampalkar).

fixes:  vim/vim#20480
closes: vim/vim#21112

1f8d3c44c5

Co-authored-by: Vrushali-Z <zampalkarvasu@gmail.com>
2026-09-08 15:24:45 +00:00
bfredl
623508dc06 Merge pull request #41751 from bfredl/stringly_nils
test(ci): non fatal errors for [NULL] safety in vim_snprintf
2026-09-08 17:12:26 +02:00
zeertzjq
3c9fe92e42 vim-patch:9.2.1045: runtime(netrw): raises E46 when changing directory fails (#41783)
Problem:  Netrw raises E46 when changing directory fails.
Solution: Remove unused assignments to the read-only directory argument
          and cover both error-handling branches with a regression test
          (Qiming zhao).

When :lcd fails with E472, s:NetrwLcd() assigns to a:newdir instead of
returning -1. The fallback assignment also refers to dirname, which is
undefined on this path. Neither assignment can update the caller's
directory variable. Keep the existing option restoration and failure
return while removing the invalid assignments.

Supported by AI.

closes: vim/vim#21226

2f72e4c72f

Co-authored-by: Qiming zhao <chemzqm@gmail.com>
2026-09-08 14:57:40 +00:00
zeertzjq
94636bdff0 vim-patch:58ed368: runtime(org): Fix escape in formatlistpat (#41782)
The regex atom \| needs an extra escape for a :set command

closes: vim/vim#21214

58ed368ab9

Co-authored-by: SPFab <42518661+SPFabGerman@users.noreply.github.com>
2026-09-08 14:28:46 +00:00
Volodymyr Chernetskyi
bea6410138 vim-patch:9.2.1046: regex: case-insensitive match fails on multi-byte string with re=1 (#41781)
Problem:  With 'regexpengine' set to 1 a case-insensitive match against
          a literal string fails when the string starts with a
          multi-byte character that is longer than a character following
          it, so the two regexp engines disagree (after v9.1.0645).
Solution: In cstrncmp() advance by the length of the character at the
          current position instead of always measuring the first
          character of "s1" (Volodymyr Chernetskyi).

cstrncmp() walks "s1" to find how many characters make up "*n" bytes, so
that it can measure out the same number of characters in "s2". The loop
decremented the remaining byte count by mb_ptr2len(s1), which always
returns the length of the *first* character, rather than the length of
the character at the current position "p".

When the first character is longer than a later one the byte count runs
out too early, the character count comes up short, and MB_STRNICMP2() is
handed a length for "s2" that is too small, so the comparison fails. For
example matching "\cüber" against "Überraschung": "über" is five bytes,
but each iteration subtracts two (the length of "ü"), so the loop runs
three times instead of four.

    :set regexpengine=1
    echo matchstr('Überraschung', '\cüber')

returns an empty string, while 'regexpengine' set to 2 correctly returns
"Über". The default value of 0 uses the NFA engine and is unaffected.

related: vim/vim#14756
closes:  vim/vim#21212

4e5ac0d68a
2026-09-08 13:50:03 +00:00
bfredl
b6b190291d fix(api): do not pull a fast one for nvim_del_augroup_by_id
This is a bit more boilerplate because of painful impedance mismatch (is
augroup names the root source of truth? is ids the source of truth? who
knows? what does truth even mean?), but show an error reflecting what
the caller actually tried (they passed in an invalid id, not a null
string)
2026-09-08 14:04:10 +02:00
bfredl
97660307f5 fix(strings): non fatal errors for [NULL] safety in vim_snprintf
context: vim_snprintf provides a "portable" replacement for a system
vsnprintf(). However we rely on system vsnprintf() in places. This
is quite arbitrary. I want to use system vsnprintf() only, and
reduce the machinery in string.c for typval printf only, which will
delet a lot of duplicated code.

The biggest hurdle here is not "portably" (we only support sane c
runtimes) but the following discrepancy: standard libc printf()
considers NULL args to %s to be undefined behavior strictly
while vim_snprintf() _defacto_ allows this by replacement to "[NULL]"

IMO, this should be seen as "graceful" error handling (nicer than crashing
on the user), not a valid means to have the string "[NULL]" intentionally
be shoved in the users face. more robust code should explicitly
check for NULL and replace it with a _appropriate_ fallback for
the situation, depending on what a NULL string actually means in the
context (unnamed buffer? anonymous namespace? global augroup?)

soo, this PR provides the most _gently_ incremental nudge towards
considering this case again as an error one could possible imagine.
If sanitizers are complied in, this minor incursion gets printed
into the sanitizers' log as a "report-and-continue" error with a
traceback. This doesn't annoy the end user or "stop the world"
in the test suite, but the CI will see the reported error and
fail the build, just like other non-fatal CI errors.
2026-09-08 12:46:24 +02:00
glepnir
b3bd442c5c fix(lsp): completion dynamicRegistration breaks capability readers #41771
Problem:
A server that supports dynamic registration must not also declare
completionProvider at initialize, so server_capabilities.completionProvider
is nil for anything reading it directly.

Solution:
Do not advertise it by default. A client that follows a registration can
declare it itself.
2026-09-08 05:48:38 -04:00
zeertzjq
03f5f25a1e fix(tui): don't treat ESC preceding repeat/release as ALT (#41770) 2026-09-08 17:25:35 +08:00
Yanuo Ma
b781e95a24 fix(scrollbind): window jumps back and forth with tall virt_lines block
Problem:
Scrolling a 'scrollbind' window whose peer has a virt_lines block
taller than the window makes the peer jump back and forth.

Solution:
After scrollup()/scrolldown(), sync w_scbind_pos to the actual
get_vtopline() when the target was not clamped to buffer bounds.

Ref: esmuellert/codediff.nvim#519
AI-assisted
2026-09-08 09:55:15 +01:00
Volodymyr Chernetskyi
8d5ebdf986 fix(lsp): announce typeHierarchy and executeCommand capabilities #41762
Problem:
Type hierarchy (#28388, #28467) and `workspace/executeCommand` (#11607) are
implemented, but `textDocument.typeHierarchy` and `workspace.executeCommand`
are missing from `make_client_capabilities()`.

Solution:
Declare both capabilities.

AI-assisted
2026-09-07 15:11:00 -04:00
Justin M. Keyes
83d1915744 Merge #41708 from janlazo/na-patch-v901
build(vim-patch): n/a patches since Vim version v9.1
2026-09-07 15:10:24 -04:00
Jan Edmund Lazo
cb81d878a2 build(vim-patch): de10c87bed5a79ed80f1bb428a391faefdff13c9 is almost n/a
Vim desktop files are N/A.
"src/po/zh_CN.UTF-8.po" is applicable.
2026-09-07 13:37:41 -04:00
Jan Edmund Lazo
53b372d443 build(vim-patch): n/a patches for src/po/
"vim.pot" file and tasks are N/A.
"clean" task is N/A after migrating to CMake.

N/A patches:
- 59bd74ed4c9ab366182c93bdc430b186729abbad
- 5d552d652b0197063565ab937d30f92a9ed28545
- 3c8d32e4fc17b4b38b17d7c039ee6203d1b01078
2026-09-07 13:37:34 -04:00
Jan Edmund Lazo
e475939fcf build(vim-patch): 015c84ce541495fad568c66b0dd17faefe1c1389 is n/a 2026-09-07 13:37:34 -04:00
Jan Edmund Lazo
552fb90f74 build(vim-patch): v9.1.0681 should be auto-n/a
Vim screendump files and Vim9script screendump code are N/A.
vim-patch.sh cannot auto-N/A the entire patch
because of "src/testdir/README.txt".
2026-09-07 13:37:33 -04:00
Volodymyr Chernetskyi
9f425311c3 fix(lsp): semantic tokens ignore 'fileformat' when sizing line endings #41733
Problem:
The eol_offset used to decode multiline semantic tokens is computed as

    vim.bo.fileformat[bufnr] == 'dos' and 2 or 1

which indexes the option value rather than the buffer, so it is never
"dos" and the offset is always 1. A token that crosses a line boundary
in a 'fileformat' of "dos" is then decoded one code unit short per line
ending, and the highlight extends past the end of the token.

Solution:
Read the option with the buffer-scoped form, vim.bo[bufnr].fileformat.

The existing multiline test covers both formats now. Its token length of
82 counts the line endings it spans, so the same token reaches four
characters less far once the buffer is "dos".

AI-assisted
2026-09-07 12:47:48 -04:00
Volodymyr Chernetskyi
ce5e230af8 fix(lsp): show_document() reports a failed buffer load #41731
Problem:
vim.lsp.util.show_document() can fail to load the target buffer, most
visibly with E325 when another process owns the file's swapfile and the
user declines to open it. The Vim error escapes show_document() and
surfaces inside whichever LSP handler called it, instead of being
reported as a failure to show the document.

Solution:
Load the buffer up front with vim.fn.bufload(), before any of that
state is touched, and report the error instead of raising.

AI-assisted
2026-09-07 16:21:11 +00:00
Lewis Russell
65ef6fdaee build: replace LuaLS with EmmyLua
Problem: LuaLS struggles with the generics used in Nvim's runtime,
requiring broad diagnostic suppressions. Indexing is also slow.

Solution: Use EmmyLua for type checks in the build and CI. It offers
more sophisticated type checking, substantially better support for
generics, and much better flow analysis.

Correct the affected annotations. Use `@internal`, supported directly by
EmmyLua, instead of `@nodoc` for shared internal declarations, and
support it in the help parser.

AI-assisted
2026-09-07 15:33:22 +01:00
Lewis Russell
cd52c77cd5 fix(help): resolve generic and nullable type references
Problem: K on generic and nullable type annotations can jump to the
wrong help tag. Generated See references repeat the type names.

Solution: Strip generic arguments and nullable suffixes when resolving
help tags. Remove the redundant generated See references.

AI-assisted
2026-09-07 15:33:22 +01:00
glepnir
937f835a3e feat(lsp): support dynamic registration of completionProvider #41748
Problem:
dynamicRegistration is declared false, and completionProvider is read
from server_capabilities only.

Solution:
Declare it, and prefer registerOptions per field.
2026-09-07 09:53:35 -04:00
Lewis Russell
fdf071959e perf(watch): prune excluded subtrees during setup
Problem:
The LSP client excludes paths such as Git objects and installed
dependencies from file watching. However, watchdirs() still traverses
these subtrees before filtering which directories receive watchers,
paying the filesystem and Lua traversal cost for excluded content.

Solution:
Prune excluded subdirectories during the initial walk, and document
that excluding a directory also excludes its descendants.

Using the LSP client's existing **/node_modules/*/** exclusion on a
synthetic macOS tree with 100 installed packages and 4,000 files:
                     Before  After
  Directory scans       503    103
  Entries inspected    4502    202
  Directory watchers    103    103

The measurements use real filesystem calls and watcher handles, with
identical watcher paths for this exclusion.

Ref: #23291

AI-assisted
2026-09-07 13:30:57 +01:00
Volodymyr Chernetskyi
a31f9affde fix(msgpack): strptime() fails east of UTC+12 #41743
Problem:
msgpack#strptime() finds a timestamp by bisecting strftime() output, and
brackets the search with the extreme UTC offsets. The lower bound
subtracts 12 hours, but -12:00 is the westernmost offset, which produces
the *largest* timestamp for a given local time. The easternmost offset
is +14:00, so in any zone east of UTC+12 the search can start above the
target and the function throws:

    internal-start-string:Internal error: start > string

With TZ=GMT-14, four of the five timestamps in msgpack_spec.lua fail,
as do three tests in shada_spec.lua, which reaches the same function
through shada#strings_to_sd().

Solution:
Subtract 14 hours instead. The upper bound is already generous enough at
+14, and widening a bisection bracket downwards cannot change the result
in zones that worked before.

The test disables the python3 provider, because msgpack#strptime() only
uses the Vimscript implementation changed here when no provider answers,
and otherwise hands the work to datetime.strptime().

AI-assisted
2026-09-07 08:24:16 -04:00
Justin M. Keyes
1c8d5581d9 Merge #41118 from altermo/optwin-tab-scope
optwin.lua improvements
2026-09-07 08:20:24 -04:00
zeertzjq
4d1793a106 Merge pull request #41717 from janlazo/vim-0ed11ba
vim-patch:9.1.1429,{0ed11ba,57d6d00}
2026-09-07 20:08:06 +08:00
Justin M. Keyes
c696bab847 fix(cmdatom): don't capture inputsecret() #41742 2026-09-07 06:47:17 -04:00
Lewis Russell
80c2181ce9 refactor(pack): use vim.async
Problem: vim.pack uses a private async implementation even though
vim.async is now available.

Solution: Remove the private implementation and run plugin operations in
structured task scopes. Bound parallel work with semaphores and use
protected awaits so cancellation still propagates.

AI-assisted
2026-09-07 10:53:56 +01:00
altermo
ec957ffd8c refactor(optwin): use current window as fallback 2026-09-07 11:05:19 +02:00
altermo
9975b73dfb fix(optwin): handle tabpage local options 2026-09-07 11:05:19 +02:00
Jan Edmund Lazo
b222398553 vim-patch:57d6d00: runtime(doc): Add documentation style
closes: vim/vim#17627

57d6d00433

Co-authored-by: Damien Lejay <damien@lejay.be>
Co-authored-by: Phạm Bình An <111893501+brianhuster@users.noreply.github.com>
2026-09-06 23:57:11 -04:00
Jan Edmund Lazo
b9aa951c00 vim-patch:9.1.1429: dragging outside the tabpanel changes tabpagenr
Problem:  dragging outside the tabpanel changes tabpagenr (char101)
Solution: set in_tab_line and in_tabpanel variables (Hirohito Higashi)

fixes: vim/vim#17385
closes: vim/vim#17431

a1522f7c0d

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
2026-09-06 23:56:32 -04:00
Jan Edmund Lazo
594381d758 vim-patch:0ed11ba: runtime(doc): Tweak documentation style a bit
0ed11ba223

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
2026-09-06 23:56:32 -04:00
zeertzjq
36cc62b276 Merge pull request #41747 from zeertzjq/vim-ca2e65a
vim-patch: runtime file updates
2026-09-07 08:20:54 +08:00
zeertzjq
31626aed41 vim-patch:a96c3bc: runtime(robot): Include ftplugin and set comment options
Set 'comments' and 'commentstring' for Robot Framework files.

closes: vim/vim#21220

a96c3bc1f7

Co-authored-by: Dawid Dziurla <dawidd0811@gmail.com>
2026-09-07 08:05:11 +08:00
zeertzjq
87ef6ad92b vim-patch:d53f436: runtime(gdb): Update syntax for GDB 17
closes: vim/vim#21219

d53f436eaa

Co-authored-by: Doug Kearns <dougkearns@gmail.com>
2026-09-07 08:05:11 +08:00
zeertzjq
1e92632c1f vim-patch:22b72dc: runtime(spup): fix E92 in syntax script
Problem:  Spup syntax fails for an out-of-range comment mode.
Solution: Add the missing let to the fallback assignment (Qiming zhao).

Setting oneline_comments above 3 raises E492 while loading the syntax
script. Use a valid legacy Vim script assignment to restore the default
value of 2.

Validated with oneline_comments set to 1, 2, 3, 4 and 99, and with the
variable unset. The original script fails with E492 for a value of 4.
The corrected script loads and applies the expected comment mode.

closes: vim/vim#21227

Supported by AI.

22b72dca7e

Co-authored-by: Qiming zhao <chemzqm@gmail.com>
2026-09-07 08:05:11 +08:00
zeertzjq
797171f45f vim-patch:ca2e65a: runtime(doc): Update quickfix help text with missing information
closes: vim/vim#21229

Supported by AI.

ca2e65a842

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
2026-09-07 08:05:09 +08:00
Justin M. Keyes
6cbde6a95f fix(multicursor): Visual-mode mapping loses its commands #41741
Problem:
A Visual-mode mapping that leaves Visual mode ("xmap I Q0i") does not
replay its motions/edits at the extra cursors, though an insert session
it starts does.

atom_map_start() skips while Visual is active, so the mapping has no
`composite`, and atom_capturable() then rejects its commands.

Solution:
Open the `composite` for Visual-mode mappings too.

Bonus: the mapping atom is now reported as:

    before: type=visual,  keys="viwcFOO<Esc>"
    after:  type=mapping, keys="viwc<Esc>iFOO<Esc>", lhs=",c"
2026-09-06 18:50:48 +00:00
Volodymyr Chernetskyi
5f1f5a8c2a fix(treesitter): conceal_lines is not applied to injected trees
Problem:
TSHighlighter._on_conceal_line() parses with the range { row, row }. A
Range2 has an exclusive end, so that is the empty range, and no injected
region ever intercepts it.

The root tree is parsed regardless, because its region is empty, so only
injections are affected: conceal_lines metadata coming from an injected
language's highlights query is dropped. on_range_impl() then records the
row in _conceal_checked, so the miss persists until the buffer changes.

For a markdown code block nested in a markdown code block, the inner
fence delimiters stay visible and nvim_win_text_height() reports 5 rows
where 3 are displayed.

Solution:
Pass the one-row range, as the on_range_impl() call below already does.

AI-assisted
2026-09-06 19:36:32 +02:00
Justin M. Keyes
2ac9dfc45d fix(multicursor): live-preview ":norm" visual selection #41736
Problem:
A Visual selection opened programmatically (`:normal! viw`) is not
previewed at the other cursors, and a user-typed operator completing it
does not cascade.

":normal" keys run as child frames, which are never user input, so the
session they build was classified "fed" once and never revisited.

Solution:
Recalculate the session kind (`vatom.state`) on every frame. Record
`vatom.frame` (the last frame to add to the session) so an enclosing
frame does not reset or recapture it.
2026-09-06 17:15:48 +00:00
Volodymyr Chernetskyi
d951908e12 fix(ui2): mouse movement dismisses expanded messages #41732
Problem:
cmd_on_key() tests the key it was handed against the literal string
'<MouseMove>', but at that point "typed" still holds raw key bytes. Thus
the comparison never holds and the branch its own comment describes
is dead. Moving the mouse over an expanded cmdline collapses it.

Solution:
Translate the key once, up front, so every comparison in the function
sees the same form.

AI-assisted
2026-09-06 12:37:28 -04:00
Volodymyr Chernetskyi
2f5df7add7 fix(treesitter): #has-parent? errors on a node with no parent
Problem:
The #has-parent? predicate indexes the result of node:parent() without
checking it, so a capture that matches a tree's root node raises

    query.lua:600: attempt to index a nil value

instead of simply not matching. In a highlights query that breaks
highlighting for the whole buffer. The sibling #has-ancestor? predicate
handles the same situation.

Solution:
Treat a missing parent as "does not match".

AI-assisted
2026-09-06 18:27:02 +02:00
Volodymyr Chernetskyi
e8a0b400d8 fix(statusline): "%.0" renders garbage #41685
Problem:
`getdigits_int(pp, strict, def)` returns `def` only when the number
fails to parse or overflows, never when the text parses to a literal
`0`, so `%.0` leaves `maxwid` at 0. Both render paths then break down:
`l > maxwid` is always true and `while (l >= maxwid)` consumes the whole
string, so `%.0f` comes out as `<` and `%.0l` as `0>3`.

Vim guards for this right after the same call. Nvim had the guard too
until 3344cffe7b ("getdigits: introduce `strict`, `def` parameters"),
which folded it into the new `def` argument and so changed what a
literal zero means. The neighbouring `minwid` call was translated
correctly.

Solution:
Fall back to 50 when the parsed width is not positive, as Vim does.

AI-assisted
2026-09-06 11:24:52 -04:00