patch 9.2.0417: completion: no support for "noinsert" with 'wildmode'

Commit: 
https://github.com/vim/vim/commit/af494af5ff188d976073cbc049249614edffa4bf
Author: glepnir <[email protected]>
Date:   Wed Apr 29 18:35:55 2026 +0000

    patch 9.2.0417: completion: no support for "noinsert" with 'wildmode'
    
    Problem:  completion: no support for "noinsert" with 'wildmode' and
              commandline completion
    Solution: Add "noinsert" value to the 'wildmode' option, mirroring
              'completeopt' "noinsert" behaviour (glepnir).
    
    fixes:  #16551
    closes: #20080
    
    Signed-off-by: glepnir <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index a0e450a43..e994d2901 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.2.  Last change: 2026 Apr 28
+*options.txt*  For Vim version 9.2.  Last change: 2026 Apr 29
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -10398,8 +10398,12 @@ A jump table for the options with a short description 
can be found at |Q_op|.
                        applies to buffer name completion.
        "noselect"      If 'wildmenu' is enabled, show the menu but do not
                        preselect the first item.
-       If only one match exists, it is completed fully, unless "noselect" is
-       specified.
+       "noinsert"      If 'wildmenu' is enabled, show the menu and preselect
+                       the first match, but do not insert it in the
+                       command line.  If both "noinsert" and "noselect" are
+                       present, "noselect" takes precedence.
+       If only one match exists, it is completed fully, unless "noselect" or
+       "noinsert" is specified.
 
        Some useful combinations of colon-separated values:
        "longest:full"          Start with the longest common string and show
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index f3bea36e1..7dbc447a9 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt* For Vim version 9.2.  Last change: 2026 Apr 28
+*version9.txt* For Vim version 9.2.  Last change: 2026 Apr 29
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -52618,6 +52618,8 @@ Other ~
 - Enable reflow support in the |:terminal|.
 - Enabled scrolling for the tabpanel when the tab page list exceeds the screen
   height.  Also added the "scrollbar" sub-option to 'tabpanelopt'.
+- Added the "noinsert" value to the 'wildmode' option for symmetry with the
+  'completeopt' option
 
 Platform specific ~
 -----------------
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 9dfb6979c..58a59bef6 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -346,7 +346,7 @@ nextwild(
            cmdline_orig.length = ccline->cmdlen;
     }
 
-    if (p != NULL && !got_int && !(options & WILD_NOSELECT))
+    if (p != NULL && !got_int && !(options & (WILD_NOSELECT | WILD_NOINSERT)))
     {
        size_t  plen = STRLEN(p);
        int     difflen;
@@ -380,7 +380,8 @@ nextwild(
 
     if (xp->xp_numfiles <= 0 && p == NULL)
        beep_flush();
-    else if (xp->xp_numfiles == 1 && !(options & WILD_NOSELECT)
+    else if (xp->xp_numfiles == 1
+           && !(options & (WILD_NOSELECT | WILD_NOINSERT))
            && !wild_navigate)
        // free expanded pattern
        (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
@@ -1295,7 +1296,11 @@ showmatches_oneline(
  *   inserted as a normal character.
  */
     int
-showmatches(expand_T *xp, int display_wildmenu, int display_list, int noselect)
+showmatches(
+    expand_T   *xp,
+    int                display_wildmenu,
+    int                display_list,
+    int                wim_flags_arg)
 {
     cmdline_info_T     *ccline = get_cmdline_info();
     int                numMatches;
@@ -1306,6 +1311,9 @@ showmatches(expand_T *xp, int display_wildmenu, int 
display_list, int noselect)
     int                columns;
     int                attr;
     int                showtail;
+    int                noselect = (wim_flags_arg & WIM_NOSELECT);
+    int                noinsert = (wim_flags_arg & WIM_NOINSERT);
+    int                cmdline_unchanged = noselect || noinsert;
 
     if (xp->xp_numfiles == -1)
     {
@@ -1328,7 +1336,7 @@ showmatches(expand_T *xp, int display_wildmenu, int 
display_list, int noselect)
            && vim_strchr(p_wop, WOP_PUM) != NULL)
     {
        int retval = cmdline_pum_create(ccline, xp, matches, numMatches,
-               showtail && !noselect);
+               showtail && !cmdline_unchanged);
        if (retval == EXPAND_OK)
        {
            compl_selected = noselect ? -1 : 0;
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 3cec79f80..1124f4586 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -958,6 +958,7 @@ cmdline_wildchar_complete(
     int                cmdpos_before;
     int                options = WILD_NO_BEEP;
     int                wim_noselect = p_wmnu && (wim_flags[0] & WIM_NOSELECT);
+    int                wim_noinsert = p_wmnu && (wim_flags[0] & WIM_NOINSERT);
 
     if (wim_flags[wim_index] & WIM_BUFLASTUSED)
        options |= WILD_BUFLASTUSED;
@@ -968,7 +969,8 @@ cmdline_wildchar_complete(
                && !*did_wild_list
                && (wim_flags[wim_index] & WIM_LIST))
        {
-           (void)showmatches(xp, FALSE, TRUE, wim_noselect);
+           (void)showmatches(xp, FALSE, TRUE,
+                   p_wmnu ? wim_flags[wim_index] : 0);
            redrawcmd();
            *did_wild_list = TRUE;
        }
@@ -1006,6 +1008,8 @@ cmdline_wildchar_complete(
        {
            if (wim_noselect || wim_list)
                options |= WILD_NOSELECT;
+           if (wim_noinsert)
+               options |= WILD_NOINSERT;
            res = nextwild(xp, WILD_EXPAND_KEEP, options, escape);
        }
 
@@ -1028,26 +1032,28 @@ cmdline_wildchar_complete(
        }
 
        // Display matches
-       if (res == OK && xp->xp_numfiles > (wim_noselect ? 0 : 1))
+       if (res == OK && xp->xp_numfiles > ((wim_noselect || wim_noinsert) ? 0 
: 1))
        {
            if (wim_longest)
            {
                int found_longest_prefix = (ccline.cmdpos != cmdpos_before);
                if (wim_list || (p_wmnu && wim_full))
-                   (void)showmatches(xp, p_wmnu, wim_list, TRUE);
+                   (void)showmatches(xp, p_wmnu, wim_list, WIM_NOSELECT);
                else if (!found_longest_prefix)
                {
                    int wim_list_next = (wim_flags[1] & WIM_LIST);
                    int wim_full_next = (wim_flags[1] & WIM_FULL);
                    int wim_noselect_next = (wim_flags[1] & WIM_NOSELECT);
+                   int wim_noinsert_next = (wim_flags[1] & WIM_NOINSERT);
                    if (wim_list_next || (p_wmnu && (wim_full_next
-                                   || wim_noselect_next)))
+                                   || wim_noselect_next || wim_noinsert_next)))
                    {
-                       if (wim_full_next && !wim_noselect_next)
+                       if (wim_full_next && !wim_noselect_next && 
!wim_noinsert_next)
                            nextwild(xp, WILD_NEXT, options, escape);
                        else
                            (void)showmatches(xp, p_wmnu, wim_list_next,
-                                   wim_noselect_next);
+                                   p_wmnu ? wim_flags[1] : 0);
+
                        if (wim_list_next)
                            *did_wild_list = TRUE;
                    }
@@ -1055,8 +1061,10 @@ cmdline_wildchar_complete(
            }
            else
            {
-               if (wim_list || (p_wmnu && (wim_full || wim_noselect)))
-                   (void)showmatches(xp, p_wmnu, wim_list, wim_noselect);
+               if (wim_list || (p_wmnu && (wim_full || wim_noselect
+                               || wim_noinsert)))
+                   (void)showmatches(xp, p_wmnu, wim_list,
+                           p_wmnu ? wim_flags[0] : 0);
                else
                    vim_beep(BO_WILD);
            }
@@ -2181,7 +2189,7 @@ getcmdline_int(
                {
                    // Trigger the popup menu when wildoptions=pum
                    showmatches(&xpc, p_wmnu, wim_flags[wim_index] & WIM_LIST,
-                           wim_flags[0] & WIM_NOSELECT);
+                           p_wmnu ? wim_flags[0] : 0);
                }
                if (nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK
                        && nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK)
@@ -2296,7 +2304,7 @@ getcmdline_int(
                goto cmdline_not_changed;
 
        case Ctrl_D:
-               if (showmatches(&xpc, FALSE, TRUE, wim_flags[0] & WIM_NOSELECT)
+               if (showmatches(&xpc, FALSE, TRUE, p_wmnu ? wim_flags[0] : 0)
                        == EXPAND_NOTHING)
                    break;      // Use ^D as normal char instead
 
@@ -2900,6 +2908,8 @@ check_opt_wim(void)
            new_wim_flags[idx] |= WIM_BUFLASTUSED;
        else if (i == 8 && STRNCMP(p, "noselect", 8) == 0)
            new_wim_flags[idx] |= WIM_NOSELECT;
+       else if (i == 8 && STRNCMP(p, "noinsert", 8) == 0)
+           new_wim_flags[idx] |= WIM_NOINSERT;
        else
            return FAIL;
        p += i;
diff --git a/src/option.h b/src/option.h
index c6607a626..cda49a4af 100644
--- a/src/option.h
+++ b/src/option.h
@@ -375,6 +375,7 @@ typedef enum {
 #define WIM_LIST       0x04
 #define WIM_BUFLASTUSED        0x08
 #define WIM_NOSELECT   0x10
+#define WIM_NOINSERT   0x20
 
 // flags for the 'wildoptions' option
 // each defined char should be unique over all values.
diff --git a/src/optionstr.c b/src/optionstr.c
index 0390d2dac..128e5a946 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -116,7 +116,7 @@ static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", 
"netterm", "jsbterm"
 #endif
 static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", 
"NONE", NULL};
 // Note: Keep this in sync with check_opt_wim()
-static char *(p_wim_values[]) = {"full", "longest", "list", "lastused", 
"noselect", NULL};
+static char *(p_wim_values[]) = {"full", "longest", "list", "lastused", 
"noselect", "noinsert", NULL};
 static char *(p_wop_values[]) = {"fuzzy", "tagfile", "pum", "exacttext", NULL};
 #ifdef FEAT_WAK
 static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro
index 388523d6f..50383b98c 100644
--- a/src/proto/cmdexpand.pro
+++ b/src/proto/cmdexpand.pro
@@ -12,7 +12,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, 
int options, int mode
 void ExpandInit(expand_T *xp);
 void ExpandCleanup(expand_T *xp);
 void clear_cmdline_orig(void);
-int showmatches(expand_T *xp, int display_wildmenu, int display_list, int 
noselect);
+int showmatches(expand_T *xp, int display_wildmenu, int display_list, int 
wim_flags_arg);
 char_u *addstar(char_u *fname, int len, int context);
 void set_expand_context(expand_T *xp);
 void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int 
use_ccline);
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index 8a170894c..80a023d2c 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -5416,4 +5416,94 @@ func Test_cmdline_complete_with_space()
   call chdir(save_cwd)
 endfunc
 
+func Test_wildmode_noinsert()
+  command! -nargs=1 -complete=custom,T MyCmd echo
+  func T(a, c, p)
+    return "oneA
oneB
oneC"
+  endfunc
+
+  set wildmenu wildoptions=pum wildmode=noinsert,full wildchar=<Tab>
+  call feedkeys(":MyCmd o\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+  call feedkeys(":MyCmd o\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneB', @:)
+  call feedkeys(":MyCmd o\<Tab>\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneC', @:)
+
+  call feedkeys(":MyCmd o\<Tab>\<C-Y>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneA', @:)
+
+  " CTRL-P from highlighted first item returns to original text
+  call feedkeys(":MyCmd o\<Tab>\<C-P>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+  " Another CTRL-P wraps to the last match
+  call feedkeys(":MyCmd o\<Tab>\<C-P>\<C-P>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneC', @:)
+
+  set wildoptions=
+  call feedkeys(":MyCmd o\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+  call feedkeys(":MyCmd o\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneB', @:)
+
+  call feedkeys(":MyCmd o\<Tab>\<C-Y>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneA', @:)
+  call feedkeys(":MyCmd o\<Tab>\<C-E>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+
+  " 'nowildmenu' should make 'noinsert' ineffective
+  set nowildmenu
+  call feedkeys(":MyCmd o\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneA', @:)
+
+  " 'noselect' takes precedence over 'noinsert'
+  set wildmenu wildoptions=pum wildmode=noselect:noinsert,full
+  call feedkeys(":MyCmd o\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+  call feedkeys(":MyCmd o\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneA', @:)
+  call feedkeys(":MyCmd o\<Tab>\<C-Y>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+
+  set wildmode=noinsert
+  call feedkeys(":MyCmd o\<Tab>\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+
+  set wildmode=noinsert,full
+  call feedkeys(":MyCmd o\<Tab>\<C-N>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd oneB', @:)
+  call feedkeys(":MyCmd o\<Tab>\<C-E>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd o', @:)
+
+  " 'longest' takes precedence over 'noinsert'
+  set wildmode=noinsert:longest
+  call feedkeys(":MyCmd o\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd one', @:)
+
+  set wildmode&
+  call feedkeys(":set wildmode=noi\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set wildmode=noinsert', @:)
+
+  set wildmode=noinsert:lastused,full
+  call assert_equal('noinsert:lastused,full', &wildmode)
+  call assert_fails('set wildmode=noinser', 'E474:')
+
+  " Single match with 'noinsert': item shown highlighted, C-Y commits
+  command! -nargs=1 -complete=custom,T1 MyCmd1 echo
+  func T1(a, c, p)
+    return "oneA"
+  endfunc
+  set wildmenu wildoptions=pum wildmode=noinsert,full
+  call feedkeys(":MyCmd1 o\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd1 o', @:)
+  call feedkeys(":MyCmd1 o\<Tab>\<C-Y>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"MyCmd1 oneA', @:)
+  delcommand MyCmd1
+  delfunc T1
+
+  set wildmenu& wildoptions& wildmode& wildchar&
+  delcommand MyCmd
+  delfunc T
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/util/gen_opt_test.vim 
b/src/testdir/util/gen_opt_test.vim
index 02f7fdf34..91ed2c80c 100644
--- a/src/testdir/util/gen_opt_test.vim
+++ b/src/testdir/util/gen_opt_test.vim
@@ -365,6 +365,7 @@ let test_values = {
       \                ['xxx']],
       \ 'wildmode': [['', 'full', 'longest', 'list', 'lastused', 'list:full',
       \                'noselect', 'noselect,full', 'noselect:lastused,full',
+      \                'noinsert', 'noinsert,full', 'noinsert:lastused,full',
       \                'full,longest', 'full,full,full,full'],
       \                ['xxx', 'a4', 'full,full,full,full,full']],
       \ 'wildoptions': [['', 'tagfile', 'pum', 'fuzzy'], ['xxx']],
diff --git a/src/version.c b/src/version.c
index e4607baba..86f3c4ffd 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    417,
 /**/
     416,
 /**/
diff --git a/src/vim.h b/src/vim.h
index 143642d04..c0c811d1d 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -904,6 +904,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
 #define WILD_NOSELECT              0x4000
 #define WILD_MAY_EXPAND_PATTERN            0x8000
 #define WILD_FUNC_TRIGGER          0x10000 // called from wildtrigger()
+#define WILD_NOINSERT              0x20000
 
 // Flags for expand_wildcards()
 #define EW_DIR         0x01    // include directory names

-- 
-- 
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 [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1wI9uC-007iyw-EI%40256bit.org.

Raspunde prin e-mail lui