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:
Developing/troubleshooting plugins has friction because "restarting"
Nvim requires quitting and manually starting again. #32484
Solution:
- Implement a `:restart` command which emits `restart` UI event.
- Handle the `restart` UI event in the builtin TUI client: stop the
`nvim --embed` server, start a new one, and attach to it.
Problem: Unable to emit a message with arbitrary kind.
Solution: Add a "kind" opts field to nvim_echo().
Use it to set the "list_cmd" kind for vim.show_pos().
Problem: "99 searchcount ought to be enough for anyone."
Solution: Increase `SEARCH_STAT_DEF_MAX_COUNT` to 999, which I'm sure
will suffice for the next twenty years.
Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
Problem: Consecutive "msg_show" events stemming from an `:echon`
command are supposed to be appended without a newline, this
information is not encoded in the "msg_show" event.
Solution: Add an "append" parameter to the "msg_show" event that is set
to true to indicate the message should not start on a new line.
Considered alternative: Emit a newline for the common case instead at the
start of a new message. That way UIs can more closely follow the logic
as it is implemented for the message grid currently. This would be a
breaking change. The "append" parameter seems OK.
|vim.glob.to_lpeg()| uses a new LPeg-based implementation (Peglob) that
provides ~50% speedup for complex patterns. The implementation restores
support for nested braces and follows LSP 3.17 specification with
additional constraints for improved correctness and resistance to
backtracking edge cases.
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>
Problem:
No way to check if a LSP config is enabled without causing it to
resolve. E.g. `vim.lsp.config['…'] ~= nil` will resolve the config,
which could be an unwanted and somewhat expensive side-effect.
Solution:
Introduce `vim.lsp.is_enabled()`.
Problem: We have an unmaintained Vimscript parser and cmdline
highlighting mechanism, with which it is hard to leverage the
treesitter highlighter. Long messages result in a hit-enter-prompt.
Solution: Implement a vim.ui_attach() UI, that replaces the message
grid (orphaning some 3000+ LOC core C code). Introduce an experimental
vim._extui module, because removing the message grid at the same time is
too risky. The new UI leverages the bundled treesitter highlighter and
parser for Vimscript, as well as the matchparen plugin, to highlight the
cmdline. Messages are truncated in the cmdline area, or placed in a
floating message box in the bottom right corner. Special ("list_cmd")
messages and the message history are shown in a, "more prompt" (now a
fully interactive regular window). Various default UI elements ('showcmd',
'ruler') are also placed in the cmdline area, as virtual text.
`require('vim._extui').enable({})` enables the experimental UI.
`{ msg.pos = 'box' }` or `:set cmdheight=0` enables the message
box variant.
Followup:
- Come to a consensus for how best to represent messages (by default).
- Start removing message grid when this is deemed a successful replacement.
When that is finished, make this new UI the default and update a lot of tests.
NEW BUILD SYSTEM!
This is a MVP implementation which supports building the "nvim" binary,
including cross-compilation for some targets.
As an example, you can build a aarch64-macos binary from
an x86-64-linux-gnu host, or vice versa
Add CI target for build.zig currently for functionaltests on linux
x86_64 only
Follow up items:
- praxis for version and dependency bumping
- windows 💀
- full integration of libintl and gettext (or a desicion not to)
- update help and API metadata files
- installation into a $PREFIX
- more tests and linters
Problem:
Directories that are "trusted" by `vim.secure.read()`, are not detectable later
(they will prompt again). https://github.com/neovim/neovim/discussions/33587#discussioncomment-12925887
Solution:
`vim.secure.read()` returns `true` if the user trusts a directory.
Also fix other bugs:
- If `f:read('*a')` returns `nil`, we treat that as a successful read of
the file, and hash it. `f:read` returns `nil` for directories, but
it's also documented as returning `nil` "if it cannot read data with the
specified format". I reworked the implementation so we explicitly
treat directories differently. Rather than hashing `nil` to put in the
trust database, we now put "directory" in there explicitly*.
- `vim.secure.trust` (used by `:trust`) didn't actually work for
directories, as it would blindly read the contents of a netrw buffer
and hash it. Now it uses the same codepath as `vim.secure.read`, and
as a result, works correctly for directories.
Problem:
`FileType` event is fired before checkhealth report is finished, so
user can't override report settings or contents.
https://github.com/neovim/neovim/pull/33172#issuecomment-2833513916
Solution:
- Trigger FileType event later.
- Document how to remove emojis.
Problem:
Users of the Roslyn (C#) LSP have encountered significant delays when
retrieving pull diagnostics in large documents while using Neovim. For
instance, diagnostics in a 2000-line .cs file can take over 20 seconds
to display after edits in Neovim, whereas in VS Code, diagnostics for
the same file are displayed almost instantly.
As [mparq noted](https://github.com/seblj/roslyn.nvim/issues/93#issuecomment-2508940330)
in https://github.com/seblj/roslyn.nvim/issues/93, VS Code leverages
additional parameters specified in the [LSP documentation for
textDocument/diagnostic](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentDiagnosticParams),
specifically:
- previousResultId
- identifier
Solution:
When requesting diagnostics, Neovim should include the
`previousResultId` and `identifier` parameters as part of the request.
These parameters enable the server to utilize caching and return
incremental results.
Support for maintaining state is already present in the
[textDocument/semanticTokens implementation](8f84167c30/runtime/lua/vim/lsp/semantic_tokens.lua (L289)).
A similar mechanism can be implemented in `textDocument/diagnostic` handler.
Problem: cannot get information about command line completion
Solution: add CmdlineLeavePre autocommand and cmdcomplete_info() Vim
script function (Girish Palya)
This commit introduces two features to improve introspection and control
over command-line completion in Vim:
- Add CmdlineLeavePre autocmd event:
A new event triggered just before leaving the command line and before
CmdlineLeave. It allows capturing completion-related state that is
otherwise cleared by the time CmdlineLeave fires.
- Add cmdcomplete_info() Vim script function:
Returns a Dictionary with details about the current command-line
completion state.
These are similar in spirit to InsertLeavePre and complete_info(),
but focused on command-line mode.
**Use case:**
In [[PR vim/vim#16759](https://github.com/vim/vim/pull/16759)], two examples
demonstrate command-line completion: one for live grep, and another for
fuzzy file finding. However, both examples share two key limitations:
1. **Broken history recall (`<Up>`)**
When selecting a completion item via `<Tab>` or `<C-n>`, the original
pattern used for searching (e.g., a regex or fuzzy string) is
overwritten in the command-line history. This makes it impossible to
recall the original query later.
This is especially problematic for interactive grep workflows, where
it’s useful to recall a previous search and simply select a different
match from the menu.
2. **Lack of default selection on `<CR>`**
Often, it’s helpful to allow `<CR>` (Enter) to accept the first match
in the completion list, even when no item is explicitly selected. This
behavior is particularly useful in fuzzy file finding.
----
Below are the updated examples incorporating these improvements:
**Live grep, fuzzy find file, fuzzy find buffer:**
```vim
command! -nargs=+ -complete=customlist,GrepComplete Grep VisitFile()
def GrepComplete(arglead: string, cmdline: string, cursorpos: number):
list<any>
return arglead->len() > 1 ? systemlist($'grep -REIHns "{arglead}"' ..
' --exclude-dir=.git --exclude=".*" --exclude="tags" --exclude="*.swp"') : []
enddef
def VisitFile()
if (selected_match != null_string)
var qfitem = getqflist({lines: [selected_match]}).items[0]
if qfitem->has_key('bufnr') && qfitem.lnum > 0
var pos = qfitem.vcol > 0 ? 'setcharpos' : 'setpos'
exec $':b +call\ {pos}(".",\ [0,\ {qfitem.lnum},\ {qfitem.col},\ 0]) {qfitem.bufnr}'
setbufvar(qfitem.bufnr, '&buflisted', 1)
endif
endif
enddef
nnoremap <leader>g :Grep<space>
nnoremap <leader>G :Grep <c-r>=expand("<cword>")<cr>
command! -nargs=* -complete=customlist,FuzzyFind Find
execute(selected_match != '' ? $'edit {selected_match}' : '')
var allfiles: list<string>
autocmd CmdlineEnter : allfiles = null_list
def FuzzyFind(arglead: string, _: string, _: number): list<string>
if allfiles == null_list
allfiles = systemlist($'find {get(g:, "fzfind_root", ".")} \! \(
-path "*/.git" -prune -o -name "*.swp" \) -type f -follow')
endif
return arglead == '' ? allfiles : allfiles->matchfuzzy(arglead)
enddef
nnoremap <leader><space> :<c-r>=execute('let
fzfind_root="."')\|''<cr>Find<space><c-@>
nnoremap <leader>fv :<c-r>=execute('let
fzfind_root="$HOME/.vim"')\|''<cr>Find<space><c-@>
nnoremap <leader>fV :<c-r>=execute('let
fzfind_root="$VIMRUNTIME"')\|''<cr>Find<space><c-@>
command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b '
.. selected_match->matchstr('\d\+'))
def FuzzyBuffer(arglead: string, _: string, _: number): list<string>
var bufs = execute('buffers', 'silent!')->split("\n")
var altbuf = bufs->indexof((_, v) => v =~ '^\s*\d\+\s\+#')
if altbuf != -1
[bufs[0], bufs[altbuf]] = [bufs[altbuf], bufs[0]]
endif
return arglead == '' ? bufs : bufs->matchfuzzy(arglead)
enddef
nnoremap <leader><bs> :Buffer <c-@>
var selected_match = null_string
autocmd CmdlineLeavePre : SelectItem()
def SelectItem()
selected_match = ''
if getcmdline() =~ '^\s*\%(Grep\|Find\|Buffer\)\s'
var info = cmdcomplete_info()
if info != {} && info.pum_visible && !info.matches->empty()
selected_match = info.selected != -1 ? info.matches[info.selected] : info.matches[0]
setcmdline(info.cmdline_orig). # Preserve search pattern in history
endif
endif
enddef
```
**Auto-completion snippet:**
```vim
set wim=noselect:lastused,full wop=pum wcm=<C-@> wmnu
autocmd CmdlineChanged : CmdComplete()
def CmdComplete()
var [cmdline, curpos] = [getcmdline(), getcmdpos()]
if getchar(1, {number: true}) == 0 # Typehead is empty (no more pasted input)
&& !pumvisible() && curpos == cmdline->len() + 1
&& cmdline =~ '\%(\w\|[*/:.-]\)$' && cmdline !~ '^\d\+$' # Reduce noise
feedkeys("\<C-@>", "ti")
SkipCmdlineChanged() # Suppress redundant completion attempts
# Remove <C-@> that get inserted when no items are available
timer_start(0, (_) => getcmdline()->substitute('\%x00', '', 'g')->setcmdline())
endif
enddef
cnoremap <expr> <up> SkipCmdlineChanged("\<up>")
cnoremap <expr> <down> SkipCmdlineChanged("\<down>")
autocmd CmdlineEnter : set bo+=error
autocmd CmdlineLeave : set bo-=error
def SkipCmdlineChanged(key = ''): string
set ei+=CmdlineChanged
timer_start(0, (_) => execute('set ei-=CmdlineChanged'))
return key != '' ? ((pumvisible() ? "\<c-e>" : '') .. key) : ''
enddef
```
These customizable snippets can serve as *lightweight* and *native*
alternatives to picker plugins like **FZF** or **Telescope** for common,
everyday workflows. Also, live grep snippet can replace **cscope**
without the overhead of building its database.
closes: vim/vim#1711592f68e26ec
Co-authored-by: Girish Palya <girishji@gmail.com>
Useful to e.g. limit the height to the window height, avoiding unnecessary
work. Or to find out how many buffer lines beyond "start_row" take up a
certain number of logical lines (returned in "end_row" and "end_vcol").
Problem: During insert-mode completion, the most relevant match is often
the one closest to the cursor—frequently just above the current line.
However, both `<C-N>` and `<C-P>` tend to rank candidates from the
current buffer that appear above the cursor near the bottom of the
completion menu, rather than near the top. This ordering can feel
unintuitive, especially when `noselect` is active, as it doesn't
prioritize the most contextually relevant suggestions.
Solution: This change introduces a new sub-option value "nearest" for the
'completeopt' setting. When enabled, matches from the current buffer
are prioritized based on their proximity to the cursor position,
improving the relevance of suggestions during completion
(Girish Palya).
Key Details:
- Option: "nearest" added to 'completeopt'
- Applies to: Matches from the current buffer only
- Effect: Sorts completion candidates by their distance from the cursor
- Interaction with other options:
- Has no effect if the `fuzzy` option is also present
This feature is helpful especially when working within large buffers where
multiple similar matches may exist at different locations.
You can test this feature with auto-completion using the snippet below. Try it
in a large file like `vim/src/insexpand.c`, where you'll encounter many
potential matches. You'll notice that the popup menu now typically surfaces the
most relevant matches—those closest to the cursor—at the top. Sorting by
spatial proximity (i.e., contextual relevance) often produces more useful
matches than sorting purely by lexical distance ("fuzzy").
Another way to sort matches is by recency, using an LRU (Least Recently Used)
cache—essentially ranking candidates based on how recently they were used.
However, this is often overkill in practice, as spatial proximity (as provided
by the "nearest" option) is usually sufficient to surface the most relevant
matches.
```vim
set cot=menuone,popup,noselect,nearest inf
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
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>
inoremap <silent><expr> <tab> pumvisible() ? "\<c-n>" : "\<tab>"
inoremap <silent><expr> <s-tab> pumvisible() ? "\<c-p>" : "\<s-tab>"
```
closes: vim/vim#17076b156588eb7
Co-authored-by: Girish Palya <girishji@gmail.com>
Problem:
`vim.version.range('>=0.10'):has('0.12.0-dev')` returns false, which is
wrong per semver.
Solution:
`vim.VersionRange:has()` shouldn't have special handling for prereleases
(Why would we need it when `__eq`, `__lt`, `__le` already handle
prereleases?).
Closes#33316
Problem: No type information for `vim.uv`.
Solution: Vendor https://github.com/LuaCATS/luv (which is what
luals bundles). This will allow other tooling to work out-of-the-box and
make these files available to users and plugins without the need for
`lazydev.nvim` etc.
Problem:
- Help tags provide a good way to navigate the Vim documentation, but
many help documents don't use them effectively. I think one of the
reasons is that help writers have to look up help tags manually with
`:help` command, which is not very convenient.
- 'iskeyword' is only set for help buffers opened by `:help` command.
That means if I'm editing a help file, I cannot jump to tag in same
file using `Ctrl-]` unless I manually set it, which is annoying.
Solution:
- Add omni completion for Vim help tags.
- Set 'iskeyword' for `ft-help`
closes: vim/vim#170730b540c6f38
Co-authored-by: Christian Brabandt <cb@256bit.org>
Problem:
As checkhealth grows, it is increasingly hard to quickly glance through
the information.
Solution:
Show a summary of ok, warn, and error outputs per section.
Problem:
First rtp directory is unpredictable and not in line with XDG
base spec.
Solution:
Use stdpath('data')/spell as directory if 'spellfile' is not set.
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Problem: Currently vim.hl.range only allows one timed highlight.
Creating another one, removes the old one.
Solution: vim.hl.range now returns a timer and a function. The timer
keeps track of how much time is left in the highlight and the function
allows you to clear it, letting the user decide what to do with old
highlights.
Problem:
Upon receiving a deadly signal, Nvim doesn't write buffers even if
the option 'autowriteall' is set.
Solution:
Write to all writable buffers upon SIGHUP or SIGQUIT (but not
SIGTERM), if the option 'autowriteall' is set.
Co-authored-by: Justin M. Keyes <justinkz@gmail.com>