Problem:
A blank line before the Content-Length header makes header parsing
fail with "Content-Length not found in header". An LF in the 'name'
state falls through to the 'invalid' state, which only return to
'name' at the *next* LF. That LF terminates the next line, so the
line with Content-Length is swallowed.
The same issue happens when a junk line is a partial match of the
header name (e.g. "Cont\nContent-Length: ...").
Solution:
When seeing an LF in the 'name' state, reset the cursor and stay in
'name' rather than entering 'invalid'.
Problem:
With these two lines (`\b` marking a backspace):
f\bfoo
xb\bb
line 1's bold run ends at byte 1 and line 2's begins at byte 1, so line 1
renders "fo" in bold rather than "f", and line 2's "b" is not bold at all.
Solution:
Only grow a highlight group that is on the current row.
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:
`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:
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:
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:
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:
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:
`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.
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.
Problem:
FAILED …/plugin/zip_spec.lua @ 442: nvim.zip reports an incorrect archive password
Expected values to be equal.
Expected:
true
Actual:
false
stack traceback:
…/plugin/zip_spec.lua:453: in function <…/plugin/zip_spec.lua:442>
Solution:
The message is scheduled, so poll for it.
Problem:
Client:_get_registrations() errors if a documentSelector pattern is not
a valid glob (e.g. "**/**.dart" from dartls). The error discards
already-matched registrations and propagates to supports_method() and
the client/registerCapability handler, disabling capabilities unrelated
to the offending pattern.
Solution:
Skip invalid globs. Treat them as non-matching, and log once per client.
(Similar approach was taken for filewatcher globs: 4d9e5acfb5)
Problem:
`zipfile://{archive}::{entry}` is ambiguous: `::` is legal in both an
archive path and an entry path, so the separator cannot be identified.
Splitting at the last `::` reads archives correctly but breaks entries
that contain it, and the plugin then emits buffer names it cannot read
back.
Solution:
Address entries as `zip://{archive}/{entry}`, joining the two paths.
Resolve the split by walking components: the first one that is a regular
file is the archive, because a regular file cannot have children on disk.
Entry paths may then contain any character, and no escaping is needed.
zipPlugin.vim keeps its own scheme, so the Java ftplugin emits whichever
form matches the active plugin until the legacy package is removed.
Problem: An error raised after the list handler ran (e.g. while
rendering) unwinds through the pcall around provider.list()
and is routed back into the now no-op handler: silently
discarded.
Solution: Re-raise the error when the handler has already run.
Problem: A failed initial directory listing discards the listing state:
the user is left in an empty modifiable non-listing buffer, and
the BufEnter autocmd retries the failing open, repeating the
error on every re-entry.
Solution: Render the failure as an empty listing with state and handlers
intact: the error is reported once and "R" or :edit retries the
listing. Keep the error on the listing state so a failed
listing is distinguishable from an empty one; the next list
clears it.
Problem:
There is no health check. Encrypted entries cannot be read at all, since
Info-ZIP takes a password only from a terminal and `-P` would expose it
in the process arguments.
Solution:
Add `:checkhealth nvim.zip`, reporting the backend and which
implementation is handling archives. Prompt for the password on a pty,
extracting to a file so the entry's bytes stay off the terminal. Report
Info-ZIP's exit code rather than inferring a cause, so an archive using
AES is not reported as a failed decryption.
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>
Problem: No way for plugins to define hooks that would be executed
during plugin lifecycle. Like after install/update or before
update/delete.
Solution: Automatically source scripts defined in plugin manifest file
(if any) after triggering corresponding `PackChanged{,Pre}` events.
During sourcing make some termporary adjustments:
- Current directory is set to plugin's root to simplify execution of
CLI calls like `make build`.
- Plugin's path is ensured to be inside 'runtimepath' to allow using
`require('plugin-module')` inside manifest scripts.
The reason to execute after triggering event is so that there is a
possibility for users to execute code both before the script (exactly
on event) and after the script (more-or-less via `vim.schedule` called
on the event).
Problem: There is no agreed way for plugins to provide extra information
about themselves. Like required Neovim version, install/update/delete
hooks, and dependencies.
Solution: Introduce basic concept of plugin manifest file `pkg.json`
based on https://packspec.org/.
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`.
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.
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.
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.
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.
Changes:
- Removed the blank line before the first healthcheck header.
- Fixed a bug where the entire "Terminal" section of `vim.health` would
not appear if the `infocmp` executable was not found.
- Fixed a typo in `vim.lsp` when displaying the LSP log level.