`opts.plain=true` does not expand tildes in addition to environment
variables, unlike `opts.expand_env=false`.
`opts.expand_env=false` is soft-deprecated.
- 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().
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).
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:
With `laststatus=3`, a pager float shares the main grid's statusline
row. Setting a diagnostic fires `DiagnosticChanged`, whose handler calls
`nvim__redraw({ statusline = true })`.
Analysis:
Inside the autocmd, `curwin` is temporarily switched to the tiled window
showing that buffer, so `win_redr_status()` paints its statusline over
the pager's `[Pager]` statusline on the shared row. After the autocmd,
`curwin` is restored but the pager statusline is never repainted.
Solution:
Use `ctx_saved_curwin()` decide whether to draw the global statusline,
matching `win_redr_stl_expr()` and `update_screen()`. No behavior change
if no buffer-context switch is active.
Problem: An OSC 52 sequence from a :terminal job passes the decoded
payload to the clipboard provider as a single list item. Command-line
providers (pbcopy, xclip, ...) receive it with channel semantics, where
a newline inside an item is sent as NUL (:h chansend()), so multiline
copies arrive with NUL bytes instead of newlines.
Solution: Split the payload on newlines into a proper list of lines.
A trailing newline yields a final empty item, which chansend() turns
back into a newline, so payloads round-trip exactly.
Problem: A winbar-only window with zero text height still occupies one row,
but win_update() returns early on w_view_height == 0 and skips the
vertical separator.
Solution: Also draw the vertical separator in the early return path.
Problem:
Several subsystems need to derive a short, filesystem-safe identifier from an
arbitrary path, and each reinvents it ad-hoc:
- `'undodir'` and `swapfiles` encode the full path into a single filename, which
may exceed filesystem length-limits.
- `:connect ssh://` needs the SSH ControlPath socket name to stay under the
104-byte `sun_path` limit on macOS; today the path overflows it.
- the upcoming :terminal state dir.
- arbitrary plugin purposes.
Solution:
Provide `vim.fs.slug()`, which generates a bounded, one-way filename from an
arbitrary string. The input is normalized so equivalent paths produce the
same result. An 8-char hash is appended for uniqueness
Problem:
Buffer-local CWD (:bcd) is "sticky", similar to window-local CWD (:lcd).
But this contradicts one of its main benefits: per-buffer "project root"
for LSP, OSC7.
Other problems:
- A buffer created with :edit/:enew/:new silently inherits b_localdir
(and b_prevdir) from the previous buffer.
- curbuf_reusable() refuses to recycle a scratch buffer that has
`b_localdir`.
- After :new/:vnew/:tabnew the CWD sticks to previous buffer's
`b_localdir` even though the new curbuf has none, so :new is not
equivalent to ":split | enew", and getcwd() disagrees with
haslocaldir().
- Requires "which buffer spawned this buffer" semantics that no other
buffer-local state has.
Solution:
Drop sticky/inherit behavior of buffer-local CWD (:bcd).
- do_ecmd: always apply the new curbuf's dir (`fix_current_dir`), like
`do_autochdir` already does. :tabnew from a :bcd buffer now reverts to
global CWD (and fires DirChanged), same as :tabnew from a :lcd window.
- curbuf_reusable(): recycling a scratch buffer frees its b_localdir.
To get sticky/inherit behavior of CWD, use `:lcd`.
Problem:
No way to set a buffer-local directory.
Use-cases:
- "Root dir" for LSP (and the "project" concept).
- `:terminal` OSC 7
Solution:
Add `:bcd` command.
- Extend `getcwd()` to take a third arg; `getcwd(-1, -1, bunfr)` returns
the buffer-local working directory.
- Buffer-local directories have less priority than window-local
ones, and more priority than tab-local ones.
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
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:
Ignore is linked to Normal by default, making the text visible instead
of hidden.
Solution:
Replace the default link with an explicit highlight definition using
ctermfg=0 guifg=bg.
Problem:
Assigning to the local value of a global-local boolean option
('autoread', 'autocomplete', 'fsync') aborts:
Assertion failed: (curval.type == newval.type), function
ex_let_option, file vars.c, line 1408.
ex_let_one
ex_let_vars
ex_let
execute_cmd0
do_cmdline
call_user_func
...
eval_map_expr
vgetorpeek
vgetc
state_enter
main
A global-local option may have local value `kObjectTypeUnset`, but
`ex_let_option()` guards only `kObjectTypeNil`.
Solution:
When curval is Unset, resolve it to the inherited global value.
Problem:
By default, `nvim` does not survive if its host terminal dies. This is
inconvenient if you want to use Nvim as a "session manager" (like tmux).
Solution:
Let users opt-in to the "survive" behavior via `:detach!` (bang "!").
This marks the current UI as "detachable", so the server will not
self-exit if the UI channel closes.
Problem:
The default 'ruler' is implemented in C instead of the 'statusline' DSL.
Solution:
Replace the C implementation with a default 'rulerformat' expression.
This is a continuation of #1248 and #33036.
Advantages:
- configuration is more discoverable, the default being a useful example
- users and plugins can augment the default
- code reuse and less C code to maintain
- ui2: due to the use of an item group with `minwid`, it can expand
instead of truncating when the content gets too long, which is
particularly useful for locales with long translations of Top/Bot/All
Implementation details:
As is the case for 'statusline', when trying to set 'rulerformat' to an
empty string, the default expression is restored instead, mimicking how
previously the default C implementation would have been activated.
Just like before, `:set rulerformat=` and `:set rulerformat&` have the
same effect, and the ruler is disabled with `:set noruler`.
The default expression uses an item group with `%=`, unlike the fallback
in the previous default statusline `%-14.(%l,%c%V%) %P`, because the
total width and how it is configured is immediately clear without
documentation, it is a more useful pattern in general that works when
both sides have flexible width, and it also works for vim, which is
useful for configuration sharing/reuse.
A truncation marker `%<` is added at the end to mimic how at small
screen widths, the scroll percentage would disappear first, so that the
cursor position can remain fully visible.
BREAKING CHANGES:
- `&rulerformat` can no longer be set to an empty string
- ui2: the default ruler is no longer of fixed width, but can expand
- at very small screen widths (< 36 columns)
- ui2: it will no longer try to shrink white-space before truncating
- it truncates gradually from the right, whereas previously, the
scroll percentage would disappear all at once
- l10n can no longer add a space after the comma between line and column
(this was only done for one language: Ukrainian)
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)
It's necessary to copy the global 'fileencoding' to the buffer-local
value before entering the buffer, otherwise 'fileencoding' is changed
when reading the file, which will mark the file as modified.
Problem: Traditionally, the ruler in the last line is one cell shorter
than in the statusline, leaving the last cell of the screen blank.
According to code comments, this is in order to prevent unwanted
scrolling on "some" (unspecified, but presumably ancient) terminals.
Berkeley vi is more specific in its `vs_modeline` function: dumb
terminals with hardware scroll, SunOS 4.1.1 and Ultrix 4.2 curses.
(n)curses still has a similar limitation in `(w)addstr`, but apparently
only for historical reasons.
Maintaining the different widths leads to awkward inconsistencies when
the ruler is configured with 'rulerformat', except for the special case
where it contains a top-level `%=`. Shifting the ruler in the last line
to the left would be a solution, but the empty cell at the end doesn't
seem to be relevant anymore.
Solution: extend the ruler in the last line all the way to the right
edge of the screen, just like in the statusline. The exact same amount
of place will be available to the rest of the UI as before.
BREAKING CHANGE:
- the default ruler width is now 18 cells
- the last cell of the screen is no longer empty
Closes#41076
Problem: the ruler is not cleared in the following circumstances:
- ui1 is running
- 'rulerformat' is configured
- the default ruler was not previously visible in the last line, for
example because 'rulerformat' is configured in init.lua
- 'ruler' is disabled without using the command-line, for example via
key-binding (entering the command-line would clear the ruler)
The corresponding test case did not fail because 'rulerformat' was set
while the default ruler was shown.
Solution: use a dedicated variable for tracking whether the ui1 ruler
was previously shown in the last line. `did_ruler_col` is now only used
for setting `msg_col`, which is not implemented in the case where
'rulerformat' is configured.
Reorder the test code to make the individual checks more independent
from each other, and to reflect the future where 'rulerformat' will
never be empty. Note that the check where 'rulerformat' was configured
relied on the default ruler not being cleared and a stale "0," still
being shown in front of the new ruler - this is also fixed with ui2.
Fixes#38777 in case 'rulerformat' is set.
See PR 38879. Original message:
Problem: When the 'ruler' is in the last line of the screen, it takes
local highlight definitions of the current window, tripping an
assert (since c1648cf).
Solution: Don't use window-local highlight definitions when the ruler is
not part of a statusline.
Problem: transstr() appends with STRCAT()/STRLEN() from the start of
the result on every iteration, making it quadratic to the
length of the string.
Solution: Keep a tail pointer and append at it. (Samuel Schlesinger).
closes: vim/vim#20925124c86868c
Co-authored-by: Samuel Schlesinger <sgschlesinger@gmail.com>
Problem: "zb" scrolls incorrectly with cursor just above fold.
Solution: Handle boff.lnum being set to the last line of a fold
(zeertzjq).
With the cursor just above fold, botline_forw() moves boff.lnum to the
last line of the fold, but curwin->w_botline is at the first line of the
fold, so the boff.lnum == curwin->w_botline condition never holds.
Instead, check that boff.lnum has just moved to or past w_botline by
comparing its previous value with w_botline.
Also make a similar change to the loff.lnum check above for symmetry.
That one doesn't change behavior, as topline_back() sets loff.lnum to
the first line of a fold.
related: neovim/neovim#41122
closes: vim/vim#20923aee686334c
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:
'breakindent' and 'showbreak' draw their own padding with no
reference to whatever decoration or syntax highlight is currently
active, so it goes unhighlighted even mid-highlight, not just past a
real EOL. Gating this on the decoration's `hl_eol` flag (as an
earlier version of this fix did) missed plain highlights with no
`hl_eol` at all, which have the exact same problem.
Solution:
Snapshot decor_attr into decor_attr_save right before it can be
reset by 'linebreak' filler handling, and pass it into
handle_breakindent()/handle_showbreak_and_filler() to extend into
their padding: it is not a real end of the highlight, just screen
cells with no buffer text. Like the 'linebreak' filler, an
underline/strikethrough/overline is excluded, since it looks like a
broken line drawn over the gap. This also fixes 'breakindent' losing
the highlight right after a real 'linebreak' word-push, since that
reset otherwise leaked into the next row.
Problem:
ui2 clears the substitute confirmation match when it updates its prompt buffer with hlsearch disabled.
Solution:
Only invalidate the match highlight when the current buffer changes.
Problem:
When 'linebreak' pushes a word entirely to the next screen row, the
filler cells left on the current row keep whatever highlight was set
by the last real character before the break, even when that highlight
should not extend past it (e.g. an underline, which looks broken drawn
over blank cells).
Solution:
Reset decor_attr and area_attr at the filler when their attribute has
an underline, undercurl, strikethrough, or overline; otherwise leave
them, since a plain background or reverse-video highlight looks
correct extending through blank filler cells, regardless of where the
pushed-down word happens to end. search_attr keeps the same check,
plus its pre-existing on_last_col case (its own match ending exactly
here).
Problem: The test comparing the top line before and after using the
autocommand window is flaky in the GUI.
Solution: Run the test in a window with a fixed size. In the GUI a
pending resize of the shell is applied at the end of a screen
update, thus the size may change between the two measurements.
related: vim/vim#20884
closes: vim/vim#2091385b8034dff
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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:
Test fails if other "build*/" dirs exist with a "nvim" file, e.g.
`build-asan/bin/nvim`.
Solution:
Do the test in an isolated dir.
This also fixes the test for the zig build.
Problem: With 'virtualedit' set to "all" and 'cursorcolumn' set, the wrong
column may be highlighted after a command that moved the cursor
into virtual space and back (van-de-bugger).
Solution: Make sure the virtual column is up to date before drawing the
window (Hirohito Higashi).
fixes: vim/vim#2576closes: vim/vim#209025a90b9dbd2
Test only. This was already fixed by #39159.
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>