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)