Problem: There are many Git forges each with a different way of
constructing permanent links to like commits and tags.
Solution: Add a private utility function that computes these special
links on the best effort basis.
Problem:
When running nvim on a remote machine over SSH, if there is high ping,
then bg detection may not complete in time. This results in a warning
every time nvim is started. #38648
Solution:
Restore 'ttyfast' option and allow it to control whether or not bg
detection is performed. Because this is during startup and before any
user config or commands, we use the environment variable
`NVIM_NOTTYFAST` to allow disabling `ttyfast` during initialization.
Problem:
Can't expand treesitter-incremental-selection to the next and previous
sibling nodes.
Solution:
Pressing `]N` in visual mode will expand the selection to the next
sibling node, and `[N` will do the same with the previous node.
Problem:
- The `ZR` feature makes it more obvious that we need some sort of flag so that
an `ExitPre` / `QuitPre` / `VimLeave` handler can handle restarts differently
than a normal exit. For example, it's common that users want `:mksession` on
restart, but perhaps not on a normal exit.
- Nvim has no way to report its "uptime".
Solution:
- Introduce `v:starttime`
- Introduce `v:exitreason`
benchmark: https://gist.github.com/ofseed/6224529d77c016c36f7ab2f977059848
local rounds = tonumber(arg[1]) or 1000
local count = tonumber(arg[2]) or 1000
-- Load the table.clear function.
local clear = require("table.clear")
local function fill(t, n)
for i = 1, n do
t[i] = i
end
end
local function bench_reassign(n_rounds, n_items)
local t = {}
local start = os.clock()
for _ = 1, n_rounds do
t = {}
collectgarbage("collect")
fill(t, n_items)
end
return os.clock() - start
end
local function bench_reassign_no_gc(n_rounds, n_items)
local t = {}
local start = os.clock()
for _ = 1, n_rounds do
t = {}
fill(t, n_items)
end
return os.clock() - start
end
local function bench_clear(n_rounds, n_items)
local t = {}
local start = os.clock()
for _ = 1, n_rounds do
clear(t)
fill(t, n_items)
end
return os.clock() - start
end
-- Warm up LuaJIT before the real benchmark.
do
local t = {}
for _ = 1, 2000 do
clear(t)
fill(t, count)
end
end
collectgarbage("collect")
local reassign_time = bench_reassign(rounds, count)
collectgarbage("collect")
local reassign_no_gc_time = bench_reassign_no_gc(rounds, count)
collectgarbage("collect")
local clear_time = bench_clear(rounds, count)
print(string.format("rounds=%d count=%d", rounds, count))
print(string.format("t = {} + GC : %.6f s", reassign_time))
print(string.format("t = {} : %.6f s", reassign_no_gc_time))
print(string.format("table.clear : %.6f s", clear_time))
print(string.format("vs + GC : %.2fx", reassign_time / clear_time))
print(string.format("vs no GC : %.2fx", reassign_no_gc_time / clear_time))
benchmark result:
rounds=1000 count=1000
t = {} + GC : 0.022469 s
t = {} : 0.002570 s
table.clear : 0.000387 s
vs + GC : 58.06x
vs no GC : 6.64x
`count` is how many items the table has, and `round` is how many rounds we fill
the table, clear, and then refill it. `table = {}` is clear the table by
resigning a new empty one, because this script does not run persistently like
nvim so GC is not triggered, so I added another extreme control group that
manually triggers GC.
Problem: :restart leads to ERR/WRN logging on Windows with --listen.
Solution: Add a log_level flag to vim._with() and use it to suppress
logging from serverstart()/serverstop() during restart.
Problem:
On Windows, :restart cannot immediately reuse the canonical --listen
address because named pipe release is asynchronous.
Solution:
Start the new Nvim server on a temporary address; in the new Nvim,
retry serverstart() with the original ("canonical") address until it
succeeds.
Problem: Invalid check for non-typed key to dismiss expanded cmdline.
Unable to delay the timer that removes a message from the msg
window.
Solution: Check for empty string instead of nil to determine whether a
key is typed.
Restart the timer if it expires while the user is in the msg
window. Allow entering the msg window with a mouse click.
Problem: After closing a tabpage while the msg window is showing a
message, it is hidden while the msg window still contains a
message.
Solution: Unhide the msg window after entering a tabpage and it still
contains a message.
Co-authored-by: Linykq <yukunlin590@gmail.com>
Problem:
- Unable to "pin" a window to prevent closing without specifically
being targeted.
- :fclose closes hidden windows (even before visible windows).
Solution:
- Add 'winpinned' window-local option. When set, window is skipped by
:fclose and :only. Pin the ui2 cmdline window (which should always be
visible), so that it is not closed by :only/fclose.
- Skip over hidden (and pinned) windows with :fclose.
Co-authored-by: glepnir <glephunter@gmail.com>
Problem: Using `buf=0`/`win=0` context in `vim._with` should be
equivalent to using explicit buffer/window identifier respectively.
Solution: Explicitly adjust context in case of `buf=0` or `win=0`.
Problem:
Too much boilerplate needed to use Lua to impl an excmd or f_xx
function.
Solution:
- Add `nlua_call_vimfn` which takes the args typval, executes
Lua, and returns a typval.
- refactor(excmd): lua impl for :log, :lsp
Problem:
- Builtin "Vimscript" functions (f_xx) are mostly implemented in C.
Partly that's because there is some boilerplate required to call out
to Lua.
- Calls to `vim.fn.foo()` always marshall over the Lua <=> Vimscript
("typval") bridge, even if `fn.foo()` is implemented entirely in Lua:
```
Lua => typval => Object => Lua => Object => typval => Lua.
```
Solution:
Functions declared in eval.lua with `func_lua` are implemented in
entirely in Lua (`_core/vimfn.lua`).
- `gen_eval.lua` wires `func_lua` entries to `lua_wrapper`, which handles
the typval conversion for Vimscript callers (slow path).
- `nlua_call()` detects `func_lua` functions and calls the Lua
implementation directly. This eliminates all conversion overhead for
Lua callers (fast path).
- Validate at build-time that `func`, `func_float`, and `func_lua` are
mutually exclusive.
- Migrate `hostname()` as a toy example, to show the idea.
Problem: - Paging keys in the dialog window consume input when the user
may not expect it. The dismissable title hint intended to
mitigate that results in having to press Escape twice to
abandon the prompt.
- Mimicked "msgsep" float border is taking up unnecessary
space when window takes up the entire screen.
Solution: - Use (conventional, albeit less convenient) keys intended
for scrolling to page the dialog window:
<(Mousewheel/Page)Up/Down>, <Home/End>.
- Only set the float top border when separation is actually
necessary, i.e. window does not reach the first row.
Problem:
Naming conventions are not automatically checked.
Solution:
Add a check to the doc generator. Eventually we should extract this
somehow, but that will require refactoring the doc generator...
Note: this also checks non-public functions, basically anything that
passes through `gen_eval_files.lua` and `gen_vimdoc.lua`. And that's
a good thing.
Replace the busted-based Lua test runner with a repo-local harness.
The new harness runs spec files directly under `nvim -ll`, ships its own
reporter and lightweight `luassert` shim, and keeps the helper/preload
flow used by the functional and unit test suites.
Keep the file boundary model shallow and busted-like by restoring `_G`,
`package.loaded`, `package.preload`, `arg`, and the process environment
between files, without carrying extra reset APIs or custom assertion
machinery.
Update the build and test entrypoints to use the new runner, add
black-box coverage for the harness itself, and drop the bundled
busted/luacheck dependency path.
AI-assisted: Codex
Problem:
Running `:Open` on an open buffer does not run `vim.ui.open()` on that file, requiring the user to type `:Open %`. This is inconsistent with most other vim commands which accept files, which default to the current buffer's file.
Solution:
Default to the current file when `:Open` is used without arguments.
Problem: When messages are appended to an already expanded cmdline,
the spilled lines indicator is not updated.
Solution: Remove early return for updating virtual text while cmdline is
expanded, guard updating "msg" virt_text at callsite instead.
Problem: 'showcmd' causes flickering when pressing "g<" to enter the
pager when the cmdline is expanded for messages.
Initial keypress for an incomplete mapping is not giving 'showcmd'
feedback while cmdline is expanded for messages (which is only
dismissed upon the vim.on_key callback after 'timeoutlen').
Solution: Delay dismissing expanded cmdline when vim.on_key() callback
receives "g".
Place 'showcmd' "last" virtual text during expanded cmdline.
Problem: Cmdline is not redrawn after an empty message clears it.
Remembered last drawn cursor position may be outdated but
equal to the current cmdline content with UI2.
Solution: Ensure cmdline is redrawn after an empty message clears it.
Compare wanted cursor position with actual cursor position.
Problem: `vim._core.ui2.enable` requires passing an empty table for
default options, unlike other (native) Lua API.
Solution: Initialize `opts` with empty table if `nil` is passed.
Problem: Mixing "buf" and "M.bufs.msg" in M.msg:start_timer().
Cannot run `require("vim._core.ui2").enable(nil)`.
Solution: Replace "M.bufs.msg" with "buf". Allow `opts == nil`.
Co-authored-by: Luuk van Baal <luukvbaal@gmail.com>
fix(ui2): prevent <CR> from focusing pager in insert/terminal mode
Problem: <CR> in insert/terminal mode can focus pager unexpectedly.
Solution: Don't enter the pager when <CR> is pressed during expanded
cmdline in insert/terminal mode.
Problem: When entering the cmdline below expanded messages, those
messages are moved to the dialog window. The dialog window
supports paging but that is unexpected in this situation where
it just serves to keep (some of, exactly those that were
visible before the cmdline was entered) the messages visible.
Wrong highlight group for dialog "more" message.
Solution: Don't create the `vim.on_key()` dialog pager callback after
entering the cmdline below expanded messages.
Use the MsgMore highlight group for the paging hint title.
Problem: - With expanded messages exceeding cfg.msg.cmd.height, entering
the cmdline scrolls to the bottom and expands to the full "cmd"
buffer text height.
- Cursor in the pager is not always at the last message and at
the bottom of the window when appending to the pager.
- unreliable test: messages2_spec: "closed msg window timer removes
empty lines".
Solution: - Achieve separation of the cmdline and message text by moving
messages to the dialog window when entering the cmdline below
expanded messages.
- Set cursor to start of the first message only when first
entering the pager. Use `norm! zb` to position last message
at the bottom of the window (which shouldn't crash anymore
since 911337eb).
- Increase cfg.msg.msg.timeout used in the test file.
The `buffer` option remains functional but is now undocumented.
Providing both will raise an error. Since providing `buf` was disallowed
before, there is no code that will break due to using `buffer` alongside
`buf`.
Problem:
- Window height is set dynamically to match the text height,
making it difficult for the user to use a different height.
- Cmdwin is closed to enter the pager but still taken into
account for the pager position, and not restored when
the pager is closed.
- Dialog pager handler may unnecessarily consume <Esc>.
Solution:
- Add maximum height config fields for each of the UI2 windows,
where a number smaller than one is a fraction of 'lines',
absolute height otherwise (i.e. `cfg.msg.pager.height = 0.5`).
- If the cmdwin will be closed to enter the pager, don't try
to position the pager above it. Re-enter the cmdwin when the
pager is closed.
- Only add vim.on_key() handler for the dialog paging is actually
possible.