From: Hirohito Higashi Date: Thu, 4 Jun 2026 19:52:50 +0000 (+0000) Subject: patch 9.2.0596: cmdline completion popup cannot be scrolled with the mouse X-Git-Tag: v9.2.0596^0 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=96dbab257a881ee4a552e1acf62e7d4168dc444a;p=thirdparty%2Fvim.git 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) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 0ef255210b..0df50ff606 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 7e26d6cdb4..ea20fa96b8 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 ddd94fffcf..9080334e71 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 ead8184985..0c320769ba 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 0000000000..dc622d74ab --- /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 0000000000..115b1b949e --- /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 0000000000..1a0c4fbcbb --- /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 31fb1f8ffc..8fbaa502b8 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'), "\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 + 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 call ScrollInfo(repeat("\", 3)) + cnoremap call ScrollInfo(repeat("\", 2)) + 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 \") + 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, "\") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_info_mouse_scroll_2', {}) + + " Scrolling back up scrolls the info popup up again. + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_info_mouse_scroll_3', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) +endfunc + func Test_cmdline_complete_findfunc_dict() CheckScreendump diff --git a/src/version.c b/src/version.c index 80200eb4ce..b8863bad28 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, /**/