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 <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
glepnir
2026-04-29 18:35:55 +00:00
committed by Christian Brabandt
parent c2bda0add9
commit af494af5ff
11 changed files with 139 additions and 20 deletions
+7 -3
View 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
+3 -1
View 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 ~
-----------------
+12 -4
View 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;
+20 -10
View 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;
+1
View 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.
+1 -1
View 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};
+1 -1
View 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);
+90
View 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
+1
View 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']],
+2
View File
@@ -729,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
417,
/**/
416,
/**/
+1
View File
@@ -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