| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
|
|
|
| |
Problem:
TM_DIRECTORY expands to the directory's basename, losing its parent path.
Solution:
Remove the tail modifier when expanding the current file's directory.
Extend the existing known-variable test to check the full directory path.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Problem: watchdirs() keeps its state in large nested callbacks in
_watch.lua, which makes its directory tracking logic harder to change.
Solution: Move the backend into its own module and store its state in a
WatchDirs instance. Split setup, event handling and cleanup into
methods. Extend the existing test to cover cancellation while a
directory stat is pending.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
After edits move diagnostic ranges, counted jumps resume searching from
their original positions. This can revisit the same diagnostic and hang
the default first/last diagnostic mappings.
Solution:
Advance the search origin using the diagnostic's current logical position.
Test forward and backward counted jumps after inserting or deleting a
line, including jumps to the last or first diagnostic without wrapping.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem: Highlight inspection is limited to a single position, and its
result types do not document the fields shared by different highlight
sources.
Solution: Accept end_row/end_col in vim.inspect_pos to inspect a range.
Keep range ends exclusive and retain the existing single-position
behavior.
Support Ex line ranges in :Inspect, including Visual selections.
Document named result types, expose shared highlight fields, and keep
the existing extmark details and buffer field for compatibility.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
|
| |
vim.diagnostic.Opts.VirtualText has a `source` option to prefix
diagnostic messages with their source (`'if_many'` or always), but the
newer virtual_lines handler never got the same option, even though the
shared helpers it needs (diagnostic_shared.prefix_source/count_sources)
are already render-target-agnostic and used by virtual_text as-is.
Add the same `source` field to vim.diagnostic.Opts.VirtualLines and
apply it in the virtual_lines handler right after message formatting,
mirroring virtual_text's own ordering (format first, then prefix the
source onto the already-formatted message).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
#42352
Problem:
- nvim_buf_call() aborts (assert) when called from an expression evaluated
for a window in another tabpage ('foldexpr', 'statusline'), because
`curwin` is not in `curtab`.
- After it returns, `curwin` is wrong if it switched windows.
- `vim._with{win=…, keepcwd=true}` does not undo `:lcd` if `win` is in
another tabpage.
Solution:
- Allow `curwin` outside `curtab`, and restore if it was outside
`curtab` at `ctx_switch()`.
- Find the target window in any tabpage when restoring its dirs.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
`nvim_set_option_value('fo', {'c','r','o'}, {operation='remove'})` does
nothing on "jcqrol": the items are joined into one `:set-=` value, which
only matches a contiguous substring. Removing map keys, e.g. {'eol','space'}
from 'listchars', never matches. `vim.opt:remove{…}` inherits this.
(Regression from f34ee3da80e8; 429338e74bf4 fixed the case where the
flags are adjacent and in order.)
Solution:
Remove each item separately.
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
Handlers defer `show()` on unloaded buffers with a buffer-local BufRead
autocmd whose id is stored per-namespace:
- `noautocmd bwipeout` deletes the autocmd, so the next show/hide fails.
- Showing another buffer in the same namespace cancels the pending show.
Solution:
Handlers skip unloaded buffers. The existing global BufRead autocmd
shows cached diagnostics when the buffer loads.
|
| |\
| |
| | |
perf(watch): skip directories outside include glob prefixes
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem: watchdirs() scans and watches every directory that is not
excluded, even when a glob only matches files in one subtree. Some
language servers register absolute dependency filenames separately.
Neovim uses the workspace root for these string globs, so each extra
registration can scan and allocate libuv handles across the whole tree.
Solution: Keep each glob's literal directory prefix with its compiled
matcher. A directory must be allowed by both the include filter and at
least one watch rule. Skip unrelated subtrees, including new directories
created later. Keep ancestors watched so matching directories can still
be found. Report events only when they match the full patterns.
For example, watching /project for /project/src/**/*.lua only needs the
src tree and /project itself:
/project/ watched
|-- src/ watched
| `-- lib/ watched
|-- tests/ skipped
| `-- fixtures/ skipped
`-- vendor/ skipped
`-- pkg/ skipped
With no exclusions, this uses three watches instead of seven.
The gain can be large, but depends on the server's patterns and the
workspace layout. Separate watches for absolute dependency filenames can
skip most directories.
For example, a test workspace has 213 directories, including 201 under
build/. The two dependency files in this example are under packages/ and
styles/, so their watches can skip build/. Before this change, each
watch starts at the workspace root, scans all 213 directories and
creates 213 libuv handles.
With this change:
- Haskell Language Server: a separate watch for
/project/packages/demo/demo.cabal watches four directories: the
project root, packages/, demo/ and its src/ child. This cuts that
watch's scans and handles by 98%. With one broad source watch and this
dependency watch, the total falls by 49%.
- Tailwind CSS v4: a separate watch for the imported
/project/styles/themes/colors.css watches three directories: the
project root, styles/ and themes/. This cuts that watch's scans and
handles by 99%. With two broad watches and this dependency watch, the
total falls by 33%.
The broad watches still cover all 213 directories. The saving comes from
avoiding repeated scans and extra handles for each dependency watch.
Most servers with watch registrations in the survey do not benefit.
Their patterns match throughout the watch root, or their watches already
start at the relevant directory. In both cases, the glob prefix cannot
exclude more directories.
AI-assisted
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem: Globs such as '#generated/*.lua' fail to match their literal
paths. The parser consumes a leading '#' as its internal in-segment
flag.
Solution: Pass the flag as an LPeg match argument instead of inserting
it into the glob text. Cover literal and braced patterns with leading
hashes.
AI-assisted
|
| | |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem:
`vim.fs.find()` checks `type` against the symlink itself, so
`{ type='file', follow=true }` skips symlinked files (e.g. `:helptags`).
Solution:
With `follow=true`, a symlink has the type of its target. Thus
`type='link'` only matches broken symlinks (like `find -L -type l`).
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem: Repeated default fields and field-by-field assertions obscure
the values that each functional test is meant to check.
Solution: Add eq_partial() to check selected record fields while
preserving list length and order. Use local helpers to fill fixed
expected defaults where tests need full equality. Keep explicit absence
checks and test-specific values at the call sites.
AI-assisted
|
| |/
|
|
|
|
|
|
|
| |
Problem:
`vim._with({ buf = 0, win = 0 }, f)` fails an assert in ctx_switch().
vim._with() allows `buf` with `win` when `win` shows `buf`, but
nlua_with() passes both, and ctx_switch() takes only one target.
Solution:
Ignore `buf` if `win` is given; the window implies its buffer.
|
| |\ |
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem: when the highlighting surrounding the diagnostics section is
not StatusLine[NC], the in-section highlights are not combined and not
correctly reset afterwards.
The spaces between the diagnostic items have the highlight of the item
to the left, which looks bad when the Diagnostic* highlight groups are
customised with background colour or underlines.
Solution: use %$ to combine, and %#(...%#) to reset.
ref #36685
ref #35806
|
| |\ \
| | |
| | | |
fix(lua): enable param-type-mismatch diagnostic
|
| | | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
Problem: Argument type mismatches are excluded from EmmyLua checks.
Solution: Correct runtime annotations and calls, document local checker
workarounds, and enable the diagnostic.
AI-assisted
|
| |/ /
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem:
vim.mpack.encode() picks the msgpack size of a negative integer from its
low 32 bits only, so -(2^32 + 5) is written as a negative fixint and
decodes as -5. Numbers beyond +-2^53 trip an assertion in debug builds.
In release builds, numbers below -2^63 or from 2^64 up go through
out-of-range conversions: -1.5 * 2^63 decodes as 2^62.
Solution:
Use int64 whenever the high word is not a sign extension. Encode numbers
outside [-2^63, 2^64), including NaN and infinities, as floats.
AI-assisted
|
| |/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
For an incomplete sequence at the end of the string, utf_ptr2len_len()
reports more bytes than are there, and mb_utf_index_to_bytes() returns
that position unclamped:
vim.str_byteindex('ab\xf0\x9f', 'utf-16', 3, false) --> 6
on a 4 byte string. LSP callers use the result as a column, so
nvim_buf_set_extmark() then fails with "Invalid 'col': out of range".
Solution:
Clamp the returned byte index to the length of the string.
AI-assisted
|
| |
|
|
|
|
|
|
|
| |
Quickfix entries can omit their start and end positions. Default absent
start coordinates to the first position and absent end coordinates to
the start, avoiding arithmetic and comparisons on nil. Cover both cases
in the conversion tests.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
LSP servers can register identical file watchers before removing old
registrations. Each registration creates another watcher tree, making
setup and teardown expensive in large workspaces.
Solution:
Move glob compilation into vim._watch and accept rules for multiple
roots alongside existing single-path and LPeg inputs. Share compatible
backends until their last subscription is cancelled.
Ref: #23291
AI-assisted
|
| |
|
|
|
|
|
|
| |
Problem:
ui.open() appends the path to the caller's opts.cmd, so reusing the
opts opens previously supplied paths again.
Solution:
Copy opt.cmd before appending the path.
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
vim.opt_local.formatoptions:remove({ 'c', 'r', 'o' }) silently does
nothing since f34ee3da.
Solution:
Join an Array with no separator for a bare flag list, matching the Dict
branch in optval_from_obj().
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem: Option validators and setters repeatedly pass error buffers and
their sizes separately. Some setters return messages in static storage.
Solution:
- Add CharBuf for borrowed writable storage, with its length including
space for a terminating NUL.
- Move option error buffers to the callers that report errors. Preserve
existing buffer sizes, truncation, and calls that do not need a
message.
- Consolidate option error formatting and the duplicated code that lists
valid option values.
- Return error messages directly from do_one_set_option().
- Remove unused arguments from get_option_newval() and
set_option_value_for().
- Handle legacy TTY assignments in the existing callers and remove the
redundant compatibility wrapper.
- Reuse CharBuf for Tree-sitter query errors.
- Pass CharBuf through the statusline and statuscolumn builders,
retaining the buffer sizes provided by their callers, including title
formatting.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem: Requests to httpbingo can outlast the helper's two-second wait
and leave a nil result, causing CI failures.
Solution: Serve the basic HTTP tests locally and check the received
methods, headers and bodies. Wait for callbacks using the test harness
and run these tests without NVIM_TEST_INTEG.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
Intersection rejects ranges whose inclusive end equals the other start,
discarding a shared character and even a one-character self-intersection.
Solution:
Use strict comparisons when checking inclusive ends for non-overlap.
Cover two ranges that share a single character.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
Converting an exclusive end at column zero moves to the preceding row
but calculates the inclusive column from the original row's text.
Solution:
Read the preceding line after decrementing the row. Cover adjacent lines
with unequal lengths.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Problem: Watcher failures are reported inconsistently, and callers
cannot detect when a watcher is no longer usable.
Solution: Route startup and event failures and unexpected inotifywait
exits through an optional on_error callback. Log to nvim-watch.log by
default. Ignore disappearing child directories and exits caused by
cancellation. Let LSP log failures and notify once per message, using
INFO for missing roots and ERROR otherwise, without interrupting
registration.
AI-assisted
|
| |\
| |
| | |
test(ci): non fatal errors for [NULL] safety in vim_snprintf
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
context: vim_snprintf provides a "portable" replacement for a system
vsnprintf(). However we rely on system vsnprintf() in places. This
is quite arbitrary. I want to use system vsnprintf() only, and
reduce the machinery in string.c for typval printf only, which will
delet a lot of duplicated code.
The biggest hurdle here is not "portably" (we only support sane c
runtimes) but the following discrepancy: standard libc printf()
considers NULL args to %s to be undefined behavior strictly
while vim_snprintf() _defacto_ allows this by replacement to "[NULL]"
IMO, this should be seen as "graceful" error handling (nicer than crashing
on the user), not a valid means to have the string "[NULL]" intentionally
be shoved in the users face. more robust code should explicitly
check for NULL and replace it with a _appropriate_ fallback for
the situation, depending on what a NULL string actually means in the
context (unnamed buffer? anonymous namespace? global augroup?)
soo, this PR provides the most _gently_ incremental nudge towards
considering this case again as an error one could possible imagine.
If sanitizers are complied in, this minor incursion gets printed
into the sanitizers' log as a "report-and-continue" error with a
traceback. This doesn't annoy the end user or "stop the world"
in the test suite, but the CI will see the reported error and
fail the build, just like other non-fatal CI errors.
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem:
The LSP client excludes paths such as Git objects and installed
dependencies from file watching. However, watchdirs() still traverses
these subtrees before filtering which directories receive watchers,
paying the filesystem and Lua traversal cost for excluded content.
Solution:
Prune excluded subdirectories during the initial walk, and document
that excluding a directory also excludes its descendants.
Using the LSP client's existing **/node_modules/*/** exclusion on a
synthetic macOS tree with 100 installed packages and 4,000 files:
Before After
Directory scans 503 103
Entries inspected 4502 202
Directory watchers 103 103
The measurements use real filesystem calls and watcher handles, with
identical watcher paths for this exclusion.
Ref: #23291
AI-assisted
|
| | |
| |
| |
| |
| |
| |
| |
| | |
Allow enough time for the timer and scheduled continuation to run on
Windows.
fixes #41699
AI-assisted
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem: Nvim has many Lua APIs that start callback-driven work: timers,
jobs, libuv handles, and other event-loop tasks. Callers that need to
sequence or cancel that work have to build their own coroutine wrappers,
task bookkeeping, and cleanup rules. This makes async control flow hard
to share, test, and document.
Solution: Add `vim.async`, a structured-concurrency module vendored from
async.nvim. It provides task handles, await/pawait helpers,
sleep/timeout helpers, completion-order iteration, and semaphores on top
of Nvim's event loop.
The API follows the same broad model as Trio: async work has an owner,
tasks are awaited explicitly, and cancellation is cooperative. Include
generated vimdoc with an introductory overview and examples, a news
entry, and functional tests for the new module.
AI-assisted
|
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Problem:
`parse_ssh_config()` compares tables against a freshly allocated empty
table.
- In `parse_multiple_values()`, the guard which avoids flushing an empty
accumulator never applies. Runs of separators and trailing whitespace
push empty strings into the results, and `is_valid()` does not filter
them. `Host alpha beta ` parses as `{ 'alpha', '', 'beta' }`.
- In `parse_value()`, the condition reduces to `chr == '"' and quoted`.
`quoted` starts false and only that branch sets it, so it can never
become true: quotes are never recognised and are inserted literally,
and the unterminated-quote check is unreachable.
Solution:
Compare `#val` instead. Add a test for repeated and trailing separators.
AI-assisted
|
| |/
|
|
|
|
|
|
|
|
|
|
|
| |
Problem:
`IterArray:take()` iterates up to `self._tail`, but `_tail` is
exclusive. When no element fails the predicate, the loop reads one index
past the last element and calls the predicate with `nil`.
Solution:
Stop at `self._tail - inc`, which is the last in-range index for both
iteration directions. Add tests using a predicate which dereferences its
argument and matches every element, forward and reversed.
AI-assisted
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
`vim.version.intersect()` returns tables with `VersionRange` (method
table) as their metatable. Although the calculated bounds are correct,
the results do not expose `VersionRange` methods.
Solution:
Construct intersection tables with `range_mt`, matching
`vim.version.range()`. Add assertions covering method access and
repeated intersection.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Problem:
When disabling, the loader list is traversed forwards while entries are
removed. As a result, some entries are skipped and remain active. Thus,
disabling `vim.loader` restores `_G.loadfile`, but does not fully
restore the original package loader chain.
Solution:
Traverse `package.loaders` in reverse when removing cached loaders.
Capture the original `package.loaders` list in the test, and assert that
the list is restored properly.
|
| |
|
|
|
|
|
|
|
|
|
| |
Problem:
Coordinates-to-position conversion for the finish column in
`vim.hl.range()` checks the start column. This results in an incorrect
finish column when only one of the column coordinates is `vim.v.maxcol`.
Solution:
Check the finish column when converting the finish position. Update the
existing screen test so that a `vim.v.maxcol` finish highlights the
end-of-line marker, matching the existing `-1` behavior.
|