Problem: A function used through 'omnifunc' or 'complete' is called the
same way whether 'autocomplete' started the completion or a
key asked for one, so it cannot answer differently.
Solution: Report which of the two it is in complete_info() as "auto". It
is returned only when asked for in {what}, so what
complete_info() says by itself does not change
(Hirohito Higashi).
closes: vim/vim#211431f56c351de
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Problem: filetype: bazelrc files are not recognized
Solution: Detect *.bazelrc and tools/bazel.rc files as bazelrc filetype,
include syntax und filetype plugins, update the menus
(Barrett Ruth).
closes: vim/vim#211461afe7ad1bb
Co-authored-by: Barrett Ruth <br@barrettruth.com>
Problem:
The vimdoc parser does not treat "\r" as whitespace, so in a CRLF
helpfile the codeblock rule fails and "*" pairs inside examples become
tags. On Windows this generates duplicate "." and "/" tags:
D:/a/neovim/neovim/build/bin/nvim.exe -u NONE -i NONE -e --headless -c "helptags ++t doc" -c "exe 'cquit' !empty(v:errmsg)""
Error in command line:
E154: Duplicate tag "." in gui.txt and repeat.txt
E154: Duplicate tag "/" in pattern.txt and usr_08.txt
E154: Duplicate tag "/" in usr_08.txt and pattern.txt
Note: The old C parser was unaffected because it read helpfiles in text
mode (`os_fopen(…, "r")`).
Solution:
Strip "\r" before parsing.
Problem:
Parent commit regressed some behavior of the old C helptags-gen impl:
- helpfiles in sub-directories are named by basename, so :help fails
- "help-tags" always names "tags", never "tags-nl"
- E150 E151 E152 E153 are never reported
- existing tags file is not overwritten if no tags were found
- a duplicate tag aborts the run, skipping the remaining directories
- `*.TXT` and `*.FRX` (uppercase) are not recognized as helpfiles
- tree-sitter-vimdoc accepts tags that the C parser rejected: `*a|b*`
(breaks |links|) and unterminated `*tag`
- PUC Lua "<" compares with (localized) `strcoll()`, so the tags file is
not sorted by byte value (E432)
- requiring `vim.treesitter` at load time breaks :help itself, not just
:helptags, where the module is unavailable
- `vim.pack` runs :helptags for plugins that have no "doc/" directory,
so every install/update emits E150
Solution:
- Fail the build if generating helptags reports any `v:errmsg`.
- Restore old behavior: tags generated for runtime/doc are now identical
to those from the C implementation. Errors are non-fatal messages
instead of exceptions, so all directories are still processed.
- Load treesitter lazily, report a plain error if parser is missing.
Problem:
Tags are manually parsed in C which is not flexible and prone to errors.
Extending the help system to allow for other formats (e.g. Markdown)
would require a large rewrite in the C core, while with Treesitter it
only needs a query update.
Solution:
Use the power of treesitter to extract the tags from helpfiles.
- build: set `$VIMRUNTIME` when generating helptags, like
`cmake/Util.cmake` already does for other generators.
- fix(help): only accept tags delimited by whitespace. The old C parser
only accepted a `*tag*` preceded by start-of-line or whitespace and
followed by whitespace or end-of-line. The vimdoc parser also captures
tags followed by other text, e.g. `*$XDG_STATE_HOME*/.../logs` in
starting.txt, which caused an E154 duplicate tag error for docs that
were previously fine.
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
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().
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.
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()`.
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.
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.
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#211051c32cede0a
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#2112363d08d4386
Co-authored-by: Josep Puigdemont <josep.puigdemont@gmail.com>
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.
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.
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).
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}
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.
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.
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.
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)
Remove the note about "this function is called only once per :find
command invocation".
fixes: vim/vim#21062bce3eb1fae
Co-authored-by: Christian Brabandt <cb@256bit.org>
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.
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.
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.
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
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.
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.
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
Problem: The reuse_client predicate does not pass the target buffer,
preventing decisions from being truly made per buffer.
Solution: Pass the target buffer.
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.
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.
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.