searchhl_spec.lua (25839B)
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, insert = n.clear, n.feed, n.insert 6 local command = n.command 7 local feed_command = n.feed_command 8 local eq = t.eq 9 local eval = n.eval 10 local fn = n.fn 11 local testprg = n.testprg 12 13 describe('search highlighting', function() 14 local screen 15 16 before_each(function() 17 clear() 18 screen = Screen.new(40, 7) 19 screen:add_extra_attr_ids({ 20 [100] = { foreground = Screen.colors.Gray100, background = Screen.colors.Grey0 }, 21 [101] = { foreground = Screen.colors.White, background = Screen.colors.DarkGreen }, 22 [102] = { background = Screen.colors.WebGreen, bold = true }, 23 [103] = { background = Screen.colors.Magenta1, italic = true }, 24 [104] = { background = Screen.colors.Yellow1, bold = true }, 25 }) 26 end) 27 28 it('is disabled by ":set nohlsearch"', function() 29 feed_command('set nohlsearch') 30 insert('some text\nmore text') 31 feed('gg/text<cr>') 32 screen:expect([[ 33 some ^text | 34 more text | 35 {1:~ }|*4 36 /text | 37 ]]) 38 end) 39 40 it('is disabled in folded text', function() 41 insert('some text\nmore text') 42 feed_command('1,2fold') 43 feed('gg/text') 44 screen:expect { 45 grid = [[ 46 {13:+-- 2 lines: some text·················}| 47 {1:~ }|*5 48 /text^ | 49 ]], 50 win_viewport = { 51 [2] = { 52 win = 1000, 53 topline = 0, 54 botline = 3, 55 curline = 0, 56 curcol = 9, 57 linecount = 2, 58 sum_scroll_delta = 0, 59 }, 60 }, 61 } 62 end) 63 64 local function test_search_hl() 65 insert([[ 66 some text 67 more textstuff 68 stupidtexttextstuff 69 a text word 70 ]]) 71 72 -- 'hlsearch' is enabled by default. #2859 73 feed('gg/text<cr>') 74 screen:expect([[ 75 some {100:^text} | 76 more {100:text}stuff | 77 stupid{100:texttext}stuff | 78 a {100:text} word | 79 | 80 {101:~ }| 81 /text | 82 ]]) 83 84 -- overlapping matches not allowed 85 feed('3nx') 86 screen:expect([[ 87 some {100:text} | 88 more {100:text}stuff | 89 stupid{100:text}^extstuff | 90 a {100:text} word | 91 | 92 {101:~ }| 93 /text | 94 ]]) 95 96 feed('ggn*') -- search for entire word 97 screen:expect([[ 98 some {100:text} | 99 more textstuff | 100 stupidtextextstuff | 101 a {100:^text} word | 102 | 103 {101:~ }| 104 /\<text\> | 105 ]]) 106 107 feed_command('nohlsearch') 108 screen:expect([[ 109 some text | 110 more textstuff | 111 stupidtextextstuff | 112 a ^text word | 113 | 114 {101:~ }| 115 :nohlsearch | 116 ]]) 117 end 118 119 it("works when 'winhighlight' is not set", function() 120 screen:add_extra_attr_ids({ 121 [100] = { background = Screen.colors.Yellow1 }, 122 [101] = { foreground = Screen.colors.Blue1, bold = true }, 123 }) 124 test_search_hl() 125 end) 126 127 it("works when 'winhighlight' doesn't change Search highlight", function() 128 command('setlocal winhl=NonText:Underlined') 129 screen:add_extra_attr_ids({ 130 [100] = { background = Screen.colors.Yellow }, 131 [101] = { foreground = Screen.colors.SlateBlue, underline = true }, 132 }) 133 test_search_hl() 134 end) 135 136 it("works when 'winhighlight' changes Search highlight", function() 137 command('setlocal winhl=Search:Underlined') 138 screen:add_extra_attr_ids({ 139 [100] = { foreground = Screen.colors.SlateBlue, underline = true }, 140 [101] = { foreground = Screen.colors.Blue1, bold = true }, 141 }) 142 test_search_hl() 143 end) 144 145 describe('CurSearch highlight', function() 146 before_each(function() 147 command('highlight CurSearch guibg=Black guifg=White') 148 end) 149 150 it('works for match under cursor', function() 151 insert([[ 152 There is no way that a bee should be 153 able to fly. Its wings are too small 154 to get its fat little body off the 155 ground. The bee, of course, flies 156 anyway because bees don't care what 157 humans think is impossible.]]) 158 159 feed('/bee<CR>') 160 screen:expect([[ 161 There is no way that a {100:^bee} should be | 162 able to fly. Its wings are too small | 163 to get its fat little body off the | 164 ground. The {10:bee}, of course, flies | 165 anyway because {10:bee}s don't care what | 166 humans think is impossible. | 167 {19:search hit BOTTOM, continuing at TOP} | 168 ]]) 169 170 feed('nn') 171 screen:expect([[ 172 There is no way that a {10:bee} should be | 173 able to fly. Its wings are too small | 174 to get its fat little body off the | 175 ground. The {10:bee}, of course, flies | 176 anyway because {100:^bee}s don't care what | 177 humans think is impossible. | 178 /bee | 179 ]]) 180 181 feed('N') 182 screen:expect([[ 183 There is no way that a {10:bee} should be | 184 able to fly. Its wings are too small | 185 to get its fat little body off the | 186 ground. The {100:^bee}, of course, flies | 187 anyway because {10:bee}s don't care what | 188 humans think is impossible. | 189 ?bee | 190 ]]) 191 end) 192 193 -- oldtest: Test_hlsearch_cursearch() 194 it('works for multiline match, no duplicate highlight', function() 195 command([[call setline(1, ['one', 'foo', 'bar', 'baz', 'foo the foo and foo', 'bar'])]]) 196 feed('gg/foo<CR>') 197 screen:expect([[ 198 one | 199 {100:^foo} | 200 bar | 201 baz | 202 {10:foo} the {10:foo} and {10:foo} | 203 bar | 204 /foo | 205 ]]) 206 feed('n') 207 screen:expect([[ 208 one | 209 {10:foo} | 210 bar | 211 baz | 212 {100:^foo} the {10:foo} and {10:foo} | 213 bar | 214 /foo | 215 ]]) 216 feed('n') 217 screen:expect([[ 218 one | 219 {10:foo} | 220 bar | 221 baz | 222 {10:foo} the {100:^foo} and {10:foo} | 223 bar | 224 /foo | 225 ]]) 226 feed('n') 227 screen:expect([[ 228 one | 229 {10:foo} | 230 bar | 231 baz | 232 {10:foo} the {10:foo} and {100:^foo} | 233 bar | 234 /foo | 235 ]]) 236 command([[call setline(5, 'foo')]]) 237 feed('0?<CR>') 238 screen:expect([[ 239 one | 240 {100:^foo} | 241 bar | 242 baz | 243 {10:foo} | 244 bar | 245 ?foo | 246 ]]) 247 feed('gg/foo\\nbar<CR>') 248 screen:expect([[ 249 one | 250 {100:^foo } | 251 {100:bar} | 252 baz | 253 {10:foo } | 254 {10:bar} | 255 /foo\nbar | 256 ]]) 257 command([[call setline(1, ['---', 'abcdefg', 'hijkl', '---', 'abcdefg', 'hijkl'])]]) 258 feed('gg/efg\\nhij<CR>') 259 screen:expect([[ 260 --- | 261 abcd{100:^efg } | 262 {100:hij}kl | 263 --- | 264 abcd{10:efg } | 265 {10:hij}kl | 266 /efg\nhij | 267 ]]) 268 feed('n') 269 screen:expect([[ 270 --- | 271 abcd{10:efg } | 272 {10:hij}kl | 273 --- | 274 abcd{100:^efg } | 275 {100:hij}kl | 276 /efg\nhij | 277 ]]) 278 279 -- check clearing CurSearch when using it for another match 280 feed('G?^abcd<CR>Y') 281 screen:expect([[ 282 --- | 283 {10:abcd}efg | 284 hijkl | 285 --- | 286 {100:^abcd}efg | 287 hijkl | 288 ?^abcd | 289 ]]) 290 feed('kkP') 291 screen:expect([[ 292 --- | 293 {10:abcd}efg | 294 {100:^abcd}efg | 295 hijkl | 296 --- | 297 {10:abcd}efg | 298 ?^abcd | 299 ]]) 300 end) 301 end) 302 303 it('highlights after EOL', function() 304 insert('\n\n\n\n\n\n') 305 306 feed('gg/^<cr>') 307 screen:expect([[ 308 {10: } | 309 {10:^ } | 310 {10: } |*4 311 /^ | 312 ]]) 313 314 -- Test that highlights are preserved after moving the cursor. 315 feed('j') 316 screen:expect([[ 317 {10: } |*2 318 {10:^ } | 319 {10: } |*3 320 /^ | 321 ]]) 322 323 -- Repeat the test in rightleft mode. 324 command('nohlsearch') 325 command('set rightleft') 326 feed('gg/^<cr>') 327 328 screen:expect([[ 329 {10: }| 330 {10:^ }| 331 {10: }|*4 332 ^/ | 333 ]]) 334 335 feed('j') 336 screen:expect([[ 337 {10: }|*2 338 {10:^ }| 339 {10: }|*3 340 ^/ | 341 ]]) 342 end) 343 344 it('is preserved during :terminal activity', function() 345 feed((':terminal "%s" REP 5000 foo<cr>'):format(testprg('shell-test'))) 346 feed(':file term<CR>') 347 screen:expect([[ 348 ^0: foo | 349 1: foo | 350 2: foo | 351 3: foo | 352 4: foo | 353 5: foo | 354 :file term | 355 ]]) 356 357 feed('G') -- Follow :terminal output. 358 feed(':vnew<CR>') 359 insert([[ 360 foo bar baz 361 bar baz foo 362 bar foo baz]]) 363 feed('/foo') 364 screen:expect([[ 365 {2:foo} bar baz │{MATCH:%d+}: {10:foo}{MATCH:%s+}| 366 bar baz {10:foo} │{MATCH:%d+}: {10:foo}{MATCH:%s+}| 367 bar {10:foo} baz │{MATCH:%d+}: {10:foo}{MATCH:%s+}| 368 {1:~ }│{MATCH:.*}|*2 369 {3:[No Name] [+] }{101:term [-] }| 370 /foo^ | 371 ]]) 372 end) 373 374 it('works with incsearch', function() 375 command('set hlsearch') 376 command('set incsearch') 377 command('set laststatus=0') 378 insert([[ 379 the first line 380 in a little file]]) 381 command('vsplit') 382 feed('gg/li') 383 screen:expect([[ 384 the first {2:li}ne │the first {10:li}ne | 385 in a {10:li}ttle file │in a {10:li}ttle file | 386 {1:~ }│{1:~ }|*4 387 /li^ | 388 ]]) 389 390 -- check that consecutive matches are caught by C-g/C-t 391 feed('<C-g>') 392 screen:expect([[ 393 the first {10:li}ne │the first {10:li}ne | 394 in a {2:li}ttle file │in a {10:li}ttle file | 395 {1:~ }│{1:~ }|*4 396 /li^ | 397 ]]) 398 399 feed('<C-t>') 400 screen:expect([[ 401 the first {2:li}ne │the first {10:li}ne | 402 in a {10:li}ttle file │in a {10:li}ttle file | 403 {1:~ }│{1:~ }|*4 404 /li^ | 405 ]]) 406 407 feed('t') 408 screen:expect([[ 409 the first line │the first line | 410 in a {2:lit}tle file │in a {10:lit}tle file | 411 {1:~ }│{1:~ }|*4 412 /lit^ | 413 ]]) 414 415 feed('<cr>') 416 screen:expect([[ 417 the first line │the first line | 418 in a {10:^lit}tle file │in a {10:lit}tle file | 419 {1:~ }│{1:~ }|*4 420 /lit | 421 ]]) 422 423 feed('/fir') 424 screen:expect([[ 425 the {2:fir}st line │the {10:fir}st line | 426 in a little file │in a little file | 427 {1:~ }│{1:~ }|*4 428 /fir^ | 429 ]]) 430 431 -- incsearch have priority over hlsearch 432 feed('<esc>/ttle') 433 screen:expect([[ 434 the first line │the first line | 435 in a li{2:ttle} file │in a li{10:ttle} file | 436 {1:~ }│{1:~ }|*4 437 /ttle^ | 438 ]]) 439 440 -- cancelling search resets to the old search term 441 feed('<esc>') 442 screen:expect([[ 443 the first line │the first line | 444 in a {10:^lit}tle file │in a {10:lit}tle file | 445 {1:~ }│{1:~ }|*4 446 | 447 ]]) 448 eq('lit', eval('@/')) 449 450 -- cancelling inc search restores the hl state 451 feed(':noh<cr>') 452 screen:expect([[ 453 the first line │the first line | 454 in a ^little file │in a little file | 455 {1:~ }│{1:~ }|*4 456 :noh | 457 ]]) 458 459 feed('/first') 460 screen:expect([[ 461 the {2:first} line │the {10:first} line | 462 in a little file │in a little file | 463 {1:~ }│{1:~ }|*4 464 /first^ | 465 ]]) 466 feed('<esc>') 467 screen:expect([[ 468 the first line │the first line | 469 in a ^little file │in a little file | 470 {1:~ }│{1:~ }|*4 471 | 472 ]]) 473 474 -- test that pressing C-g in an empty command line does not move the cursor 475 feed('gg0') 476 command([[let @/ = 'i']]) 477 -- moves to next match of previous search pattern, just like /<cr> 478 feed('/<c-g><cr>') 479 eq({ 0, 1, 6, 0 }, fn.getpos('.')) 480 -- moves to next match of previous search pattern, just like /<cr> 481 feed('/<cr>') 482 eq({ 0, 1, 12, 0 }, fn.getpos('.')) 483 -- moves to next match of previous search pattern, just like /<cr> 484 feed('/<c-t><cr>') 485 eq({ 0, 2, 1, 0 }, fn.getpos('.')) 486 487 -- 8.0.1304, test that C-g and C-t works with incsearch and empty pattern 488 feed('<esc>/fi<CR>') 489 screen:expect([[ 490 the {10:fi}rst line │the {10:fi}rst line | 491 in a little {10:^fi}le │in a little {10:fi}le | 492 {1:~ }│{1:~ }|*4 493 /fi | 494 ]]) 495 feed('//') 496 screen:expect([[ 497 the {2:fi}rst line │the {10:fi}rst line | 498 in a little {10:fi}le │in a little {10:fi}le | 499 {1:~ }│{1:~ }|*4 500 //^ | 501 ]]) 502 feed('<C-g>') 503 screen:expect([[ 504 the {10:fi}rst line │the {10:fi}rst line | 505 in a little {2:fi}le │in a little {10:fi}le | 506 {1:~ }│{1:~ }|*4 507 //^ | 508 ]]) 509 feed('<Esc>') 510 511 -- incsearch works after c_CTRL-R_CTRL-R 512 command('let @" = "file"') 513 feed('/<C-R><C-R>"') 514 screen:expect([[ 515 the first line │the first line | 516 in a little {2:file} │in a little {10:file} | 517 {1:~ }│{1:~ }|*4 518 /file^ | 519 ]]) 520 feed('<Esc>') 521 522 command('set rtp^=test/functional/fixtures') 523 -- incsearch works after c_CTRL-R inserts clipboard register 524 525 command('let @* = "first"') 526 feed('/<C-R>*') 527 screen:expect([[ 528 the {2:first} line │the {10:first} line | 529 in a little file │in a little file | 530 {1:~ }│{1:~ }|*4 531 /first^ | 532 ]]) 533 feed('<Esc>') 534 535 command('let @+ = "little"') 536 feed('/<C-R>+') 537 screen:expect([[ 538 the first line │the first line | 539 in a {2:little} file │in a {10:little} file | 540 {1:~ }│{1:~ }|*4 541 /little^ | 542 ]]) 543 feed('<Esc>') 544 end) 545 546 it('works with incsearch and offset', function() 547 feed_command('set hlsearch') 548 feed_command('set incsearch') 549 insert([[ 550 not the match you're looking for 551 the match is here]]) 552 553 feed('gg/mat/e') 554 screen:expect([[ 555 not the {2:mat}ch you're looking for | 556 the {10:mat}ch is here | 557 {1:~ }|*4 558 /mat/e^ | 559 ]]) 560 561 -- Search with count and /e offset fixed in Vim patch 7.4.532. 562 feed('<esc>2/mat/e') 563 screen:expect([[ 564 not the {10:mat}ch you're looking for | 565 the {2:mat}ch is here | 566 {1:~ }|*4 567 /mat/e^ | 568 ]]) 569 570 feed('<cr>') 571 screen:expect([[ 572 not the {10:mat}ch you're looking for | 573 the {10:ma^t}ch is here | 574 {1:~ }|*4 575 /mat/e | 576 ]]) 577 end) 578 579 it('works with multiline regexps', function() 580 feed_command('set hlsearch') 581 feed('4oa repeated line<esc>') 582 feed('/line\\na<cr>') 583 screen:expect([[ 584 | 585 a repeated {10:^line } | 586 {10:a} repeated {10:line } |*2 587 {10:a} repeated line | 588 {1:~ }| 589 {19:search hit BOTTOM, continuing at TOP} | 590 ]]) 591 592 -- it redraws rows above the changed one 593 feed('4Grb') 594 screen:expect([[ 595 | 596 a repeated {10:line } | 597 {10:a} repeated line | 598 ^b repeated {10:line } | 599 {10:a} repeated line | 600 {1:~ }| 601 {19:search hit BOTTOM, continuing at TOP} | 602 ]]) 603 end) 604 605 it('works with matchadd and syntax', function() 606 feed_command('set hlsearch') 607 insert [[ 608 very special text 609 ]] 610 feed_command('syntax on') 611 feed_command('highlight MyGroup guibg=Green gui=bold') 612 feed_command('highlight MyGroup2 guibg=Magenta gui=italic') 613 feed_command("call matchadd('MyGroup', 'special')") 614 feed_command("call matchadd('MyGroup2', 'text', 0)") 615 616 -- searchhl and matchadd matches are exclusive, only the highest priority 617 -- is used (and matches with lower priorities are not combined) 618 feed_command('/ial te') 619 screen:expect([[ 620 very {102:spec^ial}{10: te}{103:xt} | 621 | 622 {1:~ }|*4 623 {19:search hit BOTTOM, continuing at TOP} | 624 ]]) 625 626 -- check highlights work also in folds 627 feed('zf4j') 628 screen:expect([[ 629 {13:^+-- 2 lines: very special text·········}| 630 {1:~ }|*5 631 {19:search hit BOTTOM, continuing at TOP} | 632 ]]) 633 command('%foldopen') 634 screen:expect([[ 635 very {102:spec^ial}{10: te}{103:xt} | 636 | 637 {1:~ }|*4 638 {19:search hit BOTTOM, continuing at TOP} | 639 ]]) 640 641 feed_command('call clearmatches()') 642 screen:expect([[ 643 very spec{10:^ial te}xt | 644 | 645 {1:~ }|*4 646 :call clearmatches() | 647 ]]) 648 649 -- searchhl has priority over syntax, but in this case 650 -- nonconflicting attributes are combined 651 feed_command('syntax keyword MyGroup special') 652 screen:expect([[ 653 very {102:spec}{104:^ial}{10: te}xt | 654 | 655 {1:~ }|*4 656 :syntax keyword MyGroup special | 657 ]]) 658 end) 659 660 it('highlights entire pattern on :%g@a/b', function() 661 command('set inccommand=nosplit') 662 feed('ia/b/c<Esc>') 663 feed(':%g@a/b') 664 screen:expect([[ 665 {2:a/b}/c | 666 {1:~ }|*5 667 :%g@a/b^ | 668 ]]) 669 end) 670 671 it('incsearch is still visible after :redraw from K_EVENT', function() 672 fn.setline(1, { 'foo', 'bar' }) 673 feed('/foo<CR>/bar') 674 screen:expect([[ 675 foo | 676 {2:bar} | 677 {1:~ }|*4 678 /bar^ | 679 ]]) 680 command('redraw!') 681 -- There is an intermediate state where :redraw! removes 'incsearch' highlight. 682 screen:expect_unchanged(true) 683 end) 684 685 it('no ml_get error with incsearch and <Cmd> mapping that opens window', function() 686 command('cnoremap <F3> <Cmd>vnew<Bar>redraw!<CR>') 687 fn.setline(1, { 'foo', 'bar', 'baz' }) 688 feed('G/z') 689 screen:expect([[ 690 foo | 691 bar | 692 ba{2:z} | 693 {1:~ }|*3 694 /z^ | 695 ]]) 696 feed('<F3>') 697 screen:expect([[ 698 │foo | 699 {1:~ }│bar | 700 {1:~ }│baz | 701 {1:~ }│{1:~ }|*2 702 {3:[No Name] }{2:[No Name] [+] }| 703 /z^ | 704 ]]) 705 eq('', n.api.nvim_get_vvar('errmsg')) 706 feed('<C-G>') 707 screen:expect_unchanged(true) 708 eq('', n.api.nvim_get_vvar('errmsg')) 709 end) 710 711 it('highlight is not after redraw during substitute confirm prompt', function() 712 fn.setline(1, { 'foo', 'bar' }) 713 command('set nohlsearch') 714 feed(':%s/bar/baz/c<CR>') 715 screen:try_resize(screen._width, screen._height - 1) 716 screen:expect([[ 717 foo | 718 {2:bar} | 719 {1:~ }| 720 {3: }| 721 {6:replace with baz? (y)es/(n)o/(a)ll/(q)ui}| 722 {6:t/(l)ast/scroll up(^E)/down(^Y)}^ | 723 ]]) 724 end) 725 end)