search_spec.lua (22329B)
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 = n.clear 6 local command = n.command 7 local eq = t.eq 8 local eval = n.eval 9 local feed = n.feed 10 local fn = n.fn 11 local poke_eventloop = n.poke_eventloop 12 local exec = n.exec 13 14 describe('search cmdline', function() 15 local screen 16 17 before_each(function() 18 clear() 19 command('set nohlsearch inccommand=') 20 screen = Screen.new(20, 3) 21 end) 22 23 local function tenlines() 24 fn.setline(1, { 25 ' 1', 26 ' 2 these', 27 ' 3 the', 28 ' 4 their', 29 ' 5 there', 30 ' 6 their', 31 ' 7 the', 32 ' 8 them', 33 ' 9 these', 34 ' 10 foobar', 35 }) 36 command('1') 37 end 38 39 it('history can be navigated with <C-N>/<C-P>', function() 40 tenlines() 41 command('set noincsearch') 42 feed('/foobar<CR>') 43 feed('/the<CR>') 44 eq('the', eval('@/')) 45 feed('/thes<C-P><C-P><CR>') 46 eq('foobar', eval('@/')) 47 end) 48 49 describe('can traverse matches', function() 50 before_each(tenlines) 51 local function forwarditer(wrapscan) 52 command('set incsearch ' .. wrapscan) 53 feed('/the') 54 screen:expect([[ 55 1 | 56 2 {2:the}se | 57 /the^ | 58 ]]) 59 feed('<C-G>') 60 screen:expect([[ 61 2 these | 62 3 {2:the} | 63 /the^ | 64 ]]) 65 eq({ 0, 0, 0, 0 }, fn.getpos('"')) 66 feed('<C-G>') 67 screen:expect([[ 68 3 the | 69 4 {2:the}ir | 70 /the^ | 71 ]]) 72 feed('<C-G>') 73 screen:expect([[ 74 4 their | 75 5 {2:the}re | 76 /the^ | 77 ]]) 78 feed('<C-G>') 79 screen:expect([[ 80 5 there | 81 6 {2:the}ir | 82 /the^ | 83 ]]) 84 feed('<C-G>') 85 screen:expect([[ 86 6 their | 87 7 {2:the} | 88 /the^ | 89 ]]) 90 feed('<C-G>') 91 screen:expect([[ 92 7 the | 93 8 {2:the}m | 94 /the^ | 95 ]]) 96 feed('<C-G>') 97 screen:expect([[ 98 8 them | 99 9 {2:the}se | 100 /the^ | 101 ]]) 102 screen.bell = false 103 feed('<C-G>') 104 if wrapscan == 'wrapscan' then 105 screen:expect([[ 106 2 {2:the}se | 107 3 the | 108 /the^ | 109 ]]) 110 else 111 screen:expect { 112 grid = [[ 113 8 them | 114 9 {2:the}se | 115 /the^ | 116 ]], 117 condition = function() 118 eq(true, screen.bell) 119 end, 120 } 121 feed('<CR>') 122 eq({ 0, 0, 0, 0 }, fn.getpos('"')) 123 end 124 end 125 126 local function backiter(wrapscan) 127 command('set incsearch ' .. wrapscan) 128 command('$') 129 130 feed('?the') 131 screen:expect([[ 132 9 {2:the}se | 133 10 foobar | 134 ?the^ | 135 ]]) 136 screen.bell = false 137 if wrapscan == 'wrapscan' then 138 feed('<C-G>') 139 screen:expect([[ 140 2 {2:the}se | 141 3 the | 142 ?the^ | 143 ]]) 144 feed('<CR>') 145 screen:expect([[ 146 2 ^these | 147 3 the | 148 ?the | 149 ]]) 150 else 151 feed('<C-G>') 152 screen:expect { 153 grid = [[ 154 9 {2:the}se | 155 10 foobar | 156 ?the^ | 157 ]], 158 condition = function() 159 eq(true, screen.bell) 160 end, 161 } 162 feed('<CR>') 163 screen:expect([[ 164 9 ^these | 165 10 foobar | 166 ?the | 167 ]]) 168 end 169 command('$') 170 feed('?the') 171 screen:expect([[ 172 9 {2:the}se | 173 10 foobar | 174 ?the^ | 175 ]]) 176 feed('<C-T>') 177 screen:expect([[ 178 8 {2:the}m | 179 9 these | 180 ?the^ | 181 ]]) 182 for i = 1, 6 do 183 feed('<C-T>') 184 -- Avoid sleep just before expect, otherwise expect will take the full 185 -- timeout 186 if i ~= 6 then 187 screen:sleep(1) 188 end 189 end 190 screen:expect([[ 191 2 {2:the}se | 192 3 the | 193 ?the^ | 194 ]]) 195 screen.bell = false 196 feed('<C-T>') 197 if wrapscan == 'wrapscan' then 198 screen:expect([[ 199 9 {2:the}se | 200 10 foobar | 201 ?the^ | 202 ]]) 203 else 204 screen:expect { 205 grid = [[ 206 2 {2:the}se | 207 3 the | 208 ?the^ | 209 ]], 210 condition = function() 211 eq(true, screen.bell) 212 end, 213 } 214 end 215 end 216 217 it("using <C-G> and 'nowrapscan'", function() 218 forwarditer('nowrapscan') 219 end) 220 221 it("using <C-G> and 'wrapscan'", function() 222 forwarditer('wrapscan') 223 end) 224 225 it("using <C-T> and 'nowrapscan'", function() 226 backiter('nowrapscan') 227 end) 228 229 it("using <C-T> and 'wrapscan'", function() 230 backiter('wrapscan') 231 end) 232 end) 233 234 it('expands pattern with <C-L>', function() 235 tenlines() 236 command('set incsearch wrapscan') 237 238 feed('/the') 239 screen:expect([[ 240 1 | 241 2 {2:the}se | 242 /the^ | 243 ]]) 244 feed('<C-L>') 245 screen:expect([[ 246 1 | 247 2 {2:thes}e | 248 /thes^ | 249 ]]) 250 feed('<C-G>') 251 screen:expect([[ 252 9 {2:thes}e | 253 10 foobar | 254 /thes^ | 255 ]]) 256 feed('<C-G>') 257 screen:expect([[ 258 2 {2:thes}e | 259 3 the | 260 /thes^ | 261 ]]) 262 feed('<CR>') 263 screen:expect([[ 264 2 ^these | 265 3 the | 266 /thes | 267 ]]) 268 269 command('1') 270 command('set nowrapscan') 271 feed('/the') 272 screen:expect([[ 273 1 | 274 2 {2:the}se | 275 /the^ | 276 ]]) 277 feed('<C-L>') 278 screen:expect([[ 279 1 | 280 2 {2:thes}e | 281 /thes^ | 282 ]]) 283 feed('<C-G>') 284 screen:expect([[ 285 9 {2:thes}e | 286 10 foobar | 287 /thes^ | 288 ]]) 289 feed('<C-G><CR>') 290 screen:expect([[ 291 9 ^these | 292 10 foobar | 293 /thes | 294 ]]) 295 end) 296 297 it('reduces pattern with <BS> and keeps cursor position', function() 298 tenlines() 299 command('set incsearch wrapscan') 300 301 -- First match 302 feed('/thei') 303 screen:expect([[ 304 3 the | 305 4 {2:thei}r | 306 /thei^ | 307 ]]) 308 -- Match from initial cursor position when modifying search 309 feed('<BS>') 310 screen:expect([[ 311 1 | 312 2 {2:the}se | 313 /the^ | 314 ]]) 315 -- New text advances to next match 316 feed('s') 317 screen:expect([[ 318 1 | 319 2 {2:thes}e | 320 /thes^ | 321 ]]) 322 -- Stay on this match when deleting a character 323 feed('<BS>') 324 screen:expect([[ 325 1 | 326 2 {2:the}se | 327 /the^ | 328 ]]) 329 -- Advance to previous match 330 feed('<C-T>') 331 screen:expect([[ 332 9 {2:the}se | 333 10 foobar | 334 /the^ | 335 ]]) 336 -- Extend search to include next character 337 feed('<C-L>') 338 screen:expect([[ 339 9 {2:thes}e | 340 10 foobar | 341 /thes^ | 342 ]]) 343 -- Deleting all characters resets the cursor position 344 feed('<BS><BS><BS><BS>') 345 screen:expect([[ 346 1 | 347 2 these | 348 /^ | 349 ]]) 350 feed('the') 351 screen:expect([[ 352 1 | 353 2 {2:the}se | 354 /the^ | 355 ]]) 356 feed('\\>') 357 screen:expect([[ 358 2 these | 359 3 {2:the} | 360 /the\>^ | 361 ]]) 362 end) 363 364 it('can traverse matches in the same line with <C-G>/<C-T>', function() 365 fn.setline(1, { ' 1', ' 2 these', ' 3 the theother' }) 366 command('1') 367 command('set incsearch') 368 369 -- First match 370 feed('/the') 371 screen:expect([[ 372 1 | 373 2 {2:the}se | 374 /the^ | 375 ]]) 376 377 -- Next match, different line 378 feed('<C-G>') 379 screen:expect([[ 380 2 these | 381 3 {2:the} theother | 382 /the^ | 383 ]]) 384 385 -- Next match, same line 386 feed('<C-G>') 387 screen:expect([[ 388 2 these | 389 3 the {2:the}other | 390 /the^ | 391 ]]) 392 feed('<C-G>') 393 screen:expect([[ 394 2 these | 395 3 the theo{2:the}r | 396 /the^ | 397 ]]) 398 399 -- Previous match, same line 400 feed('<C-T>') 401 screen:expect([[ 402 2 these | 403 3 the {2:the}other | 404 /the^ | 405 ]]) 406 feed('<C-T>') 407 screen:expect([[ 408 2 these | 409 3 {2:the} theother | 410 /the^ | 411 ]]) 412 413 -- Previous match, different line 414 feed('<C-T>') 415 screen:expect([[ 416 2 {2:the}se | 417 3 the theother | 418 /the^ | 419 ]]) 420 end) 421 422 it('keeps the view after deleting a char from the search', function() 423 screen:try_resize(20, 6) 424 tenlines() 425 426 feed('/foo') 427 screen:expect([[ 428 6 their | 429 7 the | 430 8 them | 431 9 these | 432 10 {2:foo}bar | 433 /foo^ | 434 ]]) 435 feed('<BS>') 436 screen:expect([[ 437 6 their | 438 7 the | 439 8 them | 440 9 these | 441 10 {2:fo}obar | 442 /fo^ | 443 ]]) 444 feed('<CR>') 445 screen:expect([[ 446 6 their | 447 7 the | 448 8 them | 449 9 these | 450 10 ^foobar | 451 /fo | 452 ]]) 453 eq({ 454 lnum = 10, 455 leftcol = 0, 456 col = 4, 457 topfill = 0, 458 topline = 6, 459 coladd = 0, 460 skipcol = 0, 461 curswant = 4, 462 }, fn.winsaveview()) 463 end) 464 465 it('restores original view after failed search', function() 466 screen:try_resize(40, 3) 467 tenlines() 468 feed('0') 469 feed('/foo') 470 screen:expect([[ 471 9 these | 472 10 {2:foo}bar | 473 /foo^ | 474 ]]) 475 feed('<C-W>') 476 screen:expect([[ 477 1 | 478 2 these | 479 /^ | 480 ]]) 481 feed('<CR>') 482 screen:expect([[ 483 / | 484 {9:E35: No previous regular expression} | 485 {6:Press ENTER or type command to continue}^ | 486 ]]) 487 feed('<CR>') 488 eq({ 489 lnum = 1, 490 leftcol = 0, 491 col = 0, 492 topfill = 0, 493 topline = 1, 494 coladd = 0, 495 skipcol = 0, 496 curswant = 0, 497 }, fn.winsaveview()) 498 end) 499 500 -- oldtest: Test_search_cmdline4(). 501 it("CTRL-G with 'incsearch' and ? goes in the right direction", function() 502 screen:try_resize(40, 4) 503 command('enew!') 504 fn.setline(1, { ' 1 the first', ' 2 the second', ' 3 the third' }) 505 command('set laststatus=0 shortmess+=s') 506 command('set incsearch') 507 command('$') 508 -- Send the input in chunks, so the cmdline logic regards it as 509 -- "interactive". This mimics Vim's test_override("char_avail"). 510 -- (See legacy test: test_search.vim) 511 feed('?the') 512 poke_eventloop() 513 feed('<c-g>') 514 poke_eventloop() 515 feed('<cr>') 516 screen:expect([[ 517 1 the first | 518 2 the second | 519 3 ^the third | 520 ?the | 521 ]]) 522 523 command('$') 524 feed('?the') 525 poke_eventloop() 526 feed('<c-g>') 527 poke_eventloop() 528 feed('<c-g>') 529 poke_eventloop() 530 feed('<cr>') 531 screen:expect([[ 532 1 ^the first | 533 2 the second | 534 3 the third | 535 ?the | 536 ]]) 537 538 command('$') 539 feed('?the') 540 poke_eventloop() 541 feed('<c-g>') 542 poke_eventloop() 543 feed('<c-g>') 544 poke_eventloop() 545 feed('<c-g>') 546 poke_eventloop() 547 feed('<cr>') 548 screen:expect([[ 549 1 the first | 550 2 ^the second | 551 3 the third | 552 ?the | 553 ]]) 554 555 command('$') 556 feed('?the') 557 poke_eventloop() 558 feed('<c-t>') 559 poke_eventloop() 560 feed('<cr>') 561 screen:expect([[ 562 1 ^the first | 563 2 the second | 564 3 the third | 565 ?the | 566 ]]) 567 568 command('$') 569 feed('?the') 570 poke_eventloop() 571 feed('<c-t>') 572 poke_eventloop() 573 feed('<c-t>') 574 poke_eventloop() 575 feed('<cr>') 576 screen:expect([[ 577 1 the first | 578 2 the second | 579 3 ^the third | 580 ?the | 581 ]]) 582 583 command('$') 584 feed('?the') 585 poke_eventloop() 586 feed('<c-t>') 587 poke_eventloop() 588 feed('<c-t>') 589 poke_eventloop() 590 feed('<c-t>') 591 poke_eventloop() 592 feed('<cr>') 593 screen:expect([[ 594 1 the first | 595 2 ^the second | 596 3 the third | 597 ?the | 598 ]]) 599 end) 600 601 -- oldtest: Test_incsearch_sort_dump(). 602 it('incsearch works with :sort', function() 603 screen:try_resize(20, 4) 604 command('set incsearch hlsearch scrolloff=0') 605 fn.setline(1, { 'another one 2', 'that one 3', 'the one 1' }) 606 607 feed(':sort ni u /on') 608 screen:expect([[ 609 another {2:on}e 2 | 610 that {10:on}e 3 | 611 the {10:on}e 1 | 612 :sort ni u /on^ | 613 ]]) 614 feed('<esc>') 615 end) 616 617 -- oldtest: Test_incsearch_vimgrep_dump(). 618 it('incsearch works with :vimgrep family', function() 619 screen:try_resize(30, 4) 620 command('set incsearch hlsearch scrolloff=0') 621 fn.setline(1, { 'another one 2', 'that one 3', 'the one 1' }) 622 623 feed(':vimgrep on') 624 screen:expect([[ 625 another {2:on}e 2 | 626 that {10:on}e 3 | 627 the {10:on}e 1 | 628 :vimgrep on^ | 629 ]]) 630 feed('<esc>') 631 632 feed(':vimg /on/ *.txt') 633 screen:expect([[ 634 another {2:on}e 2 | 635 that {10:on}e 3 | 636 the {10:on}e 1 | 637 :vimg /on/ *.txt^ | 638 ]]) 639 feed('<esc>') 640 641 feed(':vimgrepadd "\\<LT>on') 642 screen:expect([[ 643 another {2:on}e 2 | 644 that {10:on}e 3 | 645 the {10:on}e 1 | 646 :vimgrepadd "\<on^ | 647 ]]) 648 feed('<esc>') 649 650 feed(':lv "tha') 651 screen:expect([[ 652 another one 2 | 653 {2:tha}t one 3 | 654 the one 1 | 655 :lv "tha^ | 656 ]]) 657 feed('<esc>') 658 659 feed(':lvimgrepa "the" **/*.txt') 660 screen:expect([[ 661 ano{2:the}r one 2 | 662 that one 3 | 663 {10:the} one 1 | 664 :lvimgrepa "the" **/*.txt^ | 665 ]]) 666 feed('<esc>') 667 end) 668 669 -- oldtest: Test_incsearch_substitute_dump2() 670 it('incsearch detects empty pattern properly vim-patch:8.2.2295', function() 671 screen:try_resize(70, 6) 672 exec([[ 673 set incsearch hlsearch scrolloff=0 674 for n in range(1, 4) 675 call setline(n, "foo " . n) 676 endfor 677 call setline(5, "abc|def") 678 3 679 ]]) 680 681 feed([[:%s/\vabc|]]) 682 screen:expect([[ 683 foo 1 | 684 foo 2 | 685 foo 3 | 686 foo 4 | 687 abc|def | 688 :%s/\vabc|^ | 689 ]]) 690 feed('<Esc>') 691 692 -- The following should not be highlighted 693 feed([[:1,5s/\v|]]) 694 screen:expect([[ 695 foo 1 | 696 foo 2 | 697 foo 3 | 698 foo 4 | 699 abc|def | 700 :1,5s/\v|^ | 701 ]]) 702 end) 703 704 -- oldtest: Test_incsearch_restore_view() 705 it('incsearch restores viewport', function() 706 screen:try_resize(20, 6) 707 exec([[ 708 set incsearch nohlsearch 709 setlocal scrolloff=0 smoothscroll 710 call setline(1, [join(range(25), ' '), '', '', '', '', 'xxx']) 711 call feedkeys("2\<C-E>", 't') 712 ]]) 713 local s = [[ 714 {1:<<<} 18 19 20 21 22 2| 715 ^3 24 | 716 |*4 717 ]] 718 screen:expect(s) 719 feed('/xx') 720 screen:expect([[ 721 |*4 722 {2:xx}x | 723 /xx^ | 724 ]]) 725 feed('x') 726 screen:expect([[ 727 |*4 728 {2:xxx} | 729 /xxx^ | 730 ]]) 731 feed('<Esc>') 732 screen:expect(s) 733 end) 734 735 -- oldtest: Test_incsearch_delimiter_ctrlg() 736 it('incsearch does not include trailing delimiter', function() 737 screen:try_resize(20, 6) 738 exec([[ 739 set incsearch hls 740 call setline(1, ["1 vim inc", "2 vim /", "3 vim /", "4 vim ?", "5 vim ?"]) 741 normal gg 742 redraw 743 ]]) 744 745 feed('/') 746 poke_eventloop() 747 feed('v') 748 poke_eventloop() 749 feed('i') 750 poke_eventloop() 751 feed('m') 752 poke_eventloop() 753 feed(' ') 754 poke_eventloop() 755 feed('/') 756 poke_eventloop() 757 feed('<C-G>') 758 screen:expect([[ 759 1 {10:vim }inc | 760 2 {2:vim }/ | 761 3 {10:vim }/ | 762 4 {10:vim }? | 763 5 {10:vim }? | 764 /vim /^ | 765 ]]) 766 feed('<Esc>') 767 768 command('5') 769 feed('?') 770 poke_eventloop() 771 feed('v') 772 poke_eventloop() 773 feed('i') 774 poke_eventloop() 775 feed('m') 776 poke_eventloop() 777 feed(' ') 778 poke_eventloop() 779 feed('?') 780 poke_eventloop() 781 feed('<C-T>') 782 screen:expect([[ 783 1 {10:vim }inc | 784 2 {10:vim }/ | 785 3 {2:vim }/ | 786 4 {10:vim }? | 787 5 {10:vim }? | 788 ?vim ?^ | 789 ]]) 790 feed('<Esc>') 791 792 feed('/') 793 poke_eventloop() 794 feed('v') 795 poke_eventloop() 796 feed('i') 797 poke_eventloop() 798 feed('m') 799 poke_eventloop() 800 feed(' ') 801 poke_eventloop() 802 feed('\\/') 803 poke_eventloop() 804 feed('<C-G>') 805 screen:expect([[ 806 1 vim inc | 807 2 {10:vim /} | 808 3 {2:vim /} | 809 4 vim ? | 810 5 vim ? | 811 /vim \/^ | 812 ]]) 813 feed('<Esc>') 814 815 command('5') 816 feed('?') 817 poke_eventloop() 818 feed('v') 819 poke_eventloop() 820 feed('i') 821 poke_eventloop() 822 feed('m') 823 poke_eventloop() 824 feed(' ') 825 poke_eventloop() 826 feed('\\?') 827 poke_eventloop() 828 feed('<C-T>') 829 screen:expect([[ 830 1 vim inc | 831 2 vim / | 832 3 vim / | 833 4 {10:vim ?} | 834 5 {2:vim ?} | 835 ?vim \?^ | 836 ]]) 837 feed('<Esc>') 838 end) 839 end) 840 841 describe('Search highlight', function() 842 before_each(clear) 843 844 -- oldtest: Test_hlsearch_dump() 845 it('beyond line end vim-patch:8.2.2542', function() 846 local screen = Screen.new(50, 6) 847 exec([[ 848 set hlsearch noincsearch cursorline 849 call setline(1, ["xxx", "xxx", "xxx"]) 850 /.* 851 2 852 ]]) 853 feed([[/\_.*<CR>]]) 854 screen:expect([[ 855 {10:xxx } |*2 856 {10:^xxx }{21: }| 857 {1:~ }|*2 858 /\_.* | 859 ]]) 860 end) 861 862 -- oldtest: Test_hlsearch_and_visual() 863 it('is combined with Visual highlight vim-patch:8.2.2797', function() 864 local screen = Screen.new(40, 6) 865 screen:add_extra_attr_ids { 866 [100] = { 867 foreground = Screen.colors.Black, 868 bold = true, 869 background = Screen.colors.LightGrey, 870 }, 871 [101] = { bold = true, background = Screen.colors.Yellow }, 872 } 873 exec([[ 874 set hlsearch noincsearch 875 call setline(1, repeat(["xxx yyy zzz"], 3)) 876 hi Search gui=bold 877 /yyy 878 call cursor(1, 6) 879 ]]) 880 feed('vjj') 881 screen:expect([[ 882 xxx {101:y}{100:yy}{17: zzz} | 883 {17:xxx }{100:yyy}{17: zzz} | 884 {17:xxx }{100:y}{101:^yy} zzz | 885 {1:~ }|*2 886 {5:-- VISUAL --} | 887 ]]) 888 end) 889 end)