Commit Graph

8480 Commits

Author SHA1 Message Date
Freddie Haddad
9ff302d0ca fix(ui): highlight bleeds into 'linebreak' filler #41072
Problem:
When 'linebreak' pushes a word entirely to the next screen row, the
filler cells left on the current row keep whatever highlight was set
by the last real character before the break, even when that highlight
should not extend past it (e.g. an underline, which looks broken drawn
over blank cells).

Solution:
Reset decor_attr and area_attr at the filler when their attribute has
an underline, undercurl, strikethrough, or overline; otherwise leave
them, since a plain background or reverse-video highlight looks
correct extending through blank filler cells, regardless of where the
pushed-down word happens to end. search_attr keeps the same check,
plus its pre-existing on_last_col case (its own match ending exactly
here).
2026-08-03 05:20:04 -04:00
Freddie Haddad
67839a72f7 fix(treesitter): make TSHighlighter.new() idempotent for an active buffer #41090
Problem:
Calling vim.treesitter.start() a second time on a buffer that already
has an active TSHighlighter creates a brand new instance instead of
reusing it, whether the parser tree is unchanged (e.g. calling start()
twice) or different (e.g. switching languages). Either way the old
instance is silently discarded without calling :destroy() on it, so
its on_bytes/on_changedtree/on_detach callbacks stay registered and
its buffer-local state (spelloptions, decoration namespace) is never
restored, both leaking indefinitely for an orphaned instance that
nothing references anymore.

Solution:
Return the existing instance when TSHighlighter.active[source] is
already set for the same parser tree, instead of unconditionally
constructing a new one. When the tree differs instead (e.g. a language
switch), destroy() the old instance first, matching stop() semantics,
before constructing the new one.
2026-08-02 14:42:47 -04:00
Rob Pilling
2f09090134 fix(ui2): :$q should not target hidden windows #40992
Similar to #36123, the ui2 windows confuse `:$q`, which doesn't close
the last window on the tab, but targets a hidden ui2 window.

The problem seems to be a combination of using
`FOR_ALL_WINDOWS_IN_TAB()`, which includes hidden windows, and then
counting the windows without factoring in `win_has_winnr()`.

There are examples of using `win_has_winnr()`, such as:
- [`eval/buffer.c`'s `buf_win_common()`](4a5062cda6/src/nvim/eval/buffer.c (L474-L475))
- [`eval/window.c`'s `f_getwininfo()`](4a5062cda6/src/nvim/eval/window.c (L130-L135))
- [`window.c`'s `win_get_tabwin()`](4a5062cda6/src/nvim/window.c (L7860-L7869))

But possibly problematic ones:
- `ex_docmd.c`'s:
	- [`invalid_range()`](4a5062cda6/src/nvim/ex_docmd.c (L3891-L3893))
		- ^ should this include hidden windows? Can a user address them by number? `win_has_winnr()` suggests not
	- [`ex_close()`](4a5062cda6/src/nvim/ex_docmd.c (L5233-L5244))
	- [`ex_hide()`](4a5062cda6/src/nvim/ex_docmd.c (L5478-L5484))
- `eval/window.c`'s:
	- [`f_winrestcmd()`](4a5062cda6/src/nvim/eval/window.c (L797-L808))
	- [`find_win_by_nr()`](4a5062cda6/src/nvim/eval/window.c (L172-L180))
2026-08-02 14:37:26 -04:00
Justin M. Keyes
f7fbe0e0c8 Merge #41106 from altermo/selection-refactor-fix 2026-08-02 05:33:17 -04:00
Justin M. Keyes
fe10c5bc36 test(fs): "fs.find() follows symlinks" #41105
Problem:
Test fails if other "build*/" dirs exist with a "nvim" file, e.g.
`build-asan/bin/nvim`.

Solution:
Do the test in an isolated dir.
This also fixes the test for the zig build.
2026-08-02 05:09:58 -04:00
zeertzjq
a5103c0853 vim-patch:9.2.0892: highlight: wrong column highlighted with 'cursorcolumn' (#41110)
Problem:  With 'virtualedit' set to "all" and 'cursorcolumn' set, the wrong
          column may be highlighted after a command that moved the cursor
          into virtual space and back (van-de-bugger).
Solution: Make sure the virtual column is up to date before drawing the
          window (Hirohito Higashi).

fixes:  vim/vim#2576
closes: vim/vim#20902

5a90b9dbd2

Test only. This was already fixed by #39159.

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 02:20:11 +00:00
Justin M. Keyes
98d767cd53 docs: func/expr options, misc #41102 2026-08-01 17:48:32 -04:00
altermo
fe8e6a7efa test(lsp-selection): of by one
The test uses the position col=5,row=5; but such a position
is outside of the buffer.

Presume that it was meant to be col=5,row=4...
2026-08-01 18:15:23 +02:00
Justin M. Keyes
e02755cd9f fix(options): ":set foo?" for Lua callbacks #41100
Problem:
`:set foo=<tab>` and `:set foo?` for func/expr options set to a Lua
function, always displays "v:lua".

The existing "<Lua N: file:line>" form was avoided because the ref id
N changes on every read of the same option.

Solution:
- Don't attempt to tab-complete Lua functions.
- Show "<Lua file:line>" (or "<Lua>" if source file is unknown).

Example:

    :set operatorfunc?
      operatorfunc=<Lua ~/.config/nvim/init.lua:42>
2026-08-01 10:07:26 -04:00
AlexCodesApps
b11f1b1f76 feat(vim.fs): abspath({cwd, plain}) #40597
Adds optional parameters to `vim.fs.abspath`:
1. `cwd` to specify directory akin to `--relative-from` in `realpath(1)`.
2. `plain` disables expansion of tilde (~) in paths.
2026-08-01 09:09:21 -04:00
Justin M. Keyes
3e01dcddbe refactor(options): memory management
Problem:
Numerous callers have to manually check optval ownership (i.e. whether,
and how, to release) via `is_callback_option`, `option_is_global_local`,
etc. This is fragile, hard to use correctly; and if we introduce another
optval variant in the future, we'll have to redo all of these careful
checks and boilerplate again.

Solution:
Provide a unified system and use it everywhere:

    optval_free_owned
    optval_is_owned
    optval_own
2026-07-31 21:50:04 +02:00
Justin M. Keyes
726d1a92d2 feat(options): lua closure/function options
Problem:
Cannot assign Lua functions/closures to "func" ('completefunc',
'tagfun', …) or "expr" ('foldexpr', 'indentexpr', …) options.

Solution:
- Store "func"/"expr" options as `Callback` instead of string.
- Delete oceans of copy-pasted code.
- BREAKING: LuaRef returned via RPC/Vimscript is now represented as
  `"<Lua N: file:line>"` (like what `:map` shows) instead of `nil`.
- Note: `man.vim` still uses `v:lua` string, bc it's a vimscript ftplugin.

Helped-by: Lewis Russell <lewis6991@gmail.com>
2026-07-31 21:50:04 +02:00
Justin M. Keyes
917232b508 Merge #40953 from barrettruth/feat/zip-extract
feat(zip): extract archive entries
2026-07-31 15:45:14 -04:00
zeertzjq
f7cc79edf4 vim-patch:9.2.0881: 'smoothscroll' position is lost when the window height changes
Problem:  With 'smoothscroll' the scroll position of a window is lost when
          its height changes.
Solution: Only reset the skipped columns when 'smoothscroll' is off, where
          they just serve to keep the cursor visible.

closes: vim/vim#20885

17f3923b8c

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:22:27 +08:00
Barrett Ruth
1bba96f259 fix(test): group zip tests and drop repeated setup 2026-07-29 14:32:37 -05:00
Barrett Ruth
1ffbd3598a test(zip): cover difficult member names 2026-07-29 14:32:37 -05:00
Barrett Ruth
36204b4323 feat(zip): extract archive members 2026-07-29 14:32:37 -05:00
Barrett Ruth
f82cf8fd59 fix(zip): list a valid empty archive as empty 2026-07-29 14:32:36 -05:00
Barrett Ruth
e63cf57106 fix(zip): keep a leading dash from becoming a backend option 2026-07-29 14:32:36 -05:00
Barrett Ruth
e29af9c32f refactor(zip)!: builtin zip.lua plugin #40846
Problem:
The bundled `zip` plugin is implemented in Vimscript, making it harder to
maintain and build on with Nvim's Lua runtime infrastructure.

Solution:
Add an opt-out `zip.lua` browser backed by `nvim.dir`, and disable the legacy
Vimscript implementation by moving it to `pack/dist/opt/zip/`. Load it with
`:packadd zip`.
2026-07-29 12:54:43 -04:00
Nathan Zeng
1dbc766fa7 fix(ui2): clear empty cmdline after backspace #41043
Problem:
Cmdline area shows stale ":" after backspacing out of the command line.

Solution:
Clear the command line for empty commands. Note that `:<CR>` will now
clear the command line too.
2026-07-29 12:40:08 -04:00
Barrett Ruth
635acc7dc8 fix(terminal): cursor moves on resize when line above is full width #40996 2026-07-28 14:59:18 -04:00
Barrett Ruth
0d36e61d70 fix(dir): "-" on a non-file buffer opens a bogus path #41034 2026-07-28 13:37:06 -04:00
Evgeni Chasnovski
4bba83b4b7 fix(pack): do not write the lockfile when installing from it #41031
Problem: Installing plugins during lockfile synchronization always
  writes the lockfile, even though its content is used during install.
  This might be a problem if the lockfile (itself or its parent
  directory) is not writeable (can only be read).

Solution: Do not write the lockfile when installing directly from it.
  This is okay since the `src` and `rev` are used directly from the
  lockfile and don't change at this step. While potential change in
  `version` (that must be written to the lockfile) is handled in other
  code path.
2026-07-28 09:46:13 -04:00
glepnir
3fa5904cf2 fix(lsp): multibyte fragment treated as commit char #41019
Problem:
commit_chars_str() reads a 0x80-0xBF byte as a 2-byte lead, so
"\xA9x" makes "x" a commit character. nvim_get_hl() returns
a dict, so `#` on it is 0 whether or not the group exists

Solution:
use vim.str_utf_end() and drop entries that are not one whole
character. use next check dict length.
2026-07-28 08:39:03 -04:00
tianrking
089c415cb2 fix(floatwin): relative='cursor' may use outdated cursor position #40768
Problem:
Cursor-relative floats can use stale screen coordinates after a cursor move is restored without a redraw.

Solution:
Validate the current cursor before converting cursor-relative coordinates.

Co-authored-by: zeertzjq <zeertzjq@outlook.com>
2026-07-28 07:32:35 -04:00
glepnir
916a6e9546 fix(eval): complete_info() equal, preselect, commit_chars #41022
Problem:  complete_info() does not report "equal", "preselect" or "commit_chars".

Solution: Report them for the items that set them.
2026-07-28 07:24:58 -04:00
Olivia Kinnear
5226e26e99 feat(pos): vim.pos.cursor() without args returns curwin pos #40975 2026-07-28 07:21:45 -04:00
Milad Rashidikhah
20ff82d9fc fix(treesitter): clamp unbounded fold ranges
Problem:
Tree-sitter uses UINT32_MAX for full-document ranges, which becomes -1 on 32-bit platforms and reaches _foldupdate as an invalid end row.

Solution:
Treat negative changed-range end rows as unbounded and clamp them to the buffer line count. Add a regression test that simulates the 32-bit sentinel.

AI-assisted: Codex
2026-07-28 11:00:30 +02:00
Barrett Ruth
e3c5974adf feat(dir): open cwd with 1- #40948 2026-07-27 18:30:54 -04:00
Justin M. Keyes
fbcb7a056c feat(exmode): "1q:", :exmode #41010
Problem:
Want `gQ` for _le multicursor_.

Solution:
- Don't use `gQ` for exmode.
- Introduce `:exmode`.
- Introduce `[count]q:` as an alias to `:exmode`.
2026-07-27 11:12:47 -04:00
Josh Hirschkorn
97a71ab484 fix(man): use direct lookup without manpath #40421
Problem:
On NetBSD, `man -w open` can return the exact manpage path, but `:Man`
may still fail when man directories cannot be discovered from `manpath
-q`, bare `man -w`, or `$MANPATH`.

Solution:
Fall back to the direct manpage lookup when directory discovery fails.
Add a test for resolving `open(2)` through `goto_tag()` without manpath
data.
2026-07-27 13:20:46 +00:00
glepnir
0b3b12da8d fix(completion): "preselect" ignores "noinsert", menu order #41007
Problem:  A preselected item is inserted even with "noinsert", and the
          first added preselected item wins over the first one shown
          in the menu.
Solution: Use K_DOWN when "noinsert" is set; pick the first preselected
          item that made it into the menu.
2026-07-27 08:49:12 -04:00
Justin M. Keyes
80cd482577 fix(lua): vim.regex():match_str abort in Luv callback #41008 2026-07-27 11:01:54 +00:00
Justin M. Keyes
359459dec6 refactor(exmode): Ex-mode as cmdwin + Lua #40991
Problem:
POSIX-compatible Ex-mode requires special-cases all over the codebase to
match various quirks that don't actually matter to users.
- The main utility of *interactive* Ex-mode is its REPL behavior, and
  that can be achieved with `cmdwin`, which also gains extra UX
  benefits.
- The main utility of *non-interactive* `nvim -es` is for shell
  scripting, where Ex-mode quirks are mostly unhelpful (e.g. the
  "Entering Ex mode" message).

Solution:
- Reimplement *interactive* Ex-mode as a "persistent, insert-mode
  cmdwin" in Lua.
  - "nvim -e/-E" is simply an alias to "gQ".
- Reframe *non-interactive* Ex-mode (`nvim -es`) as "script mode".
  - Drop POSIX Ex-mode quirks.

Improvements:
- "nvim -V1 -es" output ends with a final newline!
- "nvim -V1 -es" no longer shows the "Entering Ex mode" msg. (This was
  pointless noise, unwanted for scripting purposes.)
- stdin is no longer typeahead. Scripts (":lua io.read()") can read
  stdin as data.
- Empty line is a no-op: a stray blank line no longer moves the cursor
  (deviates from POSIX ex "+1"), no longer exits 1 at EOF (E501).

Preserved behavior:
- cursor starts at "$"
- mode()=="cv" (for non-interactive)
- multiline commands (:append/:function/heredoc pull continuation lines)
- bare-range print
- :print=>stdout
- -V1=>stderr
- CRLF input
- continue-after-error and exit codes

Dropped (regressed) POSIX behavior (non-interactive):
- Event loop only ticks while/between commands, not while blocked
  waiting for a stdin line.
- ":g/pat/visual...Q"
- input()/getchar()/":s/x/y/c" no longer consume stdin lines as
  answers: Nvim stops at end-of-input, skipping the rest of the script,
  exit 0. Use ":lua io.read()" instead.
  - If users care about this they should use interactive Ex-mode (`gQ`).
- ":@r" stops at end of the register instead of continuing to read
  cmdline input from stdin.
2026-07-27 06:25:21 -04:00
Barrett Ruth
faaa4d57f1 test: start plugin specs with --clean #40995 2026-07-27 04:26:11 -04:00
Dominic Della Valle
4a5062cda6 fix(path): DOS device path check #40976
Problem:
The current check is intended to match DOS device paths starting with
`\\?` or `\\.` but will match any path starting with `?` or `.`,
including relative paths such as `.\`.
This is because the leading slashes are removed before the comparison.

Solution:
Changes the check to include the path's prefix, assuring the path has
the correct amount of separators for a DOS device path.
2026-07-26 07:18:57 -04:00
Artem Krinitsyn
dec3d8215d fix(text): indent() ignores expandtab when indent unchanged #40932
Problem:
After expanding tabs with the `expandtab` option, if the old indent
matches the requested one, `vim.text.indent()` returns the input
unchanged.
The optimization path assumes that if old_indent == size, the input
wouldn't be changed, which is not correct when old_indent is formed by
expanded tabs.

Solution:
Apply the optimization only when `expandtab` is not set.
2026-07-26 06:24:39 -04:00
Justin M. Keyes
6539bd6602 Merge #40910 from barrettruth/fix/dir-literal-env-paths 2026-07-26 06:11:01 -04:00
Barrett Ruth
3d5de7fd11 fix(buffer): name becomes empty when it is the cwd #40980
Problem: Shortening the name of a buffer whose full name is the current
directory + path sep yields empty string -> buf unnamed.
This doesn't interact well with things like `:mksession`, which
for example could `:badd` with no arg -> E471.

Solution: Keep the full path when shortening yields an empty name, as
`path_try_shorten_fname()` already does.
2026-07-26 05:36:03 -04:00
zeertzjq
7ee1bf91ad vim-patch:9.2.0855: 'showcmd' not redrawn with empty mapping triggered on timeout
Problem:  'showcmd' not redrawn with empty mapping triggered on timeout.
Solution: Don't postpone redraw when inside vgetorpeek(). Also move test
          for tabline 'showcmd' to test_tabline.vim.

fixes:  vim/vim#20839
closes: vim/vim#20840

2e9687647a
2026-07-26 07:07:38 +08:00
Barrett Ruth
1594ca2368 fix(dir): preserve environment variables in paths 2026-07-25 18:02:55 -05:00
Barrett Ruth
ac4e5460b1 feat(vim.fs): dir(opts.normalize) 2026-07-25 18:02:55 -05:00
Justin M. Keyes
256a88d0ac Merge #40579 from barrettruth/fix/del-keymap-lhs-only-30258 2026-07-25 16:00:25 -04:00
mityu
26fed075ae fix(cmdwin): q: in visual-mode does not insert visual range #40973
Problem: `q:` in visual mode does not insert `'<,'>` automatically in
the current line on cmdwin.

Solution: Insert `'<,'>` in the current line on cmdwin when `q:` is used
in visual mode.
2026-07-25 15:50:31 -04:00
Barrett Ruth
22d44ef875 fix(api): add lhs option for keymap deletion 2026-07-25 10:36:56 -07:00
Barrett Ruth
6ffbb2962c test(api): allow added optional params 2026-07-25 10:01:08 -07:00
Justin M. Keyes
715d8887ec docs: misc #40847
Co-authored-by: Barrett Ruth <br@barrettruth.com>
Co-authored-by: Nathan Zeng <nathan.j.zeng@gmail.com>
2026-07-25 12:47:51 -04:00
Barrett Ruth
4975a186f2 fix(dir): :browse edit opens dir browser #40930
Problem: `:browse edit` (and variants) without an argument fails instead
of opening a browser.

Solution: Dispatch to dir.lua using the cwd as the implied directory.
Replace the inherited GUI-only documentation with the supported
behavior. Support vsplit, split, tabedit variants.
2026-07-25 06:56:52 -04:00
Justin M. Keyes
bf3d4210c3 refactor: 'previewpopup' #40945
- drop w_maxwidth/w_maxheight, calculate them on-demand from
  'previewpopup' (this is cheap bc of `opt_keyset`)
2026-07-24 13:49:47 -04:00