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#21064https://github.com/vim/vim/commit/08c74ce09a9269b93b01ff2289683ad63f396fba
Co-authored-by: Matthias Bruns <matthiasbruns35@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Problem: A SAL rule longer than MAXWLEN is silently truncated to an
empty lead. set_sal_first() then reorders the sl_sal entries
by their index byte and can move the terminating sentinel out
of the last slot, so spell_soundfold_wsal() reads past the end
of the array, e.g. when soundfold() or spellsuggest() is used
(Erick Alex).
Solution: Bound the sound-folding loops against sl_sal.ga_len.
closes: vim/vim#21076https://github.com/vim/vim/commit/6ac008db969677304ba888854e5d44a32ce79853
Co-authored-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: string_reduce() copies *rettv into argv[0] before calling
eval_expr_typval(). When the evaluator fails early, rettv is
never reset and still aliases argv[0] v_string.
clear_tv(&argv[0]) frees it, leaving rettv dangling and when
in vim9script get_func_tv() frees it again (Ave Dva).
Solution: Set rettv->v_type = VAR_UNKNOWN like what is done in
list_reduce() and tuple_reduce(), use tv_get_string_strict()
in f_reduce()
closes: vim/vim#21048
Supported by AI.
https://github.com/vim/vim/commit/cd59994c455a20d39d5cc41b4978ecfd2bf4be2e
Co-authored-by: Christian Brabandt <cb@256bit.org>
As a refinement upon "g:sh_no_error", support not matching
particular classes of syntax errors. Look up syntax rule
names and list them with:
‐-----------------------------------------------------------
let g:sh_no_error_rules = ["shCurlyError", "shParenError"]
‐-----------------------------------------------------------
closes: vim/vim#20935https://github.com/vim/vim/commit/5d41506eb4895fbf0b6ccf4067c7f6e3c8ac568b
Co-authored-by: Aliaksei Budavei <0x000c70@gmail.com>
Use "fromstart" syncing.
Pragment regions are delimited by shared start/end tokens which render
other syncing types largely useless. A sync point located in the middle
of a multiline comment cannot distinguish the end token from a start
token and the erroneously created region runs to EOF.
closes: vim/vim#21032https://github.com/vim/vim/commit/90a9a8c7523741f4a9cd091a0f1727902e8c9340
Co-authored-by: Doug Kearns <dougkearns@gmail.com>
Problem: sort() with "n", "N" or "f" converts an item to its number on
every comparison. For "n" that is a tv2string() plus strtod()
per comparison, so sorting a list of numbers turns each number
into a string and back O(n log n) times, dwarfing the sort.
Solution: Compute the numeric key of each item once, before the sort,
and compare the stored key (Samuel Schlesinger). Only the
builtin numeric compare modes are affected; uniq(), which
passes a bare list item to the compare function, and the
string and user-function paths are unchanged.
Sorting a list of 100000 numbers (min of 3, macOS arm64):
- sort(l, 'n'): 0.205s -> 0.017s
- sort(l, 'N'): 0.017s -> 0.010s
- sort(l, 'f'): 0.014s -> 0.010s
The result is identical, including that a string is still treated as 0
in "n" mode and that "N" keeps full 64-bit precision.
Add Test_sort_numeric_precomputed(): a large shuffled list sorted with
"n", mixed integers and floats, int64 values beyond the exact range of
a double for "N", and uniq() over the non-precomputed path.
closes: vim/vim#21003https://github.com/vim/vim/commit/c8c59db9dfdc2db5f17aa0a78ea1464f035bdf5e
Co-authored-by: Samuel Schlesinger <sgschlesinger@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Problem: Reading an undo file resolves every stored sequence number
with a linear scan over all headers, making loading
quadratic in the number of undo states.
Solution: Sort uhp_table on uh_seq once and resolve each reference
with a binary search; the duplicate uh_seq check becomes a
single pass over the sorted table (Samuel Schlesinger).
At the default 'undolevels' of 1000 the quadratic cost is not
measurable; it takes 'undolevels' in the tens of thousands to matter.
Loading an undo file with 20000 states and 50 alternate branches with
:rundo goes from 1.49s to 0.11s (min of 3, macOS arm64), with the
same undotree().
Also make old_idx/new_idx/cur_idx and the loop index "i" long instead
of short/int: they index uhp_table, whose length num_head is a long
read from the file. A short index truncated above 32767 headers,
making the restored b_u_oldhead/b_u_newhead/b_u_curhead pointers
wrong in exactly the many-headers case this change is about.
Add tests: a round-trip test with alternate branches that compares
the entries of the tree and the text at every sequence number, a
corruption test with a duplicated uh_seq, and a test for reading an
undo file with zero headers, which is written when only the line for
the "U" command is saved.
closes: vim/vim#20942https://github.com/vim/vim/commit/fccf613c8f5b550797c08a45a768e14adefd882f
Co-authored-by: Samuel Schlesinger <sgschlesinger@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Problem: When the owner of an undo file differs from the owner of
the text file and the current user, u_read_undo() returns
without freeing the file name it allocated with
u_get_undo_file_name().
Solution: Free the file name before returning (Samuel Schlesinger).
Every other exit of the function frees it under the "theend" label;
this early return sits before the file pointer is initialized, so it
cannot use that label.
closes: vim/vim#20987https://github.com/vim/vim/commit/d03735e8d29a228e6a37300335983101edb69431
Co-authored-by: Samuel Schlesinger <sgschlesinger@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Problem: When the automatic regexp engine falls back to the
backtracking engine in vim_regexec_string(), the compiled
program is freed before the replacement is compiled; when
saving the pattern fails from being out of memory the
caller's "regprog" is left pointing to freed memory and
is freed again.
Solution: Free the previous program only after compiling the
replacement succeeded, like vim_regexec_multi() already
does (Samuel Schlesinger).
closes: vim/vim#20986https://github.com/vim/vim/commit/cab0901f121d0fab74c9a42bb90583d59b3d3c21
Co-authored-by: Samuel Schlesinger <sgschlesinger@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Claim e.g. "function f () { :; }" as shFunctionTwo while
observing that parentheses after the function name are
optional when the "function" word is used and do not delimit
its body if the latter follows them in "{}" (which should
not be taken for granted with limited backtracking).
closes: vim/vim#20878https://github.com/vim/vim/commit/90e8cb0094508e9e6932adcf42f5003251c47c26
Co-authored-by: Aliaksei Budavei <0x000c70@gmail.com>
Problem:
Evaluating 'statuscolumn' overwrites transchar_charbuf[], which breaks
the drawing of an unprintable char if p_extra points there.
Solution:
Make a copy in wlv.extra so that it won't be overwritten.
It's necessary to copy the global 'fileencoding' to the buffer-local
value before entering the buffer, otherwise 'fileencoding' is changed
when reading the file, which will mark the file as modified.
Problem: "zb" scrolls incorrectly with cursor just above fold.
Solution: Handle boff.lnum being set to the last line of a fold
(zeertzjq).
With the cursor just above fold, botline_forw() moves boff.lnum to the
last line of the fold, but curwin->w_botline is at the first line of the
fold, so the boff.lnum == curwin->w_botline condition never holds.
Instead, check that boff.lnum has just moved to or past w_botline by
comparing its previous value with w_botline.
Also make a similar change to the loff.lnum check above for symmetry.
That one doesn't change behavior, as topline_back() sets loff.lnum to
the first line of a fold.
related: neovim/neovim#41122
closes: vim/vim#20923https://github.com/vim/vim/commit/aee686334c2137f8a94b04de130a3f51d928a7ed
Problem: filetype: ed script files not recognised.
Solution: Add filetype detection for *.ed files and shebang lines,
include syntax script and syntax tests.
Features of the ed syntax file:
- BSD and GNU extensions are supported
- Andrew L. Moore's ed extensions are not supported
- Rebuild synmenu.vim
closes: vim/vim#19602https://github.com/vim/vim/commit/c28515b9992e52f49c3fd90484601c248c672aec
Co-authored-by: Doug Kearns <dougkearns@gmail.com>
Problem: With 'smoothscroll' the scroll position in a long line is lost when
a window is temporarily squeezed to a couple of lines, for example
when opening and closing a help window.
Solution: When the cursor ends up in the skipped columns, skip up to the
screen line the cursor is in instead of showing the start of the
line.
closes: vim/vim#20892https://github.com/vim/vim/commit/15f8ba5cec35feea22622fbc7c78f3204aad50df
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Problem: The zip autoload script aborts loading when the "zip"
command is not available, so even read-only browsing of an
archive fails with E117 (zip#Browse undefined) on systems
that have "unzip" but not "zip" (e.g. the Windows CI
runner). Regressed in b0e0b22.
Solution: Drop the load-time executable gate and check each command
per operation instead, so a missing "zip" only affects
writing. Update the test to match the reworded message.
https://github.com/vim/vim/commit/e241ac0a62b774763733f2ccac4040dfe8aafac8
Co-authored-by: Christian Brabandt <cb@256bit.org>
Problem: An insecurely-set 'indentexpr', 'formatexpr', 'includeexpr'
or 'complete' value can end up evaluated outside the
sandbox after buf_copy_options() and clears the flag.
Solution: Copy the insecure flag alongside the value in
buf_copy_options(), and make 'complete' a per-buffer
insecure-flags field
Supported by AI.
closes: vim/vim#20861https://github.com/vim/vim/commit/35f7fdfdfb036028cc8760d7c1556361bb85206a
I'm a bit hesitant to port this, but it's a follow-up to #39452.
Co-authored-by: Christian Brabandt <cb@256bit.org>
Problem: s:SearchBracket()'s skip-expression matches syntax group
names ending in "Comment", "Todo", or "String" to decide
whether a candidate bracket is inside a string/comment
and should be skipped for indentation purposes. Byte and
raw-byte string literals (b"...", rb"...") are highlighted
via the pythonBytes/pythonRawBytes syntax groups, which
don't end in "String", so brackets inside them (e.g.
b"[") were never skipped and were counted as real,
unmatched brackets, producing incorrect indentation.
Solution: Add "Bytes" to the indent script's existing suffix match,
so pythonBytes/pythonRawBytes are recognized directly, the
same way pythonString/pythonFString/pythonRawString
already are.
This supersedes an earlier version of this fix that
renamed pythonBytes/pythonRawBytes to
pythonBytesString/pythonRawBytesString in
runtime/syntax/python.vim.
fixes: vim/vim#20812closes: vim/vim#20827https://github.com/vim/vim/commit/f88e7191da1a03b2e79a2ba5fbc26f4948bcca81
Co-authored-by: qwavies <qwavsbusiness@gmail.com>
'spellcapcheck' checks the first word in the file for a capital like any
sentence start: capcol is seeded to 0 for line 1 in spell_check_sblock().
This has been the case since the feature was added in v7.0100, so the note
that it "doesn't work for the first word in the file" is incorrect.
related: f9184a1d3151b5b727fec86c2ac0946c9c68df4d (code change)
related: 0d9c26dd8333aae4b20015f13fe2e8e1f07037bd (initial doc patch,
just a few minutes later)
related: vim/vim#20715https://github.com/vim/vim/commit/e2d3e7818b0f408ad25ff97b86d6179c303c98d7
Co-authored-by: Christian Brabandt <cb@256bit.org>
Problem: The `showcmd` statusline item may show internal command keys when a
`<Cmd>` or `<ScriptCmd>` mapping redraws the statusline, and may
leave stale text behind when `%S` is rendered directly.
Solution: Do not add these internal mapping dispatch keys to the `showcmd`
buffer, and keep the clear state in sync when `%S` renders it
(Barrett Ruth)
closes: vim/vim#20769https://github.com/vim/vim/commit/bd730293dccaa166cd99321be674cb059d498b73
Co-authored-by: Barrett Ruth <br@barrettruth.com>
Problem: Memory leak when a spell file has an SN_SAL section before an
SN_SOFO section: set_sofo() reuses sl_sal without freeing the
salitem_T entries left by read_sal_section() (after v9.2.0846).
Solution: Factor the SAL free loop into free_sal_items() and call it
before set_sofo() reuses sl_sal.
closes: vim/vim#20836
Supported by AI.
https://github.com/vim/vim/commit/5a7ce2733a2d9d9fccad4dd48047546000d9994d
Co-authored-by: Christian Brabandt <cb@256bit.org>
Problem: When a tagfunc returns a "cmd" that is neither a line number nor
a search pattern, the tag entry is corrupted: the "kind" field is
lost and taglist() returns a mangled "cmd".
Solution: Accept any Ex command in "cmd" as in a tags file, terminate a
generic command with a bar so the trailing fields are preserved,
and reject a value that cannot be stored in a tag line with E987
(Hirohito Higashi).
fixes: vim/vim#20781
related: vim/vim#20790
closes: vim/vim#20828https://github.com/vim/vim/commit/86adef19fc2f1d59a38f3ba978ce33f0b4329c0f
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Mao-Yining <mao.yining@outlook.com>
Problem: [security]: arbitrary Ex command execution during C
omni-completion (Threonine)
Solution: Match tags typeref literally to block Ex command injection
(Yasuhiro Matsumoto).
Escaping only "/" and "\" left the typeref able to break out of the
:vimgrep pattern without a "/": an unclosed "[" makes vimgrep's pattern
skipping fail, and the parser then treats a following "|" as a command
separator, so the tag value runs as Ex commands during C omni-completion.
Match the field literally with \V so no regex metacharacter can affect
pattern parsing.
Github Security Advisory:
https://github.com/vim/vim/security/advisories/GHSA-cx73-phcg-3j5ghttps://github.com/vim/vim/commit/2f628d8104958fa7421664f792ca6d4f7a39a10f
Co-authored-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Problem: [security]: code injection in netrw via bookmarks and history
(David Carliez)
Solution: Escape the '|' explicitly (Yasuhiro Matsumoto)
The bookmark and history menu builders interpolate paths into :execute'd
:menu commands using g:netrw_menu_escape, which did not escape the Ex
command separator '|'. A crafted path could break out of the :menu command
and run arbitrary Ex/shell commands when the menu was built or triggered.
Add '|' to g:netrw_menu_escape for the menu names, escape the :e right-hand
side with fnameescape(), and quote the netrw#MakeTgt() argument with
string() instead of raw single-quote interpolation.
Github Security Advisory:
https://github.com/vim/vim/security/advisories/GHSA-rcr7-f3wr-22r2https://github.com/vim/vim/commit/29c6fd090d4520592f8be7d9ec81190edf25ef69
Co-authored-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Problem: When the terminal reports no colors ("t_Co" is 0 or 1) the
insert mode completion popup menu is not shown at all, while
the command line completion popup menu ('wildoptions' contains
"pum") is shown. In 'wildmenu' completion the current match
cannot be told apart from the other matches (Maxim Kim)
Solution: Show the insert mode completion popup menu regardless of the
number of colors and add "term" attributes to the default
highlighting of Pmenu, PmenuSel and PmenuThumb. The wildmenu
is drawn with the attributes of the status line, which is
reversed, and on most terminals the standout mode is the same
as the reverse mode, thus use the underline mode for the
default highlighting of WildMenu (Hirohito Higashi).
fixes: vim/vim#20800closes: vim/vim#20803https://github.com/vim/vim/commit/52485e0d24ade45e02ecba9aeadaec66500d15fe
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>