From 2eccb4d0bec87854cfc214e1feb96b5a8f4a69df Mon Sep 17 00:00:00 2001 From: Girish Palya Date: Sun, 31 Aug 2025 18:44:29 +0200 Subject: [PATCH] patch 9.1.1714: completion: wildmode=longest:full selects wrong item Problem: completion: wildmode=longest:full selects wrong item (zeertzjq) Solution: Fix issue, refactor ex_getln.c slightly (Girish Palya) fixes: #18102 closes: #18125 Signed-off-by: Girish Palya Signed-off-by: Christian Brabandt --- src/cmdexpand.c | 80 ++++++++++++--------- src/ex_getln.c | 73 ++++++++++--------- src/proto/cmdexpand.pro | 2 +- src/testdir/dumps/Test_wildmenu_pum_30.dump | 10 +-- src/testdir/dumps/Test_wildmenu_pum_31.dump | 2 +- src/testdir/dumps/Test_wildmenu_pum_54.dump | 10 +++ src/testdir/dumps/Test_wildmenu_pum_55.dump | 10 +++ src/testdir/dumps/Test_wildmenu_pum_56.dump | 10 +++ src/testdir/dumps/Test_wildmenu_pum_57.dump | 10 +++ src/testdir/dumps/Test_wildmenu_pum_58.dump | 10 +++ src/testdir/test_cmdline.vim | 31 +++++++- src/testdir/test_ins_complete.vim | 2 +- src/version.c | 2 + 13 files changed, 173 insertions(+), 79 deletions(-) create mode 100644 src/testdir/dumps/Test_wildmenu_pum_54.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_55.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_56.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_57.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_58.dump diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 8b42d7ab1f..a3a8e467ec 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -387,8 +387,7 @@ nextwild( } /* - * 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( @@ -396,11 +395,9 @@ cmdline_pum_create( expand_T *xp, char_u **matches, int numMatches, - int showtail, - int noselect) + int showtail) { - int i; - int prefix_len; + int prefix_len; // Add all the completion matches compl_match_array = ALLOC_MULT(pumitem_T, numMatches); @@ -408,7 +405,7 @@ cmdline_pum_create( return EXPAND_UNSUCCESSFUL; compl_match_arraysize = numMatches; - for (i = 0; i < numMatches; i++) + for (int i = 0; i < numMatches; i++) { compl_match_array[i].pum_text = SHOW_MATCH(i); compl_match_array[i].pum_info = NULL; @@ -426,12 +423,6 @@ cmdline_pum_create( - vim_strsize(matches[0]); compl_startcol = MAX(0, compl_startcol - prefix_len); - // no default selection - compl_selected = noselect ? -1 : 0; - - pum_clear(); - cmdline_pum_display(); - return EXPAND_OK; } @@ -868,14 +859,27 @@ get_next_or_prev_match(int mode, expand_T *xp) } // Display matches on screen - if (compl_match_array) + if (p_wmnu) { - compl_selected = findex; - cmdline_pum_display(); + if (compl_match_array) + { + compl_selected = findex; + cmdline_pum_display(); + } + else if (vim_strchr(p_wop, WOP_PUM) != NULL) + { + if (cmdline_pum_create(get_cmdline_info(), xp, xp->xp_files, + xp->xp_numfiles, cmd_showtail) == EXPAND_OK) + { + compl_selected = findex; + pum_clear(); + cmdline_pum_display(); + } + } + else + win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, findex, + cmd_showtail); } - else if (p_wmnu) - win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, findex, - cmd_showtail); xp->xp_selected = findex; // Return the original text or the selected match @@ -1277,12 +1281,12 @@ showmatches_oneline( } /* - * 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. + * Display completion matches. + * Returns EXPAND_NOTHING when the character that triggered expansion should be + * inserted as a normal character. */ int -showmatches(expand_T *xp, int wildmenu, int noselect) +showmatches(expand_T *xp, int display_wildmenu, int display_list, int noselect) { cmdline_info_T *ccline = get_cmdline_info(); int numMatches; @@ -1311,12 +1315,21 @@ showmatches(expand_T *xp, int wildmenu, int noselect) showtail = cmd_showtail; } - if (wildmenu && vim_strchr(p_wop, WOP_PUM) != NULL) - // cmdline completion popup menu (with wildoptions=pum) - return cmdline_pum_create(ccline, xp, matches, numMatches, - showtail && !noselect, noselect); + if (display_wildmenu && !display_list + && vim_strchr(p_wop, WOP_PUM) != NULL) + { + 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(); + } + return retval; + } - if (!wildmenu) + if (display_list) { msg_didany = FALSE; // lines_left will be set msg_start(); // prepare for paging @@ -1328,10 +1341,11 @@ showmatches(expand_T *xp, int wildmenu, int noselect) } if (got_int) - got_int = FALSE; // only int. the completion, not the cmd line - else if (wildmenu) - win_redr_status_matches(xp, numMatches, matches, noselect ? -1 : 0, showtail); - else + got_int = FALSE; // only interrupt the completion, not the cmd line + else if (display_wildmenu && !display_list) + win_redr_status_matches(xp, numMatches, matches, noselect ? -1 : 0, + showtail); // display statusbar menu + else if (display_list) { // find the length of the longest file name maxlen = 0; @@ -4236,7 +4250,7 @@ wildmenu_translate_key( } } - if (did_wild_list) + if (cmdline_pum_active() || did_wild_list || wild_menu_showing) { if (c == K_LEFT) c = Ctrl_P; diff --git a/src/ex_getln.c b/src/ex_getln.c index 8426f00cbd..794075c57e 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -944,23 +944,20 @@ cmdline_wildchar_complete( { int wim_index = *wim_index_p; int res; - int j; + int cmdpos_before; int options = WILD_NO_BEEP; - int noselect = p_wmnu && (wim_flags[0] & WIM_NOSELECT); + int wim_noselect = p_wmnu && (wim_flags[0] & WIM_NOSELECT); if (wim_flags[wim_index] & WIM_BUFLASTUSED) options |= WILD_BUFLASTUSED; if (xp->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 (xp->xp_numfiles > 1 && !*did_wild_list - && ((wim_flags[wim_index] & WIM_LIST) - || (p_wmnu && (wim_flags[wim_index] & WIM_FULL) != 0))) + && (wim_flags[wim_index] & WIM_LIST)) { - (void)showmatches(xp, - p_wmnu && ((wim_flags[wim_index] & WIM_LIST) == 0), - noselect); + (void)showmatches(xp, FALSE, TRUE, wim_noselect); redrawcmd(); *did_wild_list = TRUE; } @@ -973,6 +970,11 @@ cmdline_wildchar_complete( } else // typed p_wc first time { + int wim_longest = (wim_flags[0] & WIM_LONGEST); + int wim_list = (wim_flags[0] & WIM_LIST); + int wim_full = (wim_flags[0] & WIM_FULL); + + wim_index = 0; if (c == p_wc || c == p_wcm || c == K_WILD) { options |= WILD_MAY_EXPAND_PATTERN; @@ -983,15 +985,15 @@ cmdline_wildchar_complete( else xp->xp_pre_incsearch_pos = curwin->w_cursor; } - wim_index = 0; - j = ccline.cmdpos; + cmdpos_before = ccline.cmdpos; + // if 'wildmode' first contains "longest", get longest // common part - if (wim_flags[0] & WIM_LONGEST) + if (wim_longest) res = nextwild(xp, WILD_LONGEST, options, escape); else { - if (noselect || (wim_flags[wim_index] & WIM_LIST)) + if (wim_noselect || wim_list) options |= WILD_NOSELECT; res = nextwild(xp, WILD_EXPAND_KEEP, options, escape); } @@ -1011,28 +1013,29 @@ cmdline_wildchar_complete( 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 - && xp->xp_numfiles > (noselect ? 0 : 1)) + // Display matches + if (res == OK && xp->xp_numfiles > (wim_noselect ? 0 : 1)) { - // a "longest" that didn't do anything is skipped (but not - // "list:longest") - if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j) - wim_index = 1; - if ((wim_flags[wim_index] & WIM_LIST) - || (p_wmnu && (wim_flags[wim_index] & (WIM_FULL | WIM_NOSELECT)))) + // If "longest" fails to identify the longest item, try other + // 'wim' values if available + if (wim_longest && ccline.cmdpos == cmdpos_before) { - (void)showmatches(xp, p_wmnu - && ((wim_flags[wim_index] & WIM_LIST) == 0), noselect); - redrawcmd(); - *did_wild_list = TRUE; - if (wim_flags[wim_index] & WIM_LONGEST) - nextwild(xp, WILD_LONGEST, options, escape); + if (wim_full) + nextwild(xp, WILD_NEXT, options, escape); + if (wim_list || (p_wmnu && wim_full)) + (void)showmatches(xp, p_wmnu, wim_list, FALSE); } - else - vim_beep(BO_WILD); + else if (!wim_longest) + { + if (wim_list || (p_wmnu && (wim_full || wim_noselect))) + (void)showmatches(xp, p_wmnu, wim_list, wim_noselect); + else + vim_beep(BO_WILD); + } + + redrawcmd(); + if (wim_list) + *did_wild_list = TRUE; } else if (xp->xp_numfiles == -1) xp->xp_context = EXPAND_NOTHING; @@ -1959,7 +1962,8 @@ getcmdline_int( c = wildmenu_translate_key(&ccline, c, &xpc, did_wild_list); int key_is_wc = (c == p_wc && KeyTyped) || c == p_wcm; - if ((cmdline_pum_active() || did_wild_list) && !key_is_wc) + if ((cmdline_pum_active() || wild_menu_showing || 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 @@ -2118,8 +2122,7 @@ getcmdline_int( || p_wmnu)) { // Trigger the popup menu when wildoptions=pum - showmatches(&xpc, p_wmnu - && ((wim_flags[wim_index] & WIM_LIST) == 0), + showmatches(&xpc, p_wmnu, wim_flags[wim_index] & WIM_LIST, wim_flags[0] & WIM_NOSELECT); } if (nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK @@ -2235,7 +2238,7 @@ getcmdline_int( goto cmdline_not_changed; case Ctrl_D: - if (showmatches(&xpc, FALSE, wim_flags[0] & WIM_NOSELECT) + if (showmatches(&xpc, FALSE, TRUE, wim_flags[0] & WIM_NOSELECT) == EXPAND_NOTHING) break; // Use ^D as normal char instead diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro index 69e7da9be6..ff065145fd 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 wildmenu, int noselect); +int showmatches(expand_T *xp, int display_wildmenu, int display_list, int noselect); 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/dumps/Test_wildmenu_pum_30.dump b/src/testdir/dumps/Test_wildmenu_pum_30.dump index 76e4780ea2..732e8a8ac0 100644 --- a/src/testdir/dumps/Test_wildmenu_pum_30.dump +++ b/src/testdir/dumps/Test_wildmenu_pum_30.dump @@ -1,10 +1,10 @@ -|~+0#4040ff13#ffffff0| @73 +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 |~| @73 |~| @73 |~| @73 |~| @73 |~| @73 -|:+0#0000000&|c|n| @71 -|c|n|e|w|e|r| @6|c|n|f|i|l|e| @6|c|n|o|r|e|m|a|p| @40 -|c|n|e|x|t| @7|c|n|o|r|e|a|b@1|r|e|v| @1|c|n|o|r|e|m|e|n|u| @39 -|:|c|n> @71 +|~| @73 +|~| @73 +|:+0#0000000&|c|n> @71 diff --git a/src/testdir/dumps/Test_wildmenu_pum_31.dump b/src/testdir/dumps/Test_wildmenu_pum_31.dump index 157f16c89c..76e4780ea2 100644 --- a/src/testdir/dumps/Test_wildmenu_pum_31.dump +++ b/src/testdir/dumps/Test_wildmenu_pum_31.dump @@ -7,4 +7,4 @@ |:+0#0000000&|c|n| @71 |c|n|e|w|e|r| @6|c|n|f|i|l|e| @6|c|n|o|r|e|m|a|p| @40 |c|n|e|x|t| @7|c|n|o|r|e|a|b@1|r|e|v| @1|c|n|o|r|e|m|e|n|u| @39 -|:|c|n|s> @70 +|:|c|n> @71 diff --git a/src/testdir/dumps/Test_wildmenu_pum_54.dump b/src/testdir/dumps/Test_wildmenu_pum_54.dump new file mode 100644 index 0000000000..0bdff9368c --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_54.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|c+0#0000001#ffff4012|n|e|w|e|r| +3#0000000#ffffff0@1|c|n|e|x|t| @1|c|n|f|i|l|e| @1|c|n|o|r|e|a|b@1|r|e|v| @1|c|n|o|r|e|m|a|p| @1|c|n|o|r|e|m|e|n|u| @19 +|:+0&&|c|n|e|w|e|r> @67 diff --git a/src/testdir/dumps/Test_wildmenu_pum_55.dump b/src/testdir/dumps/Test_wildmenu_pum_55.dump new file mode 100644 index 0000000000..76e4780ea2 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_55.dump @@ -0,0 +1,10 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|c|n| @71 +|c|n|e|w|e|r| @6|c|n|f|i|l|e| @6|c|n|o|r|e|m|a|p| @40 +|c|n|e|x|t| @7|c|n|o|r|e|a|b@1|r|e|v| @1|c|n|o|r|e|m|e|n|u| @39 +|:|c|n> @71 diff --git a/src/testdir/dumps/Test_wildmenu_pum_56.dump b/src/testdir/dumps/Test_wildmenu_pum_56.dump new file mode 100644 index 0000000000..b3cf107fe2 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_56.dump @@ -0,0 +1,10 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|c|n| @71 +|c|n|e|w|e|r| @6|c|n|f|i|l|e| @6|c|n|o|r|e|m|a|p| @40 +|c|n|e|x|t| @7|c|n|o|r|e|a|b@1|r|e|v| @1|c|n|o|r|e|m|e|n|u| @39 +|c+0#0000001#ffff4012|n|e|w|e|r| +3#0000000#ffffff0@1|c|n|e|x|t| @1|c|n|f|i|l|e| @1|c|n|o|r|e|a|b@1|r|e|v| @1|c|n|o|r|e|m|a|p| @1|c|n|o|r|e|m|e|n|u| @19 +|:+0&&|c|n|e|w|e|r> @67 diff --git a/src/testdir/dumps/Test_wildmenu_pum_57.dump b/src/testdir/dumps/Test_wildmenu_pum_57.dump new file mode 100644 index 0000000000..73547387eb --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_57.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| |u|n> @66 diff --git a/src/testdir/dumps/Test_wildmenu_pum_58.dump b/src/testdir/dumps/Test_wildmenu_pum_58.dump new file mode 100644 index 0000000000..4170891a48 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_58.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|u+0#0000001#ffff4012|n|d|e|f|i|n|e| +3#0000000#ffffff0@1|u|n|p|l|a|c|e| @57 +|:+0&&|s|i|g|n| |u|n|d|e|f|i|n|e> @60 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index d09faed79a..84234b803c 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -2825,11 +2825,11 @@ func Test_wildmenu_pum() call term_sendkeys(buf, "sign xyz\:sign \\\") call VerifyScreenDump(buf, 'Test_wildmenu_pum_29', {}) - " Check "list" still works + " Check that when "longest" produces no result, "list" works call term_sendkeys(buf, "\set wildmode=longest,list\") call term_sendkeys(buf, ":cn\") call VerifyScreenDump(buf, 'Test_wildmenu_pum_30', {}) - call term_sendkeys(buf, "s") + call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_wildmenu_pum_31', {}) " Tests a directory name contained full-width characters. @@ -2929,7 +2929,32 @@ func Test_wildmenu_pum() call term_sendkeys(buf, "\:set wildchazz\\\\") call VerifyScreenDump(buf, 'Test_wildmenu_pum_53', {}) - call term_sendkeys(buf, "\\") + call term_sendkeys(buf, "\:set showtabline& laststatus& lazyredraw&\") + + " Verify that if "longest" finds nothing, wildmenu is still shown + call term_sendkeys(buf, ":set wildmode=longest:full,full wildoptions&\") + call term_sendkeys(buf, ":cn\") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_54', {}) + + " Verify that if "longest" finds nothing, "list" is still shown + call term_sendkeys(buf, "\:set wildmode=longest:list,full\") + call term_sendkeys(buf, ":cn\") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_55', {}) + call term_sendkeys(buf, "\") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_56', {}) + + " Verify that if "longest" finds a candidate, wildmenu is not shown + call term_sendkeys(buf, "\:set wildmode=longest:full,full wildoptions&\") + call term_sendkeys(buf, ":sign u\") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_57', {}) + " Subsequent wildchar shows wildmenu + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_58', {}) + + call term_sendkeys(buf, "\\") call StopVimInTerminal(buf) endfunc diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index b0ed072371..f35bb18e04 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -5507,7 +5507,7 @@ func Test_completetimeout_autocompletetimeout() set completetimeout=1 call feedkeys("Gof\\\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("\Sf\\\0", 'xt!') diff --git a/src/version.c b/src/version.c index 4a04a97b8d..aa70f4c25d 100644 --- a/src/version.c +++ b/src/version.c @@ -724,6 +724,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1714, /**/ 1713, /**/