From: glepnir Date: Wed, 29 Apr 2026 18:35:55 +0000 (+0000) Subject: patch 9.2.0417: completion: no support for "noinsert" with 'wildmode' X-Git-Tag: v9.2.0417^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=af494af5ff188d976073cbc049249614edffa4bf;p=thirdparty%2Fvim.git 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 Signed-off-by: Christian Brabandt --- diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index a0e450a432..e994d2901e 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 f3bea36e1f..7dbc447a9c 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 9dfb6979c0..58a59bef67 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 3cec79f809..1124f45865 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 c6607a6262..cda49a4af5 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 0390d2dac7..128e5a9469 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 388523d6f0..50383b98cc 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 8a170894c7..80a023d2c1 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\noneB\noneC" + endfunc + + set wildmenu wildoptions=pum wildmode=noinsert,full wildchar= + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneB', @:) + call feedkeys(":MyCmd o\\\\\"\", 'xt') + call assert_equal('"MyCmd oneC', @:) + + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneA', @:) + + " CTRL-P from highlighted first item returns to original text + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + " Another CTRL-P wraps to the last match + call feedkeys(":MyCmd o\\\\\"\", 'xt') + call assert_equal('"MyCmd oneC', @:) + + set wildoptions= + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneB', @:) + + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneA', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + + " 'nowildmenu' should make 'noinsert' ineffective + set nowildmenu + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd oneA', @:) + + " 'noselect' takes precedence over 'noinsert' + set wildmenu wildoptions=pum wildmode=noselect:noinsert,full + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneA', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + + set wildmode=noinsert + call feedkeys(":MyCmd o\\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + + set wildmode=noinsert,full + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneB', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + + " 'longest' takes precedence over 'noinsert' + set wildmode=noinsert:longest + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd one', @:) + + set wildmode& + call feedkeys(":set wildmode=noi\\\"\", '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\\\"\", 'xt') + call assert_equal('"MyCmd1 o', @:) + call feedkeys(":MyCmd1 o\\\\"\", '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 02f7fdf34e..91ed2c80cd 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 e4607baba5..86f3c4ffd1 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 143642d044..c0c811d1d4 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