5084 Commits

Author SHA1 Message Date
Justin M. Keyes
a1de07418b feat(ui2): lift ui2 options into 'messagesopt' (part 1) #41474
Problem:
In order for ui2 to graduate to the main "messages ui" its configuration
needs to graduate into actual options.

Solution:
Migrate some of its config to 'messagesopt':
- "maxheight" (note: currently this is an integer treated as
  a "percentage"; if we want to support a row count we could allow
  values with units, like `"42%"`)
- "pager"
- "timeout"

Also improves error messages:

    messagesopt=hit-enter,history:500,bogus       E474: Unknown item 'bogus'
    messagesopt=hit-enter,history:500,progress:x  E474: 'progress' must be one of: , c
    messagesopt=hit-enter,history:abc             E474: 'history' requires a number
2026-08-25 03:58:17 -04:00
Justin M. Keyes
510f61555b fix(ui2): pager_char does not match keycode aliases #41468
Problem:
`pager_char` compares the `keytrans()` result exactly. E.g. `pager_char
= "<cr>"` does not work, it expects `<CR>` (uppercase).

Solution:
Normalize `pager_char` in enable().
2026-08-24 15:01:50 -04:00
Étienne Robert
d1b811b6c2 fix(lsp): advance inline completion range over matching input #41434
Problem:
`Completor:accept()` applies the item against the range the server
answered with. Characters typed after the request but before accepting
lie inside neither that range nor the replacement, so they are left
behind by the insertion: typing `a` after `foob` and accepting `foobar`
within the 200 ms debounce yields `foobara`.

Solution:
Grow the item's range over the characters typed since the request, so
that `accept()` replaces them. Only while the candidate still matches,
decided by the longest common prefix `show()` already computes, so input
that contradicts it is left alone. Snippet items are unaffected, since
`accept()` ignores the range for them.
2026-08-24 10:51:39 -04:00
Justin M. Keyes
8c440c469b fix(ui2): ui2 misinterprets getchar() input as pager_char (CR) #41465
Problem:
`vim.on_key()` callbacks are invoked for keys that `getchar()` consumes,
which never reach the main loop, and there is no way to tell such a key
apart.  With ui2 the `on_key` handler activated after interactive
cmdline, handles it as `pager_char` (thus enters the ui2 pager).

Solution:
Skip `vim.on_key()` callbacks during `getchar()`/`getcharstr()`.
2026-08-24 10:18:47 -04:00
Étienne Robert
143bbfbb12 fix(lsp): clamp inline completion range to the line on accept #41435
Problem:
`Completor:accept()` hands the item's range straight to
`nvim_buf_set_text()`. That range is resolved when the response arrives,
so text deleted before accepting (backspacing within the 200 ms
debounce) leaves the range end past what is left of the line, and the
accept aborts with `Invalid 'end_col': out of range`.

18d6436 fixed the same staleness in `Completor:show()`, which drops an
item once its range *start* falls outside the buffer. That guard does
not cover the range end, and `accept()` was never given one, so this is
a hole left by that fix rather than a regression of it.

Solution:
Clamp the end of the range to the end of the line before writing. The
start needs no clamp: `show()` dropped the item one event loop tick
earlier if it had gone out of range.
2026-08-24 08:41:57 -04:00
glepnir
2a382ffb61 fix(lsp): completion overwrites text before the word boundary #41463
Problem:
One item with an edit range before the word boundary changes the start
column for all items. For tsserver `str.`, completing `str.char` can
become `strcharAt` and break filtering.

Solution:
Prepend the missing text and filter on it.
2026-08-24 06:14:49 -04:00
glepnir
92d5531f96 vim-patch:9.2.1001: complete_info() does not report the item highlight groups (#41461)
Problem:  complete_info() omits "abbr_hlgroup" and "kind_hlgroup".
Solution: Keep the highlight group ID with the match instead of the
          resolved attribute and add both entries to the returned items
          (glepnir).

closes: vim/vim#21105

1c32cede0a
2026-08-24 14:35:25 +08:00
zeertzjq
cbe513fca6 vim-patch:63d08d4: runtime(doc): update :h write-plugin and readdir() example
1) The complete example plugin at :h write-plugin the end of the
   documentation didn't reflect the snippets provided during the
   tutorial. In most of the Add() function, interpolated strings are
   used, but the final result used concatenated strings.

2) The example provided in readdir() to get a list of files ending in
   ".txt" didn't properly escape the dot in the regular expression:
      readdir(dirname, {n -> n =~ '.txt$'})
   This would match any file ending with "txt" that is at least 4
   characters long, instead it should be:
      readdir(dirname, {n -> n =~ '\.txt$'})

closes: vim/vim#21123

63d08d4386

Co-authored-by: Josep Puigdemont <josep.puigdemont@gmail.com>
2026-08-24 08:17:27 +08:00
not_compiled
716835c232 fix(ui2): dismiss msgs after mapped motions #41450
Problem:
Mapped keys can produce on_key callbacks with an empty `typed` value.
so mapped motions such as `j -> gj` are not recognized as typed input
and do not dismiss messages

Solution:
Track whether an empty `typed` callback follows typed input, allowing
mapped motions to dismiss messages like directly typed motions.
2026-08-23 19:09:26 -04:00
Justin M. Keyes
354ea3bf2a docs: misc #41452
Co-authored-by: darkdi <rantovov5@gmail.com>
Co-authored-by: Qiaoxi Guo <28090444+breadtitor@users.noreply.github.com>
Co-authored-by: zaveshaa <zaveshaa@gmail.com>
2026-08-23 12:38:07 -04:00
Shubh
c5cb0350a6 fix(lsp): do not expand $VAR in tagfunc filenames #41398
Problem:
CTRL-] via lsp tagfunc passes URI paths through :tag, which expands
\$VAR, so files like people_.\$personId.tsx fail with E429.

Solution:
fnameescape() the filename in tag items so \$ is treated as literal.
2026-08-23 11:30:17 -04:00
Shubh
bca9c4afa7 fix(man): allow :Man to open by absolute path #41401
Problem:
man -w echoes any existent file back unchanged. :Man rejected all such
paths, so :Man /usr/share/man/man1/bash.1 failed even for real man pages.

Solution:
Accept an echoed path when it looks like a man page (.../man1/foo.1).
2026-08-23 11:28:22 -04:00
Olivia Kinnear
8ce1aac477 feat(pos): mutable vim.Pos, vim.Range fields #41318
Allow plugins to set the human-readable fields.
2026-08-23 07:13:41 -04:00
Justin M. Keyes
47cd769ed5 feat(cmdatom): mappings capture continuation
Problem:
A mapping that ends mid-operation (`nnoremap ,D d`) emits a content-free
"mapping" atom plus a `pending` field, and the "continuation" motion
arrives as a sibling atom. Consumers must stitch the two together (which
has broken cases, e.g. Insert-opening mappings (",i") lose their session
entirely).

- ",i": the session atom is dropped bc the mapping RHS is consumed
  before the session starts (typebuf_maplen()==0), so
  atom_is_user_input()=false.
- ":normal"-in-opfunc: the opfunc internal "v..y" session (a) became
  kVatomTyped just because the deferred composite was open, masking the
  real operator capture via atom_captures, and (b) its nested frames
  re-derived the outer redo.
- "Motion" based on `moved=true`, has false negatives.
- `CmdAtom.remap` is unnecessary, and clutters the docs/usage.

Solution:
- Introduce `frame_id` to identify CmdFrames.
- Classify `type=motion` better, via `NV_MOTION` flag on the `nv_cmds` table.
- Drop `CmdAtom.pending`, `CmdAtom.remap`.
- Defer atom_composite_end() at the clock edge while an operator is
  pending, Visual is active, or `restart_edit` is set: the composite
  keeps collecting, so the continuation is captured in the mapping atom.
- ",i": Now an open composite counts as user input.
- ":normal"-in-opfunc: Now handled correctly.
- `remap` is now decided by `composite.payload || 0 subatoms`.
  atom_payload_mark() records the read the resolution never captures.
- `toplevel` is now decided by `CmdFrame.parent == NULL`.

before/after:

    INPUT       BEFORE                             AFTER
    ---------------------------------------------------------------------
    ,D w        {mapping lhs=,D pending=operator}  {operator lhs=,Dw keys=dw}
                + {operator keys=dw}
    ysiw"       {mapping lhs=ys pending=operator}  {operator lhs=ysiw" keys=g@iw"}
                + {operator lhs=g@iw"}
    ,v d        {mapping pending=visual}           {visual lhs=,vd keys=viwd}
                + {visual lhs=viwd}
    ,i XY<Esc>  {normal keys=i lhs=,iXY<Esc>}      {insert keys=1iXY<Esc> text=XY}
2026-08-22 17:29:34 +02:00
Shubh
f583991553 fix(url): escape cmd.exe special chars in vim.ui.open() URL #41394
Problem:
On Windows, vim.ui.open() passes URLs to `cmd.exe /c start`. cmd.exe
treats `&` as a command separator, so query strings are truncated.

Solution:
Caret-escape `&|<>^%!` in URIs when the open handler is cmd.exe.
2026-08-22 10:49:55 -04:00
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
Barrett Ruth
855ff5f78f fix(zip): detect encryption without letting unzip prompt #41422 2026-08-22 05:16:30 -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
12c3e59e6a vim-patch:76f1eed: runtime(doc): clarify how setbufvar() handles window options (#41408)
related: vim/vim#21089
closes:  vim/vim#21104

76f1eed3a5

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
2026-08-21 09:25:40 +08:00
zeertzjq
0e140ded51 vim-patch:bce3eb1: runtime(doc): clarify the 'findfunc' option (#41389)
Remove the note about "this function is called only once per :find
command invocation".

fixes: vim/vim#21062

bce3eb1fae

Co-authored-by: Christian Brabandt <cb@256bit.org>
2026-08-20 09:11:13 +08:00
Barrett Ruth
743bca9202 fix(zip): lost password if sent before unzip disables echo #41377 2026-08-19 07:35:26 -04:00
Goldpigg
ab82c2c6b0 feat(extmark): virt_lines can highlight until EOL #41289 2026-08-18 08:35:22 -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
Justin M. Keyes
b107154ba2 docs: misc, cwd, vimscript.txt #41356
Extract vimscript.txt from repeat.txt
2026-08-17 13:37:08 -04:00
Rob Pilling
a4aa0417cf feat(ui2): drop default enter-pager-via-CR mapping #40993 2026-08-17 08:03:29 -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
zeertzjq
2edb1c0009 fix(lua): don't limit indexed vim.cmd positional argument count (#41317) 2026-08-16 07:25:04 +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
zeertzjq
1a9467ab44 vim-patch:9.2.0957: filetype: ArgoCD config file is not recognized (#41301)
Problem:  filetype: ArgoCD configuration file is not recognized
Solution: Detect */argocd/config as yaml filetype (Fionn Fitzmaurice).

Reference:
https://argo-cd.readthedocs.io/en/latest/user-guide/commands/argocd_configure/#options-inherited-from-parent-commands

closes: vim/vim#21031

7807dd2279

Co-authored-by: Fionn Fitzmaurice <git@fionn.computer>
2026-08-14 08:47:14 +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
zeertzjq
ecaf9e9396 vim-patch:02bf089: runtime(doc): clarify diff_filler() function (#41283)
fixes: vim/vim#20990

02bf0893d5

Co-authored-by: Christian Brabandt <cb@256bit.org>
2026-08-12 08:24:48 +08: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
Torben Leth
f538a4f16f vim-patch:9.2.0926: filetype: Business Central files are not recognized (#41230)
Problem:  filetype: Business Central files are not recognized
Solution: Add filetype detection logic for *.al files to detect perl or
          use either perl or al filetype (Torben Leth).

Reference:
https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-dev-overview

closes: vim/vim#20975

Supported by AI.

9b41cf6386
2026-08-09 00:49:20 +00:00
Mike J McGuirk
fe3aa64945 feat(lsp): pass target buffer to reuse_client predicate #41163
Problem: The reuse_client predicate does not pass the target buffer,
preventing decisions from being truly made per buffer.

Solution: Pass the target buffer.
2026-08-07 11:27:19 -04: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
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
9c738cb718 vim-patch:9.2.0920: filetype: json-ld files are not recognized
Problem:  filetype: json-ld files are not recognized
Solution: Detect *.jsonld files as jsonld filetype, include
          filetype, indent and syntax plugins (Bogdan Barbu).

Reference:
https://www.w3.org/TR/json-ld11/

closes: vim/vim#20954

a1e2198a2c

Co-authored-by: Bogdan Barbu <l4b.bogdan.barbu@gmail.com>
2026-08-07 12:36:41 +08: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
Justin M. Keyes
9a93ac6533 refactor(vim.fs): slug() minor cleanup 2026-08-06 10:27:29 +02:00
Justin M. Keyes
7d2249c579 docs: misc, :bcd, slug() 2026-08-06 10:27:29 +02:00
Barrett Ruth
8b0f33a1ab feat(dir): set buffer-local CWD #41174 2026-08-06 03:39:53 -04:00
Olivia Kinnear
6107629c5b feat(fs): vim.fs.normalize{plain:boolean} #41127
`opts.plain=true` does not expand tildes in addition to environment
variables, unlike `opts.expand_env=false`.

`opts.expand_env=false` is soft-deprecated.
2026-08-05 16:11:13 -04:00
Justin M. Keyes
ab80ea92cc fix(ui2): pager handling #41179
- Avoid shared state. Pass `focus` to set_pos()/expand_msg() instead of
  a shared `pager_focus` flag: the flag is only cleared when set_pos()
  actually enters the pager, so ":messages" from inside the pager left
  it set.
- pager_shown(): the pager window is invalid after leaving it with "q".
- Reuse pager_shown() in expand_msg().
2026-08-05 15:07:19 -04:00
Erdiansyah
a29a9130a9 fix(ui2): do not steal focus when consecutive cmds emit messages #41062
Problem:  A message emitted while a previous expanded message is still
          visible opens the pager and enters it, moving focus away from
          the buffer window without an explicit request (#41061).
Solution: Only enter the pager when it was explicitly requested ("g<",
          :messages, or entered from the expanded cmdline). An unfocused
          pager is dismissed by the cmdline key handler, which stays armed
          across the cmdline and no longer dismisses on non-typed keys
          (#39221).
2026-08-05 14:07:59 -04:00
Oleh Kostiuk
23525dd4e3 fix(editorconfig): avoid trim_trailing_whitespace in insert-mode #41175
Problem:
During insert-mode / replace-mode, `autowrite` may trigger. If it does, the
cursor position can shift due to the automatic removal of trailing spaces on the
current line. When I resume typing, the space between the last word and the new
word is suddenly gone.

Solution:
Disable the "remove trailing spaces" handler during Insert (or a similar) mode.
Autosave logic is not affected.
2026-08-05 14:06:28 -04:00