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 if helpers.pending_win32(pending) then return end describe('Mouse input', function() local screen before_each(function() clear() meths.set_option('mouse', 'a') meths.set_option('listchars', 'eol:$') -- set mousetime to very high value to ensure that even in valgrind/travis, -- nvim will still pick multiple clicks meths.set_option('mousetime', 5000) screen = Screen.new(25, 5) screen:attach() screen:set_default_attr_ids({ [0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {background = Screen.colors.LightGrey}, [2] = {bold = true}, [3] = { foreground = Screen.colors.Blue, background = Screen.colors.LightGrey, bold = true, }, [4] = {reverse = true}, [5] = {bold = true, reverse = true}, }) feed('itestingmousesupport and selection') screen:expect([[ testing | mouse | support and selectio^n | {0:~ }| | ]]) end) after_each(function() screen:detach() end) it('single left click moves cursor', function() feed('<2,1>') screen:expect([[ testing | mo^use | support and selection | {0:~ }| | ]]) 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}{3: } | 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 } }) screen.timeout = 15000 end) it('in tabline on filler space moves tab to the end', 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 | {0:~ }| {0:~ }| | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) feed('<14,0>') screen:expect([[ {tab: + bar }{sel: + foo }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) end) it('in tabline to the left moves tab left', function() if os.getenv("TRAVIS") and helpers.os_name() == "osx" then pending("[Fails on Travis macOS. #4874]", function() end) return end 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 | {0:~ }| {0:~ }| | ]]) feed('<11,0>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r | {0:~ }| {0:~ }| | ]]) feed('<6,0>') screen:expect([[ {sel: + bar }{tab: + foo }{fill: }{tab:X}| this is ba^r | {0:~ }| {0:~ }| | ]]) end) it('in tabline to the right moves tab right', 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 | {0:~ }| {0:~ }| | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) feed('<7,0>') screen:expect([[ {tab: + bar }{sel: + foo }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) end) it('out of tabline under filler space moves tab to the end', 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 | {0:~ }| {0:~ }| | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) feed('<4,1>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) feed('<14,1>') screen:expect([[ {tab: + bar }{sel: + foo }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) end) it('out of tabline to the left moves tab left', function() if os.getenv("TRAVIS") and helpers.os_name() == "osx" then pending("[Fails on Travis macOS. #4874]", function() end) return end 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 | {0:~ }| {0:~ }| | ]]) feed('<11,0>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r | {0:~ }| {0:~ }| | ]]) feed('<11,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| this is ba^r | {0:~ }| {0:~ }| | ]]) feed('<6,1>') screen:expect([[ {sel: + bar }{tab: + foo }{fill: }{tab:X}| this is ba^r | {0:~ }| {0:~ }| | ]]) end) it('out of tabline to the right moves tab right', 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 | {0:~ }| {0:~ }| | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) feed('<4,1>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) feed('<7,1>') screen:expect([[ {tab: + bar }{sel: + foo }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) 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() 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 | {0:~ }| {0:~ }| | ]]) feed('<4,0>') screen:expect([[ {sel: + foo }{tab: + bar }{fill: }{tab:X}| this is fo^o | {0:~ }| {0:~ }| | ]]) 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 | {0:~ }| {0:~ }| | ]]) feed('<24,0>') screen:expect([[ this is fo^o | {0:~ }| {0:~ }| {0:~ }| | ]]) 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 | {0:~ }| {0:~ }| | ]]) feed('<2-LeftMouse><4,0>') screen:expect([[ {sel: Name] }{tab: + foo + bar }{fill: }{tab:X}| ^ | {0:~ }| {0:~ }| | ]]) 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 | {0:~ }| | ]]) 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 | {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}{3: } | {1:su}^pport and selection | {0:~ }| {2:-- VISUAL --} | ]]) feed('<0,0>') screen:expect([[ ^t{1:esting}{3: } | {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 } }) 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 | {0:~ }| | ]]) feed('<10,0>') -- go to second tab helpers.wait() feed('<0,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| ^this is bar | {0:~ }| {0:~ }| | ]]) feed('<4,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| {vis:this}^ is bar | {0:~ }| {0:~ }| {sel:-- VISUAL --} | ]]) 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 | {0:~ }| {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: } | {0:~ }| {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 | {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}{3: } | {1:mouse}{3: } | {1:su}^pport and selection | {0:~ }| {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:expect([[ lines {4:|}lines | to {4:|}to | test {4:|}test | mouse scrolling {4:|}mouse scrolling | ^ {4:|} | {0:~ }{4:|}{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | test | mouse scrolling | | {0:~ }| {4:[No Name] [+] }| :vsp | ]]) feed('<0,0>') screen:expect([[ mouse scrolling {4:|}lines | ^ {4:|}to | {0:~ }{4:|}test | {0:~ }{4:|}mouse scrolling | {0:~ }{4:|} | {0:~ }{4:|}{0:~ }| {5:[No Name] [+] }{4:[No Name] [+] }| to | test | mouse scrolling | | {0:~ }| {4:[No Name] [+] }| | ]]) feed('<27,0>') screen:expect([[ mouse scrolling {4:|}text | ^ {4:|}with | {0:~ }{4:|}many | {0:~ }{4:|}lines | {0:~ }{4:|}to | {0:~ }{4:|}test | {5:[No Name] [+] }{4:[No Name] [+] }| to | test | mouse scrolling | | {0:~ }| {4:[No Name] [+] }| | ]]) feed('<27,7>') screen:expect([[ mouse scrolling {4:|}text | ^ {4:|}with | {0:~ }{4:|}many | {0:~ }{4:|}lines | {0:~ }{4:|}to | {0:~ }{4:|}test | {5:[No Name] [+] }{4:[No Name] [+] }| Inserting | text | with | many | lines | {4:[No Name] [+] }| | ]]) end) it('horizontal scrolling', function() feed(":set nowrap") feed("a 20Ab") screen:expect([[ | | bbbbbbbbbbbbbbb^b | {0:~ }| | ]]) feed("<0,0>") screen:expect([[ | | n bbbbbbbbbbbbbbbbbbb^b | {0:~ }| | ]]) feed("^<0,0>") screen:expect([[ g | | ^t and selection bbbbbbbbb| {0:~ }| | ]]) 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 }, }) 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| | {0:~ }| {0:~ }| {0:~ }| | ]]) 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| | {0:~ }| {0:~ }| {0:~ }| | ]]) 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| | {0:~ }| {0:~ }| {0:~ }| | ]]) 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| | {0:~ }| {0:~ }| {0:~ }| | ]]) 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. | | ]]) 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. | | ]]) 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. | | ]]) 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. | | ]]) 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 | | {0:~ }| {0:~ }| {0:~ }| | ]]) 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 | | {0:~ }| {0:~ }| {0:~ }| | ]]) 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 | | {0:~ }| {0:~ }| {0:~ }| | ]]) 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 | | {0:~ }| {0:~ }| {0:~ }| | ]]) 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. | | ]]) 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. | | ]]) 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. | | ]]) 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. | | ]]) 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| | {0:~ }| {0:~ }| {0:~ }| | ]]) feed('<1,0>') screen:expect([[ r^em ipum do it , conetetu| tet ta kad beren, no ea t| | {0:~ }| {0:~ }| {0:~ }| | ]]) feed('<15,0>') screen:expect([[ rem ipum do it ^, conetetu| tet ta kad beren, no ea t| | {0:~ }| {0:~ }| {0:~ }| | ]]) feed('<15,1>') screen:expect([[ rem ipum do it , conetetu| tet ta kad bere^n, no ea t| | {0:~ }| {0:~ }| {0:~ }| | ]]) 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. | | ]]) feed('<6,1>') screen:expect([[ rem ipum do it | , cone^tetur adipcin | elitr. | tet ta kad beren | , no ea takimata anctu | et. | | ]]) feed('<15,1>') screen:expect([[ rem ipum do it | , conetetur adi^pcin | elitr. | tet ta kad beren | , no ea takimata anctu | et. | | ]]) feed('<15,3>') screen:expect([[ rem ipum do it | , conetetur adipcin | elitr. | tet ta kad bere^n | , no ea takimata anctu | et. | | ]]) end) -- level 3 - wrapped end) end)