]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1639: completion: popup may be misplaced v9.1.1639
authorGirish Palya <girishji@gmail.com>
Sat, 16 Aug 2025 16:13:46 +0000 (18:13 +0200)
committerChristian Brabandt <cb@256bit.org>
Sat, 16 Aug 2025 16:13:46 +0000 (18:13 +0200)
Problem:  During commandline completiom, popup window placement can be
          incorrect when 'noselect' is present in 'wildmode'
          (Shane-XB-Qian)
Solution: Disable "showtail" feature when 'noselect' is present.

fixes: #17969
closes: #18001

Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/cmdexpand.c
src/ex_getln.c
src/proto/cmdexpand.pro
src/testdir/dumps/Test_expand_env_var_1.dump [new file with mode: 0644]
src/testdir/dumps/Test_expand_env_var_2.dump [new file with mode: 0644]
src/testdir/test_cmdline.vim
src/version.c

index dc65ae8eee9eecca14dd21504238fb8cc85822a1..416a7a7b39cc80e7255dd376c1f3383fcb680727 100644 (file)
@@ -342,7 +342,7 @@ nextwild(
     {
        size_t  plen = STRLEN(p);
        int     difflen;
-       int     v;
+       int     v = OK;
 
        difflen = (int)plen - xp->xp_pattern_len;
        if (ccline->cmdlen + difflen + 4 > ccline->cmdbufflen)
@@ -350,8 +350,7 @@ nextwild(
            v = realloc_cmdbuff(ccline->cmdlen + difflen + 4);
            xp->xp_pattern = ccline->cmdbuff + i;
        }
-       else
-           v = OK;
+
        if (v == OK)
        {
            mch_memmove(&ccline->cmdbuff[ccline->cmdpos + difflen],
@@ -395,7 +394,7 @@ cmdline_pum_create(
        int             showtail)
 {
     int                i;
-    int                columns;
+    int                prefix_len;
 
     // Add all the completion matches
     compl_match_array = ALLOC_MULT(pumitem_T, numMatches);
@@ -415,13 +414,11 @@ cmdline_pum_create(
 
     // Compute the popup menu starting column
     compl_startcol = ccline == NULL ? 0 : vim_strsize(ccline->cmdbuff) + 1;
-    columns = vim_strsize(xp->xp_pattern);
+    prefix_len = vim_strsize(xp->xp_pattern);
     if (showtail)
-    {
-       columns += vim_strsize(showmatches_gettail(matches[0]));
-       columns -= vim_strsize(matches[0]);
-    }
-    compl_startcol = MAX(0, compl_startcol - columns);
+       prefix_len += vim_strsize(showmatches_gettail(matches[0]))
+           - vim_strsize(matches[0]);
+    compl_startcol = MAX(0, compl_startcol - prefix_len);
 
     // no default selection
     compl_selected = -1;
@@ -1279,12 +1276,12 @@ showmatches_oneline(
  * be inserted like a normal character.
  */
     int
-showmatches(expand_T *xp, int wildmenu UNUSED)
+showmatches(expand_T *xp, int wildmenu, int noselect)
 {
     cmdline_info_T     *ccline = get_cmdline_info();
     int                numMatches;
     char_u     **matches;
-    int                i, j;
+    int                i;
     int                maxlen;
     int                lines;
     int                columns;
@@ -1300,12 +1297,13 @@ showmatches(expand_T *xp, int wildmenu UNUSED)
 
     if (xp->xp_numfiles == -1)
     {
+       int retval;
        set_expand_context(xp);
-       i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
-                                                   &numMatches, &matches);
+       retval = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
+               &numMatches, &matches);
+       if (retval != EXPAND_OK)
+           return retval;
        showtail = expand_showtail(xp);
-       if (i != EXPAND_OK)
-           return i;
     }
     else
     {
@@ -1316,7 +1314,8 @@ showmatches(expand_T *xp, int wildmenu UNUSED)
 
     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);
+       return cmdline_pum_create(ccline, xp, matches, numMatches,
+               showtail && !noselect);
 
     if (!wildmenu)
     {
@@ -1339,17 +1338,18 @@ showmatches(expand_T *xp, int wildmenu UNUSED)
        maxlen = 0;
        for (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));
-           if (j > maxlen)
-               maxlen = j;
+               len = vim_strsize(SHOW_MATCH(i));
+           if (len > maxlen)
+               maxlen = len;
        }
 
        if (xp->xp_context == EXPAND_TAGS_LISTFILES)
index a96ece70c5476ce7c4d0ff49539cad564287b4f9..7dbb743ecc9d002a732b8120c97e5a2e89d13287 100644 (file)
@@ -946,10 +946,11 @@ cmdline_wildchar_complete(
     int                res;
     int                j;
     int                options = WILD_NO_BEEP;
+    int                noselect = (wim_flags[0] & WIM_NOSELECT) != 0;
 
     if (wim_flags[wim_index] & WIM_BUFLASTUSED)
        options |= WILD_BUFLASTUSED;
-    if (wim_flags[0] & WIM_NOSELECT)
+    if (noselect)
        options |= WILD_KEEP_SOLE_ITEM;
     if (xp->xp_numfiles > 0)   // typed p_wc at least twice
     {
@@ -960,7 +961,8 @@ cmdline_wildchar_complete(
                    || (p_wmnu && (wim_flags[wim_index] & WIM_FULL) != 0)))
        {
            (void)showmatches(xp,
-                   p_wmnu && ((wim_flags[wim_index] & WIM_LIST) == 0));
+                   p_wmnu && ((wim_flags[wim_index] & WIM_LIST) == 0),
+                   noselect);
            redrawcmd();
            *did_wild_list = TRUE;
        }
@@ -1011,7 +1013,7 @@ cmdline_wildchar_complete(
        // "list", or no change and 'wildmode' contains "longest,list",
        // list all matches
        if (res == OK
-               && xp->xp_numfiles > ((wim_flags[wim_index] & WIM_NOSELECT) ? 0 : 1))
+               && xp->xp_numfiles > (noselect ? 0 : 1))
        {
            // a "longest" that didn't do anything is skipped (but not
            // "list:longest")
@@ -1031,7 +1033,7 @@ cmdline_wildchar_complete(
                    p_wmnu = p_wmnu_save;
                }
                (void)showmatches(xp, p_wmnu
-                       && ((wim_flags[wim_index] & WIM_LIST) == 0));
+                       && ((wim_flags[wim_index] & WIM_LIST) == 0), noselect);
                redrawcmd();
                *did_wild_list = TRUE;
                if (wim_flags[wim_index] & WIM_LONGEST)
@@ -2013,7 +2015,8 @@ getcmdline_int(
        {
            if (cmdline_pum_active())
            {
-               skip_pum_redraw = skip_pum_redraw && (vim_isprintc(c)
+               skip_pum_redraw = skip_pum_redraw && !key_is_wc
+                   && (vim_isprintc(c)
                        || c == K_BS || c == Ctrl_H || c == K_DEL
                        || c == K_KDEL || c == Ctrl_W || c == Ctrl_U);
                cmdline_pum_remove(&ccline, skip_pum_redraw);
@@ -2124,7 +2127,8 @@ getcmdline_int(
                {
                    // Trigger the popup menu when wildoptions=pum
                    showmatches(&xpc, p_wmnu
-                           && ((wim_flags[wim_index] & WIM_LIST) == 0));
+                           && ((wim_flags[wim_index] & WIM_LIST) == 0),
+                           wim_flags[0] & WIM_NOSELECT);
                }
                if (nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK
                        && nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK)
@@ -2239,7 +2243,8 @@ getcmdline_int(
                goto cmdline_not_changed;
 
        case Ctrl_D:
-               if (showmatches(&xpc, FALSE) == EXPAND_NOTHING)
+               if (showmatches(&xpc, FALSE, wim_flags[0] & WIM_NOSELECT)
+                       == EXPAND_NOTHING)
                    break;      // Use ^D as normal char instead
 
                redrawcmd();
index 388b80b13bad93169754786b2ffdfc4d6ea69abc..501b5e6420512f50d7b97fc30d6c7949ec8b9c14 100644 (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 wildmenu);
+int showmatches(expand_T *xp, int wildmenu, 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_expand_env_var_1.dump b/src/testdir/dumps/Test_expand_env_var_1.dump
new file mode 100644 (file)
index 0000000..64cad0d
--- /dev/null
@@ -0,0 +1,8 @@
+| +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| | +0#0000001#ffd7ff255|a|/|b|/|c|/| @8| +0#4040ff13#ffffff0@56
+|~| | +0#0000001#ffd7ff255|a|/|b|/|f|i|l|e|X|n|a|m|e|1| | +0#4040ff13#ffffff0@56
+|~| | +0#0000001#ffd7ff255|a|/|b|/|f|i|l|e|X|n|a|m|e|2| | +0#4040ff13#ffffff0@56
+|:+0#0000000&|e| |$|T|E|S|T|D|I|R|/> @62
diff --git a/src/testdir/dumps/Test_expand_env_var_2.dump b/src/testdir/dumps/Test_expand_env_var_2.dump
new file mode 100644 (file)
index 0000000..95bcb45
--- /dev/null
@@ -0,0 +1,8 @@
+| +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| | +0#0000001#e0e0e08|a|/|b|/|c|/| @8| +0#4040ff13#ffffff0@56
+|~| | +0#0000001#ffd7ff255|a|/|b|/|f|i|l|e|X|n|a|m|e|1| | +0#4040ff13#ffffff0@56
+|~| | +0#0000001#ffd7ff255|a|/|b|/|f|i|l|e|X|n|a|m|e|2| | +0#4040ff13#ffffff0@56
+|:+0#0000000&|e| |a|/|b|/|c|/> @65
index 523bde86be651f7a6f176ae78b1fa7c76b582d98..d2c9c7898a013d3b2f0726a5963ed7b12e9f9c1d 100644 (file)
@@ -4859,4 +4859,34 @@ func Test_wildtrigger_update_screen()
   cnoremap <buffer> <F8> <C-R>=wildtrigger()[-1]<CR>
 endfunc
 
+" Issue #17969: With 'noselect', the popup menu should appear next to the
+" environment variable being expanded. Disable 'showtail' when completing
+" file paths when 'noselect' is present.
+func Test_noselect_expand_env_var()
+  CheckScreendump
+
+  let lines =<< trim [SCRIPT]
+    set wildmenu wildoptions=pum wildmode=noselect,full
+    let $TESTDIR = 'a/b'
+  [SCRIPT]
+  call writefile(lines, 'XTest_wildmenu', 'D')
+  let buf = RunVimInTerminal('-S XTest_wildmenu', {'rows': 8})
+
+  call mkdir('a/b/c', 'pR')
+  call writefile(['asdf'], 'a/b/fileXname1')
+  call writefile(['foo'], 'a/b/fileXname2')
+
+  call term_sendkeys(buf, ":e $TESTDIR/\<Tab>")
+  call VerifyScreenDump(buf, 'Test_expand_env_var_1', {})
+
+  call term_sendkeys(buf, "\<C-N>")
+  call VerifyScreenDump(buf, 'Test_expand_env_var_2', {})
+
+  call term_sendkeys(buf, "\<C-P>")
+  call VerifyScreenDump(buf, 'Test_expand_env_var_1', {})
+  " clean up
+  call term_sendkeys(buf, "\<Esc>")
+  call StopVimInTerminal(buf)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 5488cf8a14f30e5c791bf2d161e6369083e17df3..cc08bffa8b9ff3177922966f62f048d5d3eaff5e 100644 (file)
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1639,
 /**/
     1638,
 /**/