neovim/test/functional/ui/mouse_spec.lua
bfredl e61228a214 fix(tests): needing two calls to setup a screen is cringe
Before calling "attach" a screen object is just a dummy container for
(row, col) values whose purpose is to be sent as part of the "attach"
function call anyway.

Just create the screen in an attached state directly. Keep the complete
(row, col, options) config together. It is still completely valid to
later detach and re-attach as needed, including to another session.
2024-11-14 12:40:57 +01:00

2072 lines
70 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local clear, feed, api = n.clear, n.feed, n.api
local insert, feed_command = n.insert, n.feed_command
local eq, fn = t.eq, n.fn
local poke_eventloop = n.poke_eventloop
local command = n.command
local exec = n.exec
describe('ui/mouse/input', function()
local screen
before_each(function()
clear()
api.nvim_set_option_value('mouse', 'a', {})
api.nvim_set_option_value('list', true, {})
-- NB: this is weird, but mostly irrelevant to the test
-- So I didn't bother to change it
command('set listchars=eol:$')
command('setl listchars=nbsp:x')
screen = Screen.new(25, 5)
screen:add_extra_attr_ids {
[100] = {
bold = true,
background = Screen.colors.LightGrey,
foreground = Screen.colors.Blue1,
},
}
command('set mousemodel=extend')
feed('itesting<cr>mouse<cr>support and selection<esc>')
screen:expect([[
testing |
mouse |
support and selectio^n |
{1:~ }|
|
]])
end)
it('single left click moves cursor', function()
feed('<LeftMouse><2,1>')
screen:expect {
grid = [[
testing |
mo^use |
support and selection |
{1:~ }|
|
]],
mouse_enabled = true,
}
feed('<LeftMouse><0,0>')
screen:expect([[
^testing |
mouse |
support and selection |
{1:~ }|
|
]])
end)
it("in external ui works with unset 'mouse'", function()
api.nvim_set_option_value('mouse', '', {})
feed('<LeftMouse><2,1>')
screen:expect {
grid = [[
testing |
mo^use |
support and selection |
{1:~ }|
|
]],
mouse_enabled = false,
}
feed('<LeftMouse><0,0>')
screen:expect([[
^testing |
mouse |
support and selection |
{1:~ }|
|
]])
end)
it('double left click enters visual mode', function()
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
screen:expect([[
{17:testin}^g |
mouse |
support and selection |
{1:~ }|
{5:-- VISUAL --} |
]])
end)
it('triple left click enters visual line mode', function()
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
screen:expect([[
^t{17:esting} |
mouse |
support and selection |
{1:~ }|
{5:-- VISUAL LINE --} |
]])
end)
it('quadruple left click enters visual block mode', function()
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
screen:expect([[
^testing |
mouse |
support and selection |
{1:~ }|
{5:-- VISUAL BLOCK --} |
]])
end)
describe('tab drag', function()
it('in tabline on filler space moves tab to the end', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><4,0>')
screen:expect([[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
feed('<LeftDrag><14,0>')
screen:expect([[
{24: + bar }{5: + foo }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
end)
it('in tabline to the left moves tab left', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><11,0>')
-- Prevent the case where screen:expect() with "unchanged" returns too early,
-- causing the click position to be overwritten by the next drag.
poke_eventloop()
screen:expect {
grid = [[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]],
unchanged = true,
}
feed('<LeftDrag><6,0>')
screen:expect([[
{5: + bar }{24: + foo }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
end)
it('in tabline to the right moves tab right', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><4,0>')
screen:expect([[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
feed('<LeftDrag><7,0>')
screen:expect([[
{24: + bar }{5: + foo }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
end)
it('out of tabline under filler space moves tab to the end', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><4,0>')
screen:expect([[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
feed('<LeftDrag><4,1>')
screen:expect {
grid = [[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]],
unchanged = true,
}
feed('<LeftDrag><14,1>')
screen:expect([[
{24: + bar }{5: + foo }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
end)
it('out of tabline to the left moves tab left', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><11,0>')
-- Prevent the case where screen:expect() with "unchanged" returns too early,
-- causing the click position to be overwritten by the next drag.
poke_eventloop()
screen:expect {
grid = [[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]],
unchanged = true,
}
feed('<LeftDrag><11,1>')
screen:expect {
grid = [[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]],
unchanged = true,
}
feed('<LeftDrag><6,1>')
screen:expect([[
{5: + bar }{24: + foo }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
end)
it('out of tabline to the right moves tab right', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><4,0>')
screen:expect([[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
feed('<LeftDrag><4,1>')
screen:expect {
grid = [[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]],
unchanged = true,
}
feed('<LeftDrag><7,1>')
screen:expect([[
{24: + bar }{5: + foo }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
end)
end)
describe('tabline', function()
it('left click in default tabline (tabpage label) switches to tab', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><4,0>')
screen:expect([[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
feed('<LeftMouse><6,0>')
screen:expect_unchanged()
feed('<LeftMouse><10,0>')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><12,0>')
screen:expect_unchanged()
end)
it('left click in default tabline (blank space) switches tab', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><20,0>')
screen:expect([[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
feed('<LeftMouse><22,0>')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
end)
it('left click in default tabline (close label) closes tab', function()
api.nvim_set_option_value('hidden', true, {})
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<LeftMouse><24,0>')
screen:expect([[
this is fo^o |
{1:~ }|*3
|
]])
end)
it('double click in default tabline opens new tab before', function()
feed_command('%delete')
insert('this is foo')
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<2-LeftMouse><4,0>')
screen:expect([[
{5: Name] }{24: + foo + bar }{2: }{24:X}|
{1:^$} |
{1:~ }|*2
|
]])
command('tabclose')
screen:expect([[
{5: + foo }{24: + bar }{2: }{24:X}|
this is fo^o |
{1:~ }|*2
|
]])
feed('<2-LeftMouse><20,0>')
screen:expect([[
{24: + foo + bar }{5: Name] }{2: }{24:X}|
{1:^$} |
{1:~ }|*2
|
]])
command('tabclose')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
this is ba^r{1:$} |
{1:~ }|*2
|
]])
feed('<2-LeftMouse><10,0>')
screen:expect([[
{24: + foo }{5: Name] }{24: + bar }{2: }{24:X}|
{1:^$} |
{1:~ }|*2
|
]])
end)
describe('%@ label', function()
before_each(function()
feed_command([[
function Test(...)
let g:reply = a:000
return copy(a:000) " Check for memory leaks: return should be freed
endfunction
]])
feed_command([[
function Test2(...)
return call('Test', a:000 + [2])
endfunction
]])
api.nvim_set_option_value('tabline', '%@Test@test%X-%5@Test2@test2', {})
api.nvim_set_option_value('showtabline', 2, {})
screen:expect([[
{2:test-test2 }|
testing |
mouse |
support and selectio^n |
|
]])
api.nvim_set_var('reply', {})
end)
local check_reply = function(expected)
eq(expected, api.nvim_get_var('reply'))
api.nvim_set_var('reply', {})
end
local test_click = function(name, click_str, click_num, mouse_button, modifiers)
local function doit(do_click)
eq(1, fn.has('tablineat'))
do_click(0, 3)
check_reply({ 0, click_num, mouse_button, modifiers })
do_click(0, 4)
check_reply({})
do_click(0, 6)
check_reply({ 5, click_num, mouse_button, modifiers, 2 })
do_click(0, 13)
check_reply({ 5, click_num, mouse_button, modifiers, 2 })
end
it(name .. ' works (pseudokey)', function()
doit(function(row, col)
feed(click_str .. '<' .. col .. ',' .. row .. '>')
end)
end)
it(name .. ' works (nvim_input_mouse)', function()
doit(function(row, col)
local buttons = { l = 'left', m = 'middle', r = 'right' }
local modstr = (click_num > 1) and tostring(click_num) or ''
for char in string.gmatch(modifiers, '%w') do
modstr = modstr .. char .. '-' -- - not needed but should be accepted
end
api.nvim_input_mouse(buttons[mouse_button], 'press', modstr, 0, row, col)
end)
end)
end
test_click('single left click', '<LeftMouse>', 1, 'l', ' ')
test_click('shifted single left click', '<S-LeftMouse>', 1, 'l', 's ')
test_click('shifted single left click with alt modifier', '<S-A-LeftMouse>', 1, 'l', 's a ')
test_click(
'shifted single left click with alt and ctrl modifiers',
'<S-C-A-LeftMouse>',
1,
'l',
'sca '
)
-- <C-RightMouse> does not work
test_click('shifted single right click with alt modifier', '<S-A-RightMouse>', 1, 'r', 's a ')
-- Modifiers do not work with MiddleMouse
test_click(
'shifted single middle click with alt and ctrl modifiers',
'<MiddleMouse>',
1,
'm',
' '
)
-- Modifiers do not work with N-*Mouse
test_click('double left click', '<2-LeftMouse>', 2, 'l', ' ')
test_click('triple left click', '<3-LeftMouse>', 3, 'l', ' ')
test_click('quadruple left click', '<4-LeftMouse>', 4, 'l', ' ')
test_click('double right click', '<2-RightMouse>', 2, 'r', ' ')
test_click('triple right click', '<3-RightMouse>', 3, 'r', ' ')
test_click('quadruple right click', '<4-RightMouse>', 4, 'r', ' ')
test_click('double middle click', '<2-MiddleMouse>', 2, 'm', ' ')
test_click('triple middle click', '<3-MiddleMouse>', 3, 'm', ' ')
test_click('quadruple middle click', '<4-MiddleMouse>', 4, 'm', ' ')
end)
end)
it('left drag changes visual selection', function()
-- drag events must be preceded by a click
feed('<LeftMouse><2,1>')
screen:expect([[
testing |
mo^use |
support and selection |
{1:~ }|
|
]])
feed('<LeftDrag><4,1>')
screen:expect([[
testing |
mo{17:us}^e |
support and selection |
{1:~ }|
{5:-- VISUAL --} |
]])
feed('<LeftDrag><2,2>')
screen:expect([[
testing |
mo{17:use} |
{17:su}^pport and selection |
{1:~ }|
{5:-- VISUAL --} |
]])
feed('<LeftDrag><0,0>')
screen:expect([[
^t{17:esting} |
{17:mou}se |
support and selection |
{1:~ }|
{5:-- VISUAL --} |
]])
end)
it('left drag changes visual selection after tab click', function()
feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
feed_command('tabprevious') -- go to first tab
screen:expect([[
{5: + foo }{24: + bar }{2: }{24:X}|
testing |
mouse |
support and selectio^n |
:tabprevious |
]])
feed('<LeftMouse><10,0><LeftRelease>') -- go to second tab
n.poke_eventloop()
feed('<LeftMouse><0,1>')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
^this is bar{1:$} |
{1:~ }|*2
:tabprevious |
]])
feed('<LeftDrag><4,1>')
screen:expect([[
{24: + foo }{5: + bar }{2: }{24:X}|
{17:this}^ is bar{1:$} |
{1:~ }|*2
{5:-- VISUAL --} |
]])
end)
it('left drag changes visual selection in split layout', function()
screen:try_resize(53, 14)
command('set mouse=a')
command('vsplit')
command('wincmd l')
command('below split')
command('enew')
feed('ifoo\nbar<esc>')
screen:expect {
grid = [[
testing │testing |
mouse │mouse |
support and selection │support and selection |
{1:~ }│{1:~ }|*2
{1:~ }│{2:[No Name] [+] }|
{1:~ }│foo{1:$} |
{1:~ }│ba^r{1:$} |
{1:~ }│{1:~ }|*4
{2:[No Name] [+] }{3:[No Name] [+] }|
|
]],
}
api.nvim_input_mouse('left', 'press', '', 0, 6, 27)
screen:expect {
grid = [[
testing │testing |
mouse │mouse |
support and selection │support and selection |
{1:~ }│{1:~ }|*2
{1:~ }│{2:[No Name] [+] }|
{1:~ }│^foo{1:$} |
{1:~ }│bar{1:$} |
{1:~ }│{1:~ }|*4
{2:[No Name] [+] }{3:[No Name] [+] }|
|
]],
}
api.nvim_input_mouse('left', 'drag', '', 0, 7, 30)
screen:expect {
grid = [[
testing │testing |
mouse │mouse |
support and selection │support and selection |
{1:~ }│{1:~ }|*2
{1:~ }│{2:[No Name] [+] }|
{1:~ }│{17:foo}{100:$} |
{1:~ }│{17:bar}{1:^$} |
{1:~ }│{1:~ }|*4
{2:[No Name] [+] }{3:[No Name] [+] }|
{5:-- VISUAL --} |
]],
}
end)
it('two clicks will enter VISUAL and dragging selects words', function()
feed('<LeftMouse><2,2>')
feed('<LeftRelease><2,2>')
feed('<LeftMouse><2,2>')
screen:expect([[
testing |
mouse |
{17:suppor}^t and selection |
{1:~ }|
{5:-- VISUAL --} |
]])
feed('<LeftDrag><0,1>')
screen:expect([[
testing |
^m{17:ouse} |
{17:support} and selection |
{1:~ }|
{5:-- VISUAL --} |
]])
feed('<LeftDrag><4,0>')
screen:expect([[
^t{17:esting} |
{17:mouse} |
{17:support} and selection |
{1:~ }|
{5:-- VISUAL --} |
]])
feed('<LeftDrag><14,2>')
screen:expect([[
testing |
mouse |
{17:support and selectio}^n |
{1:~ }|
{5:-- VISUAL --} |
]])
end)
it('three clicks will enter VISUAL LINE and dragging selects lines', function()
feed('<LeftMouse><2,2>')
feed('<LeftRelease><2,2>')
feed('<LeftMouse><2,2>')
feed('<LeftRelease><2,2>')
feed('<LeftMouse><2,2>')
screen:expect([[
testing |
mouse |
{17:su}^p{17:port and selection} |
{1:~ }|
{5:-- VISUAL LINE --} |
]])
feed('<LeftDrag><0,1>')
screen:expect([[
testing |
^m{17:ouse} |
{17:support and selection} |
{1:~ }|
{5:-- VISUAL LINE --} |
]])
feed('<LeftDrag><4,0>')
screen:expect([[
{17:test}^i{17:ng} |
{17:mouse} |
{17:support and selection} |
{1:~ }|
{5:-- VISUAL LINE --} |
]])
feed('<LeftDrag><14,2>')
screen:expect([[
testing |
mouse |
{17:support and se}^l{17:ection} |
{1:~ }|
{5:-- VISUAL LINE --} |
]])
end)
it('four clicks will enter VISUAL BLOCK and dragging selects blockwise', function()
feed('<LeftMouse><2,2>')
feed('<LeftRelease><2,2>')
feed('<LeftMouse><2,2>')
feed('<LeftRelease><2,2>')
feed('<LeftMouse><2,2>')
feed('<LeftRelease><2,2>')
feed('<LeftMouse><2,2>')
screen:expect([[
testing |
mouse |
su^pport and selection |
{1:~ }|
{5:-- VISUAL BLOCK --} |
]])
feed('<LeftDrag><0,1>')
screen:expect([[
testing |
^m{17:ou}se |
{17:sup}port and selection |
{1:~ }|
{5:-- VISUAL BLOCK --} |
]])
feed('<LeftDrag><4,0>')
screen:expect([[
te{17:st}^ing |
mo{17:use} |
su{17:ppo}rt and selection |
{1:~ }|
{5:-- VISUAL BLOCK --} |
]])
feed('<LeftDrag><14,2>')
screen:expect([[
testing |
mouse |
su{17:pport and se}^lection |
{1:~ }|
{5:-- VISUAL BLOCK --} |
]])
end)
it('right click extends visual selection to the clicked location', function()
feed('<LeftMouse><0,0>')
screen:expect([[
^testing |
mouse |
support and selection |
{1:~ }|
|
]])
feed('<RightMouse><2,2>')
screen:expect([[
{17:testing} |
{17:mouse} |
{17:su}^pport and selection |
{1:~ }|
{5:-- VISUAL --} |
]])
end)
it('ctrl + left click will search for a tag', function()
api.nvim_set_option_value('tags', './non-existent-tags-file', {})
feed('<C-LeftMouse><0,0>')
screen:expect([[
{9:E433: No tags file} |
{9:E426: Tag not found: test}|
{9:ing} |
{6:Press ENTER or type comma}|
{6:nd to continue}^ |
]])
feed('<cr>')
end)
it('x1 and x2 can be triggered by api', function()
api.nvim_set_var('x1_pressed', 0)
api.nvim_set_var('x1_released', 0)
api.nvim_set_var('x2_pressed', 0)
api.nvim_set_var('x2_released', 0)
command('nnoremap <X1Mouse> <Cmd>let g:x1_pressed += 1<CR>')
command('nnoremap <X1Release> <Cmd>let g:x1_released += 1<CR>')
command('nnoremap <X2Mouse> <Cmd>let g:x2_pressed += 1<CR>')
command('nnoremap <X2Release> <Cmd>let g:x2_released += 1<CR>')
api.nvim_input_mouse('x1', 'press', '', 0, 0, 0)
api.nvim_input_mouse('x1', 'release', '', 0, 0, 0)
api.nvim_input_mouse('x2', 'press', '', 0, 0, 0)
api.nvim_input_mouse('x2', 'release', '', 0, 0, 0)
eq(1, api.nvim_get_var('x1_pressed'), 'x1 pressed once')
eq(1, api.nvim_get_var('x1_released'), 'x1 released once')
eq(1, api.nvim_get_var('x2_pressed'), 'x2 pressed once')
eq(1, api.nvim_get_var('x2_released'), 'x2 released once')
end)
it('dragging vertical separator', function()
screen:try_resize(45, 5)
command('setlocal nowrap')
local oldwin = api.nvim_get_current_win()
command('rightbelow vnew')
screen:expect([[
testing │{1:^$} |
mouse │{1:~ }|
support and selection │{1:~ }|
{2:[No Name] [+] }{3:[No Name] }|
|
]])
api.nvim_input_mouse('left', 'press', '', 0, 0, 22)
poke_eventloop()
api.nvim_input_mouse('left', 'drag', '', 0, 1, 12)
screen:expect([[
testing │{1:^$} |
mouse │{1:~ }|
support and │{1:~ }|
{2:< Name] [+] }{3:[No Name] }|
|
]])
api.nvim_input_mouse('left', 'drag', '', 0, 2, 2)
screen:expect([[
te│{1:^$} |
mo│{1:~ }|
su│{1:~ }|
{2:< }{3:[No Name] }|
|
]])
api.nvim_input_mouse('left', 'release', '', 0, 2, 2)
api.nvim_set_option_value('statuscolumn', 'foobar', { win = oldwin })
screen:expect([[
{8:fo}│{1:^$} |
{8:fo}│{1:~ }|*2
{2:< }{3:[No Name] }|
|
]])
api.nvim_input_mouse('left', 'press', '', 0, 0, 2)
poke_eventloop()
api.nvim_input_mouse('left', 'drag', '', 0, 1, 12)
screen:expect([[
{8:foobar}testin│{1:^$} |
{8:foobar}mouse │{1:~ }|
{8:foobar}suppor│{1:~ }|
{2:< Name] [+] }{3:[No Name] }|
|
]])
api.nvim_input_mouse('left', 'drag', '', 0, 2, 22)
screen:expect([[
{8:foobar}testing │{1:^$} |
{8:foobar}mouse │{1:~ }|
{8:foobar}support and sele│{1:~ }|
{2:[No Name] [+] }{3:[No Name] }|
|
]])
api.nvim_input_mouse('left', 'release', '', 0, 2, 22)
end)
local function wheel(use_api)
feed('ggdG')
insert([[
Inserting
text
with
many
lines
to
test
mouse scrolling
]])
screen:try_resize(53, 14)
feed('k')
feed_command('sp', 'vsp')
screen:expect([[
lines │lines |
to │to |
test │test |
^mouse scrolling │mouse scrolling |
│ |
{1:~ }│{1:~ }|
{3:[No Name] [+] }{2:[No Name] [+] }|
to |
test |
mouse scrolling |
|
{1:~ }|
{2:[No Name] [+] }|
:vsp |
]])
if use_api then
api.nvim_input_mouse('wheel', 'down', '', 0, 0, 0)
else
feed('<ScrollWheelDown><0,0>')
end
screen:expect([[
^mouse scrolling │lines |
│to |
{1:~ }│test |
{1:~ }│mouse scrolling |
{1:~ }│ |
{1:~ }│{1:~ }|
{3:[No Name] [+] }{2:[No Name] [+] }|
to |
test |
mouse scrolling |
|
{1:~ }|
{2:[No Name] [+] }|
:vsp |
]])
if use_api then
api.nvim_input_mouse('wheel', 'up', '', 0, 0, 27)
else
feed('<ScrollWheelUp><27,0>')
end
screen:expect([[
^mouse scrolling │text |
│with |
{1:~ }│many |
{1:~ }│lines |
{1:~ }│to |
{1:~ }│test |
{3:[No Name] [+] }{2:[No Name] [+] }|
to |
test |
mouse scrolling |
|
{1:~ }|
{2:[No Name] [+] }|
:vsp |
]])
if use_api then
api.nvim_input_mouse('wheel', 'up', '', 0, 7, 27)
api.nvim_input_mouse('wheel', 'up', '', 0, 7, 27)
else
feed('<ScrollWheelUp><27,7><ScrollWheelUp>')
end
screen:expect([[
^mouse scrolling │text |
│with |
{1:~ }│many |
{1:~ }│lines |
{1:~ }│to |
{1:~ }│test |
{3:[No Name] [+] }{2:[No Name] [+] }|
Inserting |
text |
with |
many |
lines |
{2:[No Name] [+] }|
:vsp |
]])
end
it('mouse wheel will target the hovered window (pseudokey)', function()
wheel(false)
end)
it('mouse wheel will target the hovered window (nvim_input_mouse)', function()
wheel(true)
end)
it('horizontal scrolling (pseudokey)', function()
command('set sidescroll=0')
feed('<esc>:set nowrap<cr>')
feed('a <esc>17Ab<esc>3Ab<esc>')
screen:expect([[
|*2
bbbbbbbbbbbbbbb^b |
{1:~ }|
|
]])
feed('<ScrollWheelLeft><0,0>')
screen:expect([[
|*2
n bbbbbbbbbbbbbbbbbbb^b |
{1:~ }|
|
]])
feed('^<ScrollWheelRight><0,0>')
screen:expect([[
g |
|
^t and selection bbbbbbbbb|
{1:~ }|
|
]])
end)
it('horizontal scrolling (nvim_input_mouse)', function()
command('set sidescroll=0')
feed('<esc>:set nowrap<cr>')
feed('a <esc>17Ab<esc>3Ab<esc>')
screen:expect([[
|*2
bbbbbbbbbbbbbbb^b |
{1:~ }|
|
]])
api.nvim_input_mouse('wheel', 'left', '', 0, 0, 27)
screen:expect([[
|*2
n bbbbbbbbbbbbbbbbbbb^b |
{1:~ }|
|
]])
feed('^')
api.nvim_input_mouse('wheel', 'right', '', 0, 0, 0)
screen:expect([[
g |
|
^t and selection bbbbbbbbb|
{1:~ }|
|
]])
end)
it("'sidescrolloff' applies to horizontal scrolling", function()
command('set nowrap')
command('set sidescrolloff=4')
feed('I <esc>020ib<esc>0')
screen:expect([[
testing |
mouse |
^bbbbbbbbbbbbbbbbbbbb supp|
{1:~ }|
|
]])
api.nvim_input_mouse('wheel', 'right', '', 0, 0, 27)
screen:expect([[
g |
|
bbbb^bbbbbbbbbb support an|
{1:~ }|
|
]])
-- window-local 'sidescrolloff' should override global value. #21162
command('setlocal sidescrolloff=2')
feed('0')
screen:expect([[
testing |
mouse |
^bbbbbbbbbbbbbbbbbbbb supp|
{1:~ }|
|
]])
api.nvim_input_mouse('wheel', 'right', '', 0, 0, 27)
screen:expect([[
g |
|
bb^bbbbbbbbbbbb support an|
{1:~ }|
|
]])
end)
local function test_mouse_click_conceal()
it('(level 1) click on non-wrapped lines', function()
feed_command('let &conceallevel=1', 'echo')
feed('<esc><LeftMouse><0,0>')
screen:expect([[
^Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
{14:>} 私は猫が大好き{1:>---}{14: X } {1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><1,0>')
screen:expect([[
S^ection{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
{14:>} 私は猫が大好き{1:>---}{14: X } {1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><21,0>')
screen:expect([[
Section{1:>>--->--->---}{14: }^t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
{14:>} 私は猫が大好き{1:>---}{14: X } {1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><21,1>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t^3{14: } {14: }|
{14:>} 私は猫が大好き{1:>---}{14: X } {1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><0,2>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
{14:^>} 私は猫が大好き{1:>---}{14: X } {1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><7,2>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
{14:>} 私は^猫が大好き{1:>---}{14: X } {1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><21,2>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
{14:>} 私は猫が大好き{1:>---}{14: ^X } {1:>}|
|
{1:~ }|*2
|
]])
end) -- level 1 - non wrapped
it('(level 1) click on wrapped lines', function()
feed_command('let &conceallevel=1', 'let &wrap=1', 'echo')
feed('<esc><LeftMouse><24,1>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14:^ }|
t4{14: } |
{14:>} 私は猫が大好き{1:>---}{14: X} |
{14: } ✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><0,2>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
^t4{14: } |
{14:>} 私は猫が大好き{1:>---}{14: X} |
{14: } ✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><8,3>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
t4{14: } |
{14:>} 私は猫^が大好き{1:>---}{14: X} |
{14: } ✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><21,3>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
t4{14: } |
{14:>} 私は猫が大好き{1:>---}{14: ^X} |
{14: } ✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><4,4>')
screen:expect([[
Section{1:>>--->--->---}{14: }t1{14: } |
{1:>--->--->---} {14: }t2{14: } {14: }t3{14: } {14: }|
t4{14: } |
{14:>} 私は猫が大好き{1:>---}{14: X} |
{14: } ✨^🐈✨ |
|*2
]])
end) -- level 1 - wrapped
it('(level 2) click on non-wrapped lines', function()
feed_command('let &conceallevel=2', 'echo')
feed('<esc><LeftMouse><20,0>')
screen:expect([[
Section{1:>>--->--->---}^t1 |
{1:>--->--->---} t2 t3 t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><14,1>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} ^t2 t3 t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><18,1>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t^3 t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><0,2>') -- Weirdness
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
{14:^>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><8,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
{14:>} 私は猫^が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><20,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
{14:>} 私は猫が大好き{1:>---}{14:^X} ✨{1:>}|
|
{1:~ }|*2
|
]])
end) -- level 2 - non wrapped
it('(level 2) click on non-wrapped lines (insert mode)', function()
feed_command('let &conceallevel=2', 'echo')
feed('<esc>i<LeftMouse><20,0>')
screen:expect([[
Section{1:>>--->--->---}^t1 |
{1:>--->--->---} t2 t3 t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<LeftMouse><14,1>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} ^t2 t3 t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<LeftMouse><18,1>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t^3 t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<LeftMouse><0,2>') -- Weirdness
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
{14:^>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<LeftMouse><8,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
{14:>} 私は猫^が大好き{1:>---}{14:X} ✨{1:>}|
|
{1:~ }|*2
{5:-- INSERT --} |
]])
feed('<LeftMouse><20,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
{14:>} 私は猫が大好き{1:>---}{14:^X} ✨{1:>}|
|
{1:~ }|*2
{5:-- INSERT --} |
]])
end) -- level 2 - non wrapped (insert mode)
it('(level 2) click on wrapped lines', function()
feed_command('let &conceallevel=2', 'let &wrap=1', 'echo')
feed('<esc><LeftMouse><20,0>')
screen:expect([[
Section{1:>>--->--->---}^t1 |
{1:>--->--->---} t2 t3 |
t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><14,1>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} ^t2 t3 |
t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><18,1>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t^3 |
t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} |
✨🐈✨ |
|*2
]])
-- NOTE: The click would ideally be on the 't' in 't4', but wrapping
-- caused the invisible '*' right before 't4' to remain on the previous
-- screen line. This is being treated as expected because fixing this is
-- out of scope for mouse clicks. Should the wrapping behavior of
-- concealed characters change in the future, this case should be
-- reevaluated.
feed('<esc><LeftMouse><0,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 ^ |
t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><1,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t^4 |
{14:>} 私は猫が大好き{1:>---}{14:X} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><0,3>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
{14:^>} 私は猫が大好き{1:>---}{14:X} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><20,3>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
{14:>} 私は猫が大好き{1:>---}{14:^X} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><1,4>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} |
^✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><5,4>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
{14:>} 私は猫が大好き{1:>---}{14:X} |
✨🐈^✨ |
|*2
]])
end) -- level 2 - wrapped
it('(level 3) click on non-wrapped lines', function()
feed_command('let &conceallevel=3', 'echo')
feed('<esc><LeftMouse><0,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
^ 私は猫が大好き{1:>----} ✨🐈|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><1,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
^私は猫が大好き{1:>----} ✨🐈|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><13,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
私は猫が大好^き{1:>----} ✨🐈|
|
{1:~ }|*2
|
]])
feed('<esc><LeftMouse><20,2>')
feed('zH') -- FIXME: unnecessary horizontal scrolling
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 t4 |
私は猫が大好き{1:>----}^ ✨🐈|
|
{1:~ }|*2
|
]])
end) -- level 3 - non wrapped
it('(level 3) click on wrapped lines', function()
feed_command('let &conceallevel=3', 'let &wrap=1', 'echo')
feed('<esc><LeftMouse><14,1>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} ^t2 t3 |
t4 |
私は猫が大好き{1:>----} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><18,1>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t^3 |
t4 |
私は猫が大好き{1:>----} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><1,2>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t^4 |
私は猫が大好き{1:>----} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><0,3>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
^ 私は猫が大好き{1:>----} |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><20,3>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
私は猫が大好き{1:>----}^ |
✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><1,4>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
私は猫が大好き{1:>----} |
^✨🐈✨ |
|*2
]])
feed('<esc><LeftMouse><3,4>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
私は猫が大好き{1:>----} |
✨^🐈✨ |
|*2
]])
feed('<esc><LeftMouse><5,4>')
screen:expect([[
Section{1:>>--->--->---}t1 |
{1:>--->--->---} t2 t3 |
t4 |
私は猫が大好き{1:>----} |
✨🐈^✨ |
|*2
]])
end) -- level 3 - wrapped
end
describe('on concealed text', function()
-- Helpful for reading the test expectations:
-- :match Error /\^/
before_each(function()
screen:try_resize(25, 7)
feed('ggdG')
command([[setlocal concealcursor=ni nowrap shiftwidth=2 tabstop=4 list listchars=tab:>-]])
command([[highlight link X0 Normal]])
command([[highlight link X1 NonText]])
command([[highlight link X2 NonText]])
command([[highlight link X3 NonText]])
-- First column is there to retain the tabs.
insert([[
|Section *t1*
| *t2* *t3* *t4*
|x 私は猫が大好き *cats* ✨🐈✨
]])
feed('gg<c-v>Gxgg')
end)
describe('(syntax)', function()
before_each(function()
command([[syntax region X0 matchgroup=X1 start=/\*/ end=/\*/ concealends contains=X2]])
command([[syntax match X2 /cats/ conceal cchar=X contained]])
command([[syntax match X3 /\n\@<=x/ conceal cchar=>]])
end)
test_mouse_click_conceal()
end)
describe('(matchadd())', function()
before_each(function()
fn.matchadd('Conceal', [[\*]])
fn.matchadd('Conceal', [[cats]], 10, -1, { conceal = 'X' })
fn.matchadd('Conceal', [[\n\@<=x]], 10, -1, { conceal = '>' })
end)
test_mouse_click_conceal()
end)
describe('(extmarks)', function()
before_each(function()
local ns = api.nvim_create_namespace('conceal')
api.nvim_buf_set_extmark(0, ns, 0, 11, { end_col = 12, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 0, 14, { end_col = 15, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 1, 5, { end_col = 6, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 1, 8, { end_col = 9, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 1, 10, { end_col = 11, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 1, 13, { end_col = 14, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 1, 15, { end_col = 16, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 1, 18, { end_col = 19, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 2, 24, { end_col = 25, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 2, 29, { end_col = 30, conceal = '' })
api.nvim_buf_set_extmark(0, ns, 2, 25, { end_col = 29, conceal = 'X' })
api.nvim_buf_set_extmark(0, ns, 2, 0, { end_col = 1, conceal = '>' })
end)
test_mouse_click_conceal()
end)
end)
it('virtual text does not change cursor placement on concealed line', function()
command('%delete')
insert('aaaaaaaaaa|hidden|bbbbbbbbbb|hidden|cccccccccc')
command('syntax match test /|hidden|/ conceal cchar=X')
command('set conceallevel=2 concealcursor=n virtualedit=all')
screen:expect([[
aaaaaaaaaa{14:X}bbbbbbb |
bbb{14:X}ccccccccc^c |
{1:~ }|*2
|
]])
api.nvim_input_mouse('left', 'press', '', 0, 0, 22)
screen:expect([[
aaaaaaaaaa{14:X}bbbbbb^b |
bbb{14:X}cccccccccc |
{1:~ }|*2
|
]])
api.nvim_input_mouse('left', 'press', '', 0, 1, 16)
screen:expect([[
aaaaaaaaaa{14:X}bbbbbbb |
bbb{14:X}cccccccccc ^ |
{1:~ }|*2
|
]])
api.nvim_buf_set_extmark(0, api.nvim_create_namespace(''), 0, 0, {
virt_text = { { '?', 'ErrorMsg' } },
virt_text_pos = 'right_align',
virt_text_repeat_linebreak = true,
})
screen:expect([[
aaaaaaaaaa{14:X}bbbbbbb {9:?}|
bbb{14:X}cccccccccc ^ {9:?}|
{1:~ }|*2
|
]])
api.nvim_input_mouse('left', 'press', '', 0, 0, 22)
screen:expect([[
aaaaaaaaaa{14:X}bbbbbb^b {9:?}|
bbb{14:X}cccccccccc {9:?}|
{1:~ }|*2
|
]])
api.nvim_input_mouse('left', 'press', '', 0, 1, 16)
screen:expect([[
aaaaaaaaaa{14:X}bbbbbbb {9:?}|
bbb{14:X}cccccccccc ^ {9:?}|
{1:~ }|*2
|
]])
end)
it('getmousepos() works correctly', function()
local winwidth = api.nvim_get_option_value('winwidth', {})
-- Set winwidth=1 so that window sizes don't change.
api.nvim_set_option_value('winwidth', 1, {})
command('tabedit')
local tabpage = api.nvim_get_current_tabpage()
insert('hello')
command('vsplit')
local opts = {
relative = 'editor',
width = 12,
height = 1,
col = 8,
row = 1,
anchor = 'NW',
style = 'minimal',
border = 'single',
focusable = 1,
}
local float = api.nvim_open_win(api.nvim_get_current_buf(), false, opts)
command('redraw')
local lines = api.nvim_get_option_value('lines', {})
local columns = api.nvim_get_option_value('columns', {})
-- Test that screenrow and screencol are set properly for all positions.
for row = 0, lines - 1 do
for col = 0, columns - 1 do
-- Skip the X button that would close the tab.
if row ~= 0 or col ~= columns - 1 then
api.nvim_input_mouse('left', 'press', '', 0, row, col)
api.nvim_set_current_tabpage(tabpage)
local mousepos = fn.getmousepos()
eq(row + 1, mousepos.screenrow)
eq(col + 1, mousepos.screencol)
-- All other values should be 0 when clicking on the command line.
if row == lines - 1 then
eq(0, mousepos.winid)
eq(0, mousepos.winrow)
eq(0, mousepos.wincol)
eq(0, mousepos.line)
eq(0, mousepos.column)
eq(0, mousepos.coladd)
end
end
end
end
-- Test that mouse position values are properly set for the floating window
-- with a border. 1 is added to the height and width to account for the
-- border.
for win_row = 0, opts.height + 1 do
for win_col = 0, opts.width + 1 do
local row = win_row + opts.row
local col = win_col + opts.col
api.nvim_input_mouse('left', 'press', '', 0, row, col)
local mousepos = fn.getmousepos()
eq(float, mousepos.winid)
eq(win_row + 1, mousepos.winrow)
eq(win_col + 1, mousepos.wincol)
local line = 0
local column = 0
local coladd = 0
if
win_row > 0
and win_row < opts.height + 1
and win_col > 0
and win_col < opts.width + 1
then
-- Because of border, win_row and win_col don't need to be
-- incremented by 1.
line = math.min(win_row, fn.line('$'))
column = math.min(win_col, #fn.getline(line) + 1)
coladd = win_col - column
end
eq(line, mousepos.line)
eq(column, mousepos.column)
eq(coladd, mousepos.coladd)
end
end
-- Test that mouse position values are properly set for the floating
-- window, after removing the border.
opts.border = 'none'
api.nvim_win_set_config(float, opts)
command('redraw')
for win_row = 0, opts.height - 1 do
for win_col = 0, opts.width - 1 do
local row = win_row + opts.row
local col = win_col + opts.col
api.nvim_input_mouse('left', 'press', '', 0, row, col)
local mousepos = fn.getmousepos()
eq(float, mousepos.winid)
eq(win_row + 1, mousepos.winrow)
eq(win_col + 1, mousepos.wincol)
local line = math.min(win_row + 1, fn.line('$'))
local column = math.min(win_col + 1, #fn.getline(line) + 1)
local coladd = win_col + 1 - column
eq(line, mousepos.line)
eq(column, mousepos.column)
eq(coladd, mousepos.coladd)
end
end
-- Test that mouse position values are properly set for ordinary windows.
-- Set the float to be unfocusable instead of closing, to additionally test
-- that getmousepos() does not consider unfocusable floats. (see discussion
-- in PR #14937 for details).
opts.focusable = false
api.nvim_win_set_config(float, opts)
command('redraw')
for nr = 1, 2 do
for win_row = 0, fn.winheight(nr) - 1 do
for win_col = 0, fn.winwidth(nr) - 1 do
local row = win_row + fn.win_screenpos(nr)[1] - 1
local col = win_col + fn.win_screenpos(nr)[2] - 1
api.nvim_input_mouse('left', 'press', '', 0, row, col)
local mousepos = fn.getmousepos()
eq(fn.win_getid(nr), mousepos.winid)
eq(win_row + 1, mousepos.winrow)
eq(win_col + 1, mousepos.wincol)
local line = math.min(win_row + 1, fn.line('$'))
local column = math.min(win_col + 1, #fn.getline(line) + 1)
local coladd = win_col + 1 - column
eq(line, mousepos.line)
eq(column, mousepos.column)
eq(coladd, mousepos.coladd)
end
end
end
-- Restore state and release mouse.
command('tabclose!')
api.nvim_set_option_value('winwidth', winwidth, {})
api.nvim_input_mouse('left', 'release', '', 0, 0, 0)
end)
it('scroll keys are not translated into multiclicks and can be mapped #6211 #6989', function()
api.nvim_set_var('mouse_up', 0)
api.nvim_set_var('mouse_up2', 0)
command('nnoremap <ScrollWheelUp> <Cmd>let g:mouse_up += 1<CR>')
command('nnoremap <2-ScrollWheelUp> <Cmd>let g:mouse_up2 += 1<CR>')
feed('<ScrollWheelUp><0,0>')
feed('<ScrollWheelUp><0,0>')
api.nvim_input_mouse('wheel', 'up', '', 0, 0, 0)
api.nvim_input_mouse('wheel', 'up', '', 0, 0, 0)
eq(4, api.nvim_get_var('mouse_up'))
eq(0, api.nvim_get_var('mouse_up2'))
end)
it('<MouseMove> to different locations can be mapped', function()
api.nvim_set_var('mouse_move', 0)
api.nvim_set_var('mouse_move2', 0)
command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>')
command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>')
feed('<MouseMove><1,0>')
feed('<MouseMove><2,0>')
api.nvim_input_mouse('move', '', '', 0, 0, 3)
api.nvim_input_mouse('move', '', '', 0, 0, 4)
eq(4, api.nvim_get_var('mouse_move'))
eq(0, api.nvim_get_var('mouse_move2'))
end)
it('<MouseMove> to same location does not generate events #31103', function()
api.nvim_set_var('mouse_move', 0)
api.nvim_set_var('mouse_move2', 0)
command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>')
command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>')
api.nvim_input_mouse('move', '', '', 0, 0, 3)
eq(1, api.nvim_get_var('mouse_move'))
eq(0, api.nvim_get_var('mouse_move2'))
feed('<MouseMove><3,0>')
feed('<MouseMove><3,0>')
api.nvim_input_mouse('move', '', '', 0, 0, 3)
api.nvim_input_mouse('move', '', '', 0, 0, 3)
eq(1, api.nvim_get_var('mouse_move'))
eq(0, api.nvim_get_var('mouse_move2'))
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
feed('<MouseMove><3,0><Insert>')
eq(1, api.nvim_get_var('mouse_move'))
eq(0, api.nvim_get_var('mouse_move2'))
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
end)
it('feeding <MouseMove> in Normal mode does not use uninitialized memory #19480', function()
feed('<MouseMove>')
n.poke_eventloop()
n.assert_alive()
end)
it('mousemodel=popup_setpos', function()
screen:try_resize(80, 24)
exec([[
5new
call setline(1, ['the dish ran away with the spoon',
\ 'the cow jumped over the moon' ])
set mouse=a mousemodel=popup_setpos
aunmenu PopUp
nmenu PopUp.foo :let g:menustr = 'foo'<CR>
nmenu PopUp.bar :let g:menustr = 'bar'<CR>
nmenu PopUp.baz :let g:menustr = 'baz'<CR>
vmenu PopUp.foo y:<C-U>let g:menustr = 'foo'<CR>
vmenu PopUp.bar y:<C-U>let g:menustr = 'bar'<CR>
vmenu PopUp.baz y:<C-U>let g:menustr = 'baz'<CR>
]])
api.nvim_win_set_cursor(0, { 1, 0 })
api.nvim_input_mouse('right', 'press', '', 0, 0, 4)
api.nvim_input_mouse('right', 'release', '', 0, 0, 4)
feed('<Down><Down><CR>')
eq('bar', api.nvim_get_var('menustr'))
eq({ 1, 4 }, api.nvim_win_get_cursor(0))
-- Test for right click in visual mode inside the selection
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 9 })
feed('vee')
api.nvim_input_mouse('right', 'press', '', 0, 0, 11)
api.nvim_input_mouse('right', 'release', '', 0, 0, 11)
feed('<Down><CR>')
eq({ 1, 9 }, api.nvim_win_get_cursor(0))
eq('ran away', fn.getreg('"'))
-- Test for right click in visual mode right before the selection
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 9 })
feed('vee')
api.nvim_input_mouse('right', 'press', '', 0, 0, 8)
api.nvim_input_mouse('right', 'release', '', 0, 0, 8)
feed('<Down><CR>')
eq({ 1, 8 }, api.nvim_win_get_cursor(0))
eq('', fn.getreg('"'))
-- Test for right click in visual mode right after the selection
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 9 })
feed('vee')
api.nvim_input_mouse('right', 'press', '', 0, 0, 17)
api.nvim_input_mouse('right', 'release', '', 0, 0, 17)
feed('<Down><CR>')
eq({ 1, 17 }, api.nvim_win_get_cursor(0))
eq('', fn.getreg('"'))
-- Test for right click in block-wise visual mode inside the selection
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 15 })
feed('<C-V>j3l')
api.nvim_input_mouse('right', 'press', '', 0, 1, 16)
api.nvim_input_mouse('right', 'release', '', 0, 1, 16)
feed('<Down><CR>')
eq({ 1, 15 }, api.nvim_win_get_cursor(0))
eq('\0224', fn.getregtype('"'))
-- Test for right click in block-wise visual mode outside the selection
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 15 })
feed('<C-V>j3l')
api.nvim_input_mouse('right', 'press', '', 0, 1, 1)
api.nvim_input_mouse('right', 'release', '', 0, 1, 1)
feed('<Down><CR>')
eq({ 2, 1 }, api.nvim_win_get_cursor(0))
eq('v', fn.getregtype('"'))
eq('', fn.getreg('"'))
-- Test for right click in line-wise visual mode inside the selection
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 15 })
feed('V')
api.nvim_input_mouse('right', 'press', '', 0, 0, 9)
api.nvim_input_mouse('right', 'release', '', 0, 0, 9)
feed('<Down><CR>')
eq({ 1, 0 }, api.nvim_win_get_cursor(0)) -- After yanking, the cursor goes to 1,1
eq('V', fn.getregtype('"'))
eq(1, #fn.getreg('"', 1, true))
-- Test for right click in multi-line line-wise visual mode inside the selection
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 15 })
feed('Vj')
api.nvim_input_mouse('right', 'press', '', 0, 1, 19)
api.nvim_input_mouse('right', 'release', '', 0, 1, 19)
feed('<Down><CR>')
eq({ 1, 0 }, api.nvim_win_get_cursor(0)) -- After yanking, the cursor goes to 1,1
eq('V', fn.getregtype('"'))
eq(2, #fn.getreg('"', 1, true))
-- Test for right click in line-wise visual mode outside the selection
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 15 })
feed('V')
api.nvim_input_mouse('right', 'press', '', 0, 1, 9)
api.nvim_input_mouse('right', 'release', '', 0, 1, 9)
feed('<Down><CR>')
eq({ 2, 9 }, api.nvim_win_get_cursor(0))
eq('', fn.getreg('"'))
-- Try clicking outside the window
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 2, 1 })
feed('vee')
api.nvim_input_mouse('right', 'press', '', 0, 6, 1)
api.nvim_input_mouse('right', 'release', '', 0, 6, 1)
feed('<Down><CR>')
eq(2, fn.winnr())
eq('', fn.getreg('"'))
-- Test for right click in visual mode inside the selection with vertical splits
command('wincmd t')
command('rightbelow vsplit')
fn.setreg('"', '')
api.nvim_win_set_cursor(0, { 1, 9 })
feed('vee')
api.nvim_input_mouse('right', 'press', '', 0, 0, 52)
api.nvim_input_mouse('right', 'release', '', 0, 0, 52)
feed('<Down><CR>')
eq({ 1, 9 }, api.nvim_win_get_cursor(0))
eq('ran away', fn.getreg('"'))
-- Test for right click inside visual selection at bottom of window with winbar
command('setlocal winbar=WINBAR')
feed('2yyP')
fn.setreg('"', '')
feed('G$vbb')
api.nvim_input_mouse('right', 'press', '', 0, 4, 61)
api.nvim_input_mouse('right', 'release', '', 0, 4, 61)
feed('<Down><CR>')
eq({ 4, 20 }, api.nvim_win_get_cursor(0))
eq('the moon', fn.getreg('"'))
-- Try clicking in the cmdline
api.nvim_input_mouse('right', 'press', '', 0, 23, 0)
api.nvim_input_mouse('right', 'release', '', 0, 23, 0)
feed('<Down><Down><Down><CR>')
eq('baz', api.nvim_get_var('menustr'))
-- Try clicking in horizontal separator with global statusline
command('set laststatus=3')
api.nvim_input_mouse('right', 'press', '', 0, 5, 0)
api.nvim_input_mouse('right', 'release', '', 0, 5, 0)
feed('<Down><CR>')
eq('foo', api.nvim_get_var('menustr'))
-- Try clicking in the cmdline with global statusline
api.nvim_input_mouse('right', 'press', '', 0, 23, 0)
api.nvim_input_mouse('right', 'release', '', 0, 23, 0)
feed('<Down><Down><CR>')
eq('bar', api.nvim_get_var('menustr'))
end)
end)