From f3c4fec43ffeff454c391ab9f5a860c78c660f85 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 21 Apr 2025 18:59:01 +0800 Subject: vim-patch:9.1.1329: cannot get information about command line completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 (``)** When selecting a completion item via `` or ``, 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 ``** Often, it’s helpful to allow `` (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 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 g :Grep nnoremap G :Grep =expand("") command! -nargs=* -complete=customlist,FuzzyFind Find execute(selected_match != '' ? $'edit {selected_match}' : '') var allfiles: list autocmd CmdlineEnter : allfiles = null_list def FuzzyFind(arglead: string, _: string, _: number): list 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 :=execute('let fzfind_root="."')\|''Find nnoremap fv :=execute('let fzfind_root="$HOME/.vim"')\|''Find nnoremap fV :=execute('let fzfind_root="$VIMRUNTIME"')\|''Find command! -nargs=* -complete=customlist,FuzzyBuffer Buffer execute('b ' .. selected_match->matchstr('\d\+')) def FuzzyBuffer(arglead: string, _: string, _: number): list 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 :Buffer 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= 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("\", "ti") SkipCmdlineChanged() # Suppress redundant completion attempts # Remove that get inserted when no items are available timer_start(0, (_) => getcmdline()->substitute('\%x00', '', 'g')->setcmdline()) endif enddef cnoremap SkipCmdlineChanged("\") cnoremap SkipCmdlineChanged("\") 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() ? "\" : '') .. 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#17115 https://github.com/vim/vim/commit/92f68e26ec36f2c263db5bea4f39d8503e0b741c Co-authored-by: Girish Palya --- runtime/lua/vim/_meta/vimfn.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'runtime/lua/vim') diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index c1387954df..a253a87a49 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -919,6 +919,28 @@ function vim.fn.cindent(lnum) end --- @param win? integer function vim.fn.clearmatches(win) end +--- Returns a |Dictionary| with information about cmdline +--- completion. See |cmdline-completion|. +--- The items are: +--- cmdline_orig The original command-line string before +--- completion began. +--- pum_visible |TRUE| if popup menu is visible. +--- See |pumvisible()|. +--- matches List of all completion candidates. Each item +--- is a string. +--- selected Selected item index. First index is zero. +--- Index is -1 if no item is selected (showing +--- typed text only, or the last completion after +--- no item is selected when using the or +--- keys) +--- +--- Returns an empty |Dictionary| if no completion was attempted, +--- if there was only one candidate and it was fully completed, or +--- if an error occurred. +--- +--- @return table +function vim.fn.cmdcomplete_info() end + --- The result is a Number, which is the byte index of the column --- position given with {expr}. --- For accepted positions see |getpos()|. -- cgit