Problem: With 'completeopt' "fuzzy", a re-sort after complete() leaves the
last match unsorted at the end of the list.
Solution: Find the original text by its flag instead of assuming
compl_shows_dir points at it.
closes: vim/vim#211037e74722999
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:
Claude and friends love to sprinkle advertisements in commit messages.
Solution:
Allow a generic "AI-assisted" token only.
Reject useless mentions of AI services, sessions, and other cringe.
Disclosure: This PR was authored by an sapient turnip , raised in
moist, fluffy soil, tilled by the blurry wings of a hummingbird.
Problem:
Test sockets live under `$TMPDIR`, which the harness points at the build
dir. On macOS/BSD `sockaddr_un.sun_path` is 104 bytes, and a CI build
path plus "nvim.<pid>.<n>" leaves little room:
/Users/runner/work/neovim/neovim/build/Xtest_tmpdir_terminal/nvim.runner/aBcDeF/nvim.12345.0
Solution:
Point XDG_RUNTIME_DIR (`stdpath('run')`) at "/tmp/nvim_<pid>". 28 bytes:
/tmp/nvim_19916/nvim.19919.1
TODO?:
- `TEMP_DIR_NAMES` prefers `$TMPDIR` over `/tmp`, so on macOS
`stdpath('run')` defaults to the long `/var/folders/<xx>/<…>/T/` path
instead of the short `/tmp` alias.
Note:
- The 104-byte limit applies to the `bind()` arg, not its "realpath",
to, so `/tmp/…` symlinks can be used to workaround the limit.
Problem:
Cannot retry a test with its full `after_each`/`before_each` lifecycle.
Solution:
- Overload `it()` to accept an `opts` param:
```
it('flaky', { retries = 2 }, function(ctx) end) -- 3 attempts.
```
- Pass `ctx` to test functions.
- Fix a bug in `t.read_file_list()`.
Problem:
The runs ":helptags", which needs the tree-sitter-vimdoc parser.
This may cause problems for package maintainers / distros?
Solution:
Use running to generate the tags, like the zig build already does.
It also errors on duplicate tags, which is good.
The `:helptags` command still uses treesitter.
Problem:
Since 20edb8fa9a, generating helptags requires the
`tree-sitter-vimdoc` parser, but the external_deps CI job builds with
USE_BUNDLED_TS_PARSERS disabled.
Solution:
Set USE_BUNDLED_TS_PARSERS.
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
Problem: In the debugger ">up" and ">frame" select an older function call
frame, but get_funccal_local_ht() and the related functions check
current_funccal while returning a dictionary of the frame that
get_funccal() selected. A :def function keeps its local variables
on the vim9 stack, its funccall_T has no l: and a: dictionaries and
is allocated cleared, so with such a frame selected the returned
hashtab has a NULL ht_array and evaluating any variable name at the
debug prompt crashes in hash_lookup().
Solution: Check the funccal that is actually used and return NULL when it has
no l: variables, so that the variable is reported as undefined
instead.
closes: vim/vim#211115ad47b07af
Co-authored-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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:
When ui2 is enabled, opening an already-open file in another Neovim
instance results in the error `E812: Autocommands changed buffer…`.
Analysis:
On E812 the file is not loaded. The default SwapExists handler notifies
`W325: Ignoring swapfile…`, and ui2 shows that message by opening
a window, which is a temp context switch. `ctx_dirs_restore()`
re-shortens every buffer name on the way out, and `shorten_buf_fname()`
always frees and reallocates `b_sfname`. `readfile()` aliases `b_fname`
across `check_need_swap()` and compares the pointer to detect a rename.
Regression by b296666e41, which replaced the `cs_save_sfname` restore
(that kept curbuf's pointer) with `shorten_fnames(true)`.
Solution:
Keep the allocation in `shorten_buf_fname()` when the short name is
unchanged. Pointer stability is what the E200/E201/E812 guards actually
assert.
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:
A mapping that enters terminal-mode (`:FzfLua files` via `:startinsert`)
never reaches a composite-end: terminal-mode runs no "normal" CmdFrames.
The composite collects the entire terminal session (and more) into `lhs`.
E.g. if I have `<M-/>` mapped to open `:FzfLua files`, then interact
with fzf-lua UI, the emitted CmdAtom looks like:
<M-/>… => { type='mapping', lhs='<M-/><C-N><C-N><CR>', keys=nil }
Solution:
End the composite when terminal-mode is entered, which emits a more
meaningful and repeatable atom:
<M-/>… => { type='mapping', lhs='<M-/>', keys=nil }
Problem:
The "!" operator stuffs its cmdline continuation (`:.,.+1!`), so its
frame ends before capture (stuff pending) and the redo-prep disappears
with it.
!ipsort<CR> => { type='excmd', lhs=':.,.+1!sort<NL>', keys=':.,.+1!sort<NL>' }
Compare to builtin "." which works bc `do_bang()` completes the redo
(`!ip` + `sort<NL>`).
Solution:
Appoint the stuffed continuation frame as the "redo-prep" frame.
!ipsort<CR> => { type='operator', operator='!', lhs='!ipsort<NL>', keys='!ipsort<NL>' }
Notes:
- atom_cmd_end(): a frame ending with stuff pending re-points its
redo-prep to the next frame.
- atom_cmd_start(): a stuffed continuation frame (`KeyStuffed`) keeps
the redo-prep; flushed stuff discards it.
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: Regex assumes a single-digit patch number, but LTS wasmtime releases (which we track) can run into double digits.
Solution: Allow for multi-digit patch numbers.
Problem: No check that g:netrw_home has higher priority than $MYVIMDIR.
Solution: Set $MYVIMDIR to a different directory at the start of the
test function.
Also, s:NetrwHome() checks has('nvim') before exists('$MYVIMDIR'). This
ordering does make sense as Nvim's docs have no mention of $MYVIMDIR.
Match that in the test.
closes: vim/vim#21117b3526a91b8
Problem: runtime(netrw): g:netrw_home not respected on Neovim
Solution: prioritize g:netrw_home for bookmarks and history directory
setting, add tests (J. Paulo Seibt)
related: neovim/neovim@5a78c5b
closes: vim/vim#210914be03620b3
Co-authored-by: J. Paulo Seibt <jpseibt@gmail.com>
Remove "ro" from 'formatoptions'.
The universal selector "*" cannot be disambiguated from the 'comments'
middle "*" pattern, causing spurious "*" characters to be inserted after
selector lines.
fixes: vim/vim#15140closes: vim/vim#211258331682f80
Co-authored-by: Doug Kearns <dougkearns@gmail.com>
Problem:
1. `ctx_dirs_restore()` is the only chdir site that doesn't re-shorten
buffer names; `post_chdir()`, `update_cwd()` and `do_autochdir()` all
call `shorten_fnames(true)`. So after a temp window-context switch
that moved the CWD, every buffer's `b_fname` is still relative to the
other directory. ui2 renders messages in a float, and entering it is
such a switch, so with a `:bcd` in a `BufReadPost` handler 'statusline'
"%f" shows ".config/nvim/init.lua" while the CWD is already
`~/.config/nvim`, and :write resolves the name against it:
```
E212: Can't open file for writing: no such file or directory
```
2. `msg_multihl()` leaves `msg_ext_id` pointing at the caller's storage
when nothing was emitted (e.g. 'msg_silent'): the reset only ran on
a flush that produced chunks. The next message then ships a dead
stack frame as its msg_show id. After ":silent write" the id is
buf_write()'s `msg_id[MAXPATHL + 32]`:
```
id = "\0\0\0\0\0\0\0\0\29\0(<C6>k\24R\17p<C7><C7><C7>\1\0\0\0..."
```
Solution:
1. `shorten_fnames(true)` after restoring the CWD. Drop `cs_save_sfname`,
which was a partial workaround for the same bug.
2. Release the id in `msg_multihl()`, where it is set and the caller's
frame is still alive. Stop reading `.data.integer` out of a String
union member.
Problem:
With 'linebreak', a word is moved to the next screen line when it does
not fit on the current one. The check measures only the buffer text, so
inline virtual text anchored inside the word is not counted. The word is
kept on a line it cannot fit, and the virtual text is split across the
screen line boundary.
Solution:
Count the width of inline virtual text anchored within the word, so the
check uses the width that is actually displayed.