]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0596: cmdline completion popup cannot be scrolled with the mouse v9.2.0596
authorHirohito Higashi <h.east.727@gmail.com>
Thu, 4 Jun 2026 19:52:50 +0000 (19:52 +0000)
committerChristian Brabandt <cb@256bit.org>
Thu, 4 Jun 2026 19:52:50 +0000 (19:52 +0000)
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) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/options.txt
src/ex_getln.c
src/mouse.c
src/proto/mouse.pro
src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_1.dump [new file with mode: 0644]
src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_2.dump [new file with mode: 0644]
src/testdir/dumps/Test_wildmenu_pum_info_mouse_scroll_3.dump [new file with mode: 0644]
src/testdir/test_cmdline.vim
src/version.c

index 0ef255210b88202ade021771028cef2eaf5a3325..0df50ff60626d00a29c34d307064e0c31fed305e 100644 (file)
@@ -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:
index 7e26d6cdb4f45600bd4d376c4253a91305836bc5..ea20fa96b8ff9830293477e63b2b20bc24fd0f0e 100644 (file)
@@ -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:
index ddd94fffcf0e1f7b69cd072f28456dfb330e994b..9080334e7113b5e4cae628d00cbc7473e21888df 100644 (file)
@@ -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.
  */
index ead81849851ba3b5e347c79a916a8b2cd6b2df15..0c320769ba1b81851e04ae1c5695d33686552eb5 100644 (file)
@@ -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 (file)
index 0000000..dc622d7
--- /dev/null
@@ -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 (file)
index 0000000..115b1b9
--- /dev/null
@@ -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 (file)
index 0000000..1a0c4fb
--- /dev/null
@@ -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| 
index 31fb1f8ffc8cffeff6fde8b887b2b9c326c94af9..8fbaa502b8abd4ca640fb80f47f2c870b7a97069 100644 (file)
@@ -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'), "\n")
+      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
 
index 80200eb4ce47eeb8db240b56fff70406c76b7916..b8863bad28158d23d1aa847b9b1d7588048c3455 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    596,
 /**/
     595,
 /**/