diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 1e3b9ed804..999f069301 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -7512,7 +7512,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/runtime/lua/vim/_meta/options.gen.lua b/runtime/lua/vim/_meta/options.gen.lua index 60ee5a71ae..57c98a4ee4 100644 --- a/runtime/lua/vim/_meta/options.gen.lua +++ b/runtime/lua/vim/_meta/options.gen.lua @@ -8175,7 +8175,10 @@ vim.go.wim = vim.go.wildmode --- 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/nvim/ex_getln.c b/src/nvim/ex_getln.c index a9e7631223..4590b86f18 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1451,7 +1451,9 @@ static int command_line_execute(VimState *state, int key) && s->c != Ctrl_L); end_wildmenu = end_wildmenu && (!cmdline_pum_active() || (s->c != K_PAGEDOWN && s->c != K_PAGEUP - && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP)); + && s->c != K_KPAGEDOWN && s->c != K_KPAGEUP + && s->c != K_MOUSEDOWN && s->c != K_MOUSEUP + && s->c != K_MOUSELEFT && s->c != K_MOUSERIGHT)); // free expanded names when finished walking through matches if (end_wildmenu) { @@ -2210,11 +2212,21 @@ static int command_line_handle_key(CommandLineState *s) command_line_left_right_mouse(s); return command_line_not_changed(s); - // 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: + if (cmdline_pum_active()) { + cmdline_mousescroll(s->c == K_MOUSEDOWN + ? MSCR_DOWN + : (s->c == K_MOUSEUP + ? MSCR_UP + : s->c == K_MOUSELEFT ? MSCR_LEFT : MSCR_RIGHT)); + } + return command_line_not_changed(s); + // Alternate buttons ignored here case K_X1MOUSE: case K_X1DRAG: diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 596dd2d4af..76723b119e 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -8,6 +8,7 @@ #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand.h" #include "nvim/cursor.h" #include "nvim/decoration.h" #include "nvim/drawscreen.h" @@ -1135,6 +1136,58 @@ void ins_mousescroll(int dir) } } +/// 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. +bool 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 grid = mouse_grid; + int row = mouse_row; + int col = mouse_col; + + // Only scroll when the mouse is on top of the info popup. + win_T *wp = mouse_find_win_inner(&grid, &row, &col); + if (wp == NULL || !wp->w_float_is_info) { + 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 call it here. + update_screen(); + return true; +} + /// Return true if "c" is a mouse key. bool is_mouse_key(int c) { diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 295af7a334..6e73e7e6e3 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -10497,7 +10497,10 @@ local options = { 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/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 5b091af274..99fcbdb1b0 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -5690,6 +5690,191 @@ describe('builtin popupmenu', function() end end) + -- oldtest: Test_wildmenu_pum_info_mouse_scroll() + it('scrolling cmdline pum info popup', function() + screen:try_resize(55, 12) + exec([[ + 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 + ]]) + + feed(':DictCmd ') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:-------------------------------------------------------]|*11 + [3:-------------------------------------------------------]| + ## grid 2 + | + {1:~ }|*10 + ## grid 3 + :DictCmd apple^ | + ## grid 4 + {n:info line 1 }| + {n:info line 2 }| + {n:info line 3 }| + {n:info line 4 }| + {n:info line 5 }| + {n:info line 6 }| + {n:info line 7 }| + {n:info line 8 }| + {n:info line 9 }| + {n:info line 10}| + {n:info line 11}| + {n:info line 12}| + ## grid 5 + {12: apple f fruit }| + {n: banana f fruit }| + ]], + float_pos = { + [5] = { -1, 'SW', 1, 11, 8, false, 250, 3, 9, 8 }, + [4] = { 1001, 'NW', 1, 9, 24, true, 50, 1, 0, 24 }, + }, + }) + else + screen:expect([[ + {n:info line 1 } | + {1:~ }{n:info line 2 }{1: }| + {1:~ }{n:info line 3 }{1: }| + {1:~ }{n:info line 4 }{1: }| + {1:~ }{n:info line 5 }{1: }| + {1:~ }{n:info line 6 }{1: }| + {1:~ }{n:info line 7 }{1: }| + {1:~ }{n:info line 8 }{1: }| + {1:~ }{n:info line 9 }{1: }| + {1:~ }{12: apple f fruit }{n:info line 10}{1: }| + {1:~ }{n: banana f fruit info line 11}{1: }| + :DictCmd apple^ | + ]]) + end + + if send_mouse_grid then + api.nvim_input_mouse('wheel', 'down', '', 4, 0, 0) + api.nvim_input_mouse('wheel', 'down', '', 4, 0, 0) + api.nvim_input_mouse('wheel', 'down', '', 4, 0, 0) + else + api.nvim_input_mouse('wheel', 'down', '', 0, 0, 24) + api.nvim_input_mouse('wheel', 'down', '', 0, 0, 24) + api.nvim_input_mouse('wheel', 'down', '', 0, 0, 24) + end + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:-------------------------------------------------------]|*11 + [3:-------------------------------------------------------]| + ## grid 2 + | + {1:~ }|*10 + ## grid 3 + :DictCmd apple^ | + ## grid 4 + {n:info line 10}| + {n:info line 11}| + {n:info line 12}| + {n:info line 13}| + {n:info line 14}| + {n:info line 15}| + {n:info line 16}| + {n:info line 17}| + {n:info line 18}| + {n:info line 19}| + {n:info line 20}| + {n:info line 21}| + ## grid 5 + {12: apple f fruit }| + {n: banana f fruit }| + ]], + float_pos = { + [5] = { -1, 'SW', 1, 11, 8, false, 250, 3, 9, 8 }, + [4] = { 1001, 'NW', 1, 9, 24, true, 50, 1, 0, 24 }, + }, + }) + else + screen:expect([[ + {n:info line 10} | + {1:~ }{n:info line 11}{1: }| + {1:~ }{n:info line 12}{1: }| + {1:~ }{n:info line 13}{1: }| + {1:~ }{n:info line 14}{1: }| + {1:~ }{n:info line 15}{1: }| + {1:~ }{n:info line 16}{1: }| + {1:~ }{n:info line 17}{1: }| + {1:~ }{n:info line 18}{1: }| + {1:~ }{12: apple f fruit }{n:info line 19}{1: }| + {1:~ }{n: banana f fruit info line 20}{1: }| + :DictCmd apple^ | + ]]) + end + + if send_mouse_grid then + api.nvim_input_mouse('wheel', 'up', '', 4, 0, 0) + api.nvim_input_mouse('wheel', 'up', '', 4, 0, 0) + else + api.nvim_input_mouse('wheel', 'up', '', 0, 0, 24) + api.nvim_input_mouse('wheel', 'up', '', 0, 0, 24) + end + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:-------------------------------------------------------]|*11 + [3:-------------------------------------------------------]| + ## grid 2 + | + {1:~ }|*10 + ## grid 3 + :DictCmd apple^ | + ## grid 4 + {n:info line 4 }| + {n:info line 5 }| + {n:info line 6 }| + {n:info line 7 }| + {n:info line 8 }| + {n:info line 9 }| + {n:info line 10}| + {n:info line 11}| + {n:info line 12}| + {n:info line 13}| + {n:info line 14}| + {n:info line 15}| + ## grid 5 + {12: apple f fruit }| + {n: banana f fruit }| + ]], + float_pos = { + [5] = { -1, 'SW', 1, 11, 8, false, 250, 3, 9, 8 }, + [4] = { 1001, 'NW', 1, 9, 24, true, 50, 1, 0, 24 }, + }, + }) + else + screen:expect([[ + {n:info line 4 } | + {1:~ }{n:info line 5 }{1: }| + {1:~ }{n:info line 6 }{1: }| + {1:~ }{n:info line 7 }{1: }| + {1:~ }{n:info line 8 }{1: }| + {1:~ }{n:info line 9 }{1: }| + {1:~ }{n:info line 10}{1: }| + {1:~ }{n:info line 11}{1: }| + {1:~ }{n:info line 12}{1: }| + {1:~ }{12: apple f fruit }{n:info line 13}{1: }| + {1:~ }{n: banana f fruit info line 14}{1: }| + :DictCmd apple^ | + ]]) + end + + feed('') + end) + -- oldtest: Test_cmdline_complete_findfunc_dict() it("'findfunc' can return extra info for cmdline completion", function() screen:try_resize(55, 12) diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index 160f77bbda..1ebb13145d 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -4709,6 +4709,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