Commit Graph

8571 Commits

Author SHA1 Message Date
Sean Dewar
dadaa7fcad fix(ui2): offset extmarks copied to pager by srow #41425
Problem: wrong end_row when copying extmarks to the pager buffer when expanding
a message. Results in incorrect highlights or errors such as "invalid `end_col`
out of range".

Solution: don't forget to offset end_row by srow, as is already done for the
copied lines and extmark rows.
2026-08-22 10:19:16 -04:00
glepnir
2c5f37f6af fix(lsp): empty filterText/sortText/insertText not treated as unset #41421
Problem:
Empty string is falsy, so the spec's label fallback applies, but there only checks
for nil. An empty filterText drops the item, an empty sortText sorts it
first, and an empty insertText leaves the word empty.

Solution:
Treat empty string as unset.
2026-08-22 07:40:17 -04:00
Justin M. Keyes
6423657352 feat(cmdatom)!: eliminate the need for vim-repeat #41414
Problem:
- `lhs` is not fully realized. E.g. for a "payload" mapping
  `lhs` omits the `getchar()` payload during a mapping (vim-surround
  `ds'` reports `lhs="ds"`). This means plugins like vim-repeat are
  still needed...
- The "delta" fields of a CmdAtom are calculated too late.
  - `<abuf>` and `changed` check whatever (wrong) buffer a command
    (":bnext") might land in.
  - CTRL-W_w between two windows on the same buffer reports type="motion".

Solution:
- `CmdOrigin` samples (buf/win/cursor/changedtick) at each "scope" entry
  (CmdFrame, composite, Visual session, insert session).
- `dd<C-w>l` reports `changed=true` for the buffer it edited, regardless
  of where the cursor ends up.
- New fields:
  - `pos`: cursor position at command start.
  - `moved`: indicates whether the cursor moved (in same buffer).
  - `undoseq`: undo state at settlement.
- lhs now includes the payload: "ds)" reports lhs="ds)" instead of "ds".
  - Easy for users to "replay" any atom.
- Rename: type "command" => "normal", "ex" => "excmd"; `arg` => `cmdarg`
- Drop `cascade` field (no reason to expose it)
2026-08-21 13:42:17 -04:00
zeertzjq
af37d459a6 vim-patch:9.2.0985: Multiline messages not visible when mapping starts cmdline (#41405)
Problem:  Multiline messages exceeding 'cmdheight' not visible when a
          mapping starts cmdline immediately after it (after 9.2.0967).
Solution: Revert patch 9.2.0967 and use a different solution (zeertzjq).

fixes:  vim/vim#21098
closes: vim/vim#21101

fb4866a2dd
2026-08-21 07:46:34 +08:00
Justin M. Keyes
31de0d69fc fix(cmdatom): lhs not always reported in CmdAtom #41386
Problem:
An unreplayable Visual operation does not emit a CmdAtom event. That's
maybe not super important, but it hints at a flaw in how `vatom`
"voiding" is plumbed: `vatom.state=kVatomVoid` replaces the "kind", so
that info is lost to later parts in the lifecycle.

Solution:
- Define `VatomState` as "flags", so `kVatomVoid` can "poison"
  `vatom.state` without losing its kind flag.
- Unify `lhs`: always report the original user input in `CmdAtom.lhs`,
  for all kinds of user actions: visual, "translated"/"stuffed" cmds,
  and dot-repeat (".") itself.
- Unreplayable Visual atom emits CmdAtom with non-empty `lhs` and empty
  `keys`, like a mapping/macro composite.
2026-08-20 05:18:36 -04:00
Justin M. Keyes
2fea699b96 revert: "fix(lifecycle): on Windows, CTRL_CLOSE kills Nvim mid-teardown"
Revert commit 5a71131282

That change seems good in theory, but it consistently causes 2 failures

    FAILED   …/api/vim_spec.lua @ 3213: API nvim_list_chans, nvim_get_chan_info stream=job :terminal channel
    Expected values to be equal.
    Expected:
    {
      argv = { "D:/a/neovim/neovim/build/bin/nvim.exe", "-u", "NONE", "-i", "NONE" },
      exitcode = 129,
      id = 4,
      mode = "terminal",
      pty = "?",
      stream = "job"
      ...
    }
    Actual:
    {
      argv = { "D:/a/neovim/neovim/build/bin/nvim.exe", "-u", "NONE", "-i", "NONE" },
      exitcode = 143,
      id = 4,
      mode = "terminal",
      pty = "?",
      stream = "job",
      ...
    }
    stack traceback:
            …/api/vim_spec.lua:3257: in function <…/api/vim_spec.lua:3213>

    FAILED   …/terminal/tui_spec.lua @ 3669: TUI exits immediately when stdin is closed
    …/terminal/tui_spec.lua:3669: retry() attempts: 69
    Expected values to be equal.
    Expected:
    vim.NIL
    Actual:
    {
      name = "nvim.exe",
      pid = 2256,
      ppid = 8200,
    }
    stack traceback:
            …/testutil.lua:98: in function 'retry'
            …/terminal/tui_spec.lua:3669: in function <…/terminal/tui_spec.lua:3656>
2026-08-19 20:36:14 +02:00
Justin M. Keyes
5a71131282 fix(lifecycle): on Windows, CTRL_CLOSE kills Nvim mid-teardown
Problem:
`signal_ignore_deadly` doesn't work for Windows, where the console still
may terminate Nvim during teardown (after `signal_teardown`), while it
is already trying to exit. Besides interrupting any housekeeping we are
doing, it results in an unpredictable exit code (flaky tests).

    [Process exited -1073741510]  // 0xC000013A STATUS_CONTROL_C_EXIT

Solution:
Register our own CTRL_CLOSE_EVENT handler which "blocks" the signal.
Note: if exit takes longer than 5s, Windows will consider the process
"hung" and kill it anyway.
2026-08-19 15:46:58 +02:00
Justin M. Keyes
5b1c21f4b8 fix(lifecycle): late signal kills Nvim mid-teardown
Problem:
A deadly signal arriving during teardown can kill Nvim while it is
preserving swapfiles. `os_exit()` ignores deadly signals via
`signal_reject_deadly()`, but `signal_teardown()` => `uv_signal_stop()`
resets them to the default behavior, so SIGHUP arriving after that
kills the process:

    [Process exited 129]      // 128 + SIGHUP

This is a race when closing a pty: kernel sends SIGHUP to foreground
process group *and* the reads return EOF, so `chanclose()` on a TUI job
prepares to exit twice. This means it is unpredictable whether Nvim
exits 1 or is terminated.

Solution:
Ignore deadly signals once the watchers are closed. Only SIGKILL
interrupts it now.
2026-08-19 15:46:58 +02:00
zeertzjq
2cd4229d01 fix(window): crash when closing new curwin during CTRL-W_x (#41378)
Problem:  Crash when closing the new current window during CTRL-W_x.
Solution: Remove a duplicate redraw of a window that should normally be
          already mark for redraw in the previous call.
2026-08-19 20:13:08 +08:00
Justin M. Keyes
0c091cedc2 test(tui): unreliable "TUI exits immediately when stdin is closed" #41367
Problem:
Unreliable test on slow CI (ASAN/TSAN):

    FAILED  .../tui_spec.lua @ 3054: TUI exits immediately when stdin is closed
    retry() attempts: 1
    Expected: vim.NIL
    Actual: { name = "nvim", pid = 33201, ppid = -1 }

The test asserts "immediate" exit of the Nvim process, but this may be
subject to OS delays outside of our control.

Solution:
Make the "timed out waiting for DA1" log conditional on the actual
timeout, and assert the logs in the test.
2026-08-18 09:22:05 -04:00
Goldpigg
ab82c2c6b0 feat(extmark): virt_lines can highlight until EOL #41289 2026-08-18 08:35:22 -04:00
Justin M. Keyes
f6bf814378 fixfix(cmdatom): repeat Visual <Cmd>; CmdFrame + stage #41355
Problem:
- Dot-repeat of a Visual selection prepared by a `<Cmd>` mapping,
  results in E1255 and leaves Visual mode active.
- Visual-mode capture is implemented as a second, parallel "capture
  engine": it re-composes a CmdSpec from cmdarg_T per key and decides
  replayability from its own table of "void" key classes.

Solution:
- Model nested `normal_execute()` as a `CmdFrame` stack,
  instead of a single module-scoped `stage`.
  - Capture `<Cmd>` ":norm …" commands as subatoms from its nested frames.
- Produce every command exactly once; atom_push_raw() routes the atom to
  the Visual composite while a selection is open, like it already does
  for mapping composites.
  - `v/pat<CR>d` is now repeatable and cascades.
2026-08-18 07:50:27 -04:00
Justin M. Keyes
c5ea9ca2ad fix(zip): missed "password:" prompt if output trails it #41364
Problem:
Flaky test:

    RUN      T1158 nvim.zip reports an incorrect archive password: 11092.84 ms FAIL

The prompt is detected only if the pty output *ends* with "password: ",
but a read may return the prompt plus following bytes.

Solution:
- Match anywhere in the output since the last password was sent. The
  buffer is cleared before each send, so won't match stale text.
- Assert on the reported message, so a failure shows what was reported.
2026-08-18 06:23:59 -04:00
Marcus Caisey
d9b4fb1273 feat(lsp): fallback to textDocument/formatting from vim.lsp.formatexpr #40079
Problem:
With the addition of the `:help al` text object, you can now easily
format the whole buffer with `gqal`. However, `vim.lsp.formatexpr` only
uses `textDocument/rangeFormatting` which some language servers (like
gopls) don't support.

Solution:
- Fall back to `textDocument/formatting` if the whole buffer is being formatted
  and the server doesn't support `textDocument/rangeFormatting`.
  - In theory, these two methods should return the same response if the whole
    buffer is being formatted, but I preserved the existing behaviour of
    prioritising `textDocument/rangeFormatting` in case that does not hold (i.e.
    LS bug).
- Also: `vim.lsp.formatexpr` had no tests at all, so actually add tests for it.
2026-08-18 05:16:16 -04:00
zeertzjq
82c751db4e vim-patch:9.2.0967: hit-enter prompt eats keys from a running mapping (#41359)
Problem:  The hit-enter prompt fires whenever a message scrolls the screen.
          When this happens while a mapping is being processed, it consumes
          the mapping's next key, causing unexpected behavior for users.
Solution: Similar to what 9.1.1969 did for stuffed characters, skip the
          hit-enter prompt when there are still keys pending from a mapping
          in the typeahead buffer.

related: neovim/neovim#38298
related: neovim/neovim#20635
related: neovim/neovim#30890
closes:  vim/vim#20753

AI assisted.

6025ea9e02

Co-authored-by: XiaowenHu96 <me@xiaowenhu.com>
2026-08-18 09:23:34 +08:00
Rob Pilling
a4aa0417cf feat(ui2): drop default enter-pager-via-CR mapping #40993 2026-08-17 08:03:29 -04:00
not_compiled
8c0bf18374 fix(spell): avoid invalid window state after async spell select (#41346)
Problem:
When `z=` delegates to `vim.ui.select()`, the picker may change the
current window before returning. `spell_suggest()` then continues to the
cursor restoration branch with the new window and assigns `prev_cursor`,
which belongs to the original window. This can leave Normal mode with an
invalid cursor position and produce E315.

Solution:
Clean up the spell suggestion state and return immediately after handing
control to `vim.ui.select()`.
2026-08-17 11:16:11 +08:00
Justin M. Keyes
581ce0b3da fix(cmdatom): <Cmd> mappings #41347
Problem:
`<cmd>` mappings do not emit `CmdAtom.text`.
`<cmd>` and Lua-callback mappings that edit the buffer apply only at the
primary cursor, not cascaded (multicursor).

Solution:
Capture the `<cmd>` command in getcmdkeycmd().
Add kKeyOpaque ("no capturable keys"); narrow kKeySynthetic ("not
a keystroke") to K_EVENT/K_IGNORE, so an opaque mapping's edit still
sets `map_edit` and cascades via LHS-replay.
2026-08-16 18:00:55 -04:00
Willaaaaaaa
e0e2f978a0 feat(vim.fs): slug() supports URI #41241
Problem:
`vim.fs.slug()` does not handle URIs like `term://foo//123:bash`,
so callers (e.g. terminal persistence) must strip the scheme before
calling `slug()`.

Solution:
Detect `scheme://` from the raw input before `normalize()` and
replace it with a `=uri-<scheme>-` prefix.
2026-08-16 13:29:34 -04:00
Nathan Zeng
7b6f344627 fix(restart): preserve global cwd on :restart #41304
Problem:
On :restart, the new Nvim may "inherit" a local dir as its global CWD.

Solution:
Inherit the global CWD explicitly.
2026-08-16 10:34:17 -04:00
Justin M. Keyes
214bcf24cc fix(undo): crash on corrupted undo file #41339
Problem:
`:rundo` on a corrupted undo file crashes or hangs, instead of failing
with E825. Patching one 4-byte field is enough:

    ue_size = 0xFFFFFFFF  " walks a NULL ue_array
    ue_size = 0x7FFFFFF0  " 17 GB xmalloc + memset, then preserve_exit()
    ue_top  = 0xFFFFFFFB  " negative lnum reaches ml_delete()

Analysis:
Every count in the file is read with `undo_read_4c()` and then checked,
differently at each site. None bounds the value by what the file can
hold, so a 2 GB count reaches `xmalloc()`.

Note:
- Vim doesn't have `bi_fsize` because it checks `U_ALLOC_LINE` result
  everywhere (thus doesn't crash, but may thrash...); those checks were
  dropped when Nvim moved to `xmalloc()`, and the `ue_size` loop counter
  became unsigned.
- Vim *does* have the negative line numbers bug: `u_undoredo()` checks
  `top > ml_line_count || top >= bot || bot > ml_line_count + 1`, which
  rejects none of them.

Solution:
- Introduce `undo_read_len()` and use it to fail early instead of
  continuing with nonsense.
- Validate `ue_top`/`ue_bot`/ `ue_lcount`.
- Use `xcalloc()`, so no site can proceed with a NULL array.
- Report a truncated "U" line, distinguish EOF from a 0xFFFFFFFF field,
  and free the header on the extmark error path.
2026-08-16 10:24:24 -04:00
zeertzjq
2edb1c0009 fix(lua): don't limit indexed vim.cmd positional argument count (#41317) 2026-08-16 07:25:04 +08:00
Justin M. Keyes
37c670e682 fix(marks): undo reverts a mark set after the change #41330
Problem:
A named mark updated after a change is moved back (treated as the
original mark) by undo:

    :1mark d
    :$
    dw
    :2mark d   " 'd is on line 2
    :undo      " 'd is back on line 1

The undo header snapshots `b_namedm` when the change is recorded, and
`u_undoredo()` restores that snapshot indiscriminately.

Solution:
Update the pending header's snapshot when a mark is set explicitly.
Marks that the change itself moved go through mark_adjust(), not
setmark_pos(), so those are still reverted.

Similar to 2546741d1b (for extmarks): an explicit set inside an undo
block is confused with an edit-driven adjustment. But the extmarks case
is dealing with mid-edit moves, whereas named/regular marks only need
the stale snapshot dropped.
2026-08-15 13:34:51 -04:00
Justin M. Keyes
fb180287d0 fix(cwd): nvim_win_set_buf of :bcd buf, changes caller CWD #41329
Problem:
Setting a :bcd buffer into another window, modifies the caller's CWD.

    local b = vim.api.nvim_create_buf(true, true)
    vim.api.nvim_buf_call(b, function() vim.cmd.bcd('..') end)
    vim.cmd('vsplit')
    vim.api.nvim_win_set_buf(vim.fn.win_getid(2), b)

    :echo haslocaldir(0) haslocaldir(-1,0) haslocaldir(-1,-1,0)
    0 0 0
    :echo getcwd() ==# getcwd(-1,-1)
    0

Analysis:
`ctx_dirs_save` only saves CWD if it predicts the switch can change it.
But win_set_buf() replaces the target window's buffer *after* the
switch, which cannot be "predicted" from `ctx_dirs_save`.

Solution:
Always snapshot whenever the switch enters another window.
Skipping `os_dirname` was a micro-optimization.
2026-08-15 13:15:37 -04:00
Justin M. Keyes
cd2db7913a fix(cwd): nvim_win_set_buf changes global CWD #41328
Problem:
nvim_win_set_buf() on a non-current win, while the current win has
a win-local dir, changes the global CWD:

    :vsplit | lcd ..
    :call nvim_win_set_buf(other_win, buf)
    :wincmd l
    :verbose pwd
    [global] /parent        " expected: the initial cwd

Analysis:
`globaldir` is where to return when no local dir applies; NULL means the
process CWD is already there. Switching to a window with no local dir
makes update_cwd() chdir back to `globaldir` and clear it. kCtxKeepCwd
restores the process CWD but not that bookkeeping, so the restored
window-local dir is mistaken for the global one.

Solution:
Save/restore `globaldir` with the CWD.
2026-08-15 11:45:44 -04:00
Justin M. Keyes
9cca923ab4 fix(extmarks): redo of a mark created during an edit #41324
Problem:
A mark created by `nvim_buf_set_extmark()` while an undo block is open
never comes back on redo.

Analysis:
Undo deletes the text it covers and collapses the range; redo replays
the splices, which re-insert the text but cannot re-expand the mark.
`extmark_set()` records a position only for a mark it moves, not for one
it creates.

Solution:
Record the created position for redo; undo leaves the mark to the splice
replay, since it did not exist before the edit. Each side of a paired
mark gets its own entry. Redo also revives a mark that undo invalidated,
else its position returns but its highlight does not.
2026-08-15 10:13:32 -04:00
Barrett Ruth
b169d9376c test(dir_spec): fix failures with PUC Lua 2026-08-15 13:07:21 +08:00
Barrett Ruth
0af3b9827b feat(dir): user can sort/filter listings, DirReadPost event #41138
Problem:
Directory listing entries cannot be customized (filtered, reordered).
Listings are read by a BufReadCmd, which suppresses BufReadPost, so they
are the only buffers with no post-read event to hook.

Solution:
Introduce a post-render User autocmd `DirReadPost`, marking the dir
buffer writable for the duration and before the cursor is placed, so
handlers can sort or filter it with ordinary commands. Document common
recipes
2026-08-14 12:25:31 -04:00
Justin M. Keyes
64a301184e feat(input)!: CmdAtom event #41297
Problem:
There is no unified notion of a "user action".

Vim processes input by one-char-at-a-time, and mostly throws away any
hints it might gather about the user's action, with one exception: it
stores the last _edit_ action (the "redo buffer", encoded as
unstructured `["x][v][count]body` bytes).

Plugins can only observe individual keys (vim.on_key) and high-level
effects (TextChanged, CursorMoved).

Solution:
- Users can subscribe to `CmdAtom` events to handle any user action.
  - Event is deferred; handlers cannot cancel or interfere with user
    actions.
- Capture `CmdSpec` from the normal/insert/visual subsystems.
  - typeahead/readahead stay unstructured (`buffheader_T`): they are key
    streams, not commands.
  - the redo/record buffers become `StringBuilder`: fewer
    allocations/copies.
- Repurpose the input/redo engine to accept `CmdSpec` objects.

"atom": one repeatable unit of user input, as a resolved (post-mapping)
keysequence plus structured fields. Only user actions, not `:normal`,
API calls, or non-"t" `feedkeys`.

BREAKING: dot-repeat of an Insert session, replays the entire session
including cursor-moves (:help ins-repeat).

BREAKING: dot-repeat of a Visual operation, replays the selection
instead of operating on a fixed-size region.
2026-08-14 09:30:31 -04:00
Barrett Ruth
fc3f71fb7d fix(messages): inputlist() offers the mouse when it cannot be used (#41280)
Problem:
`inputlist()` advertises "click with the mouse" purely because it
implements click selection, so under the default `'mouse'` of "nvi" it
offers a click that command-line mode never receives.

Solution:
Only offer the mouse when `'mouse'` covers command-line mode, the same
condition `:help inputlist()` already documents.
2026-08-14 07:38:39 +08:00
Nathan B.
faf8345eef fix(diagnostic): don't accumulate BufRead autocmds for unloaded buffers #40869
Problem:
vim.diagnostic.set() defers extmark position computation for an
unloaded buffer via a once=true BufRead autocmd, registering a new one
on every call without replacing the previous one. Each pending autocmd
also retains that call's diagnostics.

Solution:
Instead of registering an autocmd per set() call, register a single
static BufRead autocmd that computes positions from the diagnostic
cache for any buffer with cached diagnostics when it is read. This
removes the per-call registration entirely (nothing left to
accumulate) and means diagnostics cleared while the buffer was
unloaded no longer produce stale extmarks.
2026-08-13 09:17:16 -04:00
Justin M. Keyes
a1ea2f35be Merge #41075 from echasnovski/pack-packspec-part1 2026-08-13 08:53:49 -04:00
Nathan Zeng
4a3197f2ad refactor(defaults): edit global cwd on "1-" #41254
Problem:
`1-` does nothing from a directory buffer, because we are already in the
buffer-local CWD. It's also unintuitive that this mapping behaves
differently based on the resolved CWD.

Solution:
Have `1-` open the global CWD.
2026-08-11 04:08:33 -04:00
Justin M. Keyes
2546741d1b fix(extmarks): undo-redo of a mark explicitly moved during an edit #41252
Problem:
A mark moved by nvim_buf_set_extmark() during an edit is misplaced by
undo and redo. Only splices ("edits") are recorded, and replaying them
reproduces the shifts they caused, never the explicit set: the mark ends
up wherever the text pushed it.

Solution:
When an open undo block moves an existing mark, record both positions.
Undo restores the pre-set position, redo re-applies the set.

Partially reverts 18334a4a0c ; ExtmarkSavePos.row/col were unused
because nothing recorded an explicit move, but now `extmark_set()` does.
2026-08-10 04:21:31 -04:00
Kyle
8d406ed2ac fix(defaults): emit events on automatic background change #41242
Problem:
After #40270, events are no longer emitted from the automatic background
detection. This applies not just during startup, but also if the user
manually changes the background of their terminal.

Solution:
Set the background as normal, assuming that a normal terminal will
respond within 100 ms. Change test to match expected behavior:
- BG set during startup won't trigger user autocmds since it runs before
  any user config
- If the terminal takes longer than 100 ms to respond to initial OSC 11,
  it does trigger the OptionSet, but it is triggered through the normal
  path to ensure values like v:option_new are set #38551
- BG change after startup still triggers autocmds #41146
2026-08-10 04:17:51 -04:00
Justin M. Keyes
9e283f273b test(terminal): unreliable "spawns in CWD effective at time of invocation"
FAILED  …/terminal/ex_terminal_spec.lua @ 270: :terminal (fake shell) spawns in CWD effective at time of invocation
    Expected values to differ.
    Value:
    "~/work/neovim/neovim/build/Xtest_xdg_terminal"
    stack traceback:
   …/terminal/ex_terminal_spec.lua:276: in function <…/terminal/ex_terminal_spec.lua:270>
2026-08-08 02:23:55 +02:00
tao
f0a0182285 refactor(path): pathcmp() #41035
Problem:
Redudant code

Solution:
Add `path_fold_char()` to normalize path sep.
Refactor `pathcmp()` and `path_fnamencmp()` into `path_cmp`.

| feature                  | pathcmp        | path_fnamencmp | path_cmp(now) |
| ------------------------ | -------------- | -------------- | ------------- |
| case folding             | `mb_toupper()` | `utf_fold()`   | `utf_fold()`  |
| consults fileignorecase  |              |              | `ic` param    |
| `maxlen`                 |              |              |             |
| `/` == `\`               |              |              |             |
| MSWIN drive letter       |              |              |             |
| sep affects sorting      |              |              |             |
| ignores a trailing slash |              |              |             |

Refactor `path_fnamecmp` and `path_full_compare` into `path_equal`,
with flags controlling "no filesystem" comparison (i.e. `path_cmp`), env
variables expansion, absolute path resolution and filesystem access.
2026-08-07 19:54:29 -04:00
Justin M. Keyes
b53c00b425 fix(progress): ins-compl progress-msg during pum #41226
Problem:
The insert-mode completion progress-message is in "running" state while
the user is selecting an item. That is noisy and unwanted UX; it was
only intended for the "Scanning..." task.

Solution:
End the progress-msg just after `ins_compl_show_statusmsg`.
2026-08-07 22:49:44 +00:00
Justin M. Keyes
33a688f9fe fix(messages): dangling progress-messages #41222
Problem:
Some builtin features emit progress-messages which never "complete".
- On failure, `:write` does not complete the progress-msg it started.
- ins-completion never ends its "Scanning..." message.

Solution:
- `buf_write()` emits "failed" status on failure.
- `ins_compl_stop()` ends the completion one.
2026-08-07 16:10:02 -04:00
Justin M. Keyes
0a2676e54a fix(messages): :read starts a "bufwrite" progress #41219
Problem:
filemess() treats an empty suffix as "a buffer write is starting", but
readfile() calls it that way too. So ":read" (and ":edit", …) opens a
`nvim.bufwrite "<file>"` progress that is never completed.

Users of e.g. ghostty will see a stuck "progress" spinner.

Solution:
Only `buf_write()` starts the progress, via `filemess_progress()`.
2026-08-07 13:21:17 -04:00
Erdiansyah
59597316a6 fix(cwd): :bcd lost by nvim_win_set_buf, nvim_open_win #41218
Problem:
:bcd (buffer-local directory) is not preserved after
`nvim_open_win` or `nvim_win_set_buf`

Analysis:
set_curbuf() ends with update_cwd(), which falls back to
os_chdir(globaldir) when the target buffer has no b_localdir.

Solution:
Pass kCtxKeepCwd to ctx_switch().
2026-08-07 12:09:22 -04:00
Justin M. Keyes
7e53d3ba4d fix(cwd): keep buffer-local dir when re-editing #41215
Problem:
Re-editing a buffer (`:edit!`, re-reading a dir.lua buffer, etc.) drops
its `:bcd` directory, so the CWD falls back to the global one. Whereas
other buffer-local state (`b:` vars, local options) survives a reload.

Solution:
Don't clear buf dir in `buf_freeall()`; `do_ecmd()` calls that when
reloading/re-editing. `free_buffer_stuff()` still clears them when
a buffer is freed or reused for another file.
2026-08-07 14:10:21 +00:00
Justin M. Keyes
2e0a5a596a fix(terminal): spawn in effective CWD #41211
Problem:
`:terminal` does not respect the invocation-time CWD.
This wasn't noticeable with `:lcd` because the window-local CWD gets
applied to the new terminal buffer. But it is noticeable with `:bcd`.

Solution:
Specify `cwd` in the job spec.
2026-08-07 07:44:19 -04:00
Barrett Ruth
bc9d27b0bc fix(window): 'winbar' in single-row win overlaps global 'statusline' #41188 2026-08-07 07:06:23 -04:00
Freddie Haddad
a8da01f8e6 fix(decor): highlight without 'hl_eol' bleeds into wrap gaps #41160
Problem:
'linebreak' filler and 'breakindent'/'showbreak' padding are screen
cells with no buffer character behind them, yet a decoration draws over
them whether or not it asked to cover such cells. A highlight bounded to
its text then paints a tail out to the edge of the row, most visible on
inline code spans from plugins. That same highlight already leaves the
cells past the end of a line alone, so it treats identical cells two
different ways.

Solution:
Only a decoration with 'hl_eol' draws the gaps, which is what the flag
already means at the end of a line. A full-width background such as a
fenced code block sets it and still covers them. Classic :syntax has no
such flag and is unchanged.
2026-08-07 05:09:48 -04:00
Justin M. Keyes
a4a544032a feat(cwd)!: :lcd! (bang), rearrange :bcd/:lcd/… scope precedence #41194
Problem:
- buf-local CWD scope is lower priority than :lcd, which is weird.
  ```
  win > buf > tab > global
  ```
- No way to clear current CWD at a given scope.

Solution:
- Rerrange scope precedence to:
  ```
  buf > win > tab > global
  ```
- Introduce "bang" variants (`:bcd!`/`:lcd!`/`:tcd!`) which clears the
  local CWD for the given scope.
2026-08-07 04:41:37 -04:00
zeertzjq
439c967010 fix(drawline): 'statuscolumn' breaks unprintable char wrapping (#41198)
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.
2026-08-07 04:01:38 +00:00
Barrett Ruth
3a39646693 fix(window): 'laststatus' change pushes win past last row #41189
Problem:
Giving a window a status line when `'laststatus'` starts requiring one
takes the row from a resizable frame found by walking up the frame
tree, but only the window's own leaf frame is grown back. Every frame
between the leaf and the donor keeps its old height, so a later resize
hands out a row that does not exist and a window's status line ends up
on the command line.

Solution:
Grow the window's frame with `frame_setheight()`, which takes the row
from a neighbouring frame and keeps every enclosing frame consistent.
2026-08-06 08:39:59 -04:00
Justin M. Keyes
eb19a52b7c feat(vim._with): keepcwd 2026-08-06 13:17:07 +02:00
Justin M. Keyes
1c1dc0558f feat(cwd): support explicit chdir (:bcd/:tcd/…) in temp context
Problem:
- Explicit `:bcd` (etc.) persists from `nvim_buf_call()` but not from an
  autocmd handler targeting a hidden buf (`LspAttach`, `TermRequest`, …),
  which needs a `vim.schedule()` workaround.
- `vim._with()` is supposed to work as a "sandbox", discarding
  side-effects, but it leaks CWD changes: `:lcd` from a `win` context,
  any chdir from a visible-buffer context.

Solution:
- Explicit :cd/:tcd/:bcd during a temp context persists by default.
  - "Ambient" directory changes ('autochdir', existing win-local CWD,
    etc.) are still undone, as before.
- Add `kCtxKeepDirs`: snapshot/restore the target's full CWD state
  (w/b/tp-local, global, cwd). Used by `vim._with()` and `'inccommand'`,
  which must not leak state.
2026-08-06 13:17:07 +02:00