neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

mouse_spec.lua (69280B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 local Screen = require('test.functional.ui.screen')
      4 
      5 local clear, feed, api = n.clear, n.feed, n.api
      6 local insert, feed_command = n.insert, n.feed_command
      7 local eq, fn = t.eq, n.fn
      8 local poke_eventloop = n.poke_eventloop
      9 local command = n.command
     10 local exec = n.exec
     11 
     12 describe('ui/mouse/input', function()
     13  local function with_ext_multigrid(multigrid)
     14    local screen
     15 
     16    before_each(function()
     17      clear()
     18      api.nvim_set_option_value('mouse', 'a', {})
     19      api.nvim_set_option_value('list', true, {})
     20      -- NB: this is weird, but mostly irrelevant to the test
     21      -- So I didn't bother to change it
     22      command('set listchars=eol:$')
     23      command('setl listchars=nbsp:x')
     24 
     25      screen = Screen.new(25, 5, { ext_multigrid = multigrid })
     26      screen:add_extra_attr_ids {
     27        [100] = {
     28          bold = true,
     29          background = Screen.colors.LightGrey,
     30          foreground = Screen.colors.Blue1,
     31        },
     32      }
     33      command('set mousemodel=extend')
     34      feed('itesting<cr>mouse<cr>support and selection<esc>')
     35      screen:expect({
     36        any = {
     37          'testing                  ',
     38          'mouse                    ',
     39          'support and selectio%^n    ',
     40        },
     41      })
     42    end)
     43 
     44    it('single left click moves cursor', function()
     45      feed('<LeftMouse><2,1>')
     46      screen:expect({
     47        any = 'mo%^use',
     48        mouse_enabled = true,
     49      })
     50      feed('<LeftMouse><0,0>')
     51      screen:expect({
     52        any = '%^testing',
     53      })
     54    end)
     55 
     56    it("in external ui works with unset 'mouse'", function()
     57      api.nvim_set_option_value('mouse', '', {})
     58      feed('<LeftMouse><2,1>')
     59      screen:expect({
     60        any = 'mo%^use',
     61        mouse_enabled = false,
     62      })
     63      feed('<LeftMouse><0,0>')
     64      screen:expect({
     65        any = '%^testing',
     66      })
     67    end)
     68 
     69    it('double left click enters visual mode', function()
     70      feed('<LeftMouse><0,0>')
     71      feed('<LeftRelease><0,0>')
     72      feed('<LeftMouse><0,0>')
     73      feed('<LeftRelease><0,0>')
     74      screen:expect({
     75        any = {
     76          '{17:testin}%^g',
     77          'VISUAL',
     78        },
     79      })
     80    end)
     81 
     82    it('triple left click enters visual line mode', function()
     83      feed('<LeftMouse><0,0>')
     84      feed('<LeftRelease><0,0>')
     85      feed('<LeftMouse><0,0>')
     86      feed('<LeftRelease><0,0>')
     87      feed('<LeftMouse><0,0>')
     88      feed('<LeftRelease><0,0>')
     89      screen:expect({
     90        any = {
     91          '%^t{17:esting}',
     92          'VISUAL LINE',
     93        },
     94      })
     95    end)
     96 
     97    it('quadruple left click enters visual block mode', function()
     98      feed('<LeftMouse><0,0>')
     99      feed('<LeftRelease><0,0>')
    100      feed('<LeftMouse><0,0>')
    101      feed('<LeftRelease><0,0>')
    102      feed('<LeftMouse><0,0>')
    103      feed('<LeftRelease><0,0>')
    104      feed('<LeftMouse><0,0>')
    105      feed('<LeftRelease><0,0>')
    106      screen:expect({
    107        any = {
    108          '%^testing',
    109          'VISUAL BLOCK',
    110        },
    111      })
    112    end)
    113 
    114    describe('tab drag', function()
    115      it('in tabline on filler space moves tab to the end', function()
    116        feed_command('%delete')
    117        insert('this is foo')
    118        feed_command('silent file foo | tabnew | file bar')
    119        insert('this is bar')
    120        screen:expect({
    121          any = {
    122            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    123            'this is ba%^r{1:%$}',
    124          },
    125        })
    126        feed('<LeftMouse><4,0>')
    127        screen:expect({
    128          any = {
    129            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}',
    130            'this is fo%^o',
    131          },
    132        })
    133        feed('<LeftDrag><14,0>')
    134        screen:expect({
    135          any = {
    136            '{24: %+ bar }{5: %+ foo }{2:          }{24:X}',
    137            'this is fo%^o',
    138          },
    139        })
    140      end)
    141 
    142      it('in tabline to the left moves tab left', function()
    143        feed_command('%delete')
    144        insert('this is foo')
    145        feed_command('silent file foo | tabnew | file bar')
    146        insert('this is bar')
    147        screen:expect({
    148          any = {
    149            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    150            'this is ba%^r{1:%$}',
    151          },
    152        })
    153        feed('<LeftMouse><11,0>')
    154        -- Prevent the case where screen:expect() with "unchanged" returns too early,
    155        -- causing the click position to be overwritten by the next drag.
    156        poke_eventloop()
    157        screen:expect({
    158          any = {
    159            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    160            'this is ba%^r{1:%$}',
    161          },
    162          unchanged = true,
    163        })
    164        feed('<LeftDrag><6,0>')
    165        screen:expect({
    166          any = {
    167            '{5: %+ bar }{24: %+ foo }{2:          }{24:X}',
    168            'this is ba%^r{1:%$}',
    169          },
    170        })
    171      end)
    172 
    173      it('in tabline to the right moves tab right', function()
    174        feed_command('%delete')
    175        insert('this is foo')
    176        feed_command('silent file foo | tabnew | file bar')
    177        insert('this is bar')
    178        screen:expect({
    179          any = {
    180            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    181            'this is ba%^r{1:%$}',
    182          },
    183        })
    184        feed('<LeftMouse><4,0>')
    185        screen:expect({
    186          any = {
    187            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}',
    188            'this is fo%^o',
    189          },
    190        })
    191        feed('<LeftDrag><7,0>')
    192        screen:expect({
    193          any = {
    194            '{24: %+ bar }{5: %+ foo }{2:          }{24:X}',
    195            'this is fo%^o',
    196          },
    197        })
    198      end)
    199 
    200      it('out of tabline under filler space moves tab to the end', function()
    201        feed_command('%delete')
    202        insert('this is foo')
    203        feed_command('silent file foo | tabnew | file bar')
    204        insert('this is bar')
    205        screen:expect({
    206          any = {
    207            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    208            'this is ba%^r{1:%$}',
    209          },
    210        })
    211        feed('<LeftMouse><4,0>')
    212        screen:expect({
    213          any = {
    214            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}',
    215            'this is fo%^o',
    216          },
    217        })
    218        feed('<LeftDrag><4,1>')
    219        screen:expect({
    220          any = {
    221            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}',
    222            'this is fo%^o',
    223          },
    224          unchanged = true,
    225        })
    226        feed('<LeftDrag><14,1>')
    227        screen:expect({
    228          any = {
    229            '{24: %+ bar }{5: %+ foo }{2:          }{24:X}',
    230            'this is fo%^o',
    231          },
    232        })
    233      end)
    234 
    235      it('out of tabline to the left moves tab left', function()
    236        feed_command('%delete')
    237        insert('this is foo')
    238        feed_command('silent file foo | tabnew | file bar')
    239        insert('this is bar')
    240        screen:expect({
    241          any = {
    242            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    243            'this is ba%^r{1:%$}',
    244          },
    245        })
    246        feed('<LeftMouse><11,0>')
    247        -- Prevent the case where screen:expect() with "unchanged" returns too early,
    248        -- causing the click position to be overwritten by the next drag.
    249        poke_eventloop()
    250        screen:expect({
    251          any = {
    252            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    253            'this is ba%^r{1:%$}',
    254          },
    255          unchanged = true,
    256        })
    257        feed('<LeftDrag><11,1>')
    258        screen:expect({
    259          any = {
    260            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    261            'this is ba%^r{1:%$}',
    262          },
    263          unchanged = true,
    264        })
    265        feed('<LeftDrag><6,1>')
    266        screen:expect({
    267          any = {
    268            '{5: %+ bar }{24: %+ foo }{2:          }{24:X}',
    269            'this is ba%^r{1:%$}',
    270          },
    271        })
    272      end)
    273 
    274      it('out of tabline to the right moves tab right', function()
    275        feed_command('%delete')
    276        insert('this is foo')
    277        feed_command('silent file foo | tabnew | file bar')
    278        insert('this is bar')
    279        screen:expect({
    280          any = {
    281            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    282            'this is ba%^r{1:%$}',
    283          },
    284        })
    285        feed('<LeftMouse><4,0>')
    286        screen:expect({
    287          any = {
    288            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}',
    289            'this is fo%^o',
    290          },
    291        })
    292        feed('<LeftDrag><4,1>')
    293        screen:expect({
    294          any = {
    295            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}',
    296            'this is fo%^o',
    297          },
    298          unchanged = true,
    299        })
    300        feed('<LeftDrag><7,1>')
    301        screen:expect({
    302          any = {
    303            '{24: %+ bar }{5: %+ foo }{2:          }{24:X}',
    304            'this is fo%^o',
    305          },
    306        })
    307      end)
    308    end)
    309 
    310    describe('tabline', function()
    311      it('left click in default tabline (tabpage label) switches to tab', function()
    312        feed_command('%delete')
    313        insert('this is foo')
    314        feed_command('silent file foo | tabnew | file bar')
    315        insert('this is bar')
    316        screen:expect({
    317          any = {
    318            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    319            'this is ba%^r{1:%$}',
    320          },
    321        })
    322        feed('<LeftMouse><4,0>')
    323        screen:expect({
    324          any = {
    325            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}',
    326            'this is fo%^o',
    327          },
    328        })
    329        feed('<LeftMouse><6,0>')
    330        screen:expect_unchanged()
    331        feed('<LeftMouse><10,0>')
    332        screen:expect({
    333          any = {
    334            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    335            'this is ba%^r{1:%$}',
    336          },
    337        })
    338        feed('<LeftMouse><12,0>')
    339        screen:expect_unchanged()
    340      end)
    341 
    342      it('left click in default tabline (blank space) switches tab', function()
    343        feed_command('%delete')
    344        insert('this is foo')
    345        feed_command('silent file foo | tabnew | file bar')
    346        insert('this is bar')
    347        screen:expect({
    348          any = {
    349            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    350            'this is ba%^r{1:%$}',
    351          },
    352        })
    353        feed('<LeftMouse><20,0>')
    354        screen:expect({
    355          any = {
    356            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}',
    357            'this is fo%^o',
    358          },
    359        })
    360        feed('<LeftMouse><22,0>')
    361        screen:expect({
    362          any = {
    363            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    364            'this is ba%^r{1:%$}',
    365          },
    366        })
    367      end)
    368 
    369      it('left click in default tabline (close label) closes tab', function()
    370        api.nvim_set_option_value('hidden', true, {})
    371        feed_command('%delete')
    372        insert('this is foo')
    373        feed_command('silent file foo | tabnew | file bar')
    374        insert('this is bar')
    375        screen:expect({
    376          any = {
    377            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    378            'this is ba%^r{1:%$}',
    379          },
    380        })
    381        feed('<LeftMouse><24,0>')
    382        screen:expect({
    383          any = {
    384            'this is fo%^o',
    385          },
    386          none = {
    387            '{24:X}',
    388            '%+ foo',
    389            '%+ bar',
    390          },
    391        })
    392      end)
    393 
    394      it('double click in default tabline opens new tab before', function()
    395        feed_command('%delete')
    396        insert('this is foo')
    397        feed_command('silent file foo | tabnew | file bar')
    398        insert('this is bar')
    399        screen:expect({
    400          any = {
    401            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    402            'this is ba%^r{1:%$}',
    403          },
    404        })
    405        feed('<2-LeftMouse><4,0>')
    406        screen:expect({
    407          any = {
    408            '{5:  Name] }{24: %+ foo  %+ bar }{2:  }{24:X}|',
    409            '{1:%^$}',
    410          },
    411        })
    412        command('tabclose')
    413        screen:expect({
    414          any = {
    415            '{5: %+ foo }{24: %+ bar }{2:          }{24:X}|',
    416            'this is fo%^o',
    417          },
    418        })
    419        feed('<2-LeftMouse><20,0>')
    420        screen:expect({
    421          any = {
    422            '{24: %+ foo  %+ bar }{5:  Name] }{2:  }{24:X}',
    423            '{1:%^$}',
    424          },
    425        })
    426        command('tabclose')
    427        screen:expect({
    428          any = {
    429            '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    430            'this is ba%^r{1:$}',
    431          },
    432        })
    433        feed('<2-LeftMouse><10,0>')
    434        screen:expect({
    435          any = {
    436            '{24: %+ foo }{5:  Name] }{24: %+ bar }{2:  }{24:X}',
    437            '{1:%^$}',
    438          },
    439        })
    440      end)
    441 
    442      describe('%@ label', function()
    443        before_each(function()
    444          feed_command([[
    445            function Test(...)
    446              let g:reply = a:000
    447              return copy(a:000)  " Check for memory leaks: return should be freed
    448            endfunction
    449          ]])
    450          feed_command([[
    451            function Test2(...)
    452              return call('Test', a:000 + [2])
    453            endfunction
    454          ]])
    455          api.nvim_set_option_value('tabline', '%@Test@test%X-%5@Test2@test2', {})
    456          api.nvim_set_option_value('showtabline', 2, {})
    457          screen:expect({
    458            any = {
    459              '{2:test%-test2               }',
    460              'testing',
    461              'mouse',
    462              'support and selectio%^n',
    463            },
    464          })
    465          api.nvim_set_var('reply', {})
    466        end)
    467 
    468        local check_reply = function(expected)
    469          eq(expected, api.nvim_get_var('reply'))
    470          api.nvim_set_var('reply', {})
    471        end
    472 
    473        local test_click = function(name, click_str, click_num, mouse_button, modifiers)
    474          local function doit(do_click)
    475            eq(1, fn.has('tablineat'))
    476            do_click(0, 3)
    477            check_reply({ 0, click_num, mouse_button, modifiers })
    478            do_click(0, 4)
    479            check_reply({})
    480            do_click(0, 6)
    481            check_reply({ 5, click_num, mouse_button, modifiers, 2 })
    482            do_click(0, 13)
    483            check_reply({ 5, click_num, mouse_button, modifiers, 2 })
    484          end
    485 
    486          it(name .. ' works (pseudokey)', function()
    487            doit(function(row, col)
    488              feed(click_str .. '<' .. col .. ',' .. row .. '>')
    489            end)
    490          end)
    491 
    492          it(name .. ' works (nvim_input_mouse)', function()
    493            doit(function(row, col)
    494              local buttons = { l = 'left', m = 'middle', r = 'right' }
    495              local modstr = (click_num > 1) and tostring(click_num) or ''
    496              for char in string.gmatch(modifiers, '%w') do
    497                modstr = modstr .. char .. '-' -- - not needed but should be accepted
    498              end
    499              api.nvim_input_mouse(buttons[mouse_button], 'press', modstr, 0, row, col)
    500            end)
    501          end)
    502        end
    503 
    504        test_click('single left click', '<LeftMouse>', 1, 'l', '    ')
    505        test_click('shifted single left click', '<S-LeftMouse>', 1, 'l', 's   ')
    506        test_click('shifted single left click with alt modifier', '<S-A-LeftMouse>', 1, 'l', 's a ')
    507        test_click(
    508          'shifted single left click with alt and ctrl modifiers',
    509          '<S-C-A-LeftMouse>',
    510          1,
    511          'l',
    512          'sca '
    513        )
    514        -- <C-RightMouse> does not work
    515        test_click(
    516          'shifted single right click with alt modifier',
    517          '<S-A-RightMouse>',
    518          1,
    519          'r',
    520          's a '
    521        )
    522        -- Modifiers do not work with MiddleMouse
    523        test_click(
    524          'shifted single middle click with alt and ctrl modifiers',
    525          '<MiddleMouse>',
    526          1,
    527          'm',
    528          '    '
    529        )
    530        -- Modifiers do not work with N-*Mouse
    531        test_click('double left click', '<2-LeftMouse>', 2, 'l', '    ')
    532        test_click('triple left click', '<3-LeftMouse>', 3, 'l', '    ')
    533        test_click('quadruple left click', '<4-LeftMouse>', 4, 'l', '    ')
    534        test_click('double right click', '<2-RightMouse>', 2, 'r', '    ')
    535        test_click('triple right click', '<3-RightMouse>', 3, 'r', '    ')
    536        test_click('quadruple right click', '<4-RightMouse>', 4, 'r', '    ')
    537        test_click('double middle click', '<2-MiddleMouse>', 2, 'm', '    ')
    538        test_click('triple middle click', '<3-MiddleMouse>', 3, 'm', '    ')
    539        test_click('quadruple middle click', '<4-MiddleMouse>', 4, 'm', '    ')
    540      end)
    541    end)
    542 
    543    it('left drag changes visual selection', function()
    544      -- drag events must be preceded by a click
    545      feed('<LeftMouse><2,1>')
    546      screen:expect({
    547        any = {
    548          'testing',
    549          'mo%^use',
    550          'support and selection',
    551        },
    552      })
    553      feed('<LeftDrag><4,1>')
    554      screen:expect({
    555        any = {
    556          'testing',
    557          'mo{17:us}%^e',
    558          'support and selection',
    559          'VISUAL',
    560        },
    561      })
    562      feed('<LeftDrag><2,2>')
    563      screen:expect({
    564        any = {
    565          'testing',
    566          'mo{17:use}',
    567          '{17:su}%^pport and selection',
    568          'VISUAL',
    569        },
    570      })
    571      feed('<LeftDrag><0,0>')
    572      screen:expect({
    573        any = {
    574          '%^t{17:esting}',
    575          '{17:mou}se ',
    576          'support and selection',
    577          'VISUAL',
    578        },
    579      })
    580    end)
    581 
    582    it('left drag changes visual selection after tab click', function()
    583      feed_command('silent file foo | tabnew | file bar')
    584      insert('this is bar')
    585      feed_command('tabprevious') -- go to first tab
    586      screen:expect({
    587        any = {
    588          '{5: %+ foo }{24: %+ bar }{2:          }{24:X}|',
    589          'testing',
    590          'mouse',
    591          'support and selectio%^n',
    592          ':tabprevious',
    593        },
    594      })
    595      feed('<LeftMouse><10,0><LeftRelease>') -- go to second tab
    596      n.poke_eventloop()
    597      feed('<LeftMouse><0,1>')
    598      -- This text is hidden when using multigrid
    599      local none = {
    600        'mouse',
    601        'support and selection',
    602      }
    603      if multigrid then
    604        none = nil
    605      end
    606      screen:expect({
    607        any = {
    608          '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    609          '%^this is bar{1:%$}',
    610          ':tabprevious',
    611        },
    612        none = none,
    613      })
    614      feed('<LeftDrag><4,1>')
    615      screen:expect({
    616        any = {
    617          '{24: %+ foo }{5: %+ bar }{2:          }{24:X}',
    618          '{17:this}%^ is bar{1:%$}',
    619          'VISUAL',
    620        },
    621      })
    622    end)
    623 
    624    it('left drag changes visual selection in split layout', function()
    625      screen:try_resize(53, 14)
    626      command('set mouse=a')
    627      command('vsplit')
    628      command('wincmd l')
    629      command('below split')
    630      command('enew')
    631      feed('ifoo\nbar<esc>')
    632      screen:expect({
    633        any = {
    634          'foo{1:%$}',
    635          'ba%^r{1:%$}',
    636        },
    637      })
    638      api.nvim_input_mouse('left', 'press', '', 0, 6, 27)
    639      screen:expect({
    640        any = {
    641          '%^foo{1:%$}',
    642          'bar{1:%$}',
    643        },
    644      })
    645      api.nvim_input_mouse('left', 'drag', '', 0, 7, 30)
    646      screen:expect({
    647        any = {
    648          '{17:foo}{100:%$}',
    649          '{17:bar}{1:%^%$}',
    650          'VISUAL',
    651        },
    652      })
    653    end)
    654 
    655    it('two clicks will enter VISUAL and dragging selects words', function()
    656      feed('<LeftMouse><2,2>')
    657      feed('<LeftRelease><2,2>')
    658      feed('<LeftMouse><2,2>')
    659      screen:expect({
    660        any = {
    661          'testing',
    662          'mouse',
    663          '{17:suppor}%^t and selection',
    664          'VISUAL',
    665        },
    666      })
    667      feed('<LeftDrag><0,1>')
    668      screen:expect({
    669        any = {
    670          'testing',
    671          '%^m{17:ouse}',
    672          '{17:support} and selection',
    673          'VISUAL',
    674        },
    675      })
    676      feed('<LeftDrag><4,0>')
    677      screen:expect({
    678        any = {
    679          '%^t{17:esting}',
    680          '{17:mouse}',
    681          '{17:support} and selection',
    682          'VISUAL',
    683        },
    684      })
    685      feed('<LeftDrag><14,2>')
    686      screen:expect({
    687        any = {
    688          'testing',
    689          'mouse',
    690          '{17:support and selectio}%^n',
    691          'VISUAL',
    692        },
    693      })
    694    end)
    695 
    696    it('three clicks will enter VISUAL LINE and dragging selects lines', function()
    697      feed('<LeftMouse><2,2>')
    698      feed('<LeftRelease><2,2>')
    699      feed('<LeftMouse><2,2>')
    700      feed('<LeftRelease><2,2>')
    701      feed('<LeftMouse><2,2>')
    702      screen:expect({
    703        any = {
    704          'testing',
    705          'mouse',
    706          '{17:su}%^p{17:port and selection}',
    707          'VISUAL LINE',
    708        },
    709      })
    710      feed('<LeftDrag><0,1>')
    711      screen:expect({
    712        any = {
    713          'testing',
    714          '%^m{17:ouse}',
    715          '{17:support and selection}',
    716          'VISUAL LINE',
    717        },
    718      })
    719      feed('<LeftDrag><4,0>')
    720      screen:expect({
    721        any = {
    722          '{17:test}%^i{17:ng}',
    723          '{17:mouse}',
    724          '{17:support and selection}',
    725          'VISUAL LINE',
    726        },
    727      })
    728      feed('<LeftDrag><14,2>')
    729      screen:expect({
    730        any = {
    731          'testing',
    732          'mouse',
    733          '{17:support and se}%^l{17:ection}',
    734          'VISUAL LINE',
    735        },
    736      })
    737    end)
    738 
    739    it('four clicks will enter VISUAL BLOCK and dragging selects blockwise', function()
    740      feed('<LeftMouse><2,2>')
    741      feed('<LeftRelease><2,2>')
    742      feed('<LeftMouse><2,2>')
    743      feed('<LeftRelease><2,2>')
    744      feed('<LeftMouse><2,2>')
    745      feed('<LeftRelease><2,2>')
    746      feed('<LeftMouse><2,2>')
    747      screen:expect({
    748        any = {
    749          'testing',
    750          'mouse',
    751          'su%^pport and selection',
    752          'VISUAL BLOCK',
    753        },
    754      })
    755      feed('<LeftDrag><0,1>')
    756      screen:expect({
    757        any = {
    758          'testing',
    759          '%^m{17:ou}se',
    760          '{17:sup}port and selection',
    761          'VISUAL BLOCK',
    762        },
    763      })
    764      feed('<LeftDrag><4,0>')
    765      screen:expect({
    766        any = {
    767          'te{17:st}%^ing',
    768          'mo{17:use}',
    769          'su{17:ppo}rt and selection',
    770          'VISUAL BLOCK',
    771        },
    772      })
    773      feed('<LeftDrag><14,2>')
    774      screen:expect({
    775        any = {
    776          'testing',
    777          'mouse',
    778          'su{17:pport and se}%^lection',
    779          'VISUAL BLOCK',
    780        },
    781      })
    782    end)
    783 
    784    it('right click extends visual selection to the clicked location', function()
    785      feed('<LeftMouse><0,0>')
    786      screen:expect({
    787        any = {
    788          '%^testing',
    789          'mouse',
    790          'support and selection',
    791        },
    792      })
    793      feed('<RightMouse><2,2>')
    794      screen:expect({
    795        any = {
    796          '{17:testing}',
    797          '{17:mouse}',
    798          '{17:su}^pport and selection',
    799          'VISUAL',
    800        },
    801      })
    802    end)
    803 
    804    it('ctrl + left click will search for a tag', function()
    805      api.nvim_set_option_value('tags', './non-existent-tags-file', {})
    806      feed('<C-LeftMouse><0,0>')
    807      screen:expect({
    808        any = {
    809          '{9:E433: No tags file}',
    810          '{9:E426: Tag not found: test}',
    811          '{9:ing}',
    812          '{6:Press ENTER or type comma}',
    813          '{6:nd to continue}%^',
    814        },
    815      })
    816      feed('<cr>')
    817    end)
    818 
    819    it('x1 and x2 can be triggered by api', function()
    820      api.nvim_set_var('x1_pressed', 0)
    821      api.nvim_set_var('x1_released', 0)
    822      api.nvim_set_var('x2_pressed', 0)
    823      api.nvim_set_var('x2_released', 0)
    824      command('nnoremap <X1Mouse> <Cmd>let g:x1_pressed += 1<CR>')
    825      command('nnoremap <X1Release> <Cmd>let g:x1_released += 1<CR>')
    826      command('nnoremap <X2Mouse> <Cmd>let g:x2_pressed += 1<CR>')
    827      command('nnoremap <X2Release> <Cmd>let g:x2_released += 1<CR>')
    828      api.nvim_input_mouse('x1', 'press', '', 0, 0, 0)
    829      api.nvim_input_mouse('x1', 'release', '', 0, 0, 0)
    830      api.nvim_input_mouse('x2', 'press', '', 0, 0, 0)
    831      api.nvim_input_mouse('x2', 'release', '', 0, 0, 0)
    832      eq(1, api.nvim_get_var('x1_pressed'), 'x1 pressed once')
    833      eq(1, api.nvim_get_var('x1_released'), 'x1 released once')
    834      eq(1, api.nvim_get_var('x2_pressed'), 'x2 pressed once')
    835      eq(1, api.nvim_get_var('x2_released'), 'x2 released once')
    836    end)
    837 
    838    it('dragging vertical separator', function()
    839      screen:try_resize(45, 5)
    840      command('setlocal nowrap')
    841      local oldwin = api.nvim_get_current_win()
    842      command('rightbelow vnew')
    843      screen:expect({
    844        any = {
    845          'testing               ',
    846          'mouse                 ',
    847          'support and selection ',
    848          '{1:%^%$}                     ',
    849          '{2:%[No Name%] %[%+%]          }{3:%[No Name%]             }',
    850        },
    851      })
    852      api.nvim_input_mouse('left', 'press', '', 0, 0, 22)
    853      poke_eventloop()
    854      api.nvim_input_mouse('left', 'drag', '', 0, 1, 12)
    855      screen:expect({
    856        any = {
    857          'testing     ',
    858          'mouse       ',
    859          'support and ',
    860          '{1:%^$}                               ',
    861          '{2:< Name%] %[%+%]  }{3:%[No Name%]                       }',
    862        },
    863      })
    864      api.nvim_input_mouse('left', 'drag', '', 0, 2, 2)
    865      screen:expect({
    866        any = {
    867          'te',
    868          'mo',
    869          'su',
    870          '{1:%^%$}                                         ',
    871          '{2:<  }{3:%[No Name%]                                 }',
    872        },
    873      })
    874      api.nvim_input_mouse('left', 'release', '', 0, 2, 2)
    875      api.nvim_set_option_value('statuscolumn', 'foobar', { win = oldwin })
    876      screen:expect({
    877        any = {
    878          '{8:fo}',
    879          '{1:%^%$}                                         ',
    880          '{2:<  }{3:%[No Name%]                                 }',
    881        },
    882      })
    883      api.nvim_input_mouse('left', 'press', '', 0, 0, 2)
    884      poke_eventloop()
    885      api.nvim_input_mouse('left', 'drag', '', 0, 1, 12)
    886      screen:expect({
    887        any = {
    888          '{8:foobar}testin',
    889          '{8:foobar}mouse ',
    890          '{8:foobar}suppor',
    891          '{1:%^%$}                               ',
    892          '{2:< Name%] %[%+%]  }{3:%[No Name%]                       }',
    893        },
    894      })
    895      api.nvim_input_mouse('left', 'drag', '', 0, 2, 22)
    896      screen:expect({
    897        any = {
    898          '{8:foobar}testing         ',
    899          '{8:foobar}mouse           ',
    900          '{8:foobar}support and sele',
    901          '{1:%^%$}                     ',
    902          '{2:%[No Name%] %[%+%]          }{3:%[No Name%]             }',
    903        },
    904      })
    905      api.nvim_input_mouse('left', 'release', '', 0, 2, 22)
    906    end)
    907 
    908    local function wheel(use_api)
    909      feed('ggdG')
    910      insert([[
    911      Inserting
    912      text
    913      with
    914      many
    915      lines
    916      to
    917      test
    918      mouse scrolling
    919      ]])
    920      screen:try_resize(53, 14)
    921      feed('k')
    922      api.nvim_set_option_value('statuscolumn', 'C', { win = api.nvim_get_current_win() })
    923      feed_command('sp')
    924      api.nvim_set_option_value('statuscolumn', 'B', { win = api.nvim_get_current_win() })
    925      feed_command('vsp')
    926      api.nvim_set_option_value('statuscolumn', 'A', { win = api.nvim_get_current_win() })
    927      screen:expect({
    928        any = {
    929          '{8:A}lines                    ',
    930          '{8:A}to                       ',
    931          '{8:A}test                     ',
    932          '{8:A}^mouse scrolling          ',
    933          '{8:B}lines                    ',
    934          '{8:B}to                       ',
    935          '{8:B}test                     ',
    936          '{8:B}mouse scrolling          ',
    937          '{3:%[No Name%] %[%+%]              }{2:%[No Name%] %[%+%]             }',
    938          '{8:C}to                                                  ',
    939          '{8:C}test                                                ',
    940          '{8:C}mouse scrolling                                     ',
    941          '{2:%[No Name%] %[%+%]                                        }',
    942        },
    943        none = {
    944          '{8:A}Inserting',
    945          '{8:A}text',
    946          '{8:A}with',
    947          '{8:A}many',
    948          '{8:B}Inserting',
    949          '{8:B}text',
    950          '{8:B}with',
    951          '{8:B}many',
    952          '{8:C}Inserting',
    953          '{8:C}text',
    954          '{8:C}with',
    955          '{8:C}many',
    956          '{8:C}lines',
    957        },
    958      })
    959      if use_api then
    960        api.nvim_input_mouse('wheel', 'down', '', 0, 0, 0)
    961      else
    962        feed('<ScrollWheelDown><0,0>')
    963      end
    964      screen:expect({
    965        any = {
    966          '{8:A}^mouse scrolling          ',
    967          '{8:B}lines                    ',
    968          '{8:B}to                       ',
    969          '{8:B}test                     ',
    970          '{8:B}mouse scrolling          ',
    971          '{3:%[No Name%] %[%+%]              }{2:%[No Name%] %[%+%]             }',
    972          '{8:C}to                                                  ',
    973          '{8:C}test                                                ',
    974          '{8:C}mouse scrolling                                     ',
    975          '{2:%[No Name%] %[%+%]                                        }',
    976        },
    977        none = {
    978          '{8:A}lines',
    979          '{8:A}to',
    980          '{8:A}test',
    981          '{8:B}Inserting',
    982          '{8:B}text',
    983          '{8:B}with',
    984          '{8:B}many',
    985          '{8:C}Inserting',
    986          '{8:C}text',
    987          '{8:C}with',
    988          '{8:C}many',
    989          '{8:C}lines',
    990        },
    991      })
    992      if use_api then
    993        api.nvim_input_mouse('wheel', 'up', '', 0, 0, 27)
    994      else
    995        feed('<ScrollWheelUp><27,0>')
    996      end
    997      screen:expect({
    998        any = {
    999          '{8:A}^mouse scrolling          ',
   1000          '{8:B}lines                    ',
   1001          '{8:B}to                       ',
   1002          '{8:B}test                     ',
   1003          '{3:%[No Name%] %[%+%]              }{2:%[No Name%] %[%+%]             }',
   1004          '{8:C}to                                                  ',
   1005          '{8:C}test                                                ',
   1006          '{8:C}mouse scrolling                                     ',
   1007          '{2:%[No Name%] %[%+%]                                        }',
   1008        },
   1009        none = {
   1010          '{8:A}Inserting',
   1011          '{8:A}text',
   1012          '{8:A}with',
   1013          '{8:A}many',
   1014          '{8:A}lines',
   1015          '{8:A}to',
   1016          '{8:A}test',
   1017          '{8:B}Inserting',
   1018          '{8:B}mouse scrolling',
   1019          '{8:C}Inserting',
   1020          '{8:C}text',
   1021          '{8:C}with',
   1022          '{8:C}many',
   1023          '{8:C}lines',
   1024        },
   1025      })
   1026      if use_api then
   1027        api.nvim_input_mouse('wheel', 'up', '', 0, 7, 27)
   1028        api.nvim_input_mouse('wheel', 'up', '', 0, 7, 27)
   1029      else
   1030        feed('<ScrollWheelUp><27,7><ScrollWheelUp>')
   1031      end
   1032      screen:expect({
   1033        any = {
   1034          '{8:A}^mouse scrolling          ',
   1035          '{8:B}lines                    ',
   1036          '{8:B}to                       ',
   1037          '{8:B}test                     ',
   1038          '{3:%[No Name%] %[%+%]              }{2:%[No Name%] %[%+%]             }',
   1039          '{8:C}Inserting                                           ',
   1040          '{8:C}text                                                ',
   1041          '{8:C}with                                                ',
   1042          '{8:C}many                                                ',
   1043          '{8:C}lines                                               ',
   1044          '{2:%[No Name%] %[%+%]                                        }',
   1045        },
   1046        none = {
   1047          '{8:A}Inserting',
   1048          '{8:A}text',
   1049          '{8:A}with',
   1050          '{8:A}many',
   1051          '{8:A}lines',
   1052          '{8:A}to',
   1053          '{8:A}test',
   1054          '{8:B}Inserting',
   1055          '{8:B}mouse scrolling',
   1056          '{8:C}to',
   1057          '{8:C}test',
   1058          '{8:C}mouse scrolling',
   1059        },
   1060      })
   1061    end
   1062 
   1063    it('mouse wheel will target the hovered window (pseudokey)', function()
   1064      wheel(false)
   1065    end)
   1066 
   1067    it('mouse wheel will target the hovered window (nvim_input_mouse)', function()
   1068      wheel(true)
   1069    end)
   1070 
   1071    it('horizontal scrolling (pseudokey)', function()
   1072      command('set sidescroll=0')
   1073      feed('<esc>:set nowrap<cr>')
   1074 
   1075      feed('a <esc>17Ab<esc>3Ab<esc>')
   1076      screen:expect({
   1077        any = {
   1078          'bbbbbbbbbbbbbbb%^b        ',
   1079        },
   1080      })
   1081 
   1082      feed('<ScrollWheelLeft><0,0>')
   1083      screen:expect({
   1084        any = {
   1085          'n bbbbbbbbbbbbbbbbbbb%^b   ',
   1086        },
   1087      })
   1088 
   1089      feed('^<ScrollWheelRight><0,0>')
   1090      screen:expect({
   1091        any = {
   1092          'g                        ',
   1093          '%^t and selection bbbbbbbbb',
   1094        },
   1095      })
   1096    end)
   1097 
   1098    it('horizontal scrolling (nvim_input_mouse)', function()
   1099      command('set sidescroll=0')
   1100      feed('<esc>:set nowrap<cr>')
   1101 
   1102      feed('a <esc>17Ab<esc>3Ab<esc>')
   1103      screen:expect({
   1104        any = {
   1105          'bbbbbbbbbbbbbbb%^b        ',
   1106        },
   1107      })
   1108 
   1109      api.nvim_input_mouse('wheel', 'left', '', 0, 0, 27)
   1110      screen:expect({
   1111        any = {
   1112          'n bbbbbbbbbbbbbbbbbbb%^b   ',
   1113        },
   1114      })
   1115 
   1116      feed('^')
   1117      api.nvim_input_mouse('wheel', 'right', '', 0, 0, 0)
   1118      screen:expect({
   1119        any = {
   1120          'g                        ',
   1121          '%^t and selection bbbbbbbbb',
   1122        },
   1123      })
   1124    end)
   1125 
   1126    it("'sidescrolloff' applies to horizontal scrolling", function()
   1127      command('set nowrap')
   1128      command('set sidescrolloff=4')
   1129 
   1130      feed('I <esc>020ib<esc>0')
   1131      screen:expect({
   1132        any = {
   1133          'testing                  ',
   1134          'mouse                    ',
   1135          '%^bbbbbbbbbbbbbbbbbbbb supp',
   1136        },
   1137      })
   1138 
   1139      api.nvim_input_mouse('wheel', 'right', '', 0, 0, 27)
   1140      screen:expect({
   1141        any = {
   1142          'g                        ',
   1143          '                         ',
   1144          'bbbb%^bbbbbbbbbb support an',
   1145        },
   1146      })
   1147 
   1148      -- window-local 'sidescrolloff' should override global value. #21162
   1149      command('setlocal sidescrolloff=2')
   1150      feed('0')
   1151      screen:expect({
   1152        any = {
   1153          'testing                  ',
   1154          'mouse                    ',
   1155          '%^bbbbbbbbbbbbbbbbbbbb supp',
   1156        },
   1157      })
   1158 
   1159      api.nvim_input_mouse('wheel', 'right', '', 0, 0, 27)
   1160      screen:expect({
   1161        any = {
   1162          'g                        ',
   1163          '                         ',
   1164          'bb%^bbbbbbbbbbbb support an',
   1165        },
   1166      })
   1167    end)
   1168 
   1169    local function test_mouse_click_conceal()
   1170      it('(level 1) click on non-wrapped lines', function()
   1171        feed_command('let &conceallevel=1', 'echo')
   1172 
   1173        feed('<esc><LeftMouse><0,0>')
   1174        screen:expect({
   1175          any = {
   1176            '%^Section{1:>>--->--->---}{14: }t1{14: } ',
   1177            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1178            '{14:>} 私は猫が大好き{1:>---}{14: X } {1:>}',
   1179          },
   1180        })
   1181 
   1182        feed('<esc><LeftMouse><1,0>')
   1183        screen:expect({
   1184          any = {
   1185            'S%^ection{1:>>--->--->---}{14: }t1{14: } ',
   1186            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1187            '{14:>} 私は猫が大好き{1:>---}{14: X } {1:>}',
   1188          },
   1189        })
   1190 
   1191        feed('<esc><LeftMouse><21,0>')
   1192        screen:expect({
   1193          any = {
   1194            'Section{1:>>--->--->---}{14: }%^t1{14: } ',
   1195            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1196            '{14:>} 私は猫が大好き{1:>---}{14: X } {1:>}',
   1197          },
   1198        })
   1199 
   1200        feed('<esc><LeftMouse><21,1>')
   1201        screen:expect({
   1202          any = {
   1203            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1204            '{1:>--->--->---}  {14: }t2{14: } {14: }t%^3{14: } {14: }',
   1205            '{14:>} 私は猫が大好き{1:>---}{14: X } {1:>}',
   1206          },
   1207        })
   1208 
   1209        feed('<esc><LeftMouse><0,2>')
   1210        screen:expect({
   1211          any = {
   1212            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1213            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1214            '{14:%^>} 私は猫が大好き{1:>---}{14: X } {1:>}',
   1215          },
   1216        })
   1217 
   1218        feed('<esc><LeftMouse><7,2>')
   1219        screen:expect({
   1220          any = {
   1221            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1222            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1223            '{14:>} 私は%^猫が大好き{1:>---}{14: X } {1:>}',
   1224          },
   1225        })
   1226 
   1227        feed('<esc><LeftMouse><21,2>')
   1228        screen:expect({
   1229          any = {
   1230            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1231            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1232            '{14:>} 私は猫が大好き{1:>---}{14: %^X } {1:>}',
   1233          },
   1234        })
   1235      end) -- level 1 - non wrapped
   1236 
   1237      it('(level 1) click on wrapped lines', function()
   1238        feed_command('let &conceallevel=1', 'let &wrap=1', 'echo')
   1239 
   1240        feed('<esc><LeftMouse><24,1>')
   1241        screen:expect({
   1242          any = {
   1243            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1244            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14:%^ }',
   1245            't4{14: }                      ',
   1246            '{14:>} 私は猫が大好き{1:>---}{14: X}   ',
   1247            '{14: } ✨🐈✨                 ',
   1248          },
   1249        })
   1250 
   1251        feed('<esc><LeftMouse><0,2>')
   1252        screen:expect({
   1253          any = {
   1254            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1255            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1256            '%^t4{14: }                      ',
   1257            '{14:>} 私は猫が大好き{1:>---}{14: X}   ',
   1258            '{14: } ✨🐈✨                 ',
   1259          },
   1260        })
   1261 
   1262        feed('<esc><LeftMouse><8,3>')
   1263        screen:expect({
   1264          any = {
   1265            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1266            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1267            't4{14: }                      ',
   1268            '{14:>} 私は猫%^が大好き{1:>---}{14: X}   ',
   1269            '{14: } ✨🐈✨                 ',
   1270          },
   1271        })
   1272 
   1273        feed('<esc><LeftMouse><21,3>')
   1274        screen:expect({
   1275          any = {
   1276            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1277            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1278            't4{14: }                      ',
   1279            '{14:>} 私は猫が大好き{1:>---}{14: %^X}   ',
   1280            '{14: } ✨🐈✨                 ',
   1281          },
   1282        })
   1283 
   1284        feed('<esc><LeftMouse><4,4>')
   1285        screen:expect({
   1286          any = {
   1287            'Section{1:>>--->--->---}{14: }t1{14: } ',
   1288            '{1:>--->--->---}  {14: }t2{14: } {14: }t3{14: } {14: }',
   1289            't4{14: }                      ',
   1290            '{14:>} 私は猫が大好き{1:>---}{14: X}   ',
   1291            '{14: } ✨%^🐈✨                 ',
   1292          },
   1293        })
   1294      end) -- level 1 - wrapped
   1295 
   1296      it('(level 2) click on non-wrapped lines', function()
   1297        feed_command('let &conceallevel=2', 'echo')
   1298 
   1299        feed('<esc><LeftMouse><20,0>')
   1300        screen:expect({
   1301          any = {
   1302            'Section{1:>>--->--->---}%^t1   ',
   1303            '{1:>--->--->---}  t2 t3 t4   ',
   1304            '{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}',
   1305          },
   1306        })
   1307 
   1308        feed('<esc><LeftMouse><14,1>')
   1309        screen:expect({
   1310          any = {
   1311            'Section{1:>>--->--->---}t1   ',
   1312            '{1:>--->--->---}  %^t2 t3 t4   ',
   1313            '{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}',
   1314          },
   1315        })
   1316 
   1317        feed('<esc><LeftMouse><18,1>')
   1318        screen:expect({
   1319          any = {
   1320            'Section{1:>>--->--->---}t1   ',
   1321            '{1:>--->--->---}  t2 t%^3 t4   ',
   1322            '{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}',
   1323          },
   1324        })
   1325 
   1326        feed('<esc><LeftMouse><0,2>') -- Weirdness
   1327        screen:expect({
   1328          any = {
   1329            'Section{1:>>--->--->---}t1   ',
   1330            '{1:>--->--->---}  t2 t3 t4   ',
   1331            '{14:%^>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}',
   1332          },
   1333        })
   1334 
   1335        feed('<esc><LeftMouse><8,2>')
   1336        screen:expect({
   1337          any = {
   1338            'Section{1:>>--->--->---}t1   ',
   1339            '{1:>--->--->---}  t2 t3 t4   ',
   1340            '{14:>} 私は猫%^が大好き{1:>---}{14:X} ✨{1:>}',
   1341          },
   1342        })
   1343 
   1344        feed('<esc><LeftMouse><20,2>')
   1345        screen:expect({
   1346          any = {
   1347            'Section{1:>>--->--->---}t1   ',
   1348            '{1:>--->--->---}  t2 t3 t4   ',
   1349            '{14:>} 私は猫が大好き{1:>---}{14:%^X} ✨{1:>}',
   1350          },
   1351        })
   1352      end) -- level 2 - non wrapped
   1353 
   1354      it('(level 2) click on non-wrapped lines (insert mode)', function()
   1355        feed_command('let &conceallevel=2', 'echo')
   1356 
   1357        feed('<esc>i<LeftMouse><20,0>')
   1358        screen:expect({
   1359          any = {
   1360            'Section{1:>>--->--->---}%^t1   ',
   1361            '{1:>--->--->---}  t2 t3 t4   ',
   1362            '{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}',
   1363          },
   1364        })
   1365 
   1366        feed('<LeftMouse><14,1>')
   1367        screen:expect({
   1368          any = {
   1369            'Section{1:>>--->--->---}t1   ',
   1370            '{1:>--->--->---}  %^t2 t3 t4   ',
   1371            '{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}',
   1372          },
   1373        })
   1374 
   1375        feed('<LeftMouse><18,1>')
   1376        screen:expect({
   1377          any = {
   1378            'Section{1:>>--->--->---}t1   ',
   1379            '{1:>--->--->---}  t2 t%^3 t4   ',
   1380            '{14:>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}',
   1381          },
   1382        })
   1383 
   1384        feed('<LeftMouse><0,2>') -- Weirdness
   1385        screen:expect({
   1386          any = {
   1387            'Section{1:>>--->--->---}t1   ',
   1388            '{1:>--->--->---}  t2 t3 t4   ',
   1389            '{14:%^>} 私は猫が大好き{1:>---}{14:X} ✨{1:>}',
   1390          },
   1391        })
   1392 
   1393        feed('<LeftMouse><8,2>')
   1394        screen:expect({
   1395          any = {
   1396            'Section{1:>>--->--->---}t1   ',
   1397            '{1:>--->--->---}  t2 t3 t4   ',
   1398            '{14:>} 私は猫%^が大好き{1:>---}{14:X} ✨{1:>}',
   1399          },
   1400        })
   1401 
   1402        feed('<LeftMouse><20,2>')
   1403        screen:expect({
   1404          any = {
   1405            'Section{1:>>--->--->---}t1   ',
   1406            '{1:>--->--->---}  t2 t3 t4   ',
   1407            '{14:>} 私は猫が大好き{1:>---}{14:%^X} ✨{1:>}',
   1408          },
   1409        })
   1410      end) -- level 2 - non wrapped (insert mode)
   1411 
   1412      it('(level 2) click on wrapped lines', function()
   1413        feed_command('let &conceallevel=2', 'let &wrap=1', 'echo')
   1414 
   1415        feed('<esc><LeftMouse><20,0>')
   1416        screen:expect({
   1417          any = {
   1418            'Section{1:>>--->--->---}%^t1   ',
   1419            '{1:>--->--->---}  t2 t3      ',
   1420            't4                       ',
   1421            '{14:>} 私は猫が大好き{1:>---}{14:X}    ',
   1422            ' ✨🐈✨                  ',
   1423          },
   1424        })
   1425 
   1426        feed('<esc><LeftMouse><14,1>')
   1427        screen:expect({
   1428          any = {
   1429            'Section{1:>>--->--->---}t1   ',
   1430            '{1:>--->--->---}  %^t2 t3      ',
   1431            't4                       ',
   1432            '{14:>} 私は猫が大好き{1:>---}{14:X}    ',
   1433            ' ✨🐈✨                  ',
   1434          },
   1435        })
   1436 
   1437        feed('<esc><LeftMouse><18,1>')
   1438        screen:expect({
   1439          any = {
   1440            'Section{1:>>--->--->---}t1   ',
   1441            '{1:>--->--->---}  t2 t%^3      ',
   1442            't4                       ',
   1443            '{14:>} 私は猫が大好き{1:>---}{14:X}    ',
   1444            ' ✨🐈✨                  ',
   1445          },
   1446        })
   1447 
   1448        -- NOTE: The click would ideally be on the 't' in 't4', but wrapping
   1449        -- caused the invisible '*' right before 't4' to remain on the previous
   1450        -- screen line.  This is being treated as expected because fixing this is
   1451        -- out of scope for mouse clicks.  Should the wrapping behavior of
   1452        -- concealed characters change in the future, this case should be
   1453        -- reevaluated.
   1454        feed('<esc><LeftMouse><0,2>')
   1455        screen:expect({
   1456          any = {
   1457            'Section{1:>>--->--->---}t1   ',
   1458            '{1:>--->--->---}  t2 t3 %^     ',
   1459            't4                       ',
   1460            '{14:>} 私は猫が大好き{1:>---}{14:X}    ',
   1461            ' ✨🐈✨                  ',
   1462          },
   1463        })
   1464 
   1465        feed('<esc><LeftMouse><1,2>')
   1466        screen:expect({
   1467          any = {
   1468            'Section{1:>>--->--->---}t1   ',
   1469            '{1:>--->--->---}  t2 t3      ',
   1470            't%^4                       ',
   1471            '{14:>} 私は猫が大好き{1:>---}{14:X}    ',
   1472            ' ✨🐈✨                  ',
   1473          },
   1474        })
   1475 
   1476        feed('<esc><LeftMouse><0,3>')
   1477        screen:expect({
   1478          any = {
   1479            'Section{1:>>--->--->---}t1   ',
   1480            '{1:>--->--->---}  t2 t3      ',
   1481            't4                       ',
   1482            '{14:%^>} 私は猫が大好き{1:>---}{14:X}    ',
   1483            ' ✨🐈✨                  ',
   1484          },
   1485        })
   1486 
   1487        feed('<esc><LeftMouse><20,3>')
   1488        screen:expect({
   1489          any = {
   1490            'Section{1:>>--->--->---}t1   ',
   1491            '{1:>--->--->---}  t2 t3      ',
   1492            't4                       ',
   1493            '{14:>} 私は猫が大好き{1:>---}{14:%^X}    ',
   1494            ' ✨🐈✨                  ',
   1495          },
   1496        })
   1497 
   1498        feed('<esc><LeftMouse><1,4>')
   1499        screen:expect({
   1500          any = {
   1501            'Section{1:>>--->--->---}t1   ',
   1502            '{1:>--->--->---}  t2 t3      ',
   1503            't4                       ',
   1504            '{14:>} 私は猫が大好き{1:>---}{14:X}    ',
   1505            '%^✨🐈✨                 ',
   1506          },
   1507        })
   1508 
   1509        feed('<esc><LeftMouse><5,4>')
   1510        screen:expect({
   1511          any = {
   1512            'Section{1:>>--->--->---}t1   ',
   1513            '{1:>--->--->---}  t2 t3      ',
   1514            't4                       ',
   1515            '{14:>} 私は猫が大好き{1:>---}{14:X}    ',
   1516            '✨🐈%^✨                  ',
   1517          },
   1518        })
   1519      end) -- level 2 - wrapped
   1520 
   1521      it('(level 3) click on non-wrapped lines', function()
   1522        feed_command('let &conceallevel=3', 'echo')
   1523 
   1524        feed('<esc><LeftMouse><0,2>')
   1525        screen:expect({
   1526          any = {
   1527            'Section{1:>>--->--->---}t1   ',
   1528            '{1:>--->--->---}  t2 t3 t4   ',
   1529            '%^ 私は猫が大好き{1:>----} ✨🐈',
   1530          },
   1531        })
   1532 
   1533        feed('<esc><LeftMouse><1,2>')
   1534        screen:expect({
   1535          any = {
   1536            'Section{1:>>--->--->---}t1   ',
   1537            '{1:>--->--->---}  t2 t3 t4   ',
   1538            ' %^私は猫が大好き{1:>----} ✨🐈',
   1539          },
   1540        })
   1541 
   1542        feed('<esc><LeftMouse><13,2>')
   1543        screen:expect({
   1544          any = {
   1545            'Section{1:>>--->--->---}t1   ',
   1546            '{1:>--->--->---}  t2 t3 t4   ',
   1547            ' 私は猫が大好%^き{1:>----} ✨🐈',
   1548          },
   1549        })
   1550 
   1551        feed('<esc><LeftMouse><20,2>')
   1552        feed('zH') -- FIXME: unnecessary horizontal scrolling
   1553        screen:expect({
   1554          any = {
   1555            'Section{1:>>--->--->---}t1   ',
   1556            '{1:>--->--->---}  t2 t3 t4   ',
   1557            ' 私は猫が大好き{1:>----}%^ ✨🐈',
   1558          },
   1559        })
   1560      end) -- level 3 - non wrapped
   1561 
   1562      it('(level 3) click on wrapped lines', function()
   1563        feed_command('let &conceallevel=3', 'let &wrap=1', 'echo')
   1564 
   1565        feed('<esc><LeftMouse><14,1>')
   1566        screen:expect({
   1567          any = {
   1568            'Section{1:>>--->--->---}t1   ',
   1569            '{1:>--->--->---}  %^t2 t3      ',
   1570            't4                       ',
   1571            ' 私は猫が大好き{1:>----}     ',
   1572            ' ✨🐈✨                  ',
   1573          },
   1574        })
   1575 
   1576        feed('<esc><LeftMouse><18,1>')
   1577        screen:expect({
   1578          any = {
   1579            'Section{1:>>--->--->---}t1   ',
   1580            '{1:>--->--->---}  t2 t%^3      ',
   1581            't4                       ',
   1582            ' 私は猫が大好き{1:>----}     ',
   1583            ' ✨🐈✨                  ',
   1584          },
   1585        })
   1586 
   1587        feed('<esc><LeftMouse><1,2>')
   1588        screen:expect({
   1589          any = {
   1590            'Section{1:>>--->--->---}t1   ',
   1591            '{1:>--->--->---}  t2 t3      ',
   1592            't%^4                       ',
   1593            ' 私は猫が大好き{1:>----}     ',
   1594            ' ✨🐈✨                  ',
   1595          },
   1596        })
   1597 
   1598        feed('<esc><LeftMouse><0,3>')
   1599        screen:expect({
   1600          any = {
   1601            'Section{1:>>--->--->---}t1   ',
   1602            '{1:>--->--->---}  t2 t3      ',
   1603            't4                       ',
   1604            '%^ 私は猫が大好き{1:>----}     ',
   1605            ' ✨🐈✨                  ',
   1606          },
   1607        })
   1608 
   1609        feed('<esc><LeftMouse><20,3>')
   1610        screen:expect({
   1611          any = {
   1612            'Section{1:>>--->--->---}t1   ',
   1613            '{1:>--->--->---}  t2 t3      ',
   1614            't4                       ',
   1615            ' 私は猫が大好き{1:>----}%^     ',
   1616            ' ✨🐈✨                  ',
   1617          },
   1618        })
   1619 
   1620        feed('<esc><LeftMouse><1,4>')
   1621        screen:expect({
   1622          any = {
   1623            'Section{1:>>--->--->---}t1   ',
   1624            '{1:>--->--->---}  t2 t3      ',
   1625            't4                       ',
   1626            ' 私は猫が大好き{1:>----}     ',
   1627            ' %^✨🐈✨                  ',
   1628          },
   1629        })
   1630 
   1631        feed('<esc><LeftMouse><3,4>')
   1632        screen:expect({
   1633          any = {
   1634            'Section{1:>>--->--->---}t1   ',
   1635            '{1:>--->--->---}  t2 t3      ',
   1636            't4                       ',
   1637            ' 私は猫が大好き{1:>----}     ',
   1638            ' ✨%^🐈✨                  ',
   1639          },
   1640        })
   1641 
   1642        feed('<esc><LeftMouse><5,4>')
   1643        screen:expect({
   1644          any = {
   1645            'Section{1:>>--->--->---}t1   ',
   1646            '{1:>--->--->---}  t2 t3      ',
   1647            't4                       ',
   1648            ' 私は猫が大好き{1:>----}     ',
   1649            ' ✨🐈%^✨                  ',
   1650          },
   1651        })
   1652      end) -- level 3 - wrapped
   1653    end
   1654 
   1655    describe('on concealed text', function()
   1656      -- Helpful for reading the test expectations:
   1657      -- :match Error /\^/
   1658 
   1659      before_each(function()
   1660        screen:try_resize(25, 7)
   1661        feed('ggdG')
   1662 
   1663        command([[setlocal concealcursor=ni nowrap shiftwidth=2 tabstop=4 list listchars=tab:>-]])
   1664        command([[highlight link X0 Normal]])
   1665        command([[highlight link X1 NonText]])
   1666        command([[highlight link X2 NonText]])
   1667        command([[highlight link X3 NonText]])
   1668 
   1669        -- First column is there to retain the tabs.
   1670        insert([[
   1671        |Section				*t1*
   1672        |			  *t2* *t3* *t4*
   1673        |x 私は猫が大好き	*cats* ✨🐈✨
   1674        ]])
   1675 
   1676        feed('gg<c-v>Gxgg')
   1677      end)
   1678 
   1679      describe('(syntax)', function()
   1680        before_each(function()
   1681          command([[syntax region X0 matchgroup=X1 start=/\*/ end=/\*/ concealends contains=X2]])
   1682          command([[syntax match X2 /cats/ conceal cchar=X contained]])
   1683          command([[syntax match X3 /\n\@<=x/ conceal cchar=>]])
   1684        end)
   1685        test_mouse_click_conceal()
   1686      end)
   1687 
   1688      describe('(matchadd())', function()
   1689        before_each(function()
   1690          fn.matchadd('Conceal', [[\*]])
   1691          fn.matchadd('Conceal', [[cats]], 10, -1, { conceal = 'X' })
   1692          fn.matchadd('Conceal', [[\n\@<=x]], 10, -1, { conceal = '>' })
   1693        end)
   1694        test_mouse_click_conceal()
   1695      end)
   1696 
   1697      describe('(extmarks)', function()
   1698        before_each(function()
   1699          local ns = api.nvim_create_namespace('conceal')
   1700          api.nvim_buf_set_extmark(0, ns, 0, 11, { end_col = 12, conceal = '' })
   1701          api.nvim_buf_set_extmark(0, ns, 0, 14, { end_col = 15, conceal = '' })
   1702          api.nvim_buf_set_extmark(0, ns, 1, 5, { end_col = 6, conceal = '' })
   1703          api.nvim_buf_set_extmark(0, ns, 1, 8, { end_col = 9, conceal = '' })
   1704          api.nvim_buf_set_extmark(0, ns, 1, 10, { end_col = 11, conceal = '' })
   1705          api.nvim_buf_set_extmark(0, ns, 1, 13, { end_col = 14, conceal = '' })
   1706          api.nvim_buf_set_extmark(0, ns, 1, 15, { end_col = 16, conceal = '' })
   1707          api.nvim_buf_set_extmark(0, ns, 1, 18, { end_col = 19, conceal = '' })
   1708          api.nvim_buf_set_extmark(0, ns, 2, 24, { end_col = 25, conceal = '' })
   1709          api.nvim_buf_set_extmark(0, ns, 2, 29, { end_col = 30, conceal = '' })
   1710          api.nvim_buf_set_extmark(0, ns, 2, 25, { end_col = 29, conceal = 'X' })
   1711          api.nvim_buf_set_extmark(0, ns, 2, 0, { end_col = 1, conceal = '>' })
   1712        end)
   1713        test_mouse_click_conceal()
   1714      end)
   1715    end)
   1716 
   1717    it('virtual text does not change cursor placement on concealed line', function()
   1718      command('%delete')
   1719      insert('aaaaaaaaaa|hidden|bbbbbbbbbb|hidden|cccccccccc')
   1720      command('syntax match test /|hidden|/ conceal cchar=X')
   1721      command('set conceallevel=2 concealcursor=n virtualedit=all')
   1722      screen:expect({
   1723        any = {
   1724          'aaaaaaaaaa{14:X}bbbbbbb       ',
   1725          'bbb{14:X}ccccccccc%^c           ',
   1726        },
   1727      })
   1728      api.nvim_input_mouse('left', 'press', '', 0, 0, 22)
   1729      screen:expect({
   1730        any = {
   1731          'aaaaaaaaaa{14:X}bbbbbb%^b       ',
   1732          'bbb{14:X}cccccccccc           ',
   1733        },
   1734      })
   1735      api.nvim_input_mouse('left', 'press', '', 0, 1, 16)
   1736      screen:expect({
   1737        any = {
   1738          'aaaaaaaaaa{14:X}bbbbbbb       ',
   1739          'bbb{14:X}cccccccccc  %^         ',
   1740        },
   1741      })
   1742 
   1743      api.nvim_buf_set_extmark(0, api.nvim_create_namespace(''), 0, 0, {
   1744        virt_text = { { '?', 'ErrorMsg' } },
   1745        virt_text_pos = 'right_align',
   1746        virt_text_repeat_linebreak = true,
   1747      })
   1748      screen:expect({
   1749        any = {
   1750          'aaaaaaaaaa{14:X}bbbbbbb      {9:%?}',
   1751          'bbb{14:X}cccccccccc  %^        {9:%?}',
   1752        },
   1753      })
   1754      api.nvim_input_mouse('left', 'press', '', 0, 0, 22)
   1755      screen:expect({
   1756        any = {
   1757          'aaaaaaaaaa{14:X}bbbbbb%^b      {9:%?}',
   1758          'bbb{14:X}cccccccccc          {9:%?}',
   1759        },
   1760      })
   1761      api.nvim_input_mouse('left', 'press', '', 0, 1, 16)
   1762      screen:expect({
   1763        any = {
   1764          'aaaaaaaaaa{14:X}bbbbbbb      {9:%?}',
   1765          'bbb{14:X}cccccccccc  %^        {9:%?}',
   1766        },
   1767      })
   1768    end)
   1769 
   1770    it("mouse click on window separator in statusline doesn't crash", function()
   1771      api.nvim_set_option_value('winwidth', 1, {})
   1772      api.nvim_set_option_value('statusline', '%f', {})
   1773 
   1774      command('vsplit')
   1775      command('redraw')
   1776 
   1777      local lines = api.nvim_get_option_value('lines', {})
   1778      local columns = api.nvim_get_option_value('columns', {})
   1779 
   1780      api.nvim_input_mouse('left', 'press', '', 0, lines - 1, math.floor(columns / 2))
   1781      command('redraw')
   1782    end)
   1783 
   1784    it('getmousepos() works correctly', function()
   1785      local winwidth = api.nvim_get_option_value('winwidth', {})
   1786      -- Set winwidth=1 so that window sizes don't change.
   1787      api.nvim_set_option_value('winwidth', 1, {})
   1788      command('tabedit')
   1789      local tabpage = api.nvim_get_current_tabpage()
   1790      insert('hello')
   1791      command('vsplit')
   1792      local opts = {
   1793        relative = 'editor',
   1794        width = 12,
   1795        height = 1,
   1796        col = 8,
   1797        row = 1,
   1798        anchor = 'NW',
   1799        style = 'minimal',
   1800        border = 'single',
   1801        focusable = 1,
   1802      }
   1803      local float = api.nvim_open_win(api.nvim_get_current_buf(), false, opts)
   1804      command('redraw')
   1805      local lines = api.nvim_get_option_value('lines', {})
   1806      local columns = api.nvim_get_option_value('columns', {})
   1807 
   1808      -- Test that screenrow and screencol are set properly for all positions.
   1809      for row = 0, lines - 1 do
   1810        for col = 0, columns - 1 do
   1811          -- Skip the X button that would close the tab.
   1812          if row ~= 0 or col ~= columns - 1 then
   1813            api.nvim_input_mouse('left', 'press', '', 0, row, col)
   1814            api.nvim_set_current_tabpage(tabpage)
   1815            local mousepos = fn.getmousepos()
   1816            eq(row + 1, mousepos.screenrow)
   1817            eq(col + 1, mousepos.screencol)
   1818            -- All other values should be 0 when clicking on the command line.
   1819            if row == lines - 1 then
   1820              eq(0, mousepos.winid)
   1821              eq(0, mousepos.winrow)
   1822              eq(0, mousepos.wincol)
   1823              eq(0, mousepos.line)
   1824              eq(0, mousepos.column)
   1825              eq(0, mousepos.coladd)
   1826            end
   1827          end
   1828        end
   1829      end
   1830 
   1831      -- Test that mouse position values are properly set for the floating window
   1832      -- with a border. 1 is added to the height and width to account for the
   1833      -- border.
   1834      for win_row = 0, opts.height + 1 do
   1835        for win_col = 0, opts.width + 1 do
   1836          local row = win_row + opts.row
   1837          local col = win_col + opts.col
   1838          api.nvim_input_mouse('left', 'press', '', 0, row, col)
   1839          local mousepos = fn.getmousepos()
   1840          eq(float, mousepos.winid)
   1841          eq(win_row + 1, mousepos.winrow)
   1842          eq(win_col + 1, mousepos.wincol)
   1843          local line = 0
   1844          local column = 0
   1845          local coladd = 0
   1846          if
   1847            win_row > 0
   1848            and win_row < opts.height + 1
   1849            and win_col > 0
   1850            and win_col < opts.width + 1
   1851          then
   1852            -- Because of border, win_row and win_col don't need to be
   1853            -- incremented by 1.
   1854            line = math.min(win_row, fn.line('$'))
   1855            column = math.min(win_col, #fn.getline(line) + 1)
   1856            coladd = win_col - column
   1857          end
   1858          eq(line, mousepos.line)
   1859          eq(column, mousepos.column)
   1860          eq(coladd, mousepos.coladd)
   1861        end
   1862      end
   1863 
   1864      -- Test that mouse position values are properly set for the floating
   1865      -- window, after removing the border.
   1866      opts.border = 'none'
   1867      api.nvim_win_set_config(float, opts)
   1868      command('redraw')
   1869      for win_row = 0, opts.height - 1 do
   1870        for win_col = 0, opts.width - 1 do
   1871          local row = win_row + opts.row
   1872          local col = win_col + opts.col
   1873          api.nvim_input_mouse('left', 'press', '', 0, row, col)
   1874          local mousepos = fn.getmousepos()
   1875          eq(float, mousepos.winid)
   1876          eq(win_row + 1, mousepos.winrow)
   1877          eq(win_col + 1, mousepos.wincol)
   1878          local line = math.min(win_row + 1, fn.line('$'))
   1879          local column = math.min(win_col + 1, #fn.getline(line) + 1)
   1880          local coladd = win_col + 1 - column
   1881          eq(line, mousepos.line)
   1882          eq(column, mousepos.column)
   1883          eq(coladd, mousepos.coladd)
   1884        end
   1885      end
   1886 
   1887      -- Test that mouse position values are properly set for ordinary windows.
   1888      -- Set the float to be unfocusable instead of closing, to additionally test
   1889      -- that getmousepos() does not consider unfocusable floats. (see discussion
   1890      -- in PR #14937 for details).
   1891      opts.focusable = false
   1892      api.nvim_win_set_config(float, opts)
   1893      command('redraw')
   1894      for nr = 1, 2 do
   1895        for win_row = 0, fn.winheight(nr) - 1 do
   1896          for win_col = 0, fn.winwidth(nr) - 1 do
   1897            local row = win_row + fn.win_screenpos(nr)[1] - 1
   1898            local col = win_col + fn.win_screenpos(nr)[2] - 1
   1899            api.nvim_input_mouse('left', 'press', '', 0, row, col)
   1900            local mousepos = fn.getmousepos()
   1901            eq(fn.win_getid(nr), mousepos.winid)
   1902            eq(win_row + 1, mousepos.winrow)
   1903            eq(win_col + 1, mousepos.wincol)
   1904            local line = math.min(win_row + 1, fn.line('$'))
   1905            local column = math.min(win_col + 1, #fn.getline(line) + 1)
   1906            local coladd = win_col + 1 - column
   1907            eq(line, mousepos.line)
   1908            eq(column, mousepos.column)
   1909            eq(coladd, mousepos.coladd)
   1910          end
   1911        end
   1912      end
   1913 
   1914      -- Restore state and release mouse.
   1915      command('tabclose!')
   1916      api.nvim_set_option_value('winwidth', winwidth, {})
   1917      api.nvim_input_mouse('left', 'release', '', 0, 0, 0)
   1918    end)
   1919 
   1920    it('scroll keys are not translated into multiclicks and can be mapped #6211 #6989', function()
   1921      api.nvim_set_var('mouse_up', 0)
   1922      api.nvim_set_var('mouse_up2', 0)
   1923      command('nnoremap <ScrollWheelUp> <Cmd>let g:mouse_up += 1<CR>')
   1924      command('nnoremap <2-ScrollWheelUp> <Cmd>let g:mouse_up2 += 1<CR>')
   1925      feed('<ScrollWheelUp><0,0>')
   1926      feed('<ScrollWheelUp><0,0>')
   1927      api.nvim_input_mouse('wheel', 'up', '', 0, 0, 0)
   1928      api.nvim_input_mouse('wheel', 'up', '', 0, 0, 0)
   1929      eq(4, api.nvim_get_var('mouse_up'))
   1930      eq(0, api.nvim_get_var('mouse_up2'))
   1931    end)
   1932 
   1933    it('<MouseMove> to different locations can be mapped', function()
   1934      api.nvim_set_var('mouse_move', 0)
   1935      api.nvim_set_var('mouse_move2', 0)
   1936      command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>')
   1937      command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>')
   1938      feed('<MouseMove><1,0>')
   1939      feed('<MouseMove><2,0>')
   1940      api.nvim_input_mouse('move', '', '', 0, 0, 3)
   1941      api.nvim_input_mouse('move', '', '', 0, 0, 4)
   1942      eq(4, api.nvim_get_var('mouse_move'))
   1943      eq(0, api.nvim_get_var('mouse_move2'))
   1944    end)
   1945 
   1946    it('<MouseMove> to same location does not generate events #31103', function()
   1947      api.nvim_set_var('mouse_move', 0)
   1948      api.nvim_set_var('mouse_move2', 0)
   1949      command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>')
   1950      command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>')
   1951      api.nvim_input_mouse('move', '', '', 0, 0, 3)
   1952      eq(1, api.nvim_get_var('mouse_move'))
   1953      eq(0, api.nvim_get_var('mouse_move2'))
   1954      feed('<MouseMove><3,0>')
   1955      feed('<MouseMove><3,0>')
   1956      api.nvim_input_mouse('move', '', '', 0, 0, 3)
   1957      api.nvim_input_mouse('move', '', '', 0, 0, 3)
   1958      eq(1, api.nvim_get_var('mouse_move'))
   1959      eq(0, api.nvim_get_var('mouse_move2'))
   1960      eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
   1961      feed('<MouseMove><3,0><Insert>')
   1962      eq(1, api.nvim_get_var('mouse_move'))
   1963      eq(0, api.nvim_get_var('mouse_move2'))
   1964      eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
   1965    end)
   1966 
   1967    it('feeding <MouseMove> in Normal mode does not use uninitialized memory #19480', function()
   1968      feed('<MouseMove>')
   1969      n.poke_eventloop()
   1970      n.assert_alive()
   1971    end)
   1972 
   1973    it('mousemodel=popup_setpos', function()
   1974      screen:try_resize(80, 24)
   1975      exec([[
   1976        5new
   1977        call setline(1, ['the dish ran away with the spoon',
   1978              \ 'the cow jumped over the moon' ])
   1979 
   1980        set mouse=a mousemodel=popup_setpos
   1981 
   1982        aunmenu PopUp
   1983        nmenu PopUp.foo :let g:menustr = 'foo'<CR>
   1984        nmenu PopUp.bar :let g:menustr = 'bar'<CR>
   1985        nmenu PopUp.baz :let g:menustr = 'baz'<CR>
   1986        vmenu PopUp.foo y:<C-U>let g:menustr = 'foo'<CR>
   1987        vmenu PopUp.bar y:<C-U>let g:menustr = 'bar'<CR>
   1988        vmenu PopUp.baz y:<C-U>let g:menustr = 'baz'<CR>
   1989      ]])
   1990 
   1991      api.nvim_win_set_cursor(0, { 1, 0 })
   1992      api.nvim_input_mouse('right', 'press', '', 0, 0, 4)
   1993      api.nvim_input_mouse('right', 'release', '', 0, 0, 4)
   1994      feed('<Down><Down><CR>')
   1995      eq('bar', api.nvim_get_var('menustr'))
   1996      eq({ 1, 4 }, api.nvim_win_get_cursor(0))
   1997 
   1998      -- Test for right click in visual mode inside the selection
   1999      fn.setreg('"', '')
   2000      api.nvim_win_set_cursor(0, { 1, 9 })
   2001      feed('vee')
   2002      api.nvim_input_mouse('right', 'press', '', 0, 0, 11)
   2003      api.nvim_input_mouse('right', 'release', '', 0, 0, 11)
   2004      feed('<Down><CR>')
   2005      eq({ 1, 9 }, api.nvim_win_get_cursor(0))
   2006      eq('ran away', fn.getreg('"'))
   2007 
   2008      -- Test for right click in visual mode right before the selection
   2009      fn.setreg('"', '')
   2010      api.nvim_win_set_cursor(0, { 1, 9 })
   2011      feed('vee')
   2012      api.nvim_input_mouse('right', 'press', '', 0, 0, 8)
   2013      api.nvim_input_mouse('right', 'release', '', 0, 0, 8)
   2014      feed('<Down><CR>')
   2015      eq({ 1, 8 }, api.nvim_win_get_cursor(0))
   2016      eq('', fn.getreg('"'))
   2017 
   2018      -- Test for right click in visual mode right after the selection
   2019      fn.setreg('"', '')
   2020      api.nvim_win_set_cursor(0, { 1, 9 })
   2021      feed('vee')
   2022      api.nvim_input_mouse('right', 'press', '', 0, 0, 17)
   2023      api.nvim_input_mouse('right', 'release', '', 0, 0, 17)
   2024      feed('<Down><CR>')
   2025      eq({ 1, 17 }, api.nvim_win_get_cursor(0))
   2026      eq('', fn.getreg('"'))
   2027 
   2028      -- Test for right click in block-wise visual mode inside the selection
   2029      fn.setreg('"', '')
   2030      api.nvim_win_set_cursor(0, { 1, 15 })
   2031      feed('<C-V>j3l')
   2032      api.nvim_input_mouse('right', 'press', '', 0, 1, 16)
   2033      api.nvim_input_mouse('right', 'release', '', 0, 1, 16)
   2034      feed('<Down><CR>')
   2035      eq({ 1, 15 }, api.nvim_win_get_cursor(0))
   2036      eq('\0224', fn.getregtype('"'))
   2037 
   2038      -- Test for right click in block-wise visual mode outside the selection
   2039      fn.setreg('"', '')
   2040      api.nvim_win_set_cursor(0, { 1, 15 })
   2041      feed('<C-V>j3l')
   2042      api.nvim_input_mouse('right', 'press', '', 0, 1, 1)
   2043      api.nvim_input_mouse('right', 'release', '', 0, 1, 1)
   2044      feed('<Down><CR>')
   2045      eq({ 2, 1 }, api.nvim_win_get_cursor(0))
   2046      eq('v', fn.getregtype('"'))
   2047      eq('', fn.getreg('"'))
   2048 
   2049      -- Test for right click in line-wise visual mode inside the selection
   2050      fn.setreg('"', '')
   2051      api.nvim_win_set_cursor(0, { 1, 15 })
   2052      feed('V')
   2053      api.nvim_input_mouse('right', 'press', '', 0, 0, 9)
   2054      api.nvim_input_mouse('right', 'release', '', 0, 0, 9)
   2055      feed('<Down><CR>')
   2056      eq({ 1, 0 }, api.nvim_win_get_cursor(0)) -- After yanking, the cursor goes to 1,1
   2057      eq('V', fn.getregtype('"'))
   2058      eq(1, #fn.getreg('"', 1, true))
   2059 
   2060      -- Test for right click in multi-line line-wise visual mode inside the selection
   2061      fn.setreg('"', '')
   2062      api.nvim_win_set_cursor(0, { 1, 15 })
   2063      feed('Vj')
   2064      api.nvim_input_mouse('right', 'press', '', 0, 1, 19)
   2065      api.nvim_input_mouse('right', 'release', '', 0, 1, 19)
   2066      feed('<Down><CR>')
   2067      eq({ 1, 0 }, api.nvim_win_get_cursor(0)) -- After yanking, the cursor goes to 1,1
   2068      eq('V', fn.getregtype('"'))
   2069      eq(2, #fn.getreg('"', 1, true))
   2070 
   2071      -- Test for right click in line-wise visual mode outside the selection
   2072      fn.setreg('"', '')
   2073      api.nvim_win_set_cursor(0, { 1, 15 })
   2074      feed('V')
   2075      api.nvim_input_mouse('right', 'press', '', 0, 1, 9)
   2076      api.nvim_input_mouse('right', 'release', '', 0, 1, 9)
   2077      feed('<Down><CR>')
   2078      eq({ 2, 9 }, api.nvim_win_get_cursor(0))
   2079      eq('', fn.getreg('"'))
   2080 
   2081      -- Try clicking outside the window
   2082      fn.setreg('"', '')
   2083      api.nvim_win_set_cursor(0, { 2, 1 })
   2084      feed('vee')
   2085      api.nvim_input_mouse('right', 'press', '', 0, 6, 1)
   2086      api.nvim_input_mouse('right', 'release', '', 0, 6, 1)
   2087      feed('<Down><CR>')
   2088      eq(2, fn.winnr())
   2089      eq('', fn.getreg('"'))
   2090 
   2091      -- Test for right click in visual mode inside the selection with vertical splits
   2092      command('wincmd t')
   2093      command('rightbelow vsplit')
   2094      fn.setreg('"', '')
   2095      api.nvim_win_set_cursor(0, { 1, 9 })
   2096      feed('vee')
   2097      api.nvim_input_mouse('right', 'press', '', 0, 0, 52)
   2098      api.nvim_input_mouse('right', 'release', '', 0, 0, 52)
   2099      feed('<Down><CR>')
   2100      eq({ 1, 9 }, api.nvim_win_get_cursor(0))
   2101      eq('ran away', fn.getreg('"'))
   2102 
   2103      -- Test for right click inside visual selection at bottom of window with winbar
   2104      command('setlocal winbar=WINBAR')
   2105      feed('2yyP')
   2106      fn.setreg('"', '')
   2107      feed('G$vbb')
   2108      api.nvim_input_mouse('right', 'press', '', 0, 4, 61)
   2109      api.nvim_input_mouse('right', 'release', '', 0, 4, 61)
   2110      feed('<Down><CR>')
   2111      eq({ 4, 20 }, api.nvim_win_get_cursor(0))
   2112      eq('the moon', fn.getreg('"'))
   2113 
   2114      -- Try clicking in the cmdline
   2115      api.nvim_input_mouse('right', 'press', '', 0, 23, 0)
   2116      api.nvim_input_mouse('right', 'release', '', 0, 23, 0)
   2117      feed('<Down><Down><Down><CR>')
   2118      eq('baz', api.nvim_get_var('menustr'))
   2119 
   2120      -- Try clicking in horizontal separator with global statusline
   2121      command('set laststatus=3')
   2122      api.nvim_input_mouse('right', 'press', '', 0, 5, 0)
   2123      api.nvim_input_mouse('right', 'release', '', 0, 5, 0)
   2124      feed('<Down><CR>')
   2125      eq('foo', api.nvim_get_var('menustr'))
   2126 
   2127      -- Try clicking in the cmdline with global statusline
   2128      api.nvim_input_mouse('right', 'press', '', 0, 23, 0)
   2129      api.nvim_input_mouse('right', 'release', '', 0, 23, 0)
   2130      feed('<Down><Down><CR>')
   2131      eq('bar', api.nvim_get_var('menustr'))
   2132    end)
   2133 
   2134    it('below a concealed line #33450', function()
   2135      api.nvim_set_option_value('conceallevel', 2, {})
   2136      api.nvim_buf_set_extmark(0, api.nvim_create_namespace(''), 1, 0, { conceal_lines = '' })
   2137      api.nvim_input_mouse('left', 'press', '', 0, 1, 0)
   2138      api.nvim_input_mouse('left', 'release', '', 0, 1, 0)
   2139      eq(3, fn.line('.'))
   2140      -- No error when clicking below last line that is concealed.
   2141      screen:try_resize(80, 10) -- Prevent hit-enter
   2142      api.nvim_set_option_value('cmdheight', 3, {})
   2143      local count = api.nvim_buf_line_count(0)
   2144      api.nvim_buf_set_extmark(0, 1, count - 1, 0, { conceal_lines = '' })
   2145      api.nvim_input_mouse('left', 'press', '', 0, count, 0)
   2146      eq('', api.nvim_get_vvar('errmsg'))
   2147    end)
   2148  end
   2149 
   2150  describe('with ext_multigrid', function()
   2151    with_ext_multigrid(true)
   2152  end)
   2153 
   2154  describe('without ext_multigrid', function()
   2155    with_ext_multigrid(false)
   2156  end)
   2157 end)