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('itestingmousesupport and selection') screen:expect([[ testing | mouse | support and selectio^n | {1:~ }| | ]]) end) it('single left click moves cursor', function() feed('<2,1>') screen:expect { grid = [[ testing | mo^use | support and selection | {1:~ }| | ]], mouse_enabled = true, } feed('<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('<2,1>') screen:expect { grid = [[ testing | mo^use | support and selection | {1:~ }| | ]], mouse_enabled = false, } feed('<0,0>') screen:expect([[ ^testing | mouse | support and selection | {1:~ }| | ]]) end) it('double left click enters visual mode', function() feed('<0,0>') feed('<0,0>') feed('<0,0>') feed('<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('<0,0>') feed('<0,0>') feed('<0,0>') feed('<0,0>') feed('<0,0>') feed('<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('<0,0>') feed('<0,0>') feed('<0,0>') feed('<0,0>') feed('<0,0>') feed('<0,0>') feed('<0,0>') feed('<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('<4,0>') screen:expect([[ {5: + foo }{24: + bar }{2: }{24:X}| this is fo^o | {1:~ }|*2 | ]]) feed('<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('<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('<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('<4,0>') screen:expect([[ {5: + foo }{24: + bar }{2: }{24:X}| this is fo^o | {1:~ }|*2 | ]]) feed('<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('<4,0>') screen:expect([[ {5: + foo }{24: + bar }{2: }{24:X}| this is fo^o | {1:~ }|*2 | ]]) feed('<4,1>') screen:expect { grid = [[ {5: + foo }{24: + bar }{2: }{24:X}| this is fo^o | {1:~ }|*2 | ]], unchanged = true, } feed('<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('<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('<11,1>') screen:expect { grid = [[ {24: + foo }{5: + bar }{2: }{24:X}| this is ba^r{1:$} | {1:~ }|*2 | ]], unchanged = true, } feed('<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('<4,0>') screen:expect([[ {5: + foo }{24: + bar }{2: }{24:X}| this is fo^o | {1:~ }|*2 | ]]) feed('<4,1>') screen:expect { grid = [[ {5: + foo }{24: + bar }{2: }{24:X}| this is fo^o | {1:~ }|*2 | ]], unchanged = true, } feed('<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('<4,0>') screen:expect([[ {5: + foo }{24: + bar }{2: }{24:X}| this is fo^o | {1:~ }|*2 | ]]) feed('<6,0>') screen:expect_unchanged() feed('<10,0>') screen:expect([[ {24: + foo }{5: + bar }{2: }{24:X}| this is ba^r{1:$} | {1:~ }|*2 | ]]) feed('<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('<20,0>') screen:expect([[ {5: + foo }{24: + bar }{2: }{24:X}| this is fo^o | {1:~ }|*2 | ]]) feed('<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('<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', '', 1, 'l', ' ') test_click('shifted single left click', '', 1, 'l', 's ') test_click('shifted single left click with alt modifier', '', 1, 'l', 's a ') test_click( 'shifted single left click with alt and ctrl modifiers', '', 1, 'l', 'sca ' ) -- does not work test_click('shifted single right click with alt modifier', '', 1, 'r', 's a ') -- Modifiers do not work with MiddleMouse test_click( 'shifted single middle click with alt and ctrl modifiers', '', 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('<2,1>') screen:expect([[ testing | mo^use | support and selection | {1:~ }| | ]]) feed('<4,1>') screen:expect([[ testing | mo{17:us}^e | support and selection | {1:~ }| {5:-- VISUAL --} | ]]) feed('<2,2>') screen:expect([[ testing | mo{17:use} | {17:su}^pport and selection | {1:~ }| {5:-- VISUAL --} | ]]) feed('<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('<10,0>') -- go to second tab n.poke_eventloop() feed('<0,1>') screen:expect([[ {24: + foo }{5: + bar }{2: }{24:X}| ^this is bar{1:$} | {1:~ }|*2 :tabprevious | ]]) feed('<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') 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('<2,2>') feed('<2,2>') feed('<2,2>') screen:expect([[ testing | mouse | {17:suppor}^t and selection | {1:~ }| {5:-- VISUAL --} | ]]) feed('<0,1>') screen:expect([[ testing | ^m{17:ouse} | {17:support} and selection | {1:~ }| {5:-- VISUAL --} | ]]) feed('<4,0>') screen:expect([[ ^t{17:esting} | {17:mouse} | {17:support} and selection | {1:~ }| {5:-- VISUAL --} | ]]) feed('<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('<2,2>') feed('<2,2>') feed('<2,2>') feed('<2,2>') feed('<2,2>') screen:expect([[ testing | mouse | {17:su}^p{17:port and selection} | {1:~ }| {5:-- VISUAL LINE --} | ]]) feed('<0,1>') screen:expect([[ testing | ^m{17:ouse} | {17:support and selection} | {1:~ }| {5:-- VISUAL LINE --} | ]]) feed('<4,0>') screen:expect([[ {17:test}^i{17:ng} | {17:mouse} | {17:support and selection} | {1:~ }| {5:-- VISUAL LINE --} | ]]) feed('<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('<2,2>') feed('<2,2>') feed('<2,2>') feed('<2,2>') feed('<2,2>') feed('<2,2>') feed('<2,2>') screen:expect([[ testing | mouse | su^pport and selection | {1:~ }| {5:-- VISUAL BLOCK --} | ]]) feed('<0,1>') screen:expect([[ testing | ^m{17:ou}se | {17:sup}port and selection | {1:~ }| {5:-- VISUAL BLOCK --} | ]]) feed('<4,0>') screen:expect([[ te{17:st}^ing | mo{17:use} | su{17:ppo}rt and selection | {1:~ }| {5:-- VISUAL BLOCK --} | ]]) feed('<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('<0,0>') screen:expect([[ ^testing | mouse | support and selection | {1:~ }| | ]]) feed('<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('<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('') 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 let g:x1_pressed += 1') command('nnoremap let g:x1_released += 1') command('nnoremap let g:x2_pressed += 1') command('nnoremap let g:x2_released += 1') 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('<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('<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('<27,7>') 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(':set nowrap') feed('a 17Ab3Ab') screen:expect([[ |*2 bbbbbbbbbbbbbbb^b | {1:~ }| | ]]) feed('<0,0>') screen:expect([[ |*2 n bbbbbbbbbbbbbbbbbbb^b | {1:~ }| | ]]) feed('^<0,0>') screen:expect([[ g | | ^t and selection bbbbbbbbb| {1:~ }| | ]]) end) it('horizontal scrolling (nvim_input_mouse)', function() command('set sidescroll=0') feed(':set nowrap') feed('a 17Ab3Ab') 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 020ib0') 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('<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('<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('<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('<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('<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('<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('<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('<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('<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('<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('<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('<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('<20,0>') screen:expect([[ Section{1:>>--->--->---}^t1 | {1:>--->--->---} t2 t3 t4 | {14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 | ]]) feed('<14,1>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} ^t2 t3 t4 | {14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 | ]]) feed('<18,1>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t^3 t4 | {14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 | ]]) feed('<0,2>') -- Weirdness screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 t4 | {14:^>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 | ]]) feed('<8,2>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 t4 | {14:>} 私は猫^が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 | ]]) feed('<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('i<20,0>') screen:expect([[ Section{1:>>--->--->---}^t1 | {1:>--->--->---} t2 t3 t4 | {14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 {5:-- INSERT --} | ]]) feed('<14,1>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} ^t2 t3 t4 | {14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 {5:-- INSERT --} | ]]) feed('<18,1>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t^3 t4 | {14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 {5:-- INSERT --} | ]]) feed('<0,2>') -- Weirdness screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 t4 | {14:^>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 {5:-- INSERT --} | ]]) feed('<8,2>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 t4 | {14:>} 私は猫^が大好き{1:>---}{14:X} ✨{1:>}| | {1:~ }|*2 {5:-- INSERT --} | ]]) feed('<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('<20,0>') screen:expect([[ Section{1:>>--->--->---}^t1 | {1:>--->--->---} t2 t3 | t4 | {14:>} 私は猫が大好き{1:>---}{14:X} | ✨🐈✨ | |*2 ]]) feed('<14,1>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} ^t2 t3 | t4 | {14:>} 私は猫が大好き{1:>---}{14:X} | ✨🐈✨ | |*2 ]]) feed('<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('<0,2>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 ^ | t4 | {14:>} 私は猫が大好き{1:>---}{14:X} | ✨🐈✨ | |*2 ]]) feed('<1,2>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t^4 | {14:>} 私は猫が大好き{1:>---}{14:X} | ✨🐈✨ | |*2 ]]) feed('<0,3>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t4 | {14:^>} 私は猫が大好き{1:>---}{14:X} | ✨🐈✨ | |*2 ]]) feed('<20,3>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t4 | {14:>} 私は猫が大好き{1:>---}{14:^X} | ✨🐈✨ | |*2 ]]) feed('<1,4>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t4 | {14:>} 私は猫が大好き{1:>---}{14:X} | ^✨🐈✨ | |*2 ]]) feed('<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('<0,2>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 t4 | ^ 私は猫が大好き{1:>----} ✨🐈| | {1:~ }|*2 | ]]) feed('<1,2>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 t4 | ^私は猫が大好き{1:>----} ✨🐈| | {1:~ }|*2 | ]]) feed('<13,2>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 t4 | 私は猫が大好^き{1:>----} ✨🐈| | {1:~ }|*2 | ]]) feed('<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('<14,1>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} ^t2 t3 | t4 | 私は猫が大好き{1:>----} | ✨🐈✨ | |*2 ]]) feed('<18,1>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t^3 | t4 | 私は猫が大好き{1:>----} | ✨🐈✨ | |*2 ]]) feed('<1,2>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t^4 | 私は猫が大好き{1:>----} | ✨🐈✨ | |*2 ]]) feed('<0,3>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t4 | ^ 私は猫が大好き{1:>----} | ✨🐈✨ | |*2 ]]) feed('<20,3>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t4 | 私は猫が大好き{1:>----}^ | ✨🐈✨ | |*2 ]]) feed('<1,4>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t4 | 私は猫が大好き{1:>----} | ^✨🐈✨ | |*2 ]]) feed('<3,4>') screen:expect([[ Section{1:>>--->--->---}t1 | {1:>--->--->---} t2 t3 | t4 | 私は猫が大好き{1:>----} | ✨^🐈✨ | |*2 ]]) feed('<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('ggGxgg') 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 let g:mouse_up += 1') command('nnoremap <2-ScrollWheelUp> let g:mouse_up2 += 1') feed('<0,0>') feed('<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(' to different locations can be mapped', function() api.nvim_set_var('mouse_move', 0) api.nvim_set_var('mouse_move2', 0) command('nnoremap let g:mouse_move += 1') command('nnoremap <2-MouseMove> let g:mouse_move2 += 1') feed('<1,0>') feed('<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(' 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 let g:mouse_move += 1') command('nnoremap <2-MouseMove> let g:mouse_move2 += 1') 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('<3,0>') feed('<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('<3,0>') 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 in Normal mode does not use uninitialized memory #19480', function() feed('') 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' nmenu PopUp.bar :let g:menustr = 'bar' nmenu PopUp.baz :let g:menustr = 'baz' vmenu PopUp.foo y:let g:menustr = 'foo' vmenu PopUp.bar y:let g:menustr = 'bar' vmenu PopUp.baz y:let g:menustr = 'baz' ]]) 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('') 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('') 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('') 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('') 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('j3l') api.nvim_input_mouse('right', 'press', '', 0, 1, 16) api.nvim_input_mouse('right', 'release', '', 0, 1, 16) feed('') 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('j3l') api.nvim_input_mouse('right', 'press', '', 0, 1, 1) api.nvim_input_mouse('right', 'release', '', 0, 1, 1) feed('') 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('') 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('') 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('') 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('') 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('') 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('') 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('') 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('') 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('') eq('bar', api.nvim_get_var('menustr')) end) end)