commit f0bf6d7647a4dabad0855d10bff63816b06eb24e
parent ad22d0ace9965907a83088fb52dd407b92222992
Author: zeertzjq <zeertzjq@outlook.com>
Date: Mon, 8 Sep 2025 10:38:37 +0800
Merge pull request #35673 from zeertzjq/vim-9.1.1676
vim-patch: cmdline completion fixes
Diffstat:
8 files changed, 437 insertions(+), 113 deletions(-)
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
@@ -254,6 +254,9 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
CmdlineInfo *const ccline = get_cmdline_info();
char *p;
bool from_wildtrigger_func = options & WILD_FUNC_TRIGGER;
+ bool wild_navigate = (type == WILD_NEXT || type == WILD_PREV
+ || type == WILD_PAGEUP || type == WILD_PAGEDOWN
+ || type == WILD_PUM_WANT);
if (xp->xp_numfiles == -1) {
pre_incsearch_pos = xp->xp_pre_incsearch_pos;
@@ -292,15 +295,13 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
// If cmd_silent is set then don't show the dots, because redrawcmd() below
// won't remove them.
- if (!cmd_silent && !from_wildtrigger_func
+ if (!cmd_silent && !from_wildtrigger_func && !wild_navigate
&& !(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
msg_puts("..."); // show that we are busy
ui_flush();
}
- if (type == WILD_NEXT || type == WILD_PREV
- || type == WILD_PAGEUP || type == WILD_PAGEDOWN
- || type == WILD_PUM_WANT) {
+ if (wild_navigate) {
// Get next/previous match for a previous expanded pattern.
p = ExpandOne(xp, NULL, NULL, 0, type);
} else {
@@ -313,7 +314,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
tmp = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
}
// Translate string into pattern and expand it.
- const int use_options = ((options & ~WILD_KEEP_SOLE_ITEM)
+ const int use_options = (options
| WILD_HOME_REPLACE
| WILD_ADD_SLASH
| WILD_SILENT
@@ -337,7 +338,13 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
}
}
- if (p != NULL && !got_int) {
+ // Save cmdline before inserting selected item
+ if (!wild_navigate && ccline->cmdbuff != NULL) {
+ xfree(cmdline_orig);
+ cmdline_orig = xstrnsave(ccline->cmdbuff, (size_t)ccline->cmdlen);
+ }
+
+ if (p != NULL && !got_int && !(options & WILD_NOSELECT)) {
size_t plen = strlen(p);
int difflen = (int)plen - (int)(xp->xp_pattern_len);
if (ccline->cmdlen + difflen + 4 > ccline->cmdbufflen) {
@@ -364,7 +371,7 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
if (xp->xp_numfiles <= 0 && p == NULL) {
beep_flush();
- } else if (xp->xp_numfiles == 1 && !(options & WILD_KEEP_SOLE_ITEM)) {
+ } else if (xp->xp_numfiles == 1 && !(options & WILD_NOSELECT) && !wild_navigate) {
// free expanded pattern
ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
}
@@ -374,10 +381,9 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
return OK;
}
-/// Create and display a cmdline completion popup menu with items from
-/// "matches".
+/// Create completion popup menu with items from "matches".
static int cmdline_pum_create(CmdlineInfo *ccline, expand_T *xp, char **matches, int numMatches,
- bool showtail)
+ bool showtail, bool noselect)
{
assert(numMatches >= 0);
// Add all the completion matches
@@ -395,19 +401,13 @@ static int cmdline_pum_create(CmdlineInfo *ccline, expand_T *xp, char **matches,
}
// Compute the popup menu starting column
- char *endpos = showtail ? showmatches_gettail(xp->xp_pattern, true) : xp->xp_pattern;
+ char *endpos = showtail ? showmatches_gettail(xp->xp_pattern, noselect) : xp->xp_pattern;
if (ui_has(kUICmdline) && cmdline_win == NULL) {
compl_startcol = (int)(endpos - ccline->cmdbuff);
} else {
compl_startcol = cmd_screencol((int)(endpos - ccline->cmdbuff));
}
- // no default selection
- compl_selected = -1;
-
- pum_clear();
- cmdline_pum_display(true);
-
return EXPAND_OK;
}
@@ -420,8 +420,7 @@ void cmdline_pum_display(bool changed_array)
/// Returns true if the cmdline completion popup menu is being displayed.
bool cmdline_pum_active(void)
{
- // compl_match_array != NULL should already imply pum_visible() in Nvim.
- return compl_match_array != NULL;
+ return pum_visible() && compl_match_array != NULL;
}
/// Remove the cmdline completion popup menu (if present), free the list of items.
@@ -745,11 +744,20 @@ static char *get_next_or_prev_match(int mode, expand_T *xp)
}
// Display matches on screen
- if (compl_match_array) {
- compl_selected = findex;
- cmdline_pum_display(false);
- } else if (p_wmnu) {
- redraw_wildmenu(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail);
+ if (p_wmnu) {
+ if (compl_match_array) {
+ compl_selected = findex;
+ cmdline_pum_display(false);
+ } else if (wop_flags & kOptWopFlagPum) {
+ if (cmdline_pum_create(get_cmdline_info(), xp, xp->xp_files,
+ xp->xp_numfiles, cmd_showtail, false) == EXPAND_OK) {
+ compl_selected = findex;
+ pum_clear();
+ cmdline_pum_display(true);
+ }
+ } else {
+ redraw_wildmenu(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail);
+ }
}
xp->xp_selected = findex;
@@ -925,7 +933,7 @@ char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode)
cmdline_pum_remove();
}
}
- xp->xp_selected = 0;
+ xp->xp_selected = (options & WILD_NOSELECT) ? -1 : 0;
if (mode == WILD_FREE) { // only release file name
return NULL;
@@ -1084,49 +1092,49 @@ static void showmatches_oneline(expand_T *xp, char **matches, int numMatches, in
}
}
-/// Show all matches for completion on the command line.
-/// Returns EXPAND_NOTHING when the character that triggered expansion should
-/// be inserted like a normal character.
-int showmatches(expand_T *xp, bool wildmenu)
+/// Display completion matches.
+/// Returns EXPAND_NOTHING when the character that triggered expansion should be
+/// inserted as a normal character.
+int showmatches(expand_T *xp, bool display_wildmenu, bool display_list, bool noselect)
{
CmdlineInfo *const ccline = get_cmdline_info();
int numMatches;
char **matches;
- int j;
int maxlen;
int lines;
int columns;
bool showtail;
- // Save cmdline before expansion
- if (ccline->cmdbuff != NULL) {
- xfree(cmdline_orig);
- cmdline_orig = xstrnsave(ccline->cmdbuff, (size_t)ccline->cmdlen);
- }
-
if (xp->xp_numfiles == -1) {
set_expand_context(xp);
if (xp->xp_context == EXPAND_LUA) {
nlua_expand_pat(xp);
}
- int i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
- &numMatches, &matches);
- showtail = expand_showtail(xp);
- if (i != EXPAND_OK) {
- return i;
+ int retval = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
+ &numMatches, &matches);
+ if (retval != EXPAND_OK) {
+ return retval;
}
+ showtail = expand_showtail(xp);
} else {
numMatches = xp->xp_numfiles;
matches = xp->xp_files;
showtail = cmd_showtail;
}
- if (((!ui_has(kUICmdline) || cmdline_win != NULL) && wildmenu && (wop_flags & kOptWopFlagPum))
+ if (((!ui_has(kUICmdline) || cmdline_win != NULL) && display_wildmenu && !display_list
+ && (wop_flags & kOptWopFlagPum))
|| ui_has(kUIWildmenu) || (ui_has(kUICmdline) && ui_has(kUIPopupmenu))) {
- return cmdline_pum_create(ccline, xp, matches, numMatches, showtail);
+ int retval = cmdline_pum_create(ccline, xp, matches, numMatches, showtail, noselect);
+ if (retval == EXPAND_OK) {
+ compl_selected = noselect ? -1 : 0;
+ pum_clear();
+ cmdline_pum_display(true);
+ }
+ return retval;
}
- if (!wildmenu) {
+ if (display_list) {
msg_didany = false; // lines_left will be set
msg_start(); // prepare for paging
msg_putchar('\n');
@@ -1138,22 +1146,24 @@ int showmatches(expand_T *xp, bool wildmenu)
}
if (got_int) {
- got_int = false; // only int. the completion, not the cmd line
- } else if (wildmenu) {
- redraw_wildmenu(xp, numMatches, matches, -1, showtail);
- } else {
+ got_int = false; // only interrupt the completion, not the cmd line
+ } else if (display_wildmenu && !display_list) {
+ redraw_wildmenu(xp, numMatches, matches, noselect ? -1 : 0,
+ showtail); // display statusbar menu
+ } else if (display_list) {
// find the length of the longest file name
maxlen = 0;
for (int i = 0; i < numMatches; i++) {
+ int len;
if (!showtail && (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_SHELLCMD
|| xp->xp_context == EXPAND_BUFFERS)) {
home_replace(NULL, matches[i], NameBuff, MAXPATHL, true);
- j = vim_strsize(NameBuff);
+ len = vim_strsize(NameBuff);
} else {
- j = vim_strsize(SHOW_MATCH(i));
+ len = vim_strsize(SHOW_MATCH(i));
}
- maxlen = MAX(maxlen, j);
+ maxlen = MAX(maxlen, len);
}
if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
@@ -3569,7 +3579,7 @@ int wildmenu_translate_key(CmdlineInfo *cclp, int key, expand_T *xp, bool did_wi
{
int c = key;
- if (did_wild_list) {
+ if (cmdline_pum_active() || did_wild_list || wild_menu_showing) {
if (c == K_LEFT) {
c = Ctrl_P;
} else if (c == K_RIGHT) {
diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h
@@ -40,7 +40,7 @@ enum {
WILD_NOERROR = 0x800, ///< sets EW_NOERROR
WILD_BUFLASTUSED = 0x1000,
BUF_DIFF_FILTER = 0x2000,
- WILD_KEEP_SOLE_ITEM = 0x4000,
+ WILD_NOSELECT = 0x4000,
WILD_MAY_EXPAND_PATTERN = 0x8000,
WILD_FUNC_TRIGGER = 0x10000, ///< called from wildtrigger()
};
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
@@ -1120,31 +1120,34 @@ static int command_line_wildchar_complete(CommandLineState *s)
{
int res;
int options = WILD_NO_BEEP;
+ bool escape = s->firstc != '@';
+ bool wim_noselect = p_wmnu && (wim_flags[0] & kOptWimFlagNoselect) != 0;
+
if (wim_flags[s->wim_index] & kOptWimFlagLastused) {
options |= WILD_BUFLASTUSED;
}
- if (wim_flags[0] & kOptWimFlagNoselect) {
- options |= WILD_KEEP_SOLE_ITEM;
- }
if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
- // if 'wildmode' contains "list" may still need to list
+ // If "list" is present, list matches unless already listed
if (s->xpc.xp_numfiles > 1
&& !s->did_wild_list
- && ((wim_flags[s->wim_index] & kOptWimFlagList)
- || (p_wmnu && (wim_flags[s->wim_index] & kOptWimFlagFull) != 0))) {
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
+ && (wim_flags[s->wim_index] & kOptWimFlagList)) {
+ showmatches(&s->xpc, false, true, wim_noselect);
redrawcmd();
s->did_wild_list = true;
}
-
if (wim_flags[s->wim_index] & kOptWimFlagLongest) {
- res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
+ res = nextwild(&s->xpc, WILD_LONGEST, options, escape);
} else if (wim_flags[s->wim_index] & kOptWimFlagFull) {
- res = nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
+ res = nextwild(&s->xpc, WILD_NEXT, options, escape);
} else {
res = OK; // don't insert 'wildchar' now
}
} else { // typed p_wc first time
+ bool wim_longest = (wim_flags[0] & kOptWimFlagLongest);
+ bool wim_list = (wim_flags[0] & kOptWimFlagList);
+ bool wim_full = (wim_flags[0] & kOptWimFlagFull);
+
+ s->wim_index = 0;
if (s->c == p_wc || s->c == p_wcm || s->c == K_WILD || s->c == Ctrl_Z) {
options |= WILD_MAY_EXPAND_PATTERN;
if (s->c == K_WILD) {
@@ -1152,15 +1155,17 @@ static int command_line_wildchar_complete(CommandLineState *s)
}
s->xpc.xp_pre_incsearch_pos = s->is_state.search_start;
}
- s->wim_index = 0;
- int j = ccline.cmdpos;
+ int cmdpos_before = ccline.cmdpos;
// if 'wildmode' first contains "longest", get longest
// common part
- if (wim_flags[0] & kOptWimFlagLongest) {
- res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
+ if (wim_longest) {
+ res = nextwild(&s->xpc, WILD_LONGEST, options, escape);
} else {
- res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, s->firstc != '@');
+ if (wim_noselect || wim_list) {
+ options |= WILD_NOSELECT;
+ }
+ res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, escape);
}
// if interrupted while completing, behave like it failed
@@ -1172,38 +1177,38 @@ static int command_line_wildchar_complete(CommandLineState *s)
return CMDLINE_CHANGED;
}
- // when more than one match, and 'wildmode' first contains
- // "list", or no change and 'wildmode' contains "longest,list",
- // list all matches
- if (res == OK
- && s->xpc.xp_numfiles > ((wim_flags[s->wim_index] & kOptWimFlagNoselect) ? 0 : 1)) {
- // a "longest" that didn't do anything is skipped (but not
- // "list:longest")
- if (wim_flags[0] == kOptWimFlagLongest && ccline.cmdpos == j) {
- s->wim_index = 1;
- }
- if ((wim_flags[s->wim_index] & kOptWimFlagList)
- || (p_wmnu && (wim_flags[s->wim_index] & (kOptWimFlagFull|kOptWimFlagNoselect)))) {
- if (!(wim_flags[0] & kOptWimFlagLongest)) {
- int p_wmnu_save = p_wmnu;
- p_wmnu = 0;
- // remove match
- nextwild(&s->xpc, WILD_PREV, options, s->firstc != '@');
- p_wmnu = p_wmnu_save;
+ // Display matches
+ if (res == OK && s->xpc.xp_numfiles > (wim_noselect ? 0 : 1)) {
+ if (wim_longest) {
+ bool found_longest_prefix = (ccline.cmdpos != cmdpos_before);
+ if (wim_list || (p_wmnu && wim_full)) {
+ showmatches(&s->xpc, p_wmnu, wim_list, true);
+ } else if (!found_longest_prefix) {
+ bool wim_list_next = (wim_flags[1] & kOptWimFlagList);
+ bool wim_full_next = (wim_flags[1] & kOptWimFlagFull);
+ bool wim_noselect_next = (wim_flags[1] & kOptWimFlagNoselect);
+ if (wim_list_next || (p_wmnu && (wim_full_next || wim_noselect_next))) {
+ if (wim_full_next && !wim_noselect_next) {
+ nextwild(&s->xpc, WILD_NEXT, options, escape);
+ } else {
+ showmatches(&s->xpc, p_wmnu, wim_list_next, wim_noselect_next);
+ }
+ if (wim_list_next) {
+ s->did_wild_list = true;
+ }
+ }
}
+ } else {
+ if (wim_list || (p_wmnu && (wim_full || wim_noselect))) {
+ showmatches(&s->xpc, p_wmnu, wim_list, wim_noselect);
+ } else {
+ vim_beep(kOptBoFlagWildmode);
+ }
+ }
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
- redrawcmd();
+ redrawcmd();
+ if (wim_list) {
s->did_wild_list = true;
-
- if (wim_flags[s->wim_index] & kOptWimFlagLongest) {
- nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
- } else if ((wim_flags[s->wim_index] & kOptWimFlagFull)
- && !(wim_flags[s->wim_index] & kOptWimFlagNoselect)) {
- nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
- }
- } else {
- vim_beep(kOptBoFlagWildmode);
}
} else if (s->xpc.xp_numfiles == -1) {
s->xpc.xp_context = EXPAND_NOTHING;
@@ -1344,7 +1349,7 @@ static int command_line_execute(VimState *state, int key)
int wild_type = 0;
const bool key_is_wc = (s->c == p_wc && KeyTyped) || s->c == p_wcm;
- if ((cmdline_pum_active() || s->did_wild_list) && !key_is_wc) {
+ if ((cmdline_pum_active() || wild_menu_showing || s->did_wild_list) && !key_is_wc) {
// Ctrl-Y: Accept the current selection and close the popup menu.
// Ctrl-E: cancel the cmdline popup menu and return the original text.
if (s->c == Ctrl_E || s->c == Ctrl_Y) {
@@ -1469,7 +1474,8 @@ static int command_line_execute(VimState *state, int key)
if (s->xpc.xp_numfiles > 1
&& ((!s->did_wild_list && (wim_flags[s->wim_index] & kOptWimFlagList)) || p_wmnu)) {
// Trigger the popup menu when wildoptions=pum
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
+ showmatches(&s->xpc, p_wmnu, wim_flags[s->wim_index] & kOptWimFlagList,
+ wim_flags[0] & kOptWimFlagNoselect);
}
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
@@ -2036,7 +2042,8 @@ static int command_line_handle_key(CommandLineState *s)
}
case Ctrl_D:
- if (showmatches(&s->xpc, false) == EXPAND_NOTHING) {
+ if (showmatches(&s->xpc, false, true, wim_flags[0] & kOptWimFlagNoselect)
+ == EXPAND_NOTHING) {
break; // Use ^D as normal char instead
}
diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua
@@ -530,6 +530,47 @@ describe('cmdline', function()
/global^ |
]])
end)
+
+ -- oldtest: Test_long_line_noselect()
+ it("long line is shown properly with noselect in 'wildmode'", function()
+ local screen = Screen.new(60, 8)
+ exec([[
+ set wildmenu wildoptions=pum wildmode=noselect,full
+ command -nargs=1 -complete=custom,Entries DoubleEntry echo
+ func Entries(a, b, c)
+ return 'loooooooooooooooong quite loooooooooooong, really loooooooooooong, probably too looooooooooooooooooooooooooong entry'
+ endfunc
+ ]])
+
+ feed(':DoubleEntry <Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*5
+ {1:~ }{4: loooooooooooooooong quite loooooooooooong, real}|
+ :DoubleEntry ^ |
+ ]])
+
+ feed('<C-N>')
+ screen:expect([[
+ |
+ {1:~ }|*3
+ {3: }|
+ :DoubleEntry loooooooooooooooong quite loooooooooooong, real|
+ ly loooooooo{12: loooooooooooooooong quite loooooooooooong, real}|
+ ong entry^ |
+ ]])
+
+ feed('<C-N>')
+ screen:expect([[
+ |
+ {1:~ }|*3
+ {3: }{4: loooooooooooooooong quite loooooooooooong, real}|
+ :DoubleEntry ^ |
+ |*2
+ ]])
+
+ feed('<Esc>')
+ end)
end)
describe('cmdwin', function()
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
@@ -535,12 +535,40 @@ local function test_cmdline(linegrid)
{2:långfile1 }|
|
]],
+ cmdline = { { content = { { 'b långfile1' } }, firstc = ':', pos = 12 } },
popupmenu = {
anchor = { -1, 0, 2 },
items = { { 'långfile1', '', '', '' }, { 'långfile2', '', '', '' } },
pos = 0,
},
- cmdline = { { content = { { 'b långfile1' } }, firstc = ':', pos = 12 } },
+ }
+
+ feed('<Esc>')
+ command('silent %bwipe')
+
+ command('set shellslash')
+ -- position is correct when expanding environment variable #20348
+ command('silent cd test/functional/fixtures')
+ n.fn.setenv('XNDIR', 'wildpum/Xnamedir')
+ feed(':e $XNDIR/<Tab>')
+ screen:expect {
+ grid = [[
+ ^ |
+ {1:~ }|*3
+ |
+ ]],
+ cmdline = {
+ {
+ content = { { 'e wildpum/Xnamedir/XdirA/' } },
+ firstc = ':',
+ pos = 25,
+ },
+ },
+ popupmenu = {
+ anchor = { -1, 0, 19 },
+ items = { { 'XdirA/', '', '', '' }, { 'XfileA', '', '', '' } },
+ pos = 0,
+ },
}
end)
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
@@ -1060,6 +1060,7 @@ describe('builtin popupmenu', function()
[110] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkYellow },
[111] = { background = Screen.colors.Plum1, foreground = Screen.colors.DarkBlue },
[112] = { background = Screen.colors.Plum1, foreground = Screen.colors.DarkGreen },
+ [113] = { background = Screen.colors.Yellow, foreground = Screen.colors.Black },
-- popup non-selected item
n = { background = Screen.colors.Plum1 },
-- popup scrollbar knob
@@ -4516,7 +4517,7 @@ describe('builtin popupmenu', function()
feed('<esc>')
- -- Check "list" still works
+ -- Check that when "longest" produces no result, "list" works
command('set wildmode=longest,list')
feed(':cn<Tab>')
screen:expect([[
@@ -4529,6 +4530,8 @@ describe('builtin popupmenu', function()
cnfile cnoremenu |
:cn^ |
]])
+ feed('<Tab>')
+ screen:expect_unchanged()
feed('s')
screen:expect([[
|
@@ -4694,6 +4697,108 @@ describe('builtin popupmenu', function()
feed('<Esc>')
+ -- "longest:list" shows list whether it finds a candidate or not
+ command('set wildmode=longest:list,full wildoptions=')
+ feed(':cn<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*3
+ {3: }|
+ :cn |
+ cnewer cnoreabbrev |
+ cnext cnoremap |
+ cnfile cnoremenu |
+ :cn^ |
+ ]])
+ feed('<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*2
+ {3: }|
+ :cn |
+ cnewer cnoreabbrev |
+ cnext cnoremap |
+ cnfile cnoremenu |
+ {113:cnewer}{3: cnext cnfile > }|
+ :cnewer^ |
+ ]])
+ feed('<Esc>:sign u<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*5
+ {3: }|
+ :sign un |
+ undefine unplace |
+ :sign un^ |
+ ]])
+
+ -- "longest:full" shows wildmenu whether it finds a candidate or not;
+ -- item not selected
+ feed('<Esc>')
+ command('set wildmode=longest:full,full')
+ feed(':sign u<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*7
+ {3:undefine unplace }|
+ :sign un^ |
+ ]])
+ feed('<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*7
+ {113:undefine}{3: unplace }|
+ :sign undefine^ |
+ ]])
+
+ feed('<Esc>:cn<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*7
+ {3:cnewer cnext cnfile > }|
+ :cn^ |
+ ]])
+ feed('<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*7
+ {113:cnewer}{3: cnext cnfile > }|
+ :cnewer^ |
+ ]])
+
+ -- If "longest,full" finds a candidate, wildmenu is not shown
+ feed('<Esc>')
+ command('set wildmode=longest,full')
+ feed(':sign u<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*8
+ :sign un^ |
+ ]])
+ -- Subsequent wildchar shows wildmenu
+ feed('<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*7
+ {113:undefine}{3: unplace }|
+ :sign undefine^ |
+ ]])
+
+ -- 'longest' does not find candidate, and displays menu without selecting item
+ feed('<Esc>')
+ command('set wildmode=longest,noselect')
+ feed(':cn<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*7
+ {3:cnewer cnext cnfile > }|
+ :cn^ |
+ ]])
+
+ command('set wildmode& wildoptions=pum')
+
+ feed('<C-U><Esc>')
+
-- check positioning with multibyte char in pattern
command('e långfile1')
command('sp långfile2')
@@ -4748,7 +4853,7 @@ describe('builtin popupmenu', function()
]])
feed('<esc>')
- command('close')
+ command('%bwipe')
command('set wildmode=full')
-- special case: when patterns ends with "/", show menu items aligned
@@ -4761,6 +4866,18 @@ describe('builtin popupmenu', function()
{1:~ }{n: file2 }{1: }|
:e compdir/file1^ |
]])
+
+ -- position is correct when expanding environment variable #20348
+ command('cd ..')
+ fn.setenv('XNDIR', 'wildpum/Xnamedir')
+ feed('<C-U>e $XNDIR/<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*11
+ {1:~ }{12: XdirA/ }{1: }|
+ {1:~ }{n: XfileA }{1: }|
+ :e wildpum/Xnamedir/XdirA/^ |
+ ]])
end)
end
@@ -4877,24 +4994,52 @@ describe('builtin popupmenu', function()
-- pressing <Tab> should display the wildmenu
feed('<Tab>')
- screen:expect([[
+ local s1 = [[
|
{1:~ }|*4
{1:~ }{12: undefine }{1: }|
{1:~ }{n: unplace }{1: }|
:sign undefine^ |
- ]])
+ ]]
+ screen:expect(s1)
eq(1, fn.wildmenumode())
-- pressing <Tab> second time should select the next entry in the menu
feed('<Tab>')
- screen:expect([[
+ local s2 = [[
|
{1:~ }|*4
{1:~ }{n: undefine }{1: }|
{1:~ }{12: unplace }{1: }|
:sign unplace^ |
+ ]]
+ screen:expect(s2)
+ eq(1, fn.wildmenumode())
+
+ -- If "longest" finds no candidate in "longest,full", "full" is used
+ feed('<Esc>')
+ command('set wildmode=longest,full')
+ command('set wildoptions=pum')
+ feed(':sign un<Tab>')
+ screen:expect(s1)
+ feed('<Tab>')
+ screen:expect(s2)
+
+ -- Similarly for "longest,noselect:full"
+ feed('<Esc>')
+ command('set wildmode=longest,noselect:full')
+ feed(':sign un<Tab>')
+ screen:expect([[
+ |
+ {1:~ }|*4
+ {1:~ }{n: undefine }{1: }|
+ {1:~ }{n: unplace }{1: }|
+ :sign un^ |
]])
+ feed('<Tab>')
+ screen:expect(s1)
+ feed('<Tab>')
+ screen:expect(s2)
end)
it('wildoptions=pum with a wrapped line in buffer vim-patch:8.2.4655', function()
diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim
@@ -2286,8 +2286,11 @@ func Wildmode_tests()
" when using longest completion match, matches shorter than the argument
" should be ignored (happens with :help)
set wildmode=longest,full
- call feedkeys(":help a*\t\<C-B>\"\<CR>", 'xt')
- call assert_equal('"help a', @:)
+ " XXX: This test is incorrect. ':help a*' will never yield 'help a'
+ " because '`a' exists as a menu item. The intent was to test a case
+ " handled by nextwild().
+ " call feedkeys(":help a*\t\<C-B>\"\<CR>", 'xt')
+ " call assert_equal('"help a', @:)
" non existing file
call feedkeys(":e a1b2y3z4\t\<C-B>\"\<CR>", 'xt')
call assert_equal('"e a1b2y3z4', @:)
@@ -2893,10 +2896,12 @@ func Test_wildmenu_pum()
call term_sendkeys(buf, "sign xyz\<Esc>:sign \<Tab>\<C-E>\<Up>")
call VerifyScreenDump(buf, 'Test_wildmenu_pum_29', {})
- " Check "list" still works
+ " Check that when "longest" produces no result, "list" works
call term_sendkeys(buf, "\<C-U>set wildmode=longest,list\<CR>")
call term_sendkeys(buf, ":cn\<Tab>")
call VerifyScreenDump(buf, 'Test_wildmenu_pum_30', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_30', {})
call term_sendkeys(buf, "s")
call VerifyScreenDump(buf, 'Test_wildmenu_pum_31', {})
@@ -2997,7 +3002,66 @@ func Test_wildmenu_pum()
call term_sendkeys(buf, "\<Esc>:set wildchazz\<Left>\<Left>\<Tab>\<C-Y>")
call VerifyScreenDump(buf, 'Test_wildmenu_pum_53', {})
- call term_sendkeys(buf, "\<C-U>\<CR>")
+ call term_sendkeys(buf, "\<Esc>:set showtabline& laststatus& lazyredraw&\<CR>")
+
+ " "longest:list" shows list whether it finds a candidate or not
+ call term_sendkeys(buf, ":set wildmode=longest:list,full wildoptions&\<CR>")
+ call term_sendkeys(buf, ":cn\<Tab>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_55', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_56', {})
+ call term_sendkeys(buf, "\<Esc>:sign u\<Tab>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_57', {})
+
+ " "longest:full" shows wildmenu whether it finds a candidate or not; item not selected
+ call term_sendkeys(buf, "\<Esc>:set wildmode=longest:full,full\<CR>")
+ call term_sendkeys(buf, ":sign u\<Tab>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_58', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_59', {})
+ call term_sendkeys(buf, "\<Esc>:cn\<Tab>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_60', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_61', {})
+
+ " If "longest,full" finds a candidate, wildmenu is not shown
+ call term_sendkeys(buf, "\<Esc>:set wildmode=longest,full\<CR>")
+ call term_sendkeys(buf, ":sign u\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_62', {})
+ " Subsequent wildchar shows wildmenu
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_63', {})
+
+ " 'longest' does not find candidate, and displays menu without selecting item
+ call term_sendkeys(buf, "\<Esc>:set wildmode=longest,noselect\<CR>")
+ call term_sendkeys(buf, ":cn\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_64', {})
+
+ " If "longest" finds no candidate in "longest,full", "full" is used
+ call term_sendkeys(buf, "\<Esc>:set wildmode=longest,full\<CR>")
+ call term_sendkeys(buf, ":set wildoptions=pum\<CR>")
+ call term_sendkeys(buf, ":sign un\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_09', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_10', {})
+
+ " Similarly for "longest,noselect:full"
+ call term_sendkeys(buf, "\<Esc>:set wildmode=longest,noselect:full\<CR>")
+ call term_sendkeys(buf, ":sign un\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_65', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_09', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_10', {})
+
+ call term_sendkeys(buf, "\<C-U>\<Esc>")
call StopVimInTerminal(buf)
endfunc
@@ -4406,7 +4470,7 @@ func Test_cmdcomplete_info()
call feedkeys(":h echom\<cr>", "tx") " No expansion
call assert_equal('{}', g:cmdcomplete_info)
call feedkeys($":h echoms{trig}\<cr>", "tx")
- call assert_equal('{''cmdline_orig'': '''', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
+ call assert_equal('{''cmdline_orig'': ''h echoms'', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
call feedkeys($":h echom{trig}\<cr>", "tx")
call assert_equal(
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 0, ''matches'': ['':echom'', '':echomsg''], ''selected'': 0}',
@@ -4422,7 +4486,7 @@ func Test_cmdcomplete_info()
set wildoptions=pum
call feedkeys($":h echoms{trig}\<cr>", "tx")
- call assert_equal('{''cmdline_orig'': '''', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
+ call assert_equal('{''cmdline_orig'': ''h echoms'', ''pum_visible'': 0, ''matches'': [], ''selected'': 0}', g:cmdcomplete_info)
call feedkeys($":h echom{trig}\<cr>", "tx")
call assert_equal(
\ '{''cmdline_orig'': ''h echom'', ''pum_visible'': 1, ''matches'': ['':echom'', '':echomsg''], ''selected'': 0}',
@@ -4874,4 +4938,33 @@ func Test_cmdline_changed()
call Ntest_override("char_avail", 0)
endfunc
+" Issue #18035: long lines should not get listed twice in the menu when
+" 'wildmode' contains 'noselect'
+func Test_long_line_noselect()
+ CheckScreendump
+
+ let lines =<< trim [SCRIPT]
+ set wildmenu wildoptions=pum wildmode=noselect,full
+ command -nargs=1 -complete=custom,Entries DoubleEntry echo
+ func Entries(a, b, c)
+ return 'loooooooooooooooong quite loooooooooooong, really loooooooooooong, probably too looooooooooooooooooooooooooong entry'
+ endfunc
+ [SCRIPT]
+ call writefile(lines, 'XTest_wildmenu', 'D')
+ let buf = RunVimInTerminal('-S XTest_wildmenu', {'rows': 8, 'cols': 60})
+
+ call term_sendkeys(buf, ":DoubleEntry \<Tab>")
+ call VerifyScreenDump(buf, 'Test_long_line_noselect_1', {})
+
+ call term_sendkeys(buf, "\<Esc>:DoubleEntry \<Tab>\<C-N>")
+ call VerifyScreenDump(buf, 'Test_long_line_noselect_2', {})
+
+ call term_sendkeys(buf, "\<Esc>:DoubleEntry \<Tab>\<C-N>\<C-N>")
+ call VerifyScreenDump(buf, 'Test_long_line_noselect_3', {})
+
+ " clean up
+ call term_sendkeys(buf, "\<Esc>")
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim
@@ -5620,7 +5620,7 @@ func Test_completetimeout_autocompletetimeout()
set completetimeout=1
call feedkeys("Gof\<C-N>\<F2>\<Esc>0", 'xt!')
let match_count = len(b:matches->mapnew('v:val.word'))
- call assert_true(match_count < 2000)
+ call assert_true(match_count < 4000)
set completetimeout=1000
call feedkeys("\<Esc>Sf\<C-N>\<F2>\<Esc>0", 'xt!')