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:
Windows searches the current directory before $PATH, so opening an archive
in a directory that also contains an `unzip` executable runs that one. The
legacy plugin refused this; the port dropped the check.
Solution:
Refuse to run `unzip` when it resolves to the current directory.
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:
Calling vim.treesitter.start() a second time on a buffer that already
has an active TSHighlighter creates a brand new instance instead of
reusing it, whether the parser tree is unchanged (e.g. calling start()
twice) or different (e.g. switching languages). Either way the old
instance is silently discarded without calling :destroy() on it, so
its on_bytes/on_changedtree/on_detach callbacks stay registered and
its buffer-local state (spelloptions, decoration namespace) is never
restored, both leaking indefinitely for an orphaned instance that
nothing references anymore.
Solution:
Return the existing instance when TSHighlighter.active[source] is
already set for the same parser tree, instead of unconditionally
constructing a new one. When the tree differs instead (e.g. a language
switch), destroy() the old instance first, matching stop() semantics,
before constructing the new one.
Problem: filetype: ed script files not recognised.
Solution: Add filetype detection for *.ed files and shebang lines,
include syntax script and syntax tests.
Features of the ed syntax file:
- BSD and GNU extensions are supported
- Andrew L. Moore's ed extensions are not supported
- Rebuild synmenu.vim
closes: vim/vim#19602c28515b999
Co-authored-by: Doug Kearns <dougkearns@gmail.com>
Adds optional parameters to `vim.fs.abspath`:
1. `cwd` to specify directory akin to `--relative-from` in `realpath(1)`.
2. `plain` disables expansion of tilde (~) in paths.
Problem:
Numerous callers have to manually check optval ownership (i.e. whether,
and how, to release) via `is_callback_option`, `option_is_global_local`,
etc. This is fragile, hard to use correctly; and if we introduce another
optval variant in the future, we'll have to redo all of these careful
checks and boilerplate again.
Solution:
Provide a unified system and use it everywhere:
optval_free_owned
optval_is_owned
optval_own
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:
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:
Cmdline area shows stale ":" after backspacing out of the command line.
Solution:
Clear the command line for empty commands. Note that `:<CR>` will now
clear the command line too.
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:
Tree-sitter uses UINT32_MAX for full-document ranges, which becomes -1 on 32-bit platforms and reaches _foldupdate as an invalid end row.
Solution:
Treat negative changed-range end rows as unbounded and clamp them to the buffer line count. Add a regression test that simulates the 32-bit sentinel.
AI-assisted: Codex
Problem:
Want `gQ` for _le multicursor_.
Solution:
- Don't use `gQ` for exmode.
- Introduce `:exmode`.
- Introduce `[count]q:` as an alias to `:exmode`.
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:
POSIX-compatible Ex-mode requires special-cases all over the codebase to
match various quirks that don't actually matter to users.
- The main utility of *interactive* Ex-mode is its REPL behavior, and
that can be achieved with `cmdwin`, which also gains extra UX
benefits.
- The main utility of *non-interactive* `nvim -es` is for shell
scripting, where Ex-mode quirks are mostly unhelpful (e.g. the
"Entering Ex mode" message).
Solution:
- Reimplement *interactive* Ex-mode as a "persistent, insert-mode
cmdwin" in Lua.
- "nvim -e/-E" is simply an alias to "gQ".
- Reframe *non-interactive* Ex-mode (`nvim -es`) as "script mode".
- Drop POSIX Ex-mode quirks.
Improvements:
- "nvim -V1 -es" output ends with a final newline!
- "nvim -V1 -es" no longer shows the "Entering Ex mode" msg. (This was
pointless noise, unwanted for scripting purposes.)
- stdin is no longer typeahead. Scripts (":lua io.read()") can read
stdin as data.
- Empty line is a no-op: a stray blank line no longer moves the cursor
(deviates from POSIX ex "+1"), no longer exits 1 at EOF (E501).
Preserved behavior:
- cursor starts at "$"
- mode()=="cv" (for non-interactive)
- multiline commands (:append/:function/heredoc pull continuation lines)
- bare-range print
- :print=>stdout
- -V1=>stderr
- CRLF input
- continue-after-error and exit codes
Dropped (regressed) POSIX behavior (non-interactive):
- Event loop only ticks while/between commands, not while blocked
waiting for a stdin line.
- ":g/pat/visual...Q"
- input()/getchar()/":s/x/y/c" no longer consume stdin lines as
answers: Nvim stops at end-of-input, skipping the rest of the script,
exit 0. Use ":lua io.read()" instead.
- If users care about this they should use interactive Ex-mode (`gQ`).
- ":@r" stops at end of the register instead of continuing to read
cmdline input from stdin.
## Flicker cause
ui2's `check_targets()` function creates the hidden ui2 windows with this call chain:
```
nvim_open_win()
win_set_buf()
do_buffer()
set_curbuf()
enter_buffer()
```
and `enter_buffer()` sets `need_fileinfo`:
fdc09be03c/src/nvim/buffer.c (L1837-L1839)
This is then picked up by `normal_redraw`:
fdc09be03c/src/nvim/normal.c (L1381-L1385)
The fileinfo message then causes a ui2 window to appear to display it:
fdc09be03c/runtime/lua/vim/_core/ui2/messages.lua (L325-L330)
... which ends up calling `set_pos`, which registers an `on_key` handler:
fdc09be03c/runtime/lua/vim/_core/ui2/messages.lua (L726-L728)
This handler closes the ui2 window when the user presses, for example, `l` (because the user's not focusing said window).
The handler eventually calls `check_targets()`:
fdc09be03c/runtime/lua/vim/_core/ui2/messages.lua (L594-L595)
and `check_targets()` takes us back to the beginning, causing the ui2 window flicker
## Repro
```vim
=require("vim._core.ui2").enable()
set ch=0 shm-=F ve=all
e /tmp/x
```
Then hit `Ctrl-G` to show fileinfo (if not already shown) and hold `l`
## Fix
Effectively surround the ui2 window creating with `:silent`
Problem:
After expanding tabs with the `expandtab` option, if the old indent
matches the requested one, `vim.text.indent()` returns the input
unchanged.
The optimization path assumes that if old_indent == size, the input
wouldn't be changed, which is not correct when old_indent is formed by
expanded tabs.
Solution:
Apply the optimization only when `expandtab` is not set.
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.