local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths local insert, execute = helpers.insert, helpers.execute local eq, funcs = helpers.eq, helpers.funcs describe('Mouse input', function() local screen local hlgroup_colors = { NonText = Screen.colors.Blue, Visual = Screen.colors.LightGrey } before_each(function() clear() meths.set_option('mouse', 'a') meths.set_option('listchars', 'eol:$') -- set mouset to very high value to ensure that even in valgrind/travis, -- nvim will still pick multiple clicks meths.set_option('mouset', 5000) screen = Screen.new(25, 5) screen:attach() screen:set_default_attr_ids({ [1] = {background = hlgroup_colors.Visual}, [2] = {bold = true}, [3] = { foreground = hlgroup_colors.NonText, background = hlgroup_colors.Visual, bold = true, }, }) screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText}} ) feed('itestingmousesupport and selection') screen:expect([[ testing | mouse | support and selectio^n | ~ | | ]]) end) after_each(function() screen:detach() end) it('left click moves cursor', function() feed('<2,1>') screen:expect([[ testing | mo^use | support and selection | ~ | | ]]) feed('<0,0>') screen:expect([[ ^testing | mouse | support and selection | ~ | | ]]) end) describe('tabline', function() local tab_attrs = { tab = { background=Screen.colors.LightGrey, underline=true }, sel = { bold=true }, fill = { reverse=true } } it('left click in default tabline (position 4) switches to tab', function() execute('%delete') insert('this is foo') execute('silent file foo | tabnew | file bar') insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r | ~ | ~ | | ]], tab_attrs) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | ~ | ~ | | ]], tab_attrs) end) it('left click in default tabline (position 24) closes tab', function() meths.set_option('hidden', true) execute('%delete') insert('this is foo') execute('silent file foo | tabnew | file bar') insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r | ~ | ~ | | ]], tab_attrs) feed('<24,0>') screen:expect([[ this is fo^o | ~ | ~ | ~ | | ]], tab_attrs) end) it('double click in default tabline (position 4) opens new tab', function() meths.set_option('hidden', true) execute('%delete') insert('this is foo') execute('silent file foo | tabnew | file bar') insert('this is bar') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r | ~ | ~ | | ]], tab_attrs) feed('<2-LeftMouse><4,0>') screen:expect([[ {sel: Name] }{tab: + foo + bar }{fill: }{tab:X}| ^ | ~ | ~ | | ]], tab_attrs) end) describe('%@ label', function() before_each(function() execute([[ function Test(...) let g:reply = a:000 return copy(a:000) " Check for memory leaks: return should be freed endfunction ]]) execute([[ function Test2(...) return call('Test', a:000 + [2]) endfunction ]]) meths.set_option('tabline', '%@Test@test%X-%5@Test2@test2') meths.set_option('showtabline', 2) screen:expect([[ {fill:test-test2 }| mouse | support and selectio^n | ~ | | ]], tab_attrs) meths.set_var('reply', {}) end) local check_reply = function(expected) eq(expected, meths.get_var('reply')) meths.set_var('reply', {}) end local test_click = function(name, click_str, click_num, mouse_button, modifiers) it(name .. ' works', function() eq(1, funcs.has('tablineat')) feed(click_str .. '<3,0>') check_reply({0, click_num, mouse_button, modifiers}) feed(click_str .. '<4,0>') check_reply({}) feed(click_str .. '<6,0>') check_reply({5, click_num, mouse_button, modifiers, 2}) feed(click_str .. '<13,0>') check_reply({5, click_num, mouse_button, modifiers, 2}) 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 | ~ | | ]]) feed('<4,1>') screen:expect([[ testing | mo{1:us}^e | support and selection | ~ | {2:-- VISUAL --} | ]]) feed('<2,2>') screen:expect([[ testing | mo{1:use}{3: } | {1:su}^pport and selection | ~ | {2:-- VISUAL --} | ]]) feed('<0,0>') screen:expect([[ ^t{1:esting}{3: } | {1:mou}se | support and selection | ~ | {2:-- VISUAL --} | ]]) end) it('left drag changes visual selection after tab click', function() local tab_attrs = { tab = { background=Screen.colors.LightGrey, underline=true }, sel = { bold=true }, fill = { reverse=true }, vis = { background=Screen.colors.LightGrey } } execute('silent file foo | tabnew | file bar') insert('this is bar') execute('tabprevious') -- go to first tab screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| mouse | support and selectio^n | ~ | | ]], tab_attrs) feed('<10,0>') -- go to second tab helpers.wait() feed('<0,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| ^this is bar | ~ | ~ | | ]], tab_attrs) feed('<4,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| {vis:this}^ is bar | ~ | ~ | {sel:-- VISUAL --} | ]], tab_attrs) end) it('two clicks will select the word and enter VISUAL', function() feed('<2,2><2,2>') screen:expect([[ testing | mouse | {1:suppor}^t and selection | ~ | {2:-- VISUAL --} | ]]) end) it('three clicks will select the line and enter VISUAL LINE', function() feed('<2,2><2,2><2,2>') screen:expect([[ testing | mouse | {1:su}^p{1:port and selection}{3: } | ~ | {2:-- VISUAL LINE --} | ]]) end) it('four clicks will enter VISUAL BLOCK', function() feed('<2,2><2,2><2,2><2,2>') screen:expect([[ testing | mouse | su^pport and selection | ~ | {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 | ~ | | ]]) feed('<2,2>') screen:expect([[ {1:testing}{3: } | {1:mouse}{3: } | {1:su}^pport and selection | ~ | {2:-- VISUAL --} | ]]) end) it('ctrl + left click will search for a tag', function() meths.set_option('tags', './non-existent-tags-file') feed('<0,0>') screen:expect([[ E433: No tags file | E426: tag not found: test| ing | Press ENTER or type comma| nd to continue^ | ]],nil,true) feed('') end) it('mouse whell will target the hovered window', function() feed('ggdG') insert([[ Inserting text with many lines to test mouse scrolling ]]) screen:try_resize(53, 14) execute('sp', 'vsp') screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText}, {reverse=true}, {bold=true, reverse=true}} ) screen:expect([[ lines |lines | to |to | test |test | mouse scrolling |mouse scrolling | ^ | | ~ |~ | [No Name] [+] [No Name] [+] | to | test | mouse scrolling | | ~ | [No Name] [+] | :vsp | ]]) feed('<0,0>') screen:expect([[ mouse scrolling |lines | ^ |to | ~ |test | ~ |mouse scrolling | ~ | | ~ |~ | [No Name] [+] [No Name] [+] | to | test | mouse scrolling | | ~ | [No Name] [+] | | ]]) feed('<27,0>') screen:expect([[ mouse scrolling |text | ^ |with | ~ |many | ~ |lines | ~ |to | ~ |test | [No Name] [+] [No Name] [+] | to | test | mouse scrolling | | ~ | [No Name] [+] | | ]]) feed('<27,7>') screen:expect([[ mouse scrolling |text | ^ |with | ~ |many | ~ |lines | ~ |to | ~ |test | [No Name] [+] [No Name] [+] | Inserting | text | with | many | lines | [No Name] [+] | | ]]) end) it('horizontal scrolling', function() feed(":set nowrap") feed("a 20Ab") screen:expect([[ | | bbbbbbbbbbbbbbb^b | ~ | | ]]) feed("<0,0>") screen:expect([[ | | n bbbbbbbbbbbbbbbbbbb^b | ~ | | ]]) feed("^<0,0>") screen:expect([[ g | | ^t and selection bbbbbbbbb| ~ | | ]]) end) describe('on concealed text', function() -- Helpful for reading the test expectations: -- :match Error /\^/ local concealed = { c = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray } } before_each(function() screen:try_resize(25, 7) feed('ggdG') execute('set concealcursor=n') execute('set nowrap') execute('syntax match NonText "\\" conceal') execute('syntax match NonText "\\cs\\|g." conceal cchar=X') execute('syntax match NonText "\\%(lo\\|cl\\)." conceal') execute('syntax match NonText "Lo" conceal cchar=Y') insert([[ Lorem ipsum dolor sit amet, consetetur sadipscing elitr. Stet clita kasd gubergren, no sea takimata sanctus est. ]]) feed('gg') end) it('(level 1) click on non-wrapped lines', function() execute('let &conceallevel=1', 'echo') feed('<0,0>') screen:expect([[ {c:^Y}rem ip{c:X}um do{c: } {c:X}it {c: }, con| {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| | ~ | ~ | ~ | | ]], concealed) feed('<1,0>') screen:expect([[ {c:Y}^rem ip{c:X}um do{c: } {c:X}it {c: }, con| {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| | ~ | ~ | ~ | | ]], concealed) feed('<15,0>') screen:expect([[ {c:Y}rem ip{c:X}um do{c: } {c:^X}it {c: }, con| {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en, no| | ~ | ~ | ~ | | ]], concealed) feed('<15,1>') screen:expect([[ {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: }, con| {c:X}tet {c: }ta ka{c:X}d {c:X}^ber{c:X}en, no| | ~ | ~ | ~ | | ]], concealed) end) -- level 1 - non wrapped it('(level 1) click on wrapped lines', function() execute('let &conceallevel=1', 'let &wrap=1', 'echo') feed('<0,0>') screen:expect([[ {c:^Y}rem ip{c:X}um do{c: } {c:X}it {c: } | , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | elitr. | {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | , no {c:X}ea takimata {c:X}anctu{c:X}| e{c:X}t. | | ]], concealed) feed('<6,1>') screen:expect([[ {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | , con{c:X}^etetur {c:X}adip{c:X}cin{c:X} | elitr. | {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | , no {c:X}ea takimata {c:X}anctu{c:X}| e{c:X}t. | | ]], concealed) feed('<15,1>') screen:expect([[ {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | , con{c:X}etetur {c:X}a^dip{c:X}cin{c:X} | elitr. | {c:X}tet {c: }ta ka{c:X}d {c:X}ber{c:X}en | , no {c:X}ea takimata {c:X}anctu{c:X}| e{c:X}t. | | ]], concealed) feed('<15,3>') screen:expect([[ {c:Y}rem ip{c:X}um do{c: } {c:X}it {c: } | , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | elitr. | {c:X}tet {c: }ta ka{c:X}d {c:X}^ber{c:X}en | , no {c:X}ea takimata {c:X}anctu{c:X}| e{c:X}t. | | ]], concealed) end) -- level 1 - wrapped it('(level 2) click on non-wrapped lines', function() execute('let &conceallevel=2', 'echo') feed('<0,0>') screen:expect([[ {c:^Y}rem ip{c:X}um do {c:X}it , con{c:X}e| {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | | ~ | ~ | ~ | | ]], concealed) feed('<1,0>') screen:expect([[ {c:Y}^rem ip{c:X}um do {c:X}it , con{c:X}e| {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | | ~ | ~ | ~ | | ]], concealed) feed('<15,0>') screen:expect([[ {c:Y}rem ip{c:X}um do {c:X}^it , con{c:X}e| {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en, no | | ~ | ~ | ~ | | ]], concealed) feed('<15,1>') screen:expect([[ {c:Y}rem ip{c:X}um do {c:X}it , con{c:X}e| {c:X}tet ta ka{c:X}d {c:X}b^er{c:X}en, no | | ~ | ~ | ~ | | ]], concealed) end) -- level 2 - non wrapped it('(level 2) click on wrapped lines', function() execute('let &conceallevel=2', 'let &wrap=1', 'echo') feed('<0,0>') screen:expect([[ {c:^Y}rem ip{c:X}um do {c:X}it | , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | elitr. | {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | , no {c:X}ea takimata {c:X}anctu{c:X}| e{c:X}t. | | ]], concealed) feed('<6,1>') screen:expect([[ {c:Y}rem ip{c:X}um do {c:X}it | , con{c:X}^etetur {c:X}adip{c:X}cin{c:X} | elitr. | {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | , no {c:X}ea takimata {c:X}anctu{c:X}| e{c:X}t. | | ]], concealed) feed('<15,1>') screen:expect([[ {c:Y}rem ip{c:X}um do {c:X}it | , con{c:X}etetur {c:X}a^dip{c:X}cin{c:X} | elitr. | {c:X}tet ta ka{c:X}d {c:X}ber{c:X}en | , no {c:X}ea takimata {c:X}anctu{c:X}| e{c:X}t. | | ]], concealed) feed('<15,3>') screen:expect([[ {c:Y}rem ip{c:X}um do {c:X}it | , con{c:X}etetur {c:X}adip{c:X}cin{c:X} | elitr. | {c:X}tet ta ka{c:X}d {c:X}b^er{c:X}en | , no {c:X}ea takimata {c:X}anctu{c:X}| e{c:X}t. | | ]], concealed) end) -- level 2 - wrapped it('(level 3) click on non-wrapped lines', function() execute('let &conceallevel=3', 'echo') feed('<0,0>') screen:expect([[ ^rem ipum do it , conetetu| tet ta kad beren, no ea t| | ~ | ~ | ~ | | ]], concealed) feed('<1,0>') screen:expect([[ r^em ipum do it , conetetu| tet ta kad beren, no ea t| | ~ | ~ | ~ | | ]], concealed) feed('<15,0>') screen:expect([[ rem ipum do it ^, conetetu| tet ta kad beren, no ea t| | ~ | ~ | ~ | | ]], concealed) feed('<15,1>') screen:expect([[ rem ipum do it , conetetu| tet ta kad bere^n, no ea t| | ~ | ~ | ~ | | ]], concealed) end) -- level 3 - non wrapped it('(level 3) click on wrapped lines', function() execute('let &conceallevel=3', 'let &wrap=1', 'echo') feed('<0,0>') screen:expect([[ ^rem ipum do it | , conetetur adipcin | elitr. | tet ta kad beren | , no ea takimata anctu | et. | | ]], concealed) feed('<6,1>') screen:expect([[ rem ipum do it | , cone^tetur adipcin | elitr. | tet ta kad beren | , no ea takimata anctu | et. | | ]], concealed) feed('<15,1>') screen:expect([[ rem ipum do it | , conetetur adi^pcin | elitr. | tet ta kad beren | , no ea takimata anctu | et. | | ]], concealed) feed('<15,3>') screen:expect([[ rem ipum do it | , conetetur adipcin | elitr. | tet ta kad bere^n | , no ea takimata anctu | et. | | ]], concealed) end) -- level 3 - wrapped end) end)