Problem: diff: using diff anchors with hidden buffers fails silently
Solution: Give specific error message for diff anchors when using hidden
buffers (Yee Cheng Chin).
Diff anchors currently will fail to parse if a buffer used for diff'ing
is hidden. Previously it would just fail as the code assumes it would
not happen normally, but this is actually possible to do if `closeoff`
and `hideoff` are not set in diffopt. Git's default diff tool "vimdiff3"
also takes advantage of this.
This fix this properly would require the `{address}` parser to be
smarter about whether a particular address relies on window position or
not (e.g. the `'.` address requires an active window, but `'a` or `1234`
do not). Since hidden diff buffers seem relatively niche, just provide a
better error message / documentation for now. This could be improved
later if there's a demand for it.
related: vim/vim#17615closes: vim/vim#17904cad3b2421d
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
Platform specific options are cringe and should either be fixed or
deleted.
In this case, the platform difference can trivially be implemented
using a conditional default value. If the user overrides the value,
then it is because the user wants that value, regardless of the
corporation who manufactured the OS that the user is running.
Possible alternative: delete the option by making it always on.
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>
This workarounds a bug likely in nvim__get_runtime, and fixes#35124
Though I'd argue it is more correct anyway as the point of
vim.SUBMODULE lazy loading is "only pay for what you use". If no one
has require'vim.diagnostic' yet in LSP or otherwise, there cannot
be any diagostics available and loading the lua module is wasteful.
Problem: cannot easily trigger wildcard expansion
Solution: Introduce wildtrigger() function
(Girish Palya)
This PR introduces a new `wildtrigger()` function.
See `:h wildtrigger()`
`wildtrigger()` behaves like pressing the `wildchar,` but provides a
more refined and controlled completion experience:
- Suppresses beeps when no matches are found.
- Avoids displaying irrelevant completions (like full command lists)
when the prefix is insufficient or doesn't match.
- Skips completion if the typeahead buffer has pending input or if a
wildmenu is already active.
- Does not print "..." before completion.
This is an improvement on the `feedkeys()` based autocompletion script
given in vim/vim#16759.
closes: vim/vim#17806b486ed8266
While at it, also make Ctrl-Z trigger search completion.
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: not possible to anchor specific lines in diff mode
Solution: Add support for the anchoring lines in diff mode using the
'diffanchor' option (Yee Cheng Chin).
Adds support for anchoring specific lines to each other while viewing a
diff. While lines are anchored, they are guaranteed to be aligned to
each other in a diff view, allowing the user to control and inform the
diff algorithm what the desired alignment is. Internally, this is done
by splitting up the buffer at each anchor and run the diff algorithm on
each split section separately, and then merge the results back for a
logically consistent diff result.
To do this, add a new "diffanchors" option that takes a list of
`{address}`, and a new "diffopt" option value "anchor". Each address
specified will be an anchor, and the user can choose to use any type of
address, including marks, line numbers, or pattern search. Anchors are
sorted by line number in each file, and it's possible to have multiple
anchors on the same line (this is useful when doing multi-buffer diff).
Update documentation to provide examples.
This is similar to Git diff's `--anchored` flag. Other diff tools like
Meld/Araxis Merge also have similar features (called "synchronization
points" or "synchronization links"). We are not using Git/Xdiff's
`--anchored` implementation here because it has a very limited API
(it requires usage of the Patience algorithm, and can only anchor
unique lines that are the same across both files).
Because the user could anchor anywhere, diff anchors could result in
adjacent diff blocks (one block is directly touching another without a
gap), if there is a change right above the anchor point. We don't want
to merge these diff blocks because we want to line up the change at the
anchor. Adjacent diff blocks were first allowed when linematch was
added, but the existing code had a lot of branched paths where
line-matched diff blocks were handled differently. As a part of this
change, refactor them to have a more unified code path that is
generalized enough to handle adjacent diff blocks correctly and without
needing to carve in exceptions all over the place.
closes: vim/vim#176150d9160e11c
Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
Problem:
Cannot use `nvim_open_term()` to pipe terminal scrollback > 100000
Solution:
Increase scrollback limit to 1000000
If there's no technical consequences of doing this, can be set even
higher in the future.
Problem: The maximum search count uses a hard-coded value of 99
(Andres Monge, Joschua Kesper)
Solution: Make it configurable using the 'maxsearchcount' option.
related: vim/vim#8855fixes: vim/vim#17527closes: vim/vim#17695b7b7fa04bf
Co-authored-by: Christian Brabandt <cb@256bit.org>
Problem: winborder option only supported predefined styles and lacked support for custom border characters.
Solution: implement parsing for comma-separated list format that allows specifying 8 individual border characters (topleft, top, topright, right, botright, bottom, botleft, left).
Problem: completion: search completion match may differ in case
(techntools)
Solution: add "exacttext" to 'wildoptions' value (Girish Palya)
This flag does the following:
exacttext
When this flag is present, search pattern completion
(e.g., in |/|, |?|, |:s|, |:g|, |:v|, and |:vim|)
shows exact buffer text as menu items, without
preserving regex artifacts like position
anchors (e.g., |/\<|). This provides more intuitive
menu items that match the actual buffer text. However,
searches may be less accurate since the pattern is not
preserved exactly.
By default, Vim preserves the typed pattern (with
anchors) and appends the matched word. This preserves
search correctness, especially when using regular
expressions or with 'smartcase' enabled. However, the
case of the appended matched word may not exactly
match the case of the word in the buffer.
fixes: vim/vim#17654closes: vim/vim#1766793c2d5bf7f
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: completion: can only complete from keyword characters
Solution: remove this restriction, allow completion functions when
called from i_CTRL-N/i_CTRL-P to be triggered from non-keyword
characters (Girish Palya)
Previously, functions specified in the `'complete'` option were
restricted to starting completion only from keyword characters (as
introduced in PR 17065). This change removes that restriction.
With this change, user-defined functions (e.g., `omnifunc`, `userfunc`)
used in `'complete'` can now initiate completion even when triggered
from non-keyword characters. This makes it easier to reuse existing
functions alongside other sources without having to consider whether the
cursor is on a keyword or non-keyword character, or worry about where
the replacement should begin (i.e., the `findstart=1` return value).
The logic for both the “collection” and “filtering” phases now fully
respects each source’s specified start column. This also extends to
fuzzy matching, making completions more predictable.
Internally, this builds on previously merged infrastructure that tracks
per-source metadata. This PR focuses on applying that metadata to
compute the leader string and insertion text appropriately for each
match.
Also, a memory corruption has been fixed in prepare_cpt_compl_funcs().
closes: vim/vim#17651ba11e78f1d
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: 'wildchar' does not work in search contexts
Solution: implement search completion when 'wildchar' is typed
(Girish Palya).
This change enhances Vim's command-line completion by extending
'wildmode' behavior to search pattern contexts, including:
- '/' and '?' search commands
- ':s', ':g', ':v', and ':vim' commands
Completions preserve the exact regex pattern typed by the user,
appending the completed word directly to the original input. This
ensures that all regex elements — such as '<', '^', grouping brackets
'()', wildcards '\*', '.', and other special characters — remain intact
and in their original positions.
---
**Use Case**
While searching (using `/` or `?`) for lines containing a pattern like
`"foobar"`, you can now type a partial pattern (e.g., `/f`) followed by
a trigger key (`wildchar`) to open a **popup completion menu** showing
all matching words.
This offers two key benefits:
1. **Precision**: Select the exact word you're looking for without
typing it fully.
2. **Memory aid**: When you can’t recall a full function or variable
name, typing a few letters helps you visually identify and complete the
correct symbol.
---
**What’s New**
Completion is now supported in the following contexts:
- `/` and `?` search commands
- `:s`, `:g`, `:v`, and `:vimgrep` ex-commands
---
**Design Notes**
- While `'wildchar'` (usually `<Tab>`) triggers completion, you'll have
to use `<CTRL-V><Tab>` or "\t" to search for a literal tab.
- **Responsiveness**: Search remains responsive because it checks for
user input frequently.
---
**Try It Out**
Basic setup using the default `<Tab>` as the completion trigger:
```vim
set wim=noselect,full wop=pum wmnu
```
Now type:
```
/foo<Tab>
```
This opens a completion popup for matches containing "foo".
For matches beginning with "foo" type `/\<foo<Tab>`.
---
**Optional: Autocompletion**
For automatic popup menu completion as you type in search or `:`
commands, include this in your `.vimrc`:
```vim
vim9script
set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu
autocmd CmdlineChanged [:/?] CmdComplete()
def CmdComplete()
var [cmdline, curpos, cmdmode] = [getcmdline(), getcmdpos(),
expand('<afile>') == ':']
var trigger_char = '\%(\w\|[*/:.-]\)$'
var not_trigger_char = '^\%(\d\|,\|+\|-\)\+$' # Exclude numeric range
if getchar(1, {number: true}) == 0 # Typehead is empty, no more
pasted input
&& !wildmenumode() && curpos == cmdline->len() + 1
&& (!cmdmode || (cmdline =~ trigger_char && cmdline !~
not_trigger_char))
SkipCmdlineChanged()
feedkeys("\<C-@>", "t")
timer_start(0, (_) => getcmdline()->substitute('\%x00', '',
'ge')->setcmdline()) # Remove <C-@>
endif
enddef
def SkipCmdlineChanged(key = ''): string
set ei+=CmdlineChanged
timer_start(0, (_) => execute('set ei-=CmdlineChanged'))
return key == '' ? '' : ((wildmenumode() ? "\<C-E>" : '') .. key)
enddef
**Optional: Preserve history recall behavior**
cnoremap <expr> <Up> SkipCmdlineChanged("\<Up>")
cnoremap <expr> <Down> SkipCmdlineChanged("\<Down>")
**Optional: Customize popup height**
autocmd CmdlineEnter : set bo+=error | exec $'set ph={max([10,
winheight(0) - 4])}'
autocmd CmdlineEnter [/?] set bo+=error | set ph=8
autocmd CmdlineLeave [:/?] set bo-=error ph&
```
closes: vim/vim#175706b49fba8c8
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: File paths change from symlink to target path after :cd command
when editing files through symbolic links
Solution: Add "~" flag to 'cpoptions' to control symlink resolution.
When not included (default), symlinks are resolved maintaining
backward compatibility. When included, symlinks are preserved
providing the improved behavior. (glepnir)
related: neovim/neovim#15695closes: vim/vim#176284ade668fb6
fix(exrc): lua exrc files know their location
Problem:
'exrc' files are inherently bound to their location / workspace and
therefore require to "know" their location on the filesystem. However,
currently using `debug.getinfo(1, 'S')` returns `"<nvim>"`.
Solution:
Include the filepath as chunkname in `loadstring()` and `nlua_exec()`.
Before this commit, I had trouble finding information about configuring
the insert mode completion. In particular, it was not clear that the
'wildopt' config that I already had in my vimrc does not apply here.
Also, `insert.txt` barely mentioned 'completeopt' except when
describing popups (I was more interested in bash-like behavior
where the unique prefix of all completions is completed first).
I'm hoping these edits will make the relevant docs easier to find.
closes: vim/vim#17515053aee01f7
Co-authored-by: Ilya Grigoriev <ilyagr@users.noreply.github.com>
Problem: using f-flag in 'complete' conflicts with Neovims filename
completion (glepnir, after v9.1.1301).
Solution: use upper-case "F" flag for completion functions
(Girish Palya).
fixes: vim/vim#17347closes: vim/vim#1737814f6da5ba8
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: completion: not possible to limit number of matches
Solution: allow to limit the matches for 'complete' sources by using the
"{flag}^{limit}" notation (Girish Palya)
This change extends the 'complete' option to support limiting the
number of matches returned from individual completion sources.
**Rationale:** In large files, certain sources (such as the current
buffer) can generate an overwhelming number of matches, which may cause
more relevant results from other sources (e.g., LSP or tags) to be
pushed out of view. By specifying per-source match limits, the
completion menu remains balanced and diverse, improving visibility and
relevance of suggestions.
A caret (`^`) followed by a number can be appended to a source flag to
specify the maximum number of matches for that source. For example:
```
:set complete=.^9,w,u,t^5
```
In this configuration:
- The current buffer (`.`) will return up to 9 matches.
- The tag completion (`t`) will return up to 5 matches.
- Other sources (`w`, `u`) are not limited.
This feature is fully backward-compatible and does not affect behavior
when the `^count` suffix is not used.
The caret (`^`) was chosen as the delimiter because it is least likely
to appear in file names.
closes: vim/vim#170870ac1eb3555
Cherry-pick test_options.vim change from patch 9.1.1325.
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem: completion: cannot configure completion functions with
'complete'
Solution: add support for setting completion functions using the f and o
flag for 'complete' (Girish Palya)
This change adds two new values to the `'complete'` (`'cpt'`) option:
- `f` – invokes the function specified by the `'completefunc'` option
- `f{func}` – invokes a specific function `{func}` (can be a string or `Funcref`)
These new flags extend keyword completion behavior (e.g., via `<C-N>` /
`<C-P>`) by allowing function-based sources to participate in standard keyword
completion.
**Key behaviors:**
- Multiple `f{func}` values can be specified, and all will be called in order.
- Functions should follow the interface defined in `:help complete-functions`.
- When using `f{func}`, escaping is required for spaces (with `\`) and commas
(with `\\`) in `Funcref` names.
- If a function sets `'refresh'` to `'always'`, it will be re-invoked on every
change to the input text. Otherwise, Vim will attempt to reuse and filter
existing matches as the input changes, which matches the default behavior of
other completion sources.
- Matches are inserted at the keyword boundary for consistency with other completion methods.
- If finding matches is time-consuming, `complete_check()` can be used to
maintain responsiveness.
- Completion matches are gathered in the sequence defined by the `'cpt'`
option, preserving source priority.
This feature increases flexibility of standard completion mechanism and may
reduce the need for external completion plugins for many users.
**Examples:**
Complete matches from [LSP](https://github.com/yegappan/lsp) client. Notice the use of `refresh: always` and `function()`.
```vim
set cpt+=ffunction("g:LspCompletor"\\,\ [5]). # maxitems = 5
def! g:LspCompletor(maxitems: number, findstart: number, base: string): any
if findstart == 1
return g:LspOmniFunc(findstart, base)
endif
return {words: g:LspOmniFunc(findstart, base)->slice(0, maxitems), refresh: 'always'}
enddef
autocmd VimEnter * g:LspOptionsSet({ autoComplete: false, omniComplete: true })
```
Complete matches from `:iabbrev`.
```vim
set cpt+=fAbbrevCompletor
def! g:AbbrevCompletor(findstart: number, base: string): any
if findstart > 0
var prefix = getline('.')->strpart(0, col('.') - 1)->matchstr('\S\+$')
if prefix->empty()
return -2
endif
return col('.') - prefix->len() - 1
endif
var lines = execute('ia', 'silent!')
if lines =~? gettext('No abbreviation found')
return v:none # Suppresses warning message
endif
var items = []
for line in lines->split("\n")
var m = line->matchlist('\v^i\s+\zs(\S+)\s+(.*)$')
if m->len() > 2 && m[1]->stridx(base) == 0
items->add({ word: m[1], info: m[2], dup: 1 })
endif
endfor
return items->empty() ? v:none :
items->sort((v1, v2) => v1.word < v2.word ? -1 : v1.word ==# v2.word ? 0 : 1)
enddef
```
**Auto-completion:**
Vim's standard completion frequently checks for user input while searching for
new matches. It is responsive irrespective of file size. This makes it
well-suited for smooth auto-completion. You can try with above examples:
```vim
set cot=menuone,popup,noselect inf
autocmd TextChangedI * InsComplete()
def InsComplete()
if getcharstr(1) == '' && getline('.')->strpart(0, col('.') - 1) =~ '\k$'
SkipTextChangedIEvent()
feedkeys("\<c-n>", "n")
endif
enddef
inoremap <silent> <c-e> <c-r>=<SID>SkipTextChangedIEvent()<cr><c-e>
def SkipTextChangedIEvent(): string
# Suppress next event caused by <c-e> (or <c-n> when no matches found)
set eventignore+=TextChangedI
timer_start(1, (_) => {
set eventignore-=TextChangedI
})
return ''
enddef
```
closes: vim/vim#17065cbe53191d0
Temporarily remove bufname completion with #if 0 to make merging easier.
Co-authored-by: Girish Palya <girishji@gmail.com>
Co-authored-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: glepnir <glephunter@gmail.com>
Problem: It is difficult to ignore all but some events.
Solution: Add support for a "-" prefix syntax in '(win)eventignore' that
subtracts an event from the ignored set if present
(Luuk van Baal).
8cc6d8b187
Problem: not easily possible to complete from register content
Solution: add register-completion submode using i_CTRL-X_CTRL-R
(glepnir)
closes: vim/vim#173540546068aae
Problem:
No way for a user to limit 'exrc' search in parent directories (compare
editorconfig.root).
Solution:
A configuration file can unset 'exrc', disabling the search for its
parent directories.
Problem: The 'grepformat' option is global option, but it would be
useful to have it buffer-local, similar to 'errorformat' and
other quickfix related options (Dani Dickstein)
Solution: Add the necessary code to support global-local 'grepformat',
allowing different buffers to parse different grep output
formats (glepnir)
fixes: vim/vim#17316closes: vim/vim#173157b9eb6389d
Co-authored-by: glepnir <glephunter@gmail.com>
feat(exrc): search exrc in parent directories
Problem:
`.nvim.lua` is only loaded from current directory, which is not flexible
when working from a subfolder of the project.
Solution:
Also search parent directories for configuration file.
Problem: Currently, 'smartcase' is respected when completing keywords
using <C-N>, <C-P>, <C-X><C-N>, and <C-X><C-P>. However, when
a user continues typing and the completion menu is filtered
using cached matches, 'smartcase' is not applied. This leads
to poor-quality or irrelevant completion suggestions, as shown
in the example below.
Solution: When filtering cached completion items after typing additional
characters, apply case-sensitive comparison if 'smartcase' is
enabled and the typed pattern includes uppercase characters.
This ensures consistent and expected completion behavior.
(Girish Palya)
closes: vim/vim#17271dc314053e1
Co-authored-by: Girish Palya <girishji@gmail.com>
clarify complete_match() documentation to better explain its backward
search behavior, argument handling, and return value format and add an
example of isexpand
closes: https://github.com/vim/vim/pull/17212ffc89e47d0
* feat(shada): don't store jumplist if '0 in 'shada'
* fix(shada): don't store search and sub patterns if /0 in 'shada'
* fix(shada): don't store empty replacement string
* fix(shada): don't add '0' mark if f0 in 'shada'