Problem: make_floating_popup_options only shows when opts.border is explicitly set, ignoring global winborder setting
Solution: check both opts.border and vim.o.winborder when determining whether to show title
Problem:
Previously, 'null' value in LSP responses were decoded as 'nil'.
This caused ambiguity for fields typed as '? | null' and led to
loss of explicit 'null' values, particularly in 'data' parameters.
Solution:
Decode all JSON 'null' values as 'vim.NIL' and adjust handling
where needed. This better aligns with the LSP specification,
where 'null' and absent fields are distinct, and 'null' should
not be used to represent missing values.
This also enables proper validation of response messages to
ensure that exactly one of 'result' or 'error' is present, as
required by the JSON-RPC specification.
Problem: cannot perform autocompletion
Solution: Add the 'autocomplete' option value
(Girish Palya)
This change introduces the 'autocomplete' ('ac') boolean option to
enable automatic popup menu completion during insert mode. When enabled,
Vim shows a completion menu as you type, similar to pressing |i\_CTRL-N|
manually. The items are collected from sources defined in the
'complete' option.
To ensure responsiveness, this feature uses a time-sliced strategy:
- Sources earlier in the 'complete' list are given more time.
- If a source exceeds its allocated timeout, it is interrupted.
- The next source is then started with a reduced timeout (exponentially
decayed).
- A small minimum ensures every source still gets a brief chance to
contribute.
The feature is fully compatible with other |i_CTRL-X| completion modes,
which can temporarily suspend automatic completion when triggered.
See :help 'autocomplete' and :help ins-autocompletion for more details.
To try it out, use :set ac
You should see a popup menu appear automatically with suggestions. This
works seamlessly across:
- Large files (multi-gigabyte size)
- Massive codebases (:argadd thousands of .c or .h files)
- Large dictionaries via the `k` option
- Slow or blocking LSP servers or user-defined 'completefunc'
Despite potential slowness in sources, the menu remains fast,
responsive, and useful.
Compatibility: This mode is fully compatible with existing completion
methods. You can still invoke any CTRL-X based completion (e.g.,
CTRL-X CTRL-F for filenames) at any time (CTRL-X temporarily
suspends 'autocomplete'). To specifically use i_CTRL-N, dismiss the
current popup by pressing CTRL-E first.
---
How it works
To keep completion snappy under all conditions, autocompletion uses a
decaying time-sliced algorithm:
- Starts with an initial timeout (80ms).
- If a source does not complete within the timeout, it's interrupted and
the timeout is halved for the next source.
- This continues recursively until a minimum timeout (5ms) is reached.
- All sources are given a chance, but slower ones are de-prioritized
quickly.
Most of the time, matches are computed well within the initial window.
---
Implementation details
- Completion logic is mostly triggered in `edit.c` and handled in
insexpand.c.
- Uses existing inc_compl_check_keys() mechanism, so no new polling
hooks are needed.
- The completion system already checks for user input periodically; it
now also checks for timer expiry.
---
Design notes
- The menu doesn't continuously update after it's shown to prevent
visual distraction (due to resizing) and ensure the internal list
stays synchronized with the displayed menu.
- The 'complete' option determines priority—sources listed earlier get
more time.
- The exponential time-decay mechanism prevents indefinite collection,
contributing to low CPU usage and a minimal memory footprint.
- Timeout values are intentionally not configurable—this system is
optimized to "just work" out of the box. If autocompletion feels slow,
it typically indicates a deeper performance bottleneck (e.g., a slow
custom function not using `complete_check()`) rather than a
configuration issue.
---
Performance
Based on testing, the total roundtrip time for completion is generally
under 200ms. For common usage, it often responds in under 50ms on an
average laptop, which falls within the "feels instantaneous" category
(sub-100ms) for perceived user experience.
| Upper Bound (ms) | Perceived UX
|----------------- |-------------
| <100 ms | Excellent; instantaneous
| <200 ms | Good; snappy
| >300 ms | Noticeable lag
| >500 ms | Sluggish/Broken
---
Why this belongs in core:
- Minimal and focused implementation, tightly integrated with existing
Insert-mode completion logic.
- Zero reliance on autocommands and external scripting.
- Makes full use of Vim’s highly composable 'complete' infrastructure
while avoiding the complexity of plugin-based solutions.
- Gives users C native autocompletion with excellent responsiveness and
no configuration overhead.
- Adds a key UX functionality in a simple, performant, and Vim-like way.
closes: vim/vim#17812af9a7a04f1
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem:
When setting extmark for a codelens after it's asynchronously resolved,
the line may have been removed, raising "invalid 'line': out of range"
error. This is a regression from #34888.
Solution:
Re-introduce the line count check.
**Problem:** For unchanged document diagnostic reports, the `resultId`
is ignored completely, even though it should still be saved for the
request (in fact, the spec marks it as mandatory for unchanged reports,
so it should be extra important).
**Solution:** Always store the `resultId`.
Problem:
After 28b7c2d (found with bisect) the hover preview window does not
close when :edit'ing another file, even when you move the cursor.
Solution:
Change the BufLeave to target the original buffer, not the preview
buffer.
Problem:
- util._refresh() is only used by `inlay_hint.lua` and `document_color.lua`, and
both have their own wrapper functions;
- util._refresh() provides unified parameters, but this layer of wrapping is
almost meaningless because
- document color does not need the range parameter;
- inlay hint requires a range parameter, but it is not complicated
Therefore, it can be considered redundant.
ref https://github.com/neovim/neovim/pull/32887#discussion_r1996413602
Solution:
Remove it.
Problem:
Calling lsp.codelens.refresh() causes transient visual flicker because
codelens virtual texts are briefly replaced with "Unresolved lens ..."
before being resolved and redrawn. Since refresh() is triggered
frequently (e.g., on CursorHold or InsertLeave), this leads to redundant
and noisy virtual text updates, even when the final text hasn't changed.
Solution:
Do not update virtual text for a line if some lenses for that line are
not resolved yet.
A trade-off is that the user may temporarily see outdated virtual text.
However, that's preferable to spamming updates on every refresh.
This commit allows users to jump to the location specified in a
diagnostic's `relatedInformation`, using `gf` from within the
`open_float` window. The cursor need only be on line that displays the
related info.
Problem: Health check floating window gets closed when pressing 'gO' to show TOC because LSP floating preview system auto-closes on BufEnter events triggered by :lopen.
Solution: Temporarily disable BufEnter event for the current window during TOC operations and adjust window layout to prevent overlap.
Problem:
Closes#31453
Solution:
Introduce `vim.lsp.Capability`, which may serve as the base class for
all LSP features that require caching data. it
- was created if there is at least one client that supports the specific method;
- was destroyed if all clients that support the method were detached.
- Apply the refactor for `folding_range.lua` and `semantic_tokens.lua`.
- Show active features in :checkhealth.
Future:
I found that these features that are expected to be refactored by
`vim.lsp.Capability` have one characteristic in common: they all send
LSP requests once the document is modified. The following code is
different, but they are all for this purpose.
- semantic tokens:
fb8dba413f/runtime/lua/vim/lsp/semantic_tokens.lua (L192-L198)
- inlay hints, folding ranges, document color
fb8dba413f/runtime/lua/vim/lsp/inlay_hint.lua (L250-L266)
I think I can sum up this characteristic as the need to keep certain
data synchronized with the latest version computed by the server.
I believe we can handle this at the `vim.lsp.Capability` level, and
I think it will be very useful.
Therefore, my next step is to implement LSP request sending and data
synchronization on `vim.lsp.Capability`, rather than limiting it to the
current create/destroy data approach.
In https://github.com/neovim/neovim/pull/34092 we changed the
healthcheck to display root markers as a concatenated list if the first
item in root_markers is a string (not a table). However, this does not
solve the general case, because root_markers can contain a string as the
first element, but a table as the 2nd element.
Because root_markers has a more complex structure we should always just
display it using vim.inspect, rather than adding a special case for when
all items are a string.
**Problem:** When enabling document_color multiple times for the same
buffer (or when toggling it on and off), duplicate autocmds are created
since the previous ones are not cleared.
**Solution:** Clear the appropriate buffer-local autocmds when
enabling/disabling document color functionality.
Problem:
In LSP configs, the function form of `cmd()` cannot easily get the
resolved root dir (workspace). One of the main use-cases of a dynamic
`cmd()` is to be able to start a new server whose binary may be located
*in the workspace* ([example](https://github.com/neovim/nvim-lspconfig/pull/3912)).
Compare `reuse_client()`, which also receives the resolved config.
Solution:
Pass the resolved config to `cmd()`.
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>