mirror of
https://github.com/neovim/neovim.git
synced 2026-06-15 16:23:48 +00:00
vim-patch:9.2.0596: cmdline completion popup cannot be scrolled with the mouse (#40142)
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: vim/vim#20146
closes: vim/vim#20418
96dbab257a
Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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:
|
||||
|
||||
5
runtime/lua/vim/_meta/options.gen.lua
generated
5
runtime/lua/vim/_meta/options.gen.lua
generated
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 <q-args>
|
||||
set wildmenu wildoptions=pum completeopt=menu,popup mouse=a
|
||||
]])
|
||||
|
||||
feed(':DictCmd <Tab>')
|
||||
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('<Esc>')
|
||||
end)
|
||||
|
||||
-- oldtest: Test_cmdline_complete_findfunc_dict()
|
||||
it("'findfunc' can return extra info for cmdline completion", function()
|
||||
screen:try_resize(55, 12)
|
||||
|
||||
@@ -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 <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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user