local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, api = helpers.clear, helpers.feed, helpers.api local insert, feed_command = helpers.insert, helpers.feed_command local eq, fn = helpers.eq, helpers.fn local poke_eventloop = helpers.poke_eventloop local command = helpers.command local exec = helpers.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:attach() screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, [1] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, [2] = { bold = true }, [3] = { foreground = Screen.colors.Blue, background = Screen.colors.LightGrey, bold = true, }, [4] = { reverse = true }, [5] = { bold = true, reverse = true }, [6] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, [7] = { bold = true, foreground = Screen.colors.SeaGreen4 }, [8] = { foreground = Screen.colors.Brown }, [9] = { background = Screen.colors.DarkGrey, foreground = Screen.colors.LightGrey }, }) command('set mousemodel=extend') feed('itestingmousesupport and selection') screen:expect([[ testing | mouse | support and selectio^n | {0:~ }| | ]]) end) it('single left click moves cursor', function() feed('<2,1>') screen:expect { grid = [[ testing | mo^use | support and selection | {0:~ }| | ]], mouse_enabled = true, } feed('<0,0>') screen:expect([[ ^testing | mouse | support and selection | {0:~ }| | ]]) 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 | {0:~ }| | ]], mouse_enabled = false, } feed('<0,0>') screen:expect([[ ^testing | mouse | support and selection | {0:~ }| | ]]) end) it('double left click enters visual mode', function() feed('<0,0>') feed('<0,0>') feed('<0,0>') feed('<0,0>') screen:expect([[ {1:testin}^g | mouse | support and selection | {0:~ }| {2:-- 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{1:esting} | mouse | support and selection | {0:~ }| {2:-- 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 | {0:~ }| {2:-- VISUAL BLOCK --} | ]]) end) describe('tab drag', function() before_each(function() screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, tab = { background = Screen.colors.LightGrey, underline = true }, sel = { bold = true }, fill = { reverse = true }, }) end) 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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }|*2 | ]]) feed('<14,0>') screen:expect([[ {tab: + bar }{sel: + foo }{fill: }{tab:X}| this is fo^o | {0:~ }|*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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*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 = [[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]], unchanged = true, } feed('<6,0>') screen:expect([[ {sel: + bar }{tab: + foo }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }|*2 | ]]) feed('<7,0>') screen:expect([[ {tab: + bar }{sel: + foo }{fill: }{tab:X}| this is fo^o | {0:~ }|*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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }|*2 | ]]) feed('<4,1>') screen:expect { grid = [[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }|*2 | ]], unchanged = true, } feed('<14,1>') screen:expect([[ {tab: + bar }{sel: + foo }{fill: }{tab:X}| this is fo^o | {0:~ }|*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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*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 = [[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]], unchanged = true, } feed('<11,1>') screen:expect { grid = [[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]], unchanged = true, } feed('<6,1>') screen:expect([[ {sel: + bar }{tab: + foo }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }|*2 | ]]) feed('<4,1>') screen:expect { grid = [[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }|*2 | ]], unchanged = true, } feed('<7,1>') screen:expect([[ {tab: + bar }{sel: + foo }{fill: }{tab:X}| this is fo^o | {0:~ }|*2 | ]]) end) end) describe('tabline', function() before_each(function() screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, tab = { background = Screen.colors.LightGrey, underline = true }, sel = { bold = true }, fill = { reverse = true }, }) end) it('left click in default tabline (position 4) 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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }|*2 | ]]) end) it('left click in default tabline (position 24) 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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]]) feed('<24,0>') screen:expect([[ this is fo^o | {0:~ }|*3 | ]]) end) it('double click in default tabline (position 4) opens new 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([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r{0:$} | {0:~ }|*2 | ]]) feed('<2-LeftMouse><4,0>') screen:expect([[ {sel: Name] }{tab: + foo + bar }{fill: }{tab:X}| {0:^$} | {0:~ }|*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([[ {fill: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 | {0:~ }| | ]]) feed('<4,1>') screen:expect([[ testing | mo{1:us}^e | support and selection | {0:~ }| {2:-- VISUAL --} | ]]) feed('<2,2>') screen:expect([[ testing | mo{1:use} | {1:su}^pport and selection | {0:~ }| {2:-- VISUAL --} | ]]) feed('<0,0>') screen:expect([[ ^t{1:esting} | {1:mou}se | support and selection | {0:~ }| {2:-- VISUAL --} | ]]) end) it('left drag changes visual selection after tab click', function() screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, tab = { background = Screen.colors.LightGrey, underline = true }, sel = { bold = true }, fill = { reverse = true }, vis = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, }) feed_command('silent file foo | tabnew | file bar') insert('this is bar') feed_command('tabprevious') -- go to first tab screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| testing | mouse | support and selectio^n | :tabprevious | ]]) feed('<10,0>') -- go to second tab helpers.poke_eventloop() feed('<0,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| ^this is bar{0:$} | {0:~ }|*2 :tabprevious | ]]) feed('<4,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| {vis:this}^ is bar{0:$} | {0:~ }|*2 {sel:-- 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 | {0:~ }│{0:~ }|*2 {0:~ }│{4:[No Name] [+] }| {0:~ }│foo{0:$} | {0:~ }│ba^r{0:$} | {0:~ }│{0:~ }|*4 {4:[No Name] [+] }{5:[No Name] [+] }| | ]], } api.nvim_input_mouse('left', 'press', '', 0, 6, 27) screen:expect { grid = [[ testing │testing | mouse │mouse | support and selection │support and selection | {0:~ }│{0:~ }|*2 {0:~ }│{4:[No Name] [+] }| {0:~ }│^foo{0:$} | {0:~ }│bar{0:$} | {0:~ }│{0:~ }|*4 {4:[No Name] [+] }{5:[No Name] [+] }| | ]], } api.nvim_input_mouse('left', 'drag', '', 0, 7, 30) screen:expect { grid = [[ testing │testing | mouse │mouse | support and selection │support and selection | {0:~ }│{0:~ }|*2 {0:~ }│{4:[No Name] [+] }| {0:~ }│{1:foo}{3:$} | {0:~ }│{1:bar}{0:^$} | {0:~ }│{0:~ }|*4 {4:[No Name] [+] }{5:[No Name] [+] }| {2:-- 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 | {1:suppor}^t and selection | {0:~ }| {2:-- VISUAL --} | ]]) feed('<0,1>') screen:expect([[ testing | ^m{1:ouse} | {1:support} and selection | {0:~ }| {2:-- VISUAL --} | ]]) feed('<4,0>') screen:expect([[ ^t{1:esting} | {1:mouse} | {1:support} and selection | {0:~ }| {2:-- VISUAL --} | ]]) feed('<14,2>') screen:expect([[ testing | mouse | {1:support and selectio}^n | {0:~ }| {2:-- 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 | {1:su}^p{1:port and selection} | {0:~ }| {2:-- VISUAL LINE --} | ]]) feed('<0,1>') screen:expect([[ testing | ^m{1:ouse} | {1:support and selection} | {0:~ }| {2:-- VISUAL LINE --} | ]]) feed('<4,0>') screen:expect([[ {1:test}^i{1:ng} | {1:mouse} | {1:support and selection} | {0:~ }| {2:-- VISUAL LINE --} | ]]) feed('<14,2>') screen:expect([[ testing | mouse | {1:support and se}^l{1:ection} | {0:~ }| {2:-- 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 | {0:~ }| {2:-- VISUAL BLOCK --} | ]]) feed('<0,1>') screen:expect([[ testing | ^m{1:ou}se | {1:sup}port and selection | {0:~ }| {2:-- VISUAL BLOCK --} | ]]) feed('<4,0>') screen:expect([[ te{1:st}^ing | mo{1:use} | su{1:ppo}rt and selection | {0:~ }| {2:-- VISUAL BLOCK --} | ]]) feed('<14,2>') screen:expect([[ testing | mouse | su{1:pport and se}^lection | {0:~ }| {2:-- VISUAL BLOCK --} | ]]) end) it('right click extends visual selection to the clicked location', function() feed('<0,0>') screen:expect([[ ^testing | mouse | support and selection | {0:~ }| | ]]) feed('<2,2>') screen:expect([[ {1:testing} | {1:mouse} | {1:su}^pport and selection | {0:~ }| {2:-- 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([[ {6:E433: No tags file} | {6:E426: Tag not found: test}| {6:ing} | {7:Press ENTER or type comma}| {7: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 │{0:^$} | mouse │{0:~ }| support and selection │{0:~ }| {4:[No Name] [+] }{5:[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 │{0:^$} | mouse │{0:~ }| support and │{0:~ }| {4:< Name] [+] }{5:[No Name] }| | ]]) api.nvim_input_mouse('left', 'drag', '', 0, 2, 2) screen:expect([[ te│{0:^$} | mo│{0:~ }| su│{0:~ }| {4:< }{5:[No Name] }| | ]]) api.nvim_input_mouse('left', 'release', '', 0, 2, 2) api.nvim_set_option_value('statuscolumn', 'foobar', { win = oldwin }) screen:expect([[ {8:fo}│{0:^$} | {8:fo}│{0:~ }|*2 {4:< }{5:[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│{0:^$} | {8:foobar}mouse │{0:~ }| {8:foobar}suppor│{0:~ }| {4:< Name] [+] }{5:[No Name] }| | ]]) api.nvim_input_mouse('left', 'drag', '', 0, 2, 22) screen:expect([[ {8:foobar}testing │{0:^$} | {8:foobar}mouse │{0:~ }| {8:foobar}support and sele│{0:~ }| {4:[No Name] [+] }{5:[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 | │ | {0:~ }│{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | test | mouse scrolling | | {0:~ }| {4:[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 | {0:~ }│test | {0:~ }│mouse scrolling | {0:~ }│ | {0:~ }│{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | test | mouse scrolling | | {0:~ }| {4:[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 | {0:~ }│many | {0:~ }│lines | {0:~ }│to | {0:~ }│test | {5:[No Name] [+] }{4:[No Name] [+] }| to | test | mouse scrolling | | {0:~ }| {4:[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 | {0:~ }│many | {0:~ }│lines | {0:~ }│to | {0:~ }│test | {5:[No Name] [+] }{4:[No Name] [+] }| Inserting | text | with | many | lines | {4:[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 20Ab') screen:expect([[ |*2 bbbbbbbbbbbbbbb^b | {0:~ }| | ]]) feed('<0,0>') screen:expect([[ |*2 n bbbbbbbbbbbbbbbbbbb^b | {0:~ }| | ]]) feed('^<0,0>') screen:expect([[ g | | ^t and selection bbbbbbbbb| {0:~ }| | ]]) end) it('horizontal scrolling (nvim_input_mouse)', function() command('set sidescroll=0') feed(':set nowrap') feed('a 20Ab') screen:expect([[ |*2 bbbbbbbbbbbbbbb^b | {0:~ }| | ]]) api.nvim_input_mouse('wheel', 'left', '', 0, 0, 27) screen:expect([[ |*2 n bbbbbbbbbbbbbbbbbbb^b | {0:~ }| | ]]) feed('^') api.nvim_input_mouse('wheel', 'right', '', 0, 0, 0) screen:expect([[ g | | ^t and selection bbbbbbbbb| {0:~ }| | ]]) end) it("'sidescrolloff' applies to horizontal scrolling", function() command('set nowrap') command('set sidescrolloff=4') feed('I 020ib0') screen:expect([[ testing | mouse | ^bbbbbbbbbbbbbbbbbbbb supp| {0:~ }| | ]]) api.nvim_input_mouse('wheel', 'right', '', 0, 0, 27) screen:expect([[ g | | bbbb^bbbbbbbbbb support an| {0:~ }| | ]]) -- window-local 'sidescrolloff' should override global value. #21162 command('setlocal sidescrolloff=2') feed('0') screen:expect([[ testing | mouse | ^bbbbbbbbbbbbbbbbbbbb supp| {0:~ }| | ]]) api.nvim_input_mouse('wheel', 'right', '', 0, 0, 27) screen:expect([[ g | | bb^bbbbbbbbbbbb support an| {0:~ }| | ]]) 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{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }|*2 | ]]) feed('<1,0>') screen:expect([[ S^ection{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }|*2 | ]]) feed('<21,0>') screen:expect([[ Section{0:>>--->--->---}{c: }^t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }|*2 | ]]) feed('<21,1>') screen:expect([[ Section{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t^3{c: } {c: }| {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }|*2 | ]]) feed('<0,2>') screen:expect([[ Section{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| {c:^>} 私は猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }|*2 | ]]) feed('<7,2>') screen:expect([[ Section{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| {c:>} 私は^猫が大好き{0:>---}{c: X } {0:>}| | {0:~ }|*2 | ]]) feed('<21,2>') screen:expect([[ Section{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| {c:>} 私は猫が大好き{0:>---}{c: ^X } {0:>}| | {0:~ }|*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{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c:^ }| t4{c: } | {c:>} 私は猫が大好き{0:>---}{c: X} | {c: } ✨🐈✨ | |*2 ]]) feed('<0,2>') screen:expect([[ Section{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| ^t4{c: } | {c:>} 私は猫が大好き{0:>---}{c: X} | {c: } ✨🐈✨ | |*2 ]]) feed('<8,3>') screen:expect([[ Section{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| t4{c: } | {c:>} 私は猫^が大好き{0:>---}{c: X} | {c: } ✨🐈✨ | |*2 ]]) feed('<21,3>') screen:expect([[ Section{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| t4{c: } | {c:>} 私は猫が大好き{0:>---}{c: ^X} | {c: } ✨🐈✨ | |*2 ]]) feed('<4,4>') screen:expect([[ Section{0:>>--->--->---}{c: }t1{c: } | {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }| t4{c: } | {c:>} 私は猫が大好き{0:>---}{c: X} | {c: } ✨^🐈✨ | |*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{0:>>--->--->---}^t1 | {0:>--->--->---} t2 t3 t4 | {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 | ]]) feed('<14,1>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} ^t2 t3 t4 | {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 | ]]) feed('<18,1>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t^3 t4 | {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 | ]]) feed('<0,2>') -- Weirdness screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | {c:^>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 | ]]) feed('<8,2>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | {c:>} 私は猫^が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 | ]]) feed('<20,2>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | {c:>} 私は猫が大好き{0:>---}{c:^X} ✨{0:>}| | {0:~ }|*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{0:>>--->--->---}^t1 | {0:>--->--->---} t2 t3 t4 | {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 {sm:-- INSERT --} | ]]) feed('<14,1>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} ^t2 t3 t4 | {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 {sm:-- INSERT --} | ]]) feed('<18,1>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t^3 t4 | {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 {sm:-- INSERT --} | ]]) feed('<0,2>') -- Weirdness screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | {c:^>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 {sm:-- INSERT --} | ]]) feed('<8,2>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | {c:>} 私は猫^が大好き{0:>---}{c:X} ✨{0:>}| | {0:~ }|*2 {sm:-- INSERT --} | ]]) feed('<20,2>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | {c:>} 私は猫が大好き{0:>---}{c:^X} ✨{0:>}| | {0:~ }|*2 {sm:-- 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{0:>>--->--->---}^t1 | {0:>--->--->---} t2 t3 | t4 | {c:>} 私は猫が大好き{0:>---}{c:X} | ✨🐈✨ | |*2 ]]) feed('<14,1>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} ^t2 t3 | t4 | {c:>} 私は猫が大好き{0:>---}{c:X} | ✨🐈✨ | |*2 ]]) feed('<18,1>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t^3 | t4 | {c:>} 私は猫が大好き{0:>---}{c: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{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 ^ | t4 | {c:>} 私は猫が大好き{0:>---}{c:X} | ✨🐈✨ | |*2 ]]) feed('<1,2>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t^4 | {c:>} 私は猫が大好き{0:>---}{c:X} | ✨🐈✨ | |*2 ]]) feed('<0,3>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | {c:^>} 私は猫が大好き{0:>---}{c:X} | ✨🐈✨ | |*2 ]]) feed('<20,3>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | {c:>} 私は猫が大好き{0:>---}{c:^X} | ✨🐈✨ | |*2 ]]) feed('<1,4>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | {c:>} 私は猫が大好き{0:>---}{c:X} | ^✨🐈✨ | |*2 ]]) feed('<5,4>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | {c:>} 私は猫が大好き{0:>---}{c: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{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | ^ 私は猫が大好き{0:>----} ✨🐈| | {0:~ }|*2 | ]]) feed('<1,2>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | ^私は猫が大好き{0:>----} ✨🐈| | {0:~ }|*2 | ]]) feed('<13,2>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | 私は猫が大好^き{0:>----} ✨🐈| | {0:~ }|*2 | ]]) feed('<20,2>') feed('zH') -- FIXME: unnecessary horizontal scrolling screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 t4 | 私は猫が大好き{0:>----}^ ✨🐈| | {0:~ }|*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{0:>>--->--->---}t1 | {0:>--->--->---} ^t2 t3 | t4 | 私は猫が大好き{0:>----} | ✨🐈✨ | |*2 ]]) feed('<18,1>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t^3 | t4 | 私は猫が大好き{0:>----} | ✨🐈✨ | |*2 ]]) feed('<1,2>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t^4 | 私は猫が大好き{0:>----} | ✨🐈✨ | |*2 ]]) feed('<0,3>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | ^ 私は猫が大好き{0:>----} | ✨🐈✨ | |*2 ]]) feed('<20,3>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | 私は猫が大好き{0:>----}^ | ✨🐈✨ | |*2 ]]) feed('<1,4>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | 私は猫が大好き{0:>----} | ^✨🐈✨ | |*2 ]]) feed('<3,4>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | 私は猫が大好き{0:>----} | ✨^🐈✨ | |*2 ]]) feed('<5,4>') screen:expect([[ Section{0:>>--->--->---}t1 | {0:>--->--->---} t2 t3 | t4 | 私は猫が大好き{0:>----} | ✨🐈^✨ | |*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) screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, c = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray }, sm = { bold = true }, }) 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{9:X}bbbbbbb | bbb{9:X}ccccccccc^c | {0:~ }|*2 | ]]) api.nvim_input_mouse('left', 'press', '', 0, 0, 22) screen:expect([[ aaaaaaaaaa{9:X}bbbbbb^b | bbb{9:X}cccccccccc | {0:~ }|*2 | ]]) api.nvim_input_mouse('left', 'press', '', 0, 1, 16) screen:expect([[ aaaaaaaaaa{9:X}bbbbbbb | bbb{9:X}cccccccccc ^ | {0:~ }|*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{9:X}bbbbbbb {6:?}| bbb{9:X}cccccccccc ^ {6:?}| {0:~ }|*2 | ]]) api.nvim_input_mouse('left', 'press', '', 0, 0, 22) screen:expect([[ aaaaaaaaaa{9:X}bbbbbb^b {6:?}| bbb{9:X}cccccccccc {6:?}| {0:~ }|*2 | ]]) api.nvim_input_mouse('left', 'press', '', 0, 1, 16) screen:expect([[ aaaaaaaaaa{9:X}bbbbbbb {6:?}| bbb{9:X}cccccccccc ^ {6:?}| {0:~ }|*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(' is not translated into multiclicks and 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('<0,0>') feed('<0,0>') api.nvim_input_mouse('move', '', '', 0, 0, 0) api.nvim_input_mouse('move', '', '', 0, 0, 0) eq(4, api.nvim_get_var('mouse_move')) eq(0, api.nvim_get_var('mouse_move2')) end) it('feeding in Normal mode does not use uninitialized memory #19480', function() feed('') helpers.poke_eventloop() helpers.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('"')) end) end)