]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0417: completion: no support for "noinsert" with 'wildmode' v9.2.0417
authorglepnir <glephunter@gmail.com>
Wed, 29 Apr 2026 18:35:55 +0000 (18:35 +0000)
committerChristian Brabandt <cb@256bit.org>
Wed, 29 Apr 2026 18:35:55 +0000 (18:35 +0000)
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 <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/options.txt
runtime/doc/version9.txt
src/cmdexpand.c
src/ex_getln.c
src/option.h
src/optionstr.c
src/proto/cmdexpand.pro
src/testdir/test_cmdline.vim
src/testdir/util/gen_opt_test.vim
src/version.c
src/vim.h

index a0e450a432e7bf3689484dcaeeaa9320b59613ea..e994d2901ef8f930f7eff22bfe20a2c46ed0962d 100644 (file)
@@ -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
index f3bea36e1f9fbbedc23f21cbf8e47a861fe3a51d..7dbc447a9cb235ad26c7344a0e04f3219734c924 100644 (file)
@@ -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 ~
 -----------------
index 9dfb6979c0435325cb4fb43c038c5a2db7b851b9..58a59bef679fe30ebea0f5693bf3f35d5e30cac5 100644 (file)
@@ -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;
index 3cec79f809a72d29dfa64d4e3188fb86d32c8854..1124f458655141cec2de2f8aea0983d28135d9be 100644 (file)
@@ -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;
index c6607a6262ed6ea7ec6d6272987b9c954953def7..cda49a4af50839f5e97f223d1fd21c887b61ffef 100644 (file)
@@ -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.
index 0390d2dac77e14a70d79325d6191a3495eb787bd..128e5a9469bdfae3e5635450694795510e047e0c 100644 (file)
@@ -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};
index 388523d6f05106ad036a96be8dd7df3f75187b91..50383b98cc1133572c32d057dc47e006f432f4a1 100644 (file)
@@ -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);
index 8a170894c7cfe892af44007b52822f94932d6f89..80a023d2c1c71f93f1b109a4dad9bf088bbe43ee 100644 (file)
@@ -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\noneB\noneC"
+  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
index 02f7fdf34ee9a570fd5f3b2d11a3aad17a9443e6..91ed2c80cd69031a6a846f72153932668e8ede4f 100644 (file)
@@ -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']],
index e4607baba5af0c1d991258080ec777feefcc4811..86f3c4ffd1bff0b08cd9a7718deb862f54a96ede 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    417,
 /**/
     416,
 /**/
index 143642d044895099001fa2f717856726c7662ff5..c0c811d1d459dd07e232edce4fa00c87d7fbb850 100644 (file)
--- 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