patch 9.1.1329: cannot get information about command line completion

Commit: 
https://github.com/vim/vim/commit/92f68e26ec36f2c263db5bea4f39d8503e0b741c
Author: Girish Palya <giris...@gmail.com>
Date:   Mon Apr 21 11:12:41 2025 +0200

    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("
")
        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 <giris...@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index b9b76ffce..204a9117c 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -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
@@ -398,6 +398,7 @@ Name                        triggered by ~
 |CmdlineChanged|       after a change was made to the command-line text
 |CmdlineEnter|         after the cursor moves to the command line
 |CmdlineLeave|         before the cursor leaves the command line
+|CmdlineLeavePre|      before preparing to leave the command line
 
 |InsertEnter|          starting Insert 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,
                                indicating the type of command-line.
                                |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                    After entering the command-line window.
                                Useful for setting options specifically for
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index b69f6b463..7353776f5 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -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
@@ -129,6 +129,8 @@ charidx({string}, {idx} [, {countcc} [, {utf16}]])
 chdir({dir})                   String  change current working directory
 cindent({lnum})                        Number  C indent for line {lnum}
 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
 complete({startcol}, {matches}) none   set Insert mode completion
 complete_add({expr})           Number  add completion match
@@ -1832,6 +1834,29 @@ clearmatches([{win}])                                    
*clearmatches()*
                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()*
                The result is a Number, which is the byte index of the column
                position given with {expr}.
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 961bde3a2..d51afbf0e 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -4079,6 +4079,7 @@ Cmdline-mode      cmdline.txt     /*Cmdline-mode*
 CmdlineChanged autocmd.txt     /*CmdlineChanged*
 CmdlineEnter   autocmd.txt     /*CmdlineEnter*
 CmdlineLeave   autocmd.txt     /*CmdlineLeave*
+CmdlineLeavePre        autocmd.txt     /*CmdlineLeavePre*
 CmdwinEnter    autocmd.txt     /*CmdwinEnter*
 CmdwinLeave    autocmd.txt     /*CmdwinLeave*
 ColorScheme    autocmd.txt     /*ColorScheme*
@@ -6568,6 +6569,7 @@ close_cb  channel.txt     /*close_cb*
 closure        eval.txt        /*closure*
 cmdarg-variable        eval.txt        /*cmdarg-variable*
 cmdbang-variable       eval.txt        /*cmdbang-variable*
+cmdcomplete_info()     builtin.txt     /*cmdcomplete_info()*
 cmdline-arguments      vi_diff.txt     /*cmdline-arguments*
 cmdline-changed        version5.txt    /*cmdline-changed*
 cmdline-completion     cmdline.txt     /*cmdline-completion*
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 8279e3e99..eca836281 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -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
 
@@ -1111,6 +1111,7 @@ Command line:                                     
*command-line-functions*
        getcmdwintype()         return the current command-line window type
        getcompletion()         list of command-line completion matches
        fullcommand()           get full command name
+       cmdcomplete_info()      get current completion information
 
 Quickfix and location lists:                   *quickfix-functions*
        getqflist()             list of quickfix errors
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 527ed1850..1ef04bd2e 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -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
@@ -41685,6 +41685,7 @@ Functions: ~
 |base64_encode()|      encode a blob into a base64 string
 |blob2str()|           convert a blob into a List of strings
 |bindtextdomain()|     set message lookup translation base path
+|cmdcomplete_info()|   get current cmdline completion info
 |diff()|               diff two Lists of strings
 |filecopy()|           copy a file {from} to {to}
 |foreach()|            apply function to List items
@@ -41708,6 +41709,7 @@ Functions: ~
 
 Autocommands: ~
 
+|CmdlineLeavePre|      before preparing to leave the command line
 |CursorMovedC|         after the cursor was moved in the command-line
 |KeyInputPre|          before processing any key event in any mode
 |SessionWritePost|     after writing the session file |:mksession|
diff --git a/src/autocmd.c b/src/autocmd.c
index 392168269..6ee6c11af 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -110,6 +110,7 @@ static keyvalue_T event_tab[NUM_EVENTS] = {
     KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"),
     KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"),
     KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"),
+    KEYVALUE_ENTRY(EVENT_CMDLINELEAVEPRE, "CmdlineLeavePre"),
     KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"),
     KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"),
     KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"),
@@ -2253,6 +2254,7 @@ apply_autocmds_group(
                || event == EVENT_SYNTAX
                || event == EVENT_CMDLINECHANGED
                || event == EVENT_CMDLINEENTER
+               || event == EVENT_CMDLINELEAVEPRE
                || event == EVENT_CMDLINELEAVE
                || event == EVENT_CURSORMOVEDC
                || event == EVENT_CMDWINENTER
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 85422b802..45f69bb2f 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -32,6 +32,8 @@ static int compl_match_arraysize;
 // First column in cmdline of the matched item for completion.
 static int compl_startcol;
 static int compl_selected;
+// cmdline before expansion
+static char_u *cmdline_orig = NULL;
 
 #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();
     VIM_CLEAR(compl_match_array);
+    compl_match_arraysize = 0;
     p_lz = FALSE;  // avoid the popup menu hanging around
     update_screen(0);
     p_lz = save_p_lz;
@@ -1112,6 +1115,7 @@ ExpandInit(expand_T *xp)
     xp->xp_backslash = XP_BS_NONE;
     xp->xp_prefix = XP_PREFIX_NONE;
     xp->xp_numfiles = -1;
+    VIM_CLEAR(cmdline_orig);
 }
 
 /*
@@ -1238,6 +1242,10 @@ showmatches(expand_T *xp, int wildmenu UNUSED)
     int                attr;
     int                showtail;
 
+    // Save cmdline before expansion
+    if (ccline->cmdbuff != NULL)
+       cmdline_orig = vim_strnsave(ccline->cmdbuff, ccline->cmdlen);
+
     if (xp->xp_numfiles == -1)
     {
        set_expand_context(xp);
@@ -4299,4 +4307,36 @@ f_getcompletion(typval_T *argvars, typval_T *rettv)
     vim_free(pat);
     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
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 2aa516dc4..39f6aa9ab 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -2092,6 +2092,8 @@ static funcentry_T global_functions[] =
                        ret_number,         f_cindent},
     {"clearmatches",   0, 1, FEARG_1,      arg1_number,
                        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,
                        ret_number,         f_col},
     {"complete",       2, 2, FEARG_2,      arg2_number_list,
diff --git a/src/ex_getln.c b/src/ex_getln.c
index c4b0a0095..1137708ae 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -1915,6 +1915,11 @@ getcmdline_int(
            }
        }
 
+       // Trigger CmdlineLeavePre autocommand
+       if (ccline.cmdfirstc != NUL && (c == '
' || c == '
' || 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
        // 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).
diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro
index e627639d9..73db378bf 100644
--- a/src/proto/cmdexpand.pro
+++ b/src/proto/cmdexpand.pro
@@ -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);
 void wildmenu_cleanup(cmdline_info_T *cclp);
 void f_getcompletion(typval_T *argvars, typval_T *rettv);
+void f_cmdcomplete_info(typval_T *argvars UNUSED, typval_T *rettv);
 /* vim: set ft=c : */
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 24a5f6138..ae587117e 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -2000,6 +2000,47 @@ func Test_QuitPre()
   bwipe Xbar
 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()
   au! CmdlineChanged : let g:text = getcmdline()
   let g:text = 0
@@ -2073,30 +2114,54 @@ func Test_Cmdline()
 
   au! CmdlineEnter : let g:entered = expand('<afile>')
   au! CmdlineLeave : let g:left = expand('<afile>')
+  au! CmdlineLeavePre : let g:leftpre = expand('<afile>')
   let g:entered = 0
   let g:left = 0
+  let g:leftpre = 0
   call feedkeys(":echo 'hello'\<CR>", 'xt')
   call assert_equal(':', g:entered)
   call assert_equal(':', g:left)
+  call assert_equal(':', g:leftpre)
   au! CmdlineEnter
   au! CmdlineLeave
+  au! CmdlineLeavePre
 
   let save_shellslash = &shellslash
   set noshellslash
   au! CmdlineEnter / let g:entered = expand('<afile>')
   au! CmdlineLeave / let g:left = expand('<afile>')
+  au! CmdlineLeavePre / let g:leftpre = expand('<afile>')
   let g:entered = 0
   let g:left = 0
+  let g:leftpre = 0
   new
   call setline(1, 'hello')
   call feedkeys("/hello\<CR>", 'xt')
   call assert_equal('/', g:entered)
   call assert_equal('/', g:left)
+  call assert_equal('/', g:leftpre)
   bwipe!
   au! CmdlineEnter
   au! CmdlineLeave
+  au! CmdlineLeavePre
   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()]
   let g:pos = []
   call feedkeys(":foo bar baz\<C-W>\<C-W>\<C-W>\<Esc>", 'xt')
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index 57b57cb8d..364909cc5 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -4268,4 +4268,48 @@ func Test_cd_bslash_completion_windows()
   let &shellslash = save_shellslash
 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
diff --git a/src/version.c b/src/version.c
index 40cff483e..60c524385 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1329,
 /**/
     1328,
 /**/
diff --git a/src/vim.h b/src/vim.h
index 29c3cb888..41984dadc 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1371,6 +1371,7 @@ enum auto_event
     EVENT_BUFWRITEPRE,         // before writing a buffer
     EVENT_CMDLINECHANGED,      // command line was modified
     EVENT_CMDLINEENTER,                // after entering the command line
+    EVENT_CMDLINELEAVEPRE,     // just before leaving the command line
     EVENT_CMDLINELEAVE,                // before leaving the command line
     EVENT_CMDUNDEFINED,                // command undefined
     EVENT_CMDWINENTER,         // after entering the cmdline window

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1u6nTZ-00GGUw-2B%40256bit.org.

Raspunde prin e-mail lui