patch 9.2.0596: cmdline completion popup cannot be scrolled with the mouse

Commit: 
https://github.com/vim/vim/commit/96dbab257a881ee4a552e1acf62e7d4168dc444a
Author: Hirohito Higashi <[email protected]>
Date:   Thu Jun 4 19:52:50 2026 +0000

    patch 9.2.0596: cmdline completion popup cannot be scrolled with the mouse
    
    Problem:  In command-line completion with a popup menu ('wildoptions'
              contains "pum"), the info popup shown next to the menu could
              not be scrolled, unlike the Insert mode completion info popup
              which scrolls with the mouse wheel.
    Solution: When the mouse pointer is on top of the info popup, scroll it
              with the mouse wheel in command-line mode as well, without
              closing the completion popup menu.
    
    closes: #20146
    closes: #20418
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    Signed-off-by: Hirohito Higashi <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 0ef255210..0df50ff60 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.2.  Last change: 2026 May 25
+*options.txt*  For Vim version 9.2.  Last change: 2026 Jun 04
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -10523,7 +10523,10 @@ A jump table for the options with a short description 
can be found at |Q_op|.
                        is not supported for file and directory names and
                        instead wildcard expansion is used.
          pum           Display the completion matches using the popup menu in
-                       the same style as the |ins-completion-menu|.
+                       the same style as the |ins-completion-menu|.  When an
+                       info popup is shown next to the menu, it can be
+                       scrolled by moving the mouse pointer on top of it and
+                       using the scroll wheel.
          tagfile       When using CTRL-D to list matching tags, the kind of
                        tag and the file of the tag is listed.  Only one match
                        is displayed per line.  Often used tag kinds are:
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 7e26d6cdb..ea20fa96b 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -2064,12 +2064,15 @@ getcmdline_int(
        // navigating the wild menu (i.e. the key is not 'wildchar' or
        // 'wildcharm' or Ctrl-N or Ctrl-P or Ctrl-A or Ctrl-L).
        // If the popup menu is displayed, then PageDown and PageUp keys are
-       // also used to navigate the menu.
+       // also used to navigate the menu, and the mouse scroll wheel keys
+       // scroll the info popup.
        end_wildmenu = (!key_is_wc
                && c != Ctrl_N && c != Ctrl_P && c != Ctrl_A && c != Ctrl_L);
        end_wildmenu = end_wildmenu && (!cmdline_pum_active() ||
                            (c != K_PAGEDOWN && c != K_PAGEUP
-                            && c != K_KPAGEDOWN && c != K_KPAGEUP));
+                            && c != K_KPAGEDOWN && c != K_KPAGEUP
+                            && c != K_MOUSEDOWN && c != K_MOUSEUP
+                            && c != K_MOUSELEFT && c != K_MOUSERIGHT));
 
        // free expanded names when finished walking through matches
        if (end_wildmenu)
@@ -2413,11 +2416,21 @@ getcmdline_int(
                cmdline_left_right_mouse(c, &ignore_drag_release);
                goto cmdline_not_changed;
 
-       // Mouse scroll wheel: ignored here
+       // Mouse scroll wheel: scroll the completion info popup when the mouse
+       // is on top of it, otherwise ignored here.
        case K_MOUSEDOWN:
        case K_MOUSEUP:
        case K_MOUSELEFT:
        case K_MOUSERIGHT:
+#ifdef FEAT_PROP_POPUP
+               if (cmdline_pum_active())
+                   cmdline_mousescroll(c == K_MOUSEDOWN ? MSCR_DOWN
+                                     : c == K_MOUSEUP ? MSCR_UP
+                                     : c == K_MOUSELEFT ? MSCR_LEFT
+                                     : MSCR_RIGHT);
+#endif
+               goto cmdline_not_changed;
+
        // Alternate buttons ignored here
        case K_X1MOUSE:
        case K_X1DRAG:
diff --git a/src/mouse.c b/src/mouse.c
index ddd94fffc..9080334e7 100644
--- a/src/mouse.c
+++ b/src/mouse.c
@@ -1396,6 +1396,62 @@ ins_mousescroll(int dir)
     }
 }
 
+#if defined(FEAT_PROP_POPUP) || defined(PROTO)
+/*
+ * Command-line mode implementation for scrolling in direction "dir", which is
+ * one of the MSCR_ values.  Scrolls the completion info popup when the mouse
+ * pointer is on top of it.
+ * Returns TRUE when the info popup was scrolled.
+ */
+    int
+cmdline_mousescroll(int dir)
+{
+    cmdarg_T   cap;
+    oparg_T    oa;
+
+    CLEAR_FIELD(cap);
+    clear_oparg(&oa);
+    cap.oap = &oa;
+    cap.arg = dir;
+
+    switch (dir)
+    {
+       case MSCR_UP:    cap.cmdchar = K_MOUSEUP; break;
+       case MSCR_DOWN:  cap.cmdchar = K_MOUSEDOWN; break;
+       case MSCR_LEFT:  cap.cmdchar = K_MOUSELEFT; break;
+       case MSCR_RIGHT: cap.cmdchar = K_MOUSERIGHT; break;
+    }
+
+    if (mouse_row < 0 || mouse_col < 0)
+       return FALSE;
+
+    int            row = mouse_row;
+    int            col = mouse_col;
+    win_T   *wp;
+
+    // Only scroll when the mouse is on top of the info popup.
+    wp = mouse_find_win(&row, &col, FIND_POPUP);
+    if (wp == NULL || !WIN_IS_POPUP(wp) || !(wp->w_popup_flags & POPF_INFO)
+                                                       || !wp->w_has_scrollbar)
+       return FALSE;
+
+    win_T   *old_curwin = curwin;
+
+    curwin = wp;
+    curbuf = wp->w_buffer;
+    // Call the common mouse scroll function shared with other modes.
+    do_mousescroll(&cap);
+    curwin = old_curwin;
+    curbuf = curwin->w_buffer;
+
+    // Cmdline mode doesn't normally call update_screen(), so redraw the
+    // completion popup menu, which also repaints the info popup.
+    if (cmdline_pum_active())
+       cmdline_pum_display();
+    return TRUE;
+}
+#endif
+
 /*
  * Return TRUE if "c" is a mouse key.
  */
diff --git a/src/proto/mouse.pro b/src/proto/mouse.pro
index ead818498..0c320769b 100644
--- a/src/proto/mouse.pro
+++ b/src/proto/mouse.pro
@@ -4,6 +4,7 @@ void mouse_set_hor_scroll_step(long step);
 int do_mouse(oparg_T *oap, int c, int dir, long count, int fixindent);
 void ins_mouse(int c);
 void ins_mousescroll(int dir);
+int cmdline_mousescroll(int dir);
 int is_mouse_key(int c);
 int get_mouse_button(int code, int *is_click, int *is_drag);
 int get_pseudo_mouse_code(int button, int is_click, int is_drag);
diff --git a/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_1.dump 
b/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_1.dump
new file mode 100644
index 000000000..dc622d74a
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_1.dump
@@ -0,0 +1,12 @@
+| +0&#ffffff0@23|╔+0#0000001#e0e0e08|═@14|X| +0#0000000#ffffff0@33
+|~+0#4040ff13&| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1| @1| 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |2| @1| 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |3| @1| 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |4| @1| 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |5| @1| 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |6| @1| 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |7| @1| 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |8| @1| 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @6| +0#0000001#e0e0e08|a|p@1|l|e| @1|f| |f|r|u|i|t| |║| |i|n|f|o| 
|l|i|n|e| |9| @1| +0#0000000#a8a8a8255|║+0#0000001#e0e0e08| 
+0#4040ff13#ffffff0@33
+|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| 
|╚+0&#e0e0e08|═@14|⇲| +0#4040ff13#ffffff0@33
+|:+0#0000000&|D|i|c|t|C|m|d| |a|p@1|l|e> @60
diff --git a/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_2.dump 
b/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_2.dump
new file mode 100644
index 000000000..115b1b949
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_2.dump
@@ -0,0 +1,12 @@
+| +0&#ffffff0@23|╔+0#0000001#e0e0e08|═@14|X| +0#0000000#ffffff0@33
+|~+0#4040ff13&| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1|0| | 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1@1| | 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1|2| | 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1|3| | 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1|4| | 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1|5| | 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1|6| | 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1|7| | 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @6| +0#0000001#e0e0e08|a|p@1|l|e| @1|f| |f|r|u|i|t| |║| |i|n|f|o| 
|l|i|n|e| |1|8| | +0#0000000#a8a8a8255|║+0#0000001#e0e0e08| 
+0#4040ff13#ffffff0@33
+|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| 
|╚+0&#e0e0e08|═@14|⇲| +0#4040ff13#ffffff0@33
+|:+0#0000000&|D|i|c|t|C|m|d| |a|p@1|l|e> @42|0|,|0|-|1| @8|A|l@1| 
diff --git a/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_3.dump 
b/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_3.dump
new file mode 100644
index 000000000..1a0c4fbcb
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_3.dump
@@ -0,0 +1,12 @@
+| +0&#ffffff0@23|╔+0#0000001#e0e0e08|═@14|X| +0#0000000#ffffff0@33
+|~+0#4040ff13&| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |4| @1| 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |5| @1| 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |6| @1| 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |7| @1| 
+0#0000000#0000001|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |8| @1| 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |9| @1| 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1|0| | 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @22|║+0#0000001#e0e0e08| |i|n|f|o| |l|i|n|e| |1@1| | 
+0#0000000#a8a8a8255|║+0#0000001#e0e0e08| +0#4040ff13#ffffff0@33
+|~| @6| +0#0000001#e0e0e08|a|p@1|l|e| @1|f| |f|r|u|i|t| |║| |i|n|f|o| 
|l|i|n|e| |1|2| | +0#0000000#a8a8a8255|║+0#0000001#e0e0e08| 
+0#4040ff13#ffffff0@33
+|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| 
|╚+0&#e0e0e08|═@14|⇲| +0#4040ff13#ffffff0@33
+|:+0#0000000&|D|i|c|t|C|m|d| |a|p@1|l|e> @42|0|,|0|-|1| @8|A|l@1| 
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index 31fb1f8ff..8fbaa502b 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -4768,6 +4768,52 @@ func Test_customlist_dict_completion_info_popup()
   call StopVimInTerminal(buf)
 endfunc
 
+" Test that the mouse scroll wheel scrolls the info popup of the command line
+" completion popup menu when the mouse pointer is on top of it.
+func Test_wildmenu_pum_info_mouse_scroll()
+  CheckScreendump
+  CheckFeature quickfix
+
+  let lines =<< trim END
+    func DictComp(A, L, P)
+      let info = join(map(range(1, 30), '"info line " .. v:val'), "
")
+      return [
+            \ {'word': 'apple',  'kind': 'f', 'menu': 'fruit', 'info': info},
+            \ {'word': 'banana', 'kind': 'f', 'menu': 'fruit', 'info': info},
+            \ ]
+    endfunc
+    command -nargs=1 -complete=customlist,DictComp DictCmd echo <q-args>
+    set wildmenu wildoptions=pum completeopt=menu,popup mouse=a
+
+    " Put the mouse on top of the info popup and turn the scroll wheel.
+    func ScrollInfo(keys)
+      let pos = popup_getpos(popup_findinfo())
+      call test_setmouse(pos.line + 1, pos.col + 1)
+      call feedkeys(a:keys, 'nt')
+    endfunc
+    cnoremap <F6> <Cmd>call ScrollInfo(repeat("\<ScrollWheelDown>", 3))<CR>
+    cnoremap <F7> <Cmd>call ScrollInfo(repeat("\<ScrollWheelUp>", 2))<CR>
+  END
+  call writefile(lines, 'XtestWildmenuMouseScroll', 'D')
+  let buf = RunVimInTerminal('-S XtestWildmenuMouseScroll', #{rows: 12})
+
+  " The info popup is shown next to the completion popup menu.
+  call term_sendkeys(buf, ":DictCmd \<Tab>")
+  call VerifyScreenDump(buf, 'Test_wildmenu_pum_info_mouse_scroll_1', {})
+
+  " Scrolling down with the wheel scrolls the info popup without closing the
+  " completion popup menu.
+  call term_sendkeys(buf, "\<F6>")
+  call VerifyScreenDump(buf, 'Test_wildmenu_pum_info_mouse_scroll_2', {})
+
+  " Scrolling back up scrolls the info popup up again.
+  call term_sendkeys(buf, "\<F7>")
+  call VerifyScreenDump(buf, 'Test_wildmenu_pum_info_mouse_scroll_3', {})
+
+  call term_sendkeys(buf, "\<Esc>")
+  call StopVimInTerminal(buf)
+endfunc
+
 func Test_cmdline_complete_findfunc_dict()
   CheckScreendump
 
diff --git a/src/version.c b/src/version.c
index 80200eb4c..b8863bad2 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    596,
 /**/
     595,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1wVEEZ-005cVU-IQ%40256bit.org.

Raspunde prin e-mail lui