patch 9.2.1031: 'wildmode' list:full does not show 'wildmenu'
Commit:
https://github.com/vim/vim/commit/fa1ddffcce24f22d61608c0086e55adadd1d2c11
Author: Hirohito Higashi <
h.eas...@gmail.com>
Date: Tue Sep 1 21:22:50 2026 +0000
patch 9.2.1031: 'wildmode' list:full does not show 'wildmenu'
Problem: With 'wildmode' set to list:full the matches are listed but the
wildmenu is not shown, although it is "full" that starts
wildmenu mode (zeertzjq).
Solution: List the matches and show the menu, as the two behaviors in
the same phase ask for. The menu is left to the phases that
ask for it, so that "list" on its own still only lists
(Hirohito Higashi).
fixes: #21196
closes: #21205
Co-Authored-By: Claude Opus 5 (1M context) <
nor...@anthropic.com>
Signed-off-by: Hirohito Higashi <
h.eas...@gmail.com>
Signed-off-by: zeertzjq <
zeer...@outlook.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 45a9a7dd4..81ead514a 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -1478,6 +1478,30 @@ showmatches_oneline(
out_flush(); // show one line at a time
}
+/*
+ * Show the matches in a popup menu, with the first one selected unless
+ * "noselect" is set.
+ */
+ static int
+show_pum_matches(
+ cmdline_info_T *ccline,
+ expand_T *xp,
+ char_u **matches,
+ int numMatches,
+ int showtail,
+ int noselect)
+{
+ int retval = cmdline_pum_create(ccline, xp, matches, numMatches, showtail);
+
+ if (retval == EXPAND_OK)
+ {
+ compl_selected = noselect ? -1 : 0;
+ pum_clear();
+ cmdline_pum_display();
+ }
+ return retval;
+}
+
/*
* Display completion matches.
* Returns EXPAND_NOTHING when the character that triggered expansion should be
@@ -1522,17 +1546,8 @@ showmatches(
if (display_wildmenu && !display_list
&& vim_strchr(p_wop, WOP_PUM) != NULL)
- {
- int retval = cmdline_pum_create(ccline, xp, matches, numMatches,
- showtail && !cmdline_unchanged);
- if (retval == EXPAND_OK)
- {
- compl_selected = noselect ? -1 : 0;
- pum_clear();
- cmdline_pum_display();
- }
- return retval;
- }
+ return show_pum_matches(ccline, xp, matches, numMatches,
+ showtail && !cmdline_unchanged, noselect);
if (display_list)
{
@@ -1609,6 +1624,18 @@ showmatches(
cmdline_row = msg_row; // will put it back later
}
+ // "list" and "full" in the same 'wildmode' phase: the matches are listed
+ // above and shown in the menu as well.
+ if (display_wildmenu && display_list)
+ {
+ if (vim_strchr(p_wop, WOP_PUM) != NULL)
+ (void)show_pum_matches(ccline, xp, matches, numMatches,
+ showtail && !cmdline_unchanged, noselect);
+ else
+ win_redr_status_matches(xp, numMatches, matches,
+ noselect ? -1 : 0, showtail);
+ }
+
if (xp->xp_numfiles == -1)
{
FreeWild(numMatches, matches);
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 0b55039d1..00c062e07 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -1037,8 +1037,10 @@ cmdline_wildchar_complete(
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, WIM_NOSELECT);
+ int show_menu = p_wmnu && wim_full;
+
+ if (wim_list || show_menu)
+ (void)showmatches(xp, show_menu, wim_list, WIM_NOSELECT);
else if (!found_longest_prefix)
{
int wim_list_next = (wim_flags[1] & WIM_LIST);
@@ -1051,7 +1053,8 @@ cmdline_wildchar_complete(
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,
+ (void)showmatches(xp, p_wmnu && (wim_noselect_next
+ || wim_noinsert_next), wim_list_next,
p_wmnu ? wim_flags[1] : 0);
if (wim_list_next)
@@ -1061,9 +1064,11 @@ cmdline_wildchar_complete(
}
else
{
- if (wim_list || (p_wmnu && (wim_full || wim_noselect
- || wim_noinsert)))
- (void)showmatches(xp, p_wmnu, wim_list,
+ int show_menu = p_wmnu && (wim_full || wim_noselect
+ || wim_noinsert);
+
+ if (wim_list || show_menu)
+ (void)showmatches(xp, show_menu, wim_list,
p_wmnu ? wim_flags[0] : 0);
else
vim_beep(BO_WILD);
@@ -2194,7 +2199,9 @@ getcmdline_int(
|| p_wmnu))
{
// Trigger the popup menu when wildoptions=pum
- showmatches(&xpc, p_wmnu, wim_flags[wim_index] & WIM_LIST,
+ int list = wim_flags[wim_index] & WIM_LIST;
+
+ showmatches(&xpc, p_wmnu && !list, list,
p_wmnu ? wim_flags[0] : 0);
}
if (nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index a2ed5c8dd..d712ff843 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -3346,6 +3346,45 @@ func Test_wildmenu_pum_info_async_update()
call StopVimInTerminal(buf)
endfunc
+" Test that "list" and "full" in the same 'wildmode' phase list the matches
+" and show the wildmenu as well
+func Test_wildmenu_with_list_full()
+ call writefile([], 'XwildA', 'D')
+ call writefile([], 'XwildB', 'D')
+ func! SaveScreenLineAbove()
+ let g:Sabove = Screenline(&lines - 2)
+ return ''
+ endfunc
+ cnoremap <expr> <F2> wildmenumode()
+ cnoremap <expr> <F3> SaveScreenLineAbove()
+ set wildmenu wildmode=list:full
+
+ " The matches are listed above the menu.
+ let [g:Sabove, g:Sline] = ['', '']
+ call feedkeys(":e Xwild\<Tab>\<F3>\<F4>\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e XwildA1', @:)
+ call assert_equal('XwildA XwildB', g:Sabove)
+ call assert_equal('XwildA XwildB', g:Sline)
+
+ " The popup menu is shown over the listed matches.
+ set wildoptions=pum
+ call feedkeys(":e Xwild\<Tab>\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e XwildA1', @:)
+
+ " Without 'wildmenu' the matches are listed as before.
+ set nowildmenu wildoptions=
+ let g:Sline = ''
+ call feedkeys(":e Xwild\<Tab>\<F4>\<F2>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('XwildA XwildB', g:Sline)
+ call assert_equal('"e XwildA0', @:)
+
+ cunmap <F2>
+ cunmap <F3>
+ delfunc SaveScreenLineAbove
+ unlet g:Sabove
+ set nowildmenu wildoptions& wildmode&
+endfunc
+
" Test for wildmenumode() with the cmdline popup menu
func Test_wildmenumode_with_pum()
set wildmenu
diff --git a/src/version.c b/src/version.c
index 0c7284322..5fdd1cad0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1031,
/**/
1030,
/**/