Problem:
No lint for strtol().
Solution:
Add lint, and update existing usages.
Callers that previously got a (garbage) large positive value:
- `indent.c`: use getdigits() (intmax, no int truncation) with def=1 so
overflow/too-large values stay positive and fall through to the
existing "too big" check (E475).
- `file_search.c`: use getdigits() with def=255 so overflow/too-large
values keep the "max expand" (else) branch.
The other conversions (api/command.c, eval/window.c, highlight_group.c,
tui/tui.c) only diverge on pathological overflow inputs where strtol's
result was already garbage and the observable outcome is unchanged.
socketserver.c is not compatible with Neovim's server implementation.
cairo, kitty, sixel, and similar backends for image rendering in popup
are not required in Neovim's C core.
Neovim can do this and even run Doom.
See patch 9.2.0612.
The following let-heredoc can cause legacy scripts to be misidentified
as Vim9 script if it appears early in the file. Only match :vim9script
at the start of a line where it sensibly belongs.
let x =<< trim LINES
vim9script
...
LINES
fixes: vim/vim#20647 (reported by Maxim Kim).
closes: vim/vim#206549a899af7e8
Co-authored-by: Doug Kearns <dougkearns@gmail.com>
Problem:
`nvim_set_option_value` cannot "update" options similar to `:set opt=`,
`:set opt+=`, etc. The Lua impls of "vim.opt" / "vim.o" have incomplete,
bespoke reimplementations of those operations.
ref #38420
Solution:
- Add `operation` param to `nvim_set_option_value`, which may be "set",
"append", "prepend", or "remove".
- Use this feature to implement `vim.opt` / `vim.o`.
Problem:
`os.exit()` in `nvim -l` exits through normal teardown. But, as #39783 shows,
when it is called from a libuv callback, teardown polling the main loop
when inside `uv_run()` can trip the recursive poll guard and cause a crash.
Fast callbacks are not safe to teardown, so it's better to schedule an exit out
of a callback rather than convolutedly handle this as I tried before.
Solution:
Reject `os.exit()` from fast callbacks with `E5560` error.
* refactor(autocmd): safely parse buflocal buffer numbers
Replace unsafe atoi with a robust integer conversion when extracting
the buffer number out of a "<buffer=123>" pattern in
aupat\_get\_buflocal\_nr.
This prevents potential silent integer overflows and ensures variables
fit safely within INT\_MAX boundaries before truncation.
* fix(autocmd): use getdigits_int to safely parse buffer numbers
* fix(autocmd): raise error for invalid buffer-local pattern values
When defining an autocommand, an invalid buffer-local pattern format
(such as `<buffer=foo>` or `<buffer=0>`) would previously fall through
silently and default to a generic `<buffer>` tag behavior.
Fix this regression by trapping invalid buffer numbers
early from `aupat_get_buflocal_nr()` and throwing a proper E680 error
to match Vim's runtime validation behavior.
Problem: due to totally async install/update/checkout there is no
guaranteed order of `PackChanged{Pre,}` events across different
plugins. This might lead to conflicts when callback for some "main"
plugin relies on features from "dependency" plugin: i.e. callback for
"main" plugin can trigger before installing/updating "dependency"
plugin. The installation order can be enforced by separate
vim.pack.add() calls, but update/checkout can not.
Solution: Trigger events in bulk independently of async execution:
- `PackChangedPre` before any action for all input plugins in order
they are supplied. It will also trigger even if an action will fail.
- `PackChanged` after all actions finished for all sucessfully
affected plugins in order they are supplied.
This also comes with a couple of side effect changes:
- `PackChangedPre kind=delete` is now also triggered even if the
delete won't be done. This makes it more aligned with `kind=install`
and `kind=update`.
- Force update (`:packupdate!`) and "udpate LSP action" now do two
async steps: download/compute updates and apply them. This also
results in two progress reports.
This is mostly a by-product of the implementation (there has to be
a pre-computation of target revision for all plugins before doing
`PackChangedPre` in bulk before possibly applying an update), but I
kind of like it more this way as it is more explicit of what's going
on. If absolutely not acceptable, there might be some hacks to
mitigate it at least in code action, but I'd keep it like this.
Problem:
dir.lua leaves previously-navigated directory buffers around.
This is fine by default, but users need a simple way to opt out.
Solution:
1. Respect `set hidden` (via `'bufhidden'`) as one way to make
previously-navigated dir buffers from showing up.
2. Document a one-liner to hide these buffers
Problem: plugin/shada.lua does not respect the `+` flag of 'cpoptions'
when writing a *.shada buffer into a different file.
Solution: Check the presence of `+` in 'cpoptions' and whether the
buffer is written to a different file in BufWriteCmd for
*.shada before resetting the 'modified' option of the buffer.
This little defect appeared after plugin/shada.vim was rewritten into
Lua in #34725.
Problem: Separation markers (%=) are ignored within item groups. This
lead to a regression when the C implementation of the statusline was
replaced with a default expression. When the user configured a custom
ruler expression with a %= and used the overloaded item group syntax to
set the ruler width, the separation marker worked in the ruler, but not
when the ruler was incorporated into the statusline where the item group
syntax was interpreted in the usual way.
Solution: Analogously to top-level behaviour, expand separation markers
evenly within item groups until `minwid` is reached (if set).
ref https://github.com/neovim/neovim/pull/33036
fix https://github.com/neovim/neovim/issues/39984
ref https://github.com/neovim/neovim/issues/40247
Problem: The recursion offset into the static `stl_items` was not taken
into account when adjusting the item count after truncation.
Steps to reproduce: first prepare `stl_items`:
set stl=%{%repeat('%#Error#',10)%}
then watch how the Error highlight leaks into the recursive call:
set stl=%l%l%l%{%nvim_eval_statusline('test%l%<',{'maxwidth':3,'highlights':1}).highlights%}
ref https://github.com/neovim/neovim/issues/32259
* fix(statusline): consistent truncation at multicell character
Problem 1: truncation of item groups at multicell character didn't take
into account that minwid can be specified as a negative number.
Problem 2: after truncation at top-level from the right at multicell
character, the returned width was always `maxwidth`, even though the
actual width was reduced. In vim, this can be observed as a statusline
that is not fully drawn until the edge of the screen:
vim --clean +"set ls=2 stl=%{%repeat('x',&columns-2)%}🙂x%<"
Problem 3: after truncation at top-level from the left at multicell
character, the resulting gap to reach `maxwidth` again was filled with
fillchars, but then the final NUL was not set correctly.
This can be seen in the following example, where the statuscolumn spills
into the editing area starting from line 10:
nvim --clean +"set number stc=%<x🙂%{repeat('x',43)}%l" +"norm yy10p"
Solution: fix the small errors and, at top-level, consistently reduce
the size instead of compensating with fillchars. In the case of the
statusline and the winbar, the remaining place is filled with the
configured fillchars in `win_redr_custom`, after `build_stl_str_hl` has
returned. In all other cases (title, icon, statuscol, tabline, ruler),
there seems to be no point in adding additional spaces at the end.
* feat(statusline)!: scope %< to item groups
Problem:
Previously, item groups were only truncated at the beginning, which is
often not desired. In the example
%.15(path: %f%)
the group's title/label is truncated away:
<th/to/file.txt
Truncation markers (%<) in item groups were processed at the top-level
in the end, which can be confusing. Only the first %< is used for the
whole string, and it is used even if the containing item group is
hidden. Additionally, in the case of hidden item groups, the marker's
position was not adapted. For example,
%(hidden%<%)%f
had the effect of truncating the path somewhere in the middle:
/path/<file.txt
Solution:
Make truncation consistent with top-level behaviour, which has a better
default of truncating at the first `Normal` item, i.e.
path: <file.txt
and allows for fine-grained control with truncation markers (%<). E.g.
%.15(path: %f%<%)
now yields
path: /path/to>
The original behaviour can be restored like so:
%.15(%<path: %f%)
BREAKING CHANGE: %< is no longer processed at top-level
- the default truncation behaviour has changed: now at first item
- truncation markers inside item groups don't affect truncation outside
of the item group anymore
- several truncation markers can now have an effect when separated with
item groups, whereas previously only the first one globally had
ref https://github.com/neovim/neovim/issues/39984
Problem: LspNotify will fire for any attached client. If there's at
least two clients where one has semantic tokens enabled and one that
doesn't, the disabled one will get the LspNotify requests but won't have
a client state.
Solution: Only process LspNotify autocmds if there's a client state to
act on.
Fixes#40448
Problem: A crafted swap file can cause an out-of-bounds write during
recovery when the same block is referenced twice with
different pe_page_count values (cipher-creator)
Solution: Check hp->bh_page_count against page_count after mf_get() and
clamp page_count to the actual block size.
closes: vim/vim#2064543939cf9eb
Co-authored-by: Christian Brabandt <cb@256bit.org>
Problem: With PHP omni-completion, a crafted file can potentially
execute arbitrary commands when completing a class member.
Solution: Quote the class name before inserting it into the search()
pattern run via win_execute().
43afc581a3
Tests only. The bug is not ported yet.
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: [security]: With C omni-completion, a crafted tags file can execute
arbitrary Ex commands when completing a struct/union member
(cipher-creator)
Solution: Escape the type field before inserting it into the :vimgrep
pattern so it cannot close the pattern and start a new command
(Hirohito Higashi).
Github Security Advisory:
https://github.com/vim/vim/security/advisories/GHSA-mf92-v4xw-j45x6b611b0d15
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Problem: The "%" command and bracket/text-object motions can skip the
matching paren or bracket on a line with quotes and a trailing
backslash.
Solution: Use the quote state at the search start when an odd-quote line
is continued with a backslash, instead of always treating the
search as starting in quotes (Barrett Ruth).
closes: vim/vim#20631c44a6561cc
Co-authored-by: Barrett Ruth <br@barrettruth.com>
Problem: ":filetype detect" is a valid command but not offered by
command-line completion.
Solution: Add "detect" to the completion candidates (glepnir)
closes: vim/vim#20625984f29860e
Co-authored-by: glepnir <glephunter@gmail.com>
Problem: filetype: supertux info pattern is relative to current
directory (after 9.2.0716).
Solution: Add `*/` to the start of the pattern (zeertzjq)
closes: vim/vim#20629c30ee2efa9
fix(lsp): define autocmds for capabilities in new(), not on_attach()
Problem: Defining autocmds in on_attach() caused issues when multiple
clients provide the same capability for a buffer. Each attaching client
would "replace" the previously defined one since they are all identical.
Then the first one to detach clears them out and any remaining attached
clients would no longer trigger the autocmd for the capability. Further,
the semantic tokens module itself didn't quite work with multiple
clients since any LspNotify (from any client) would send a token request
to all attached clients with no differentiation and the debounce timer
was shared across all clients.
Solution: Always define the buffer-local autocmds in the capability's
`new()` function, and don't mess with the autocmds in on_attach or
on_detach. The capability framework itself will clear the autocmds when
the last client detaches. Also, refactor a bit of the semantic tokens
module so that the various methods take a specific client_id to perform
the work on, and split out timers so each client has its own.
Problem:
Legacy cmdwin set 'buflisted', but new one doesn't.
https://github.com/neovim/neovim/issues/40431#issuecomment-4811593353
'buflisted' useful for:
- "bufferline" style tablines, they usually show only listed buffers.
- some automatic actions can be conditioned on whether a buffer is
listed; 'buflisted' signals that the buffer is "important enough".
Solution:
Set 'buflisted'.
Problem:
After ctrl-f from the cmdline, the last 2 lines of cmdwin are redundant.
Solution:
In `open_cmdwin`, clear the live cmdline so that unwinding it (via
Ctrl_C) does not add it to history.
Problem:
Diagnostic tracking used a separate bufstates table and manual
LspDetach/LspNotify autocmd management via _enable()/_refresh(),
duplicating the lifecycle logic already provided by the Capability
framework. This caused inconsistencies in how client attach/detach and
buffer teardown were handled compared to other LSP features.
Solution:
Replace the ad-hoc bufstate tracking and _enable/_refresh pattern in
vim.lsp.diagnostic with a proper Diagnostics subclass of Capability.
This cleans up a few random places in the main lsp module and client
module that were poking the diagnostics. It also fixes some pre-existing
bugs and inconsistencies that were discovered:
- Refresh diagnostics immediately on attach instead of lazily by the
first didOpen/didChange notification
- Fix Capability.active lookup in M.enable() to key by it_bufnr instead
of the filter bufnr
- Set lsp defaults before calling the _text_document_did_open_handler in
Client:on_attach() so defaults are there before any lsp notification
occurs
- Log (and return early) on any error from a diagnostic request result
instead of only returning early for server cancelled errors
Problem:
cmd given as string[] always starts using Nvim's CWD, which is arbitrary.
Solution:
If cmd_cwd is not given, use root_dir as CWD.
BREAKING CHANGE: LSP commands given as string arrays now use `root_dir` as
the process working directory when `cmd_cwd` is unset.
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
vim-patch:ccdc81701 CI: Restore daily Coverity Scan
vim-patch:9.2.0710: GTK4 GUI resize handling can be improved
vim-patch:9.2.0712: GTK4: dialogs not handling mnemonics correctly
vim-patch:9.2.0720: GTK4: no support for browsefilter
vim-patch:9.2.0722: GTK4: find/replace dialog can be improved
vim-patch:9.2.0724: Use-after-free when freeing exit_cb job on exit
vim-patch:9.2.0709: GTK4: a few minor issues
vim-patch:9.2.0711: leak in ins_compl_infercase_gettext() in error case
vim-patch:9.0.0996: if 'keyprotocol' is empty "xterm" still uses modifyOtherKeys
vim-patch:9.0.1504: no error when calling remote_startserver("")
vim-patch:9.2.0042: crypt: cannot decrypt empty file
vim-patch:9.2.0113: winhighlight pointer may be used uninitialized
vim-patch:9.2.0118: memory leak in w_hl when reusing a popup window
vim-patch:9.2.0119: incorrect highlight initialization in win_init()
vim-patch:9.2.0292: E340 internal error when using method call on void value
ArrayOf metadata existed before the LuaCATS type documentation refactor and is
useful to typed clients consuming api_info().
Keep Tuple metadata normalized to Array, since tuple element types can be mixed,
but preserve ArrayOf(...) for exported API metadata.
Fixes#38734
AI-assisted: Codex
Problem:
`:edit <dir>` and `nvim <dir>` currently rely on netrw to show local directory
contents.
Solution:
- Provide `filetype=directory`.
- Introduce dir.lua, a small plugin that provides directory listing, opening
items, parent navigation, and refresh.
- `netrw` remains available for `:Explore`, remote paths, archives, and file
operations. To continue
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Problem:
Executing :messages while in cmdwin fails:
Error in "msg_history_show" UI event handler (ns=nvim.ui2):
Lua: …/_core/ui2/messages.lua:699: Invalid 'height': expected positive Integer
stack traceback:
[C]: in function 'nvim_win_set_config'
…/_core/ui2/messages.lua:699: in function 'set_pos'
…/_core/ui2/messages.lua:329: in function 'set_target_pos'
…/_core/ui2/messages.lua:389: in function 'show_msg'
…/_core/ui2/messages.lua:553: in function 'handler'
…/_core/ui2.lua:161: in function 'ui_callback'
…/_core/ui2.lua:210: in function <…/_core/ui2.lua:202>
The bug: when `texth.all` is small (e.g. 0 from a hidden pager whose new
content isn't laid out yet), or when the available height after
subtracting the cmdwin is small, `math.min(min, …)` can yield 0, and
`nvim_win_set_config` rejects `height=0`.
Solution:
Floor at 1.
Problem:
cmdwin (the `:q` cmdline buffer) has various limitations which require
special-casing all over the codebase.
Besides complicating the code, it also breaks async plugins if they try
to create buffers/windows after some work is done, if the user happens
to open cmdwin at the wrong the moment:
Lua callback: …/guh.nvim/lua/guh/util.lua:531:
E11: Invalid in command-line window; <CR> executes, CTRL-C quits
stack traceback:
[C]: in function 'nvim_buf_delete'
…/guh.nvim/lua/guh/util.lua:531: in function <…/guh.nvim/lua/guh/util.lua:526>
Solution:
Just say no to "inception". Reimplement cmdwin as a normal buffer+window.
All of the cmdwin contortions (in both core, and innocent plugins) exist
literally only to support "inception": recursive
cmdwin-in-cmdline-things, like `<c-r>=`, `/`, search-during-substitute,
`:input()`, etc. So we just won't support that (though I have
a potential plan for that later, which I call "modal parking lot").
The benefit is that plugins, and core, no longer have to care about
cmdwin.
BONUS:
- mouse-drag on vertical separators works (it only worked for
horizontal/statusline before)
- inccommand-in-cmdwin now works correctly, for free (thus don't need
#40077).
POTENTIAL FOLLOWUPS
- Drop `CHECK_CMDWIN` ("E11: Invalid in command-line window"), allow chaos.
- Unify `BUFLOCK_OK` / `LOCK_OK` ?
DESIGN:
- Eliminate lots of C globals, `EX_CMDWIN`, etc.
- `text_locked()` no longer reports true for cmdwin.
- cmdwin = a normal window with 'winfixbuf', 'bufhidden=wipe',
'buftype=nofile'. Invariants come from those options rather than
special cases throughout the codebase.
- `nv_record` for q:/q//q? calls Lua
`nlua_call_vimfn("vim._core.cmdwin", …)`. No `K_CMDWIN`
/ cmdline-reader detour.
- `cedit_key` (`c_CTRL-F`) schedules a deferred event that calls
`vim._core.cmdwin.open(type, content, pos)` and returns `Ctrl_C` so
the in-flight cmdline cancels. Reader state is not serialized; instead
the captured `(type, line, col)` is replayed via
`nvim_feedkeys(type..line.."<CR>", "nt", …)` after user confirms.
- On confirm/cancel: `<CR>` / `<C-C>` calls into Lua which closes the
window and re-feeds the cmdline.
BREAKING CHANGES:
- Expression-register cmdline (`<C-R>=` from insert-mode) no longer
supports cmdwin. Same applies to `input()` / `inputlist()` (already
covered by `text_locked`).
- Usage of cmdwin in macros/mappings will probably break (assuming they
ever worked).
Problem: The table `history` in treesitter/_select.lua stores references
to previously selected TSNodes, but a TSNode needs to keep its
whole TSTree alive in memory, which may be kept alive even
after closing the buffer to which this history corresponds.
Solution: Store just the bits of information from the TSNode we need to
go back in selection history (the range and ID), without
referencing the TSNode itself.
Problem: The function visual_select() in treesitter/_select.lua is
executing `:normal! v<Esc>` to ensure that `gv` later goes
back to character Visual mode, but doing so when mode() is
already `v` first switches back to the Normal mode, then
executes <Esc>, which causes a beep.
Solution: Check if the mode() is already `v` and avoid any switching in
that case.
Problem:
- The `reviewers_add`/`reviewers_remove` CI jobs checkout the repo only
for the purpose of `require('…/reviewers_add.js')`.
- `reviewers_remove` also runs on `closed`, so a merged fork PR will trip
the new allow-unsafe-pr-checkout guard of `actions/checkout@v7`.
Solution:
- Drop the use of `actions/checkout`.
- Inline the script in the CI yaml definitions.
- Also await the mutating calls (previously fire-and-forget) and guard
empty reviewer lists.
Problem:
The backport workflow started failing since we updated to v7 of
`actions/checkout@v7.0.0`. The workflow runs on `pull_request_target`
(closed + merged) and failed with:
Refusing to check out fork pull request code from a 'pull_request_target'
workflow. Review the risks at https://gh.io/securely-using-pull_request_target
Example: https://github.com/neovim/neovim/actions/runs/28161004305
Analysis:
v7.0.0 added the `allow-unsafe-pr-checkout` guard, which refuses when the
resolved checkout SHA matches the PR's head.sha *or* merge_commit_sha. With no
explicit `ref`, checkout resolves to the base branch tip (`github.sha`). For a
merged PR that tip *is* the merge commit, so the guard refuses even
though it is already-merged trusted base code, not live fork code.
Solution:
Checkout the base branch by name. A branch ref is neither a SHA nor
a `refs/pull/*` ref, so the guard passes. This is also semantically more
correct: backports should branch off the ref the PR merged into.
Some servers register `workspace/didChangeWatchedFiles` watchers for URI
schemes that cannot be watched locally. Skipping the unsupported glob
and keep the rest of the registration batch active.
Problem: LspNotify autocmds were not being triggered for didChange
requests when being used during undo/redo (and possibly other) actions.
autocmds are blocked when calling on_lines() callbacks while doing the
undo/redo action.
Solution: Defer firing the autocmd until after the action is complete.
This is closer to what existed before, but now there's a check in the
deferred function to only fire the autocmd if the client is still
active and the buffer is still attached, if applicable.
Problem: [security]: A crafted spell file with non-collapsing SAL rules
can make soundfold() write one byte past the end of the
MAXWLEN result buffer. This is the same class of
out-of-bounds write as GHSA-q8mh-6qm3-25g4 (fixed in 9.2.0698
for the SOFO branch), found while auditing the surrounding
code.
Solution: Bound the single-byte SAL result writes and the terminating
NUL to MAXWLEN - 1, matching the SOFO branch.
The single-byte branch of spell_soundfold_sal() guarded its writes with
"reslen < MAXWLEN", allowing reslen to reach MAXWLEN (254). The trailing
"res[reslen] = NUL" then wrote at index 254 of the 254-byte stack buffer
res[MAXWLEN], an off-by-one out-of-bounds write. Input is case-folded to
about 253 characters, so a 253-character argument together with a SAL map
that does not collapse (collapse_result false) reaches the boundary.
Related to previous issue
[GHSA-q8mh-6qm3-25g4](https://github.com/vim/vim/security/advisories/GHSA-q8mh-6qm3-25g4)
(9.2.0698)
Github Security Advisory:
https://github.com/vim/vim/security/advisories/GHSA-m3hf-xcm3-xhm2d22ff1c955
This is N/A as Nvim doesn't have spell_soundfold_sal() which is only
used in the !has_mbyte code path.
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem: tests: strange indent in Test_autocmd_dup_arg()
(after v9.2.0708)
Solution: Indent using the settings in the modeline.
closes: vim/vim#2061905df981c35