patch 9.1.1329: cannot get information about command line completion

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 #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: #17115

Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Girish Palya
2025-04-21 11:12:41 +02:00
committed by Christian Brabandt
parent eac45c558e
commit 92f68e26ec
14 changed files with 209 additions and 4 deletions

View File

@ -1,4 +1,4 @@
*autocmd.txt* For Vim version 9.1. Last change: 2025 Apr 04 *autocmd.txt* For Vim version 9.1. Last change: 2025 Apr 21
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -398,6 +398,7 @@ Name triggered by ~
|CmdlineChanged| after a change was made to the command-line text |CmdlineChanged| after a change was made to the command-line text
|CmdlineEnter| after the cursor moves to the command line |CmdlineEnter| after the cursor moves to the command line
|CmdlineLeave| before the cursor leaves the command line |CmdlineLeave| before the cursor leaves the command line
|CmdlineLeavePre| before preparing to leave the command line
|InsertEnter| starting Insert mode |InsertEnter| starting Insert mode
|InsertChange| when typing <Insert> while in Insert or Replace mode |InsertChange| when typing <Insert> while in Insert or Replace mode
@ -639,6 +640,18 @@ CmdlineLeave Before leaving the command line; including
<afile> is set to a single character, <afile> is set to a single character,
indicating the type of command-line. indicating the type of command-line.
|cmdwin-char| |cmdwin-char|
*CmdlineLeavePre*
CmdlineLeavePre Just before leaving the command line, and
before |CmdlineLeave|. Useful for capturing
completion info with |cmdcomplete_info()|, as
this information is cleared before
|CmdlineLeave| is triggered. Triggered for
non-interactive use of ":" in a mapping, but
not when using |<Cmd>|. Also triggered when
abandoning the command line by typing CTRL-C
or <Esc>. <afile> is set to a single
character indicating the command-line type.
See |cmdwin-char| for details.
*CmdwinEnter* *CmdwinEnter*
CmdwinEnter After entering the command-line window. CmdwinEnter After entering the command-line window.
Useful for setting options specifically for Useful for setting options specifically for

View File

@ -1,4 +1,4 @@
*builtin.txt* For Vim version 9.1. Last change: 2025 Apr 18 *builtin.txt* For Vim version 9.1. Last change: 2025 Apr 21
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -129,6 +129,8 @@ charidx({string}, {idx} [, {countcc} [, {utf16}]])
chdir({dir}) String change current working directory chdir({dir}) String change current working directory
cindent({lnum}) Number C indent for line {lnum} cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches clearmatches([{win}]) none clear all matches
cmdcomplete_info() Dict get current cmdline completion
information
col({expr} [, {winid}]) Number column byte index of cursor or mark col({expr} [, {winid}]) Number column byte index of cursor or mark
complete({startcol}, {matches}) none set Insert mode completion complete({startcol}, {matches}) none set Insert mode completion
complete_add({expr}) Number add completion match complete_add({expr}) Number add completion match
@ -1832,6 +1834,29 @@ clearmatches([{win}]) *clearmatches()*
Return type: |Number| Return type: |Number|
cmdcomplete_info([{what}]) *cmdcomplete_info()*
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 <Up> or
<Down> 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 type: dict<any>
col({expr} [, {winid}]) *col()* col({expr} [, {winid}]) *col()*
The result is a Number, which is the byte index of the column The result is a Number, which is the byte index of the column
position given with {expr}. position given with {expr}.

View File

@ -4079,6 +4079,7 @@ Cmdline-mode cmdline.txt /*Cmdline-mode*
CmdlineChanged autocmd.txt /*CmdlineChanged* CmdlineChanged autocmd.txt /*CmdlineChanged*
CmdlineEnter autocmd.txt /*CmdlineEnter* CmdlineEnter autocmd.txt /*CmdlineEnter*
CmdlineLeave autocmd.txt /*CmdlineLeave* CmdlineLeave autocmd.txt /*CmdlineLeave*
CmdlineLeavePre autocmd.txt /*CmdlineLeavePre*
CmdwinEnter autocmd.txt /*CmdwinEnter* CmdwinEnter autocmd.txt /*CmdwinEnter*
CmdwinLeave autocmd.txt /*CmdwinLeave* CmdwinLeave autocmd.txt /*CmdwinLeave*
ColorScheme autocmd.txt /*ColorScheme* ColorScheme autocmd.txt /*ColorScheme*
@ -6568,6 +6569,7 @@ close_cb channel.txt /*close_cb*
closure eval.txt /*closure* closure eval.txt /*closure*
cmdarg-variable eval.txt /*cmdarg-variable* cmdarg-variable eval.txt /*cmdarg-variable*
cmdbang-variable eval.txt /*cmdbang-variable* cmdbang-variable eval.txt /*cmdbang-variable*
cmdcomplete_info() builtin.txt /*cmdcomplete_info()*
cmdline-arguments vi_diff.txt /*cmdline-arguments* cmdline-arguments vi_diff.txt /*cmdline-arguments*
cmdline-changed version5.txt /*cmdline-changed* cmdline-changed version5.txt /*cmdline-changed*
cmdline-completion cmdline.txt /*cmdline-completion* cmdline-completion cmdline.txt /*cmdline-completion*

View File

@ -1,4 +1,4 @@
*usr_41.txt* For Vim version 9.1. Last change: 2025 Mar 30 *usr_41.txt* For Vim version 9.1. Last change: 2025 Apr 21
VIM USER MANUAL - by Bram Moolenaar VIM USER MANUAL - by Bram Moolenaar
@ -1111,6 +1111,7 @@ Command line: *command-line-functions*
getcmdwintype() return the current command-line window type getcmdwintype() return the current command-line window type
getcompletion() list of command-line completion matches getcompletion() list of command-line completion matches
fullcommand() get full command name fullcommand() get full command name
cmdcomplete_info() get current completion information
Quickfix and location lists: *quickfix-functions* Quickfix and location lists: *quickfix-functions*
getqflist() list of quickfix errors getqflist() list of quickfix errors

View File

@ -1,4 +1,4 @@
*version9.txt* For Vim version 9.1. Last change: 2025 Apr 18 *version9.txt* For Vim version 9.1. Last change: 2025 Apr 21
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -41685,6 +41685,7 @@ Functions: ~
|base64_encode()| encode a blob into a base64 string |base64_encode()| encode a blob into a base64 string
|blob2str()| convert a blob into a List of strings |blob2str()| convert a blob into a List of strings
|bindtextdomain()| set message lookup translation base path |bindtextdomain()| set message lookup translation base path
|cmdcomplete_info()| get current cmdline completion info
|diff()| diff two Lists of strings |diff()| diff two Lists of strings
|filecopy()| copy a file {from} to {to} |filecopy()| copy a file {from} to {to}
|foreach()| apply function to List items |foreach()| apply function to List items
@ -41708,6 +41709,7 @@ Functions: ~
Autocommands: ~ Autocommands: ~
|CmdlineLeavePre| before preparing to leave the command line
|CursorMovedC| after the cursor was moved in the command-line |CursorMovedC| after the cursor was moved in the command-line
|KeyInputPre| before processing any key event in any mode |KeyInputPre| before processing any key event in any mode
|SessionWritePost| after writing the session file |:mksession| |SessionWritePost| after writing the session file |:mksession|

View File

@ -110,6 +110,7 @@ static keyvalue_T event_tab[NUM_EVENTS] = {
KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"), KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"),
KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"), KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"),
KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"), KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"),
KEYVALUE_ENTRY(EVENT_CMDLINELEAVEPRE, "CmdlineLeavePre"),
KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"), KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"),
KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"), KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"),
KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"), KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"),
@ -2253,6 +2254,7 @@ apply_autocmds_group(
|| event == EVENT_SYNTAX || event == EVENT_SYNTAX
|| event == EVENT_CMDLINECHANGED || event == EVENT_CMDLINECHANGED
|| event == EVENT_CMDLINEENTER || event == EVENT_CMDLINEENTER
|| event == EVENT_CMDLINELEAVEPRE
|| event == EVENT_CMDLINELEAVE || event == EVENT_CMDLINELEAVE
|| event == EVENT_CURSORMOVEDC || event == EVENT_CURSORMOVEDC
|| event == EVENT_CMDWINENTER || event == EVENT_CMDWINENTER

View File

@ -32,6 +32,8 @@ static int compl_match_arraysize;
// First column in cmdline of the matched item for completion. // First column in cmdline of the matched item for completion.
static int compl_startcol; static int compl_startcol;
static int compl_selected; static int compl_selected;
// cmdline before expansion
static char_u *cmdline_orig = NULL;
#define SHOW_MATCH(m) (showtail ? showmatches_gettail(matches[m]) : matches[m]) #define SHOW_MATCH(m) (showtail ? showmatches_gettail(matches[m]) : matches[m])
@ -432,6 +434,7 @@ cmdline_pum_remove(cmdline_info_T *cclp UNUSED)
pum_undisplay(); pum_undisplay();
VIM_CLEAR(compl_match_array); VIM_CLEAR(compl_match_array);
compl_match_arraysize = 0;
p_lz = FALSE; // avoid the popup menu hanging around p_lz = FALSE; // avoid the popup menu hanging around
update_screen(0); update_screen(0);
p_lz = save_p_lz; p_lz = save_p_lz;
@ -1112,6 +1115,7 @@ ExpandInit(expand_T *xp)
xp->xp_backslash = XP_BS_NONE; xp->xp_backslash = XP_BS_NONE;
xp->xp_prefix = XP_PREFIX_NONE; xp->xp_prefix = XP_PREFIX_NONE;
xp->xp_numfiles = -1; xp->xp_numfiles = -1;
VIM_CLEAR(cmdline_orig);
} }
/* /*
@ -1238,6 +1242,10 @@ showmatches(expand_T *xp, int wildmenu UNUSED)
int attr; int attr;
int showtail; int showtail;
// Save cmdline before expansion
if (ccline->cmdbuff != NULL)
cmdline_orig = vim_strnsave(ccline->cmdbuff, ccline->cmdlen);
if (xp->xp_numfiles == -1) if (xp->xp_numfiles == -1)
{ {
set_expand_context(xp); set_expand_context(xp);
@ -4299,4 +4307,36 @@ f_getcompletion(typval_T *argvars, typval_T *rettv)
vim_free(pat); vim_free(pat);
ExpandCleanup(&xpc); ExpandCleanup(&xpc);
} }
/*
* "cmdcomplete_info()" function
*/
void
f_cmdcomplete_info(typval_T *argvars UNUSED, typval_T *rettv)
{
cmdline_info_T *ccline = get_cmdline_info();
dict_T *retdict;
list_T *li;
int idx;
int ret = OK;
if (rettv_dict_alloc(rettv) == FAIL || ccline == NULL
|| ccline->xpc == NULL || ccline->xpc->xp_files == NULL)
return;
retdict = rettv->vval.v_dict;
ret = dict_add_string(retdict, "cmdline_orig", cmdline_orig);
if (ret == OK)
ret = dict_add_number(retdict, "pum_visible", pum_visible());
if (ret == OK)
ret = dict_add_number(retdict, "selected", ccline->xpc->xp_selected);
if (ret == OK)
{
li = list_alloc();
if (li == NULL)
return;
ret = dict_add_list(retdict, "matches", li);
for (idx = 0; ret == OK && idx < ccline->xpc->xp_numfiles; idx++)
list_append_string(li, ccline->xpc->xp_files[idx], -1);
}
}
#endif // FEAT_EVAL #endif // FEAT_EVAL

View File

@ -2092,6 +2092,8 @@ static funcentry_T global_functions[] =
ret_number, f_cindent}, ret_number, f_cindent},
{"clearmatches", 0, 1, FEARG_1, arg1_number, {"clearmatches", 0, 1, FEARG_1, arg1_number,
ret_void, f_clearmatches}, ret_void, f_clearmatches},
{"cmdcomplete_info",0, 0, 0, NULL,
ret_dict_any, f_cmdcomplete_info},
{"col", 1, 2, FEARG_1, arg2_string_or_list_number, {"col", 1, 2, FEARG_1, arg2_string_or_list_number,
ret_number, f_col}, ret_number, f_col},
{"complete", 2, 2, FEARG_2, arg2_number_list, {"complete", 2, 2, FEARG_2, arg2_number_list,

View File

@ -1915,6 +1915,11 @@ getcmdline_int(
} }
} }
// Trigger CmdlineLeavePre autocommand
if (ccline.cmdfirstc != NUL && (c == '\n' || c == '\r' || c == K_KENTER
|| c == ESC || c == Ctrl_C))
trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINELEAVEPRE);
// The wildmenu is cleared if the pressed key is not used for // The wildmenu is cleared if the pressed key is not used for
// navigating the wild menu (i.e. the key is not 'wildchar' or // navigating the wild menu (i.e. the key is not 'wildchar' or
// 'wildcharm' or Ctrl-N or Ctrl-P or Ctrl-A or Ctrl-L). // 'wildcharm' or Ctrl-N or Ctrl-P or Ctrl-A or Ctrl-L).

View File

@ -23,4 +23,5 @@ int wildmenu_translate_key(cmdline_info_T *cclp, int key, expand_T *xp, int did_
int wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp); int wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp);
void wildmenu_cleanup(cmdline_info_T *cclp); void wildmenu_cleanup(cmdline_info_T *cclp);
void f_getcompletion(typval_T *argvars, typval_T *rettv); void f_getcompletion(typval_T *argvars, typval_T *rettv);
void f_cmdcomplete_info(typval_T *argvars UNUSED, typval_T *rettv);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@ -2000,6 +2000,47 @@ func Test_QuitPre()
bwipe Xbar bwipe Xbar
endfunc endfunc
func Test_Cmdline_Trigger()
autocmd CmdlineLeavePre : let g:log = "CmdlineLeavePre"
new
let g:log = ''
nnoremap <F1> <Cmd>echo "hello"<CR>
call feedkeys("\<F1>", 'x')
call assert_equal('', g:log)
nunmap <F1>
let g:log = ''
nnoremap <F1> :echo "hello"<CR>
call feedkeys("\<F1>", 'x')
call assert_equal('CmdlineLeavePre', g:log)
nunmap <F1>
let g:log = ''
split
call assert_equal('', g:log)
call feedkeys(":echo hello", "tx")
call assert_equal('CmdlineLeavePre', g:log)
let g:log = ''
close
call assert_equal('', g:log)
call feedkeys(":echo hello", "tx")
call assert_equal('CmdlineLeavePre', g:log)
let g:log = ''
tabnew
call assert_equal('', g:log)
call feedkeys(":echo hello", "tx")
call assert_equal('CmdlineLeavePre', g:log)
let g:log = ''
split
call assert_equal('', g:log)
call feedkeys(":echo hello", "tx")
call assert_equal('CmdlineLeavePre', g:log)
let g:log = ''
tabclose
call assert_equal('', g:log)
call feedkeys(":echo hello", "tx")
call assert_equal('CmdlineLeavePre', g:log)
bw!
endfunc
func Test_Cmdline() func Test_Cmdline()
au! CmdlineChanged : let g:text = getcmdline() au! CmdlineChanged : let g:text = getcmdline()
let g:text = 0 let g:text = 0
@ -2073,30 +2114,54 @@ func Test_Cmdline()
au! CmdlineEnter : let g:entered = expand('<afile>') au! CmdlineEnter : let g:entered = expand('<afile>')
au! CmdlineLeave : let g:left = expand('<afile>') au! CmdlineLeave : let g:left = expand('<afile>')
au! CmdlineLeavePre : let g:leftpre = expand('<afile>')
let g:entered = 0 let g:entered = 0
let g:left = 0 let g:left = 0
let g:leftpre = 0
call feedkeys(":echo 'hello'\<CR>", 'xt') call feedkeys(":echo 'hello'\<CR>", 'xt')
call assert_equal(':', g:entered) call assert_equal(':', g:entered)
call assert_equal(':', g:left) call assert_equal(':', g:left)
call assert_equal(':', g:leftpre)
au! CmdlineEnter au! CmdlineEnter
au! CmdlineLeave au! CmdlineLeave
au! CmdlineLeavePre
let save_shellslash = &shellslash let save_shellslash = &shellslash
set noshellslash set noshellslash
au! CmdlineEnter / let g:entered = expand('<afile>') au! CmdlineEnter / let g:entered = expand('<afile>')
au! CmdlineLeave / let g:left = expand('<afile>') au! CmdlineLeave / let g:left = expand('<afile>')
au! CmdlineLeavePre / let g:leftpre = expand('<afile>')
let g:entered = 0 let g:entered = 0
let g:left = 0 let g:left = 0
let g:leftpre = 0
new new
call setline(1, 'hello') call setline(1, 'hello')
call feedkeys("/hello\<CR>", 'xt') call feedkeys("/hello\<CR>", 'xt')
call assert_equal('/', g:entered) call assert_equal('/', g:entered)
call assert_equal('/', g:left) call assert_equal('/', g:left)
call assert_equal('/', g:leftpre)
bwipe! bwipe!
au! CmdlineEnter au! CmdlineEnter
au! CmdlineLeave au! CmdlineLeave
au! CmdlineLeavePre
let &shellslash = save_shellslash let &shellslash = save_shellslash
let g:left = "cancelled"
let g:leftpre = "cancelled"
au! CmdlineLeave : let g:left = "triggered"
au! CmdlineLeavePre : let g:leftpre = "triggered"
call feedkeys(":echo 'hello'\<esc>", 'xt')
call assert_equal('triggered', g:left)
call assert_equal('triggered', g:leftpre)
let g:left = "cancelled"
let g:leftpre = "cancelled"
au! CmdlineLeave : let g:left = "triggered"
call feedkeys(":echo 'hello'\<c-c>", 'xt')
call assert_equal('triggered', g:left)
call assert_equal('triggered', g:leftpre)
au! CmdlineLeave
au! CmdlineLeavePre
au! CursorMovedC : let g:pos += [getcmdpos()] au! CursorMovedC : let g:pos += [getcmdpos()]
let g:pos = [] let g:pos = []
call feedkeys(":foo bar baz\<C-W>\<C-W>\<C-W>\<Esc>", 'xt') call feedkeys(":foo bar baz\<C-W>\<C-W>\<C-W>\<Esc>", 'xt')

View File

@ -4268,4 +4268,48 @@ func Test_cd_bslash_completion_windows()
let &shellslash = save_shellslash let &shellslash = save_shellslash
endfunc endfunc
" Testg cmdcomplete_info() with CmdlineLeavePre autocmd
func Test_cmdcomplete_info()
augroup test_CmdlineLeavePre
autocmd!
autocmd CmdlineLeavePre * let g:cmdcomplete_info = string(cmdcomplete_info())
augroup END
new
call assert_equal({}, cmdcomplete_info())
call feedkeys(":h echom\<cr>", "tx") " No expansion
call assert_equal('{}', g:cmdcomplete_info)
call feedkeys(":h echoms\<tab>\<cr>", "tx")
call assert_equal('{''cmdline_orig'': '''', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
call feedkeys(":h echom\<tab>\<cr>", "tx")
call assert_equal(
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': 0}',
\ g:cmdcomplete_info)
call feedkeys(":h echom\<tab>\<tab>\<cr>", "tx")
call assert_equal(
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': 1}',
\ g:cmdcomplete_info)
call feedkeys(":h echom\<tab>\<tab>\<tab>\<cr>", "tx")
call assert_equal(
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': -1}',
\ g:cmdcomplete_info)
set wildoptions=pum
call feedkeys(":h echoms\<tab>\<cr>", "tx")
call assert_equal('{''cmdline_orig'': '''', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
call feedkeys(":h echom\<tab>\<cr>", "tx")
call assert_equal(
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': 0}',
\ g:cmdcomplete_info)
call feedkeys(":h echom\<tab>\<tab>\<cr>", "tx")
call assert_equal(
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': 1}',
\ g:cmdcomplete_info)
call feedkeys(":h echom\<tab>\<tab>\<tab>\<cr>", "tx")
call assert_equal(
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': -1}',
\ g:cmdcomplete_info)
bw!
set wildoptions&
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1329,
/**/ /**/
1328, 1328,
/**/ /**/

View File

@ -1371,6 +1371,7 @@ enum auto_event
EVENT_BUFWRITEPRE, // before writing a buffer EVENT_BUFWRITEPRE, // before writing a buffer
EVENT_CMDLINECHANGED, // command line was modified EVENT_CMDLINECHANGED, // command line was modified
EVENT_CMDLINEENTER, // after entering the command line EVENT_CMDLINEENTER, // after entering the command line
EVENT_CMDLINELEAVEPRE, // just before leaving the command line
EVENT_CMDLINELEAVE, // before leaving the command line EVENT_CMDLINELEAVE, // before leaving the command line
EVENT_CMDUNDEFINED, // command undefined EVENT_CMDUNDEFINED, // command undefined
EVENT_CMDWINENTER, // after entering the cmdline window EVENT_CMDWINENTER, // after entering the cmdline window