inccommand_spec.lua (84175B)
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 expect = n.expect 10 local feed = n.feed 11 local insert = n.insert 12 local fn = n.fn 13 local api = n.api 14 local matches = t.matches 15 local ok = t.ok 16 local retry = t.retry 17 local source = n.source 18 local poke_eventloop = n.poke_eventloop 19 local sleep = vim.uv.sleep 20 local testprg = n.testprg 21 local assert_alive = n.assert_alive 22 23 local default_text = [[ 24 Inc substitution on 25 two lines 26 ]] 27 28 local multiline_text = [[ 29 1 2 3 30 A B C 31 4 5 6 32 X Y Z 33 7 8 9 34 ]] 35 36 local multimatch_text = [[ 37 a bdc eae a fgl lzia r 38 x 39 ]] 40 41 local multibyte_text = [[ 42 £ ¥ ѫѫ PEPPERS 43 £ ¥ ѫfѫ 44 a£ ѫ¥KOL 45 £ ¥ libm 46 £ ¥ 47 ]] 48 49 local long_multiline_text = [[ 50 1 2 3 51 A B C 52 4 5 6 53 X Y Z 54 7 8 9 55 K L M 56 a b c 57 d e f 58 q r s 59 x y z 60 £ m n 61 t œ ¥ 62 ]] 63 64 local function common_setup(screen, inccommand, text) 65 if screen then 66 command('syntax on') 67 command('set nohlsearch') 68 command('hi Substitute guifg=red guibg=yellow') 69 70 screen:add_extra_attr_ids { 71 [100] = { underline = true }, 72 [101] = { underline = true, foreground = Screen.colors.SlateBlue, bold = true }, 73 vis = { background = Screen.colors.LightGrey }, 74 } 75 end 76 77 command('set inccommand=' .. (inccommand or '')) 78 79 if text then 80 insert(text) 81 end 82 end 83 84 describe(':substitute, inccommand=split interactivity', function() 85 before_each(function() 86 clear() 87 common_setup(nil, 'split', default_text) 88 end) 89 90 -- Test the tests: verify that the `1==bufnr('$')` assertion 91 -- in the "no preview" tests (below) actually means something. 92 it('previews interactive cmdline', function() 93 feed(':%s/tw/MO/g') 94 retry(nil, 1000, function() 95 eq(2, eval("bufnr('$')")) 96 end) 97 end) 98 99 it('no preview if invoked by a script', function() 100 source('%s/tw/MO/g') 101 poke_eventloop() 102 eq(1, eval("bufnr('$')")) 103 -- sanity check: assert the buffer state 104 expect(default_text:gsub('tw', 'MO')) 105 end) 106 107 it('no preview if invoked by feedkeys()', function() 108 -- in a script... 109 source([[:call feedkeys(":%s/tw/MO/g\<CR>")]]) 110 -- or interactively... 111 feed([[:call feedkeys(":%s/bs/BUU/g\<lt>CR>")<CR>]]) 112 eq(1, eval("bufnr('$')")) 113 -- sanity check: assert the buffer state 114 expect(default_text:gsub('tw', 'MO'):gsub('bs', 'BUU')) 115 end) 116 end) 117 118 describe(":substitute, 'inccommand' preserves", function() 119 before_each(clear) 120 121 it('listed buffers (:ls)', function() 122 local screen = Screen.new(30, 10) 123 common_setup(screen, 'split', 'ABC') 124 125 feed(':%s/AB/BA/') 126 poke_eventloop() 127 feed('<CR>') 128 feed(':ls<CR>') 129 130 screen:expect([[ 131 BAC | 132 {1:~ }|*3 133 {3: }| 134 :ls | 135 1 %a + "[No Name]" | 136 line 1 | 137 {6:Press ENTER or type command to}| 138 {6: continue}^ | 139 ]]) 140 end) 141 142 it("'[ and '] marks #26439", function() 143 local screen = Screen.new(30, 10) 144 common_setup(screen, 'nosplit', ('abc\ndef\n'):rep(50)) 145 146 feed('ggyG') 147 local X = api.nvim_get_vvar('maxcol') 148 eq({ 0, 1, 1, 0 }, fn.getpos("'[")) 149 eq({ 0, 101, X, 0 }, fn.getpos("']")) 150 151 feed(":'[,']s/def/") 152 poke_eventloop() 153 eq({ 0, 1, 1, 0 }, fn.getpos("'[")) 154 eq({ 0, 101, X, 0 }, fn.getpos("']")) 155 156 feed('DEF/g') 157 poke_eventloop() 158 eq({ 0, 1, 1, 0 }, fn.getpos("'[")) 159 eq({ 0, 101, X, 0 }, fn.getpos("']")) 160 161 feed('<CR>') 162 expect(('abc\nDEF\n'):rep(50)) 163 end) 164 165 for _, case in pairs { '', 'split', 'nosplit' } do 166 it('various delimiters (inccommand=' .. case .. ')', function() 167 insert(default_text) 168 command('set inccommand=' .. case) 169 170 local delims = { '/', '#', ';', '%', ',', '@', '!' } 171 for _, delim in pairs(delims) do 172 feed(':%s' .. delim .. 'lines' .. delim .. 'LINES' .. delim .. 'g') 173 poke_eventloop() 174 feed('<CR>') 175 expect([[ 176 Inc substitution on 177 two LINES 178 ]]) 179 command('undo') 180 end 181 end) 182 end 183 184 for _, case in pairs { '', 'split', 'nosplit' } do 185 it("'undolevels' (inccommand=" .. case .. ')', function() 186 command('set undolevels=139') 187 command('setlocal undolevels=34') 188 command('split') -- Show the buffer in multiple windows 189 command('set inccommand=' .. case) 190 insert('as') 191 feed(':%s/as/glork/') 192 poke_eventloop() 193 feed('<enter>') 194 eq(139, api.nvim_get_option_value('undolevels', { scope = 'global' })) 195 eq(34, api.nvim_get_option_value('undolevels', { buf = 0 })) 196 end) 197 end 198 199 for _, case in ipairs({ '', 'split', 'nosplit' }) do 200 it('empty undotree() (inccommand=' .. case .. ')', function() 201 command('set undolevels=1000') 202 command('set inccommand=' .. case) 203 local expected_undotree = eval('undotree()') 204 205 -- Start typing an incomplete :substitute command. 206 feed([[:%s/e/YYYY/g]]) 207 poke_eventloop() 208 -- Cancel the :substitute. 209 feed([[<C-\><C-N>]]) 210 211 -- The undo tree should be unchanged. 212 eq(expected_undotree, eval('undotree()')) 213 eq({}, eval('undotree()')['entries']) 214 end) 215 end 216 217 for _, case in ipairs({ '', 'split', 'nosplit' }) do 218 it('undotree() with branches (inccommand=' .. case .. ')', function() 219 command('set undolevels=1000') 220 command('set inccommand=' .. case) 221 -- Make some changes. 222 feed([[isome text 1<C-\><C-N>]]) 223 feed([[osome text 2<C-\><C-N>]]) 224 -- Add an undo branch. 225 feed([[u]]) 226 -- More changes, more undo branches. 227 feed([[osome text 3<C-\><C-N>]]) 228 feed([[AX<C-\><C-N>]]) 229 feed([[...]]) 230 feed([[uu]]) 231 feed([[osome text 4<C-\><C-N>]]) 232 feed([[u<C-R>u]]) 233 feed([[osome text 5<C-\><C-N>]]) 234 expect([[ 235 some text 1 236 some text 3XX 237 some text 5]]) 238 local expected_undotree = eval('undotree()') 239 eq(5, #expected_undotree['entries']) -- sanity 240 241 -- Start typing an incomplete :substitute command. 242 feed([[:%s/e/YYYY/g]]) 243 poke_eventloop() 244 -- Cancel the :substitute. 245 feed([[<C-\><C-N>]]) 246 247 -- The undo tree should be unchanged. 248 eq(expected_undotree, eval('undotree()')) 249 end) 250 end 251 252 for _, case in pairs { '', 'split', 'nosplit' } do 253 it('b:changedtick (inccommand=' .. case .. ')', function() 254 command('set inccommand=' .. case) 255 feed([[isome text 1<C-\><C-N>]]) 256 feed([[osome text 2<C-\><C-N>]]) 257 local expected_tick = eval('b:changedtick') 258 ok(expected_tick > 0) 259 260 expect([[ 261 some text 1 262 some text 2]]) 263 feed(':%s/e/XXX/') 264 poke_eventloop() 265 266 eq(expected_tick, eval('b:changedtick')) 267 end) 268 end 269 270 for _, case in ipairs({ '', 'split', 'nosplit' }) do 271 it('previous substitute string ~ (inccommand=' .. case .. ') #12109', function() 272 local screen = Screen.new(30, 10) 273 common_setup(screen, case, default_text) 274 275 feed(':%s/Inc/SUB<CR>') 276 expect([[ 277 SUB substitution on 278 two lines 279 ]]) 280 281 feed(':%s/line/') 282 poke_eventloop() 283 feed('~') 284 poke_eventloop() 285 feed('<CR>') 286 expect([[ 287 SUB substitution on 288 two SUBs 289 ]]) 290 291 feed(':%s/sti/') 292 poke_eventloop() 293 feed('~') 294 poke_eventloop() 295 feed('B') 296 poke_eventloop() 297 feed('<CR>') 298 expect([[ 299 SUB subSUBBtution on 300 two SUBs 301 ]]) 302 303 feed(':%s/ion/NEW<CR>') 304 expect([[ 305 SUB subSUBBtutNEW on 306 two SUBs 307 ]]) 308 309 feed(':%s/two/') 310 poke_eventloop() 311 feed('N') 312 poke_eventloop() 313 feed('~') 314 poke_eventloop() 315 feed('<CR>') 316 expect([[ 317 SUB subSUBBtutNEW on 318 NNEW SUBs 319 ]]) 320 321 feed(':%s/bS/') 322 poke_eventloop() 323 feed('~') 324 poke_eventloop() 325 feed('W') 326 poke_eventloop() 327 feed('<CR>') 328 expect([[ 329 SUB suNNEWWUBBtutNEW on 330 NNEW SUBs 331 ]]) 332 end) 333 end 334 end) 335 336 describe(":substitute, 'inccommand' preserves undo", function() 337 local cases = { '', 'split', 'nosplit' } 338 339 local substrings = { 340 { ':%s/', '1' }, 341 { ':%s/', '1', '/' }, 342 { ':%s/', '1', '/', '<bs>' }, 343 { ':%s/', '1', '/', 'a' }, 344 { ':%s/', '1', '/', 'a', '<bs>' }, 345 { ':%s/', '1', '/', 'a', 'x' }, 346 { ':%s/', '1', '/', 'a', 'x', '<bs>' }, 347 { ':%s/', '1', '/', 'a', 'x', '<bs>', '<bs>' }, 348 { ':%s/', '1', '/', 'a', 'x', '<bs>', '<bs>', '<bs>' }, 349 { ':%s/', '1', '/', 'a', 'x', '/' }, 350 { ':%s/', '1', '/', 'a', 'x', '/', '<bs>' }, 351 { ':%s/', '1', '/', 'a', 'x', '/', '<bs>', '/' }, 352 { ':%s/', '1', '/', 'a', 'x', '/', 'g' }, 353 { ':%s/', '1', '/', 'a', 'x', '/', 'g', '<bs>' }, 354 { ':%s/', '1', '/', 'a', 'x', '/', 'g', '<bs>', '<bs>' }, 355 } 356 357 local function test_sub(substring, split, redoable) 358 command('bwipe!') 359 command('set inccommand=' .. split) 360 361 insert('1') 362 feed('o2<esc>') 363 command('undo') 364 feed('o3<esc>') 365 if redoable then 366 feed('o4<esc>') 367 command('undo') 368 end 369 for _, s in pairs(substring) do 370 feed(s) 371 end 372 poke_eventloop() 373 feed('<enter>') 374 command('undo') 375 376 feed('g-') 377 expect([[ 378 1 379 2]]) 380 381 feed('g+') 382 expect([[ 383 1 384 3]]) 385 end 386 387 local function test_notsub(substring, split, redoable) 388 command('bwipe!') 389 command('set inccommand=' .. split) 390 391 insert('1') 392 feed('o2<esc>') 393 command('undo') 394 feed('o3<esc>') 395 if redoable then 396 feed('o4<esc>') 397 command('undo') 398 end 399 for _, s in pairs(substring) do 400 feed(s) 401 end 402 poke_eventloop() 403 feed('<esc>') 404 405 feed('g-') 406 expect([[ 407 1 408 2]]) 409 410 feed('g+') 411 expect([[ 412 1 413 3]]) 414 415 if redoable then 416 feed('<c-r>') 417 expect([[ 418 1 419 3 420 4]]) 421 end 422 end 423 424 local function test_threetree(substring, split) 425 command('bwipe!') 426 command('set inccommand=' .. split) 427 428 insert('1') 429 feed('o2<esc>') 430 feed('o3<esc>') 431 feed('uu') 432 feed('oa<esc>') 433 feed('ob<esc>') 434 feed('uu') 435 feed('oA<esc>') 436 feed('oB<esc>') 437 438 -- This is the undo tree (x-Axis is timeline), we're at B now 439 -- ----------------A - B 440 -- / 441 -- | --------a - b 442 -- |/ 443 -- 1 - 2 - 3 444 445 feed('2u') 446 for _, s in pairs(substring) do 447 feed(s) 448 poke_eventloop() 449 end 450 feed('<esc>') 451 expect([[ 452 1]]) 453 feed('g-') 454 expect([[ 455 ]]) 456 feed('g+') 457 expect([[ 458 1]]) 459 feed('<c-r>') 460 expect([[ 461 1 462 A]]) 463 464 feed('g-') -- go to b 465 feed('2u') 466 for _, s in pairs(substring) do 467 feed(s) 468 poke_eventloop() 469 end 470 feed('<esc>') 471 feed('<c-r>') 472 expect([[ 473 1 474 a]]) 475 476 feed('g-') -- go to 3 477 feed('2u') 478 for _, s in pairs(substring) do 479 feed(s) 480 poke_eventloop() 481 end 482 feed('<esc>') 483 feed('<c-r>') 484 expect([[ 485 1 486 2]]) 487 end 488 489 before_each(clear) 490 491 it('at a non-leaf of the undo tree', function() 492 for _, case in pairs(cases) do 493 for _, str in pairs(substrings) do 494 for _, redoable in pairs({ true }) do 495 test_sub(str, case, redoable) 496 end 497 end 498 end 499 end) 500 501 it('at a leaf of the undo tree', function() 502 for _, case in pairs(cases) do 503 for _, str in pairs(substrings) do 504 for _, redoable in pairs({ false }) do 505 test_sub(str, case, redoable) 506 end 507 end 508 end 509 end) 510 511 it('when interrupting substitution', function() 512 for _, case in pairs(cases) do 513 for _, str in pairs(substrings) do 514 for _, redoable in pairs({ true, false }) do 515 test_notsub(str, case, redoable) 516 end 517 end 518 end 519 end) 520 521 it('in a complex undo scenario', function() 522 for _, case in pairs(cases) do 523 for _, str in pairs(substrings) do 524 test_threetree(str, case) 525 end 526 end 527 end) 528 529 it('with undolevels=0', function() 530 for _, case in pairs(cases) do 531 clear() 532 common_setup(nil, case, default_text) 533 command('set undolevels=0') 534 535 feed('1G0') 536 insert('X') 537 feed(':%s/tw/MO/') 538 poke_eventloop() 539 feed('<esc>') 540 command('undo') 541 expect(default_text) 542 command('undo') 543 expect(default_text:gsub('Inc', 'XInc')) 544 command('undo') 545 546 feed(':%s/tw/MO/g') 547 poke_eventloop() 548 feed('<CR>') 549 expect(default_text:gsub('tw', 'MO')) 550 command('undo') 551 expect(default_text) 552 command('undo') 553 expect(default_text:gsub('tw', 'MO')) 554 end 555 end) 556 557 it('with undolevels=1', function() 558 for _, case in pairs(cases) do 559 clear() 560 local screen = Screen.new(20, 10) 561 common_setup(screen, case, default_text) 562 screen:expect([[ 563 Inc substitution on | 564 two lines | 565 ^ | 566 {1:~ }|*6 567 | 568 ]]) 569 command('set undolevels=1') 570 571 feed('1G0') 572 insert('X') 573 feed('IY<esc>') 574 feed(':%s/tw/MO/') 575 poke_eventloop() 576 feed('<esc>') 577 feed('u') 578 expect(default_text:gsub('Inc', 'XInc')) 579 feed('u') 580 expect(default_text) 581 582 feed(':%s/tw/MO/g') 583 poke_eventloop() 584 feed('<enter>') 585 feed(':%s/MO/GO/g') 586 poke_eventloop() 587 feed('<enter>') 588 feed(':%s/GO/NO/g') 589 poke_eventloop() 590 feed('<enter>') 591 feed('u') 592 expect(default_text:gsub('tw', 'GO')) 593 feed('u') 594 expect(default_text:gsub('tw', 'MO')) 595 feed('u') 596 597 if case == 'split' then 598 screen:expect([[ 599 Inc substitution on | 600 ^MOo lines | 601 | 602 {1:~ }|*6 603 Already ...t change | 604 ]]) 605 else 606 screen:expect([[ 607 Inc substitution on | 608 ^MOo lines | 609 | 610 {1:~ }|*6 611 Already ...t change | 612 ]]) 613 end 614 end 615 end) 616 617 it('with undolevels=2', function() 618 for _, case in pairs(cases) do 619 clear() 620 local screen = Screen.new(20, 10) 621 common_setup(screen, case, default_text) 622 command('set undolevels=2') 623 624 feed('2GAx<esc>') 625 feed('Ay<esc>') 626 feed('Az<esc>') 627 feed(':%s/tw/AR') 628 poke_eventloop() 629 feed('<esc>') 630 feed('u') 631 expect(default_text:gsub('lines', 'linesxy')) 632 feed('u') 633 expect(default_text:gsub('lines', 'linesx')) 634 feed('u') 635 expect(default_text) 636 feed('u') 637 638 if case == 'split' then 639 screen:expect([[ 640 Inc substitution on | 641 two line^s | 642 | 643 {1:~ }|*6 644 Already ...t change | 645 ]]) 646 else 647 screen:expect([[ 648 Inc substitution on | 649 two line^s | 650 | 651 {1:~ }|*6 652 Already ...t change | 653 ]]) 654 end 655 656 feed(':%s/tw/MO/g') 657 poke_eventloop() 658 feed('<enter>') 659 feed(':%s/MO/GO/g') 660 poke_eventloop() 661 feed('<enter>') 662 feed(':%s/GO/NO/g') 663 poke_eventloop() 664 feed('<enter>') 665 feed(':%s/NO/LO/g') 666 poke_eventloop() 667 feed('<enter>') 668 feed('u') 669 expect(default_text:gsub('tw', 'NO')) 670 feed('u') 671 expect(default_text:gsub('tw', 'GO')) 672 feed('u') 673 expect(default_text:gsub('tw', 'MO')) 674 feed('u') 675 676 if case == 'split' then 677 screen:expect([[ 678 Inc substitution on | 679 ^MOo lines | 680 | 681 {1:~ }|*6 682 Already ...t change | 683 ]]) 684 else 685 screen:expect([[ 686 Inc substitution on | 687 ^MOo lines | 688 | 689 {1:~ }|*6 690 Already ...t change | 691 ]]) 692 end 693 end 694 end) 695 696 it('with undolevels=-1', function() 697 for _, case in pairs(cases) do 698 clear() 699 local screen = Screen.new(20, 10) 700 common_setup(screen, case, default_text) 701 702 command('set undolevels=-1') 703 feed(':%s/tw/MO/g') 704 poke_eventloop() 705 feed('<enter>') 706 feed('u') 707 if case == 'split' then 708 screen:expect([[ 709 Inc substitution on | 710 ^MOo lines | 711 | 712 {1:~ }|*6 713 Already ...t change | 714 ]]) 715 else 716 screen:expect([[ 717 Inc substitution on | 718 ^MOo lines | 719 | 720 {1:~ }|*6 721 Already ...t change | 722 ]]) 723 end 724 725 -- repeat with an interrupted substitution 726 clear() 727 screen = Screen.new(20, 10) 728 common_setup(screen, case, default_text) 729 730 command('set undolevels=-1') 731 feed('1G') 732 feed('IL<esc>') 733 feed(':%s/tw/MO/g') 734 poke_eventloop() 735 feed('<esc>') 736 feed('u') 737 738 screen:expect([[ 739 ^LInc substitution on| 740 two lines | 741 | 742 {1:~ }|*6 743 Already ...t change | 744 ]]) 745 end 746 end) 747 end) 748 749 describe(':substitute, inccommand=split', function() 750 local screen 751 752 before_each(function() 753 clear() 754 screen = Screen.new(30, 15) 755 common_setup(screen, 'split', default_text .. default_text) 756 end) 757 758 it("preserves 'modified' buffer flag", function() 759 command('set nomodified') 760 feed(':%s/tw') 761 screen:expect([[ 762 Inc substitution on | 763 {20:tw}o lines | 764 Inc substitution on | 765 {20:tw}o lines | 766 | 767 {3:[No Name] }| 768 |2| {20:tw}o lines | 769 |4| {20:tw}o lines | 770 {1:~ }|*5 771 {2:[Preview] }| 772 :%s/tw^ | 773 ]]) 774 feed([[<C-\><C-N>]]) -- Cancel the :substitute command. 775 eq(0, eval('&modified')) 776 end) 777 778 it('shows preview when cmd modifiers are present', function() 779 -- one modifier 780 feed(':keeppatterns %s/tw/to') 781 screen:expect { any = [[{20:to}o lines]] } 782 feed('<Esc>') 783 screen:expect { any = [[two lines]] } 784 785 -- multiple modifiers 786 feed(':keeppatterns silent %s/tw/to') 787 screen:expect { any = [[{20:to}o lines]] } 788 feed('<Esc>') 789 screen:expect { any = [[two lines]] } 790 791 -- non-modifier prefix 792 feed(':silent tabedit %s/tw/to') 793 screen:expect([[ 794 Inc substitution on | 795 two lines | 796 Inc substitution on | 797 two lines | 798 | 799 {1:~ }|*9 800 :silent tabedit %s/tw/to^ | 801 ]]) 802 feed('<Esc>') 803 804 -- leading colons 805 feed(':::%s/tw/to') 806 screen:expect { any = [[{20:to}o lines]] } 807 feed('<Esc>') 808 screen:expect { any = [[two lines]] } 809 end) 810 811 it('ignores new-window modifiers when splitting the preview window', function() 812 -- one modifier 813 feed(':topleft %s/tw/to') 814 screen:expect([[ 815 Inc substitution on | 816 {20:to}o lines | 817 Inc substitution on | 818 {20:to}o lines | 819 | 820 {3:[No Name] [+] }| 821 |2| {20:to}o lines | 822 |4| {20:to}o lines | 823 {1:~ }|*5 824 {2:[Preview] }| 825 :topleft %s/tw/to^ | 826 ]]) 827 feed('<Esc>') 828 screen:expect { any = [[two lines]] } 829 830 -- multiple modifiers 831 feed(':topleft vert %s/tw/to') 832 screen:expect([[ 833 Inc substitution on | 834 {20:to}o lines | 835 Inc substitution on | 836 {20:to}o lines | 837 | 838 {3:[No Name] [+] }| 839 |2| {20:to}o lines | 840 |4| {20:to}o lines | 841 {1:~ }|*5 842 {2:[Preview] }| 843 :topleft vert %s/tw/to^ | 844 ]]) 845 feed('<Esc>') 846 screen:expect { any = [[two lines]] } 847 end) 848 849 it('shows split window when typing the pattern', function() 850 feed(':%s/tw') 851 screen:expect([[ 852 Inc substitution on | 853 {20:tw}o lines | 854 Inc substitution on | 855 {20:tw}o lines | 856 | 857 {3:[No Name] [+] }| 858 |2| {20:tw}o lines | 859 |4| {20:tw}o lines | 860 {1:~ }|*5 861 {2:[Preview] }| 862 :%s/tw^ | 863 ]]) 864 end) 865 866 it('shows preview with empty replacement', function() 867 feed(':%s/tw/') 868 screen:expect([[ 869 Inc substitution on | 870 o lines | 871 Inc substitution on | 872 o lines | 873 | 874 {3:[No Name] [+] }| 875 |2| o lines | 876 |4| o lines | 877 {1:~ }|*5 878 {2:[Preview] }| 879 :%s/tw/^ | 880 ]]) 881 882 feed('x') 883 screen:expect([[ 884 Inc substitution on | 885 {20:x}o lines | 886 Inc substitution on | 887 {20:x}o lines | 888 | 889 {3:[No Name] [+] }| 890 |2| {20:x}o lines | 891 |4| {20:x}o lines | 892 {1:~ }|*5 893 {2:[Preview] }| 894 :%s/tw/x^ | 895 ]]) 896 897 feed('<bs>') 898 screen:expect([[ 899 Inc substitution on | 900 o lines | 901 Inc substitution on | 902 o lines | 903 | 904 {3:[No Name] [+] }| 905 |2| o lines | 906 |4| o lines | 907 {1:~ }|*5 908 {2:[Preview] }| 909 :%s/tw/^ | 910 ]]) 911 end) 912 913 it('shows split window when typing replacement', function() 914 feed(':%s/tw/XX') 915 screen:expect([[ 916 Inc substitution on | 917 {20:XX}o lines | 918 Inc substitution on | 919 {20:XX}o lines | 920 | 921 {3:[No Name] [+] }| 922 |2| {20:XX}o lines | 923 |4| {20:XX}o lines | 924 {1:~ }|*5 925 {2:[Preview] }| 926 :%s/tw/XX^ | 927 ]]) 928 end) 929 930 it('does not show split window for :s/', function() 931 feed('2gg') 932 feed(':s/tw') 933 screen:expect([[ 934 Inc substitution on | 935 {20:tw}o lines | 936 Inc substitution on | 937 two lines | 938 | 939 {1:~ }|*9 940 :s/tw^ | 941 ]]) 942 end) 943 944 it("'hlsearch' is active, 'cursorline' is not", function() 945 command('set hlsearch cursorline') 946 feed('gg') 947 948 -- Assert that 'cursorline' is active. 949 screen:expect([[ 950 {21:^Inc substitution on }| 951 two lines | 952 Inc substitution on | 953 two lines | 954 | 955 {1:~ }|*9 956 | 957 ]]) 958 959 feed(':%s/tw') 960 -- 'cursorline' is NOT active during preview. 961 screen:expect([[ 962 Inc substitution on | 963 {20:tw}o lines | 964 Inc substitution on | 965 {20:tw}o lines | 966 | 967 {3:[No Name] [+] }| 968 |2| {20:tw}o lines | 969 |4| {20:tw}o lines | 970 {1:~ }|*5 971 {2:[Preview] }| 972 :%s/tw^ | 973 ]]) 974 end) 975 976 it('highlights the replacement text', function() 977 feed('ggO') 978 feed('M M M<esc>') 979 feed(':%s/M/123/g') 980 screen:expect([[ 981 {20:123} {20:123} {20:123} | 982 Inc substitution on | 983 two lines | 984 Inc substitution on | 985 two lines | 986 {3:[No Name] [+] }| 987 |1| {20:123} {20:123} {20:123} | 988 {1:~ }|*6 989 {2:[Preview] }| 990 :%s/M/123/g^ | 991 ]]) 992 end) 993 994 it("highlights nothing when there's no match", function() 995 feed('gg') 996 feed(':%s/Inx') 997 screen:expect([[ 998 Inc substitution on | 999 two lines | 1000 Inc substitution on | 1001 two lines | 1002 | 1003 {3:[No Name] [+] }| 1004 | 1005 {1:~ }|*6 1006 {2:[Preview] }| 1007 :%s/Inx^ | 1008 ]]) 1009 end) 1010 1011 it('previews correctly when previewhight is small', function() 1012 command('set cwh=3') 1013 command('set hls') 1014 feed('ggdG') 1015 insert(string.rep('abc abc abc\n', 20)) 1016 feed(':%s/abc/MMM/g') 1017 screen:expect([[ 1018 {20:MMM} {20:MMM} {20:MMM} |*9 1019 {3:[No Name] [+] }| 1020 | 1| {20:MMM} {20:MMM} {20:MMM} | 1021 | 2| {20:MMM} {20:MMM} {20:MMM} | 1022 | 3| {20:MMM} {20:MMM} {20:MMM} | 1023 {2:[Preview] }| 1024 :%s/abc/MMM/g^ | 1025 ]]) 1026 end) 1027 1028 it('actually replaces text', function() 1029 feed(':%s/tw/XX/g') 1030 poke_eventloop() 1031 feed('<Enter>') 1032 1033 screen:expect([[ 1034 Inc substitution on | 1035 XXo lines | 1036 Inc substitution on | 1037 ^XXo lines | 1038 | 1039 {1:~ }|*9 1040 :%s/tw/XX/g | 1041 ]]) 1042 end) 1043 1044 it('shows correct line numbers with many lines', function() 1045 feed('gg') 1046 feed('2yy') 1047 feed('2000p') 1048 command('1,1000s/tw/BB/g') 1049 1050 feed(':%s/tw/X') 1051 screen:expect([[ 1052 Inc substitution on | 1053 BBo lines | 1054 Inc substitution on | 1055 {20:X}o lines | 1056 Inc substitution on | 1057 {3:[No Name] [+] }| 1058 |1001| {20:X}o lines | 1059 |1003| {20:X}o lines | 1060 |1005| {20:X}o lines | 1061 |1007| {20:X}o lines | 1062 |1009| {20:X}o lines | 1063 |1011| {20:X}o lines | 1064 |1013| {20:X}o lines | 1065 {2:[Preview] }| 1066 :%s/tw/X^ | 1067 ]]) 1068 end) 1069 1070 it('does not spam the buffer numbers', function() 1071 -- The preview buffer is re-used (unless user deleted it), so buffer numbers 1072 -- will not increase on each keystroke. 1073 feed(':%s/tw/Xo/g') 1074 -- Delete and re-type the g a few times. 1075 feed('<BS>') 1076 poke_eventloop() 1077 feed('g') 1078 poke_eventloop() 1079 feed('<BS>') 1080 poke_eventloop() 1081 feed('g') 1082 poke_eventloop() 1083 feed('<CR>') 1084 poke_eventloop() 1085 feed(':vs tmp<enter>') 1086 eq(3, fn.bufnr('$')) 1087 end) 1088 1089 it('works with the n flag', function() 1090 feed(':%s/tw/Mix/n') 1091 poke_eventloop() 1092 feed('<Enter>') 1093 screen:expect([[ 1094 Inc substitution on | 1095 two lines | 1096 Inc substitution on | 1097 two lines | 1098 ^ | 1099 {1:~ }|*9 1100 2 matches on 2 lines | 1101 ]]) 1102 end) 1103 1104 it("deactivates if 'redrawtime' is exceeded #5602", function() 1105 -- prevent redraws from 'incsearch' 1106 api.nvim_set_option_value('incsearch', false, {}) 1107 -- Assert that 'inccommand' is ENABLED initially. 1108 eq('split', eval('&inccommand')) 1109 -- Set 'redrawtime' to minimal value, to ensure timeout is triggered. 1110 command('set redrawtime=1 nowrap') 1111 -- Load a big file. 1112 command('silent edit! test/functional/fixtures/bigfile_oneline.txt') 1113 -- Start :substitute with a slow pattern. 1114 feed([[:%s/B.*N/x]]) 1115 poke_eventloop() 1116 1117 -- Assert that 'inccommand' is DISABLED in cmdline mode. 1118 eq('', eval('&inccommand')) 1119 -- Assert that preview cleared (or never manifested). 1120 screen:expect([[ 1121 0000;<control>;Cc;0;BN;;;;;N;N| 1122 2F923;CJK COMPATIBILITY IDEOGR| 1123 2F924;CJK COMPATIBILITY IDEOGR| 1124 2F925;CJK COMPATIBILITY IDEOGR| 1125 2F926;CJK COMPATIBILITY IDEOGR| 1126 2F927;CJK COMPATIBILITY IDEOGR| 1127 2F928;CJK COMPATIBILITY IDEOGR| 1128 2F929;CJK COMPATIBILITY IDEOGR| 1129 2F92A;CJK COMPATIBILITY IDEOGR| 1130 2F92B;CJK COMPATIBILITY IDEOGR| 1131 2F92C;CJK COMPATIBILITY IDEOGR| 1132 2F92D;CJK COMPATIBILITY IDEOGR| 1133 2F92E;CJK COMPATIBILITY IDEOGR| 1134 2F92F;CJK COMPATIBILITY IDEOGR| 1135 :%s/B.*N/x^ | 1136 ]]) 1137 1138 -- Assert that 'inccommand' is again ENABLED after leaving cmdline mode. 1139 feed([[<C-\><C-N>]]) 1140 eq('split', eval('&inccommand')) 1141 end) 1142 1143 it("deactivates if 'foldexpr' is slow #9557", function() 1144 insert([[ 1145 a 1146 a 1147 a 1148 a 1149 a 1150 a 1151 a 1152 a 1153 ]]) 1154 source([[ 1155 function! Slowfold(lnum) 1156 sleep 5m 1157 return a:lnum % 3 1158 endfun 1159 ]]) 1160 command('set redrawtime=1 inccommand=split') 1161 command('set foldmethod=expr foldexpr=Slowfold(v:lnum)') 1162 feed(':%s/a/bcdef') 1163 1164 -- Assert that 'inccommand' is DISABLED in cmdline mode. 1165 retry(nil, nil, function() 1166 eq('', eval('&inccommand')) 1167 end) 1168 1169 -- Assert that 'inccommand' is again ENABLED after leaving cmdline mode. 1170 feed([[<C-\><C-N>]]) 1171 retry(nil, nil, function() 1172 eq('split', eval('&inccommand')) 1173 end) 1174 end) 1175 1176 it('clears preview if non-previewable command is edited #5585', function() 1177 feed('gg') 1178 -- Put a non-previewable command in history. 1179 feed(":echo 'foo'<CR>") 1180 -- Start an incomplete :substitute command. 1181 feed(':1,2s/t/X') 1182 1183 screen:expect([[ 1184 Inc subs{20:X}itution on | 1185 {20:X}wo lines | 1186 Inc substitution on | 1187 two lines | 1188 | 1189 {3:[No Name] [+] }| 1190 |1| Inc subs{20:X}itution on | 1191 |2| {20:X}wo lines | 1192 {1:~ }|*5 1193 {2:[Preview] }| 1194 :1,2s/t/X^ | 1195 ]]) 1196 1197 -- Select the previous command. 1198 feed('<C-P>') 1199 -- Assert that preview was cleared. 1200 screen:expect([[ 1201 Inc substitution on | 1202 two lines | 1203 Inc substitution on | 1204 two lines | 1205 | 1206 {1:~ }|*9 1207 :echo 'foo'^ | 1208 ]]) 1209 end) 1210 1211 it([[preview changes correctly with c_CTRL-R_= and c_CTRL-\_e]], function() 1212 feed('gg') 1213 feed(':1,2s/t/X') 1214 screen:expect([[ 1215 Inc subs{20:X}itution on | 1216 {20:X}wo lines | 1217 Inc substitution on | 1218 two lines | 1219 | 1220 {3:[No Name] [+] }| 1221 |1| Inc subs{20:X}itution on | 1222 |2| {20:X}wo lines | 1223 {1:~ }|*5 1224 {2:[Preview] }| 1225 :1,2s/t/X^ | 1226 ]]) 1227 1228 feed([[<C-R>='Y']]) 1229 -- preview should be unchanged during c_CTRL-R_= editing 1230 screen:expect([[ 1231 Inc subs{20:X}itution on | 1232 {20:X}wo lines | 1233 Inc substitution on | 1234 two lines | 1235 | 1236 {3:[No Name] [+] }| 1237 |1| Inc subs{20:X}itution on | 1238 |2| {20:X}wo lines | 1239 {1:~ }|*5 1240 {2:[Preview] }| 1241 ={26:'Y'}^ | 1242 ]]) 1243 1244 feed('<CR>') 1245 -- preview should be changed by the result of the expression 1246 screen:expect([[ 1247 Inc subs{20:XY}itution on | 1248 {20:XY}wo lines | 1249 Inc substitution on | 1250 two lines | 1251 | 1252 {3:[No Name] [+] }| 1253 |1| Inc subs{20:XY}itution on | 1254 |2| {20:XY}wo lines | 1255 {1:~ }|*5 1256 {2:[Preview] }| 1257 :1,2s/t/XY^ | 1258 ]]) 1259 1260 feed([[<C-\>e'echo']]) 1261 -- preview should be unchanged during c_CTRL-\_e editing 1262 screen:expect([[ 1263 Inc subs{20:XY}itution on | 1264 {20:XY}wo lines | 1265 Inc substitution on | 1266 two lines | 1267 | 1268 {3:[No Name] [+] }| 1269 |1| Inc subs{20:XY}itution on | 1270 |2| {20:XY}wo lines | 1271 {1:~ }|*5 1272 {2:[Preview] }| 1273 ={26:'echo'}^ | 1274 ]]) 1275 1276 feed('<CR>') 1277 -- preview should be cleared if command is changed to a non-previewable one 1278 screen:expect([[ 1279 Inc substitution on | 1280 two lines | 1281 Inc substitution on | 1282 two lines | 1283 | 1284 {1:~ }|*9 1285 :echo^ | 1286 ]]) 1287 end) 1288 end) 1289 1290 describe('inccommand=nosplit', function() 1291 local screen 1292 1293 before_each(function() 1294 clear() 1295 screen = Screen.new(20, 10) 1296 common_setup(screen, 'nosplit', default_text .. default_text) 1297 end) 1298 1299 it('works with :smagic, :snomagic', function() 1300 command('set hlsearch') 1301 insert('Line *.3.* here') 1302 1303 feed(':%smagic/3.*/X') -- start :smagic command 1304 screen:expect([[ 1305 Inc substitution on | 1306 two lines | 1307 Inc substitution on | 1308 two lines | 1309 Line *.{20:X} | 1310 {1:~ }|*4 1311 :%smagic/3.*/X^ | 1312 ]]) 1313 1314 feed([[<C-\><C-N>]]) -- cancel 1315 feed(':%snomagic/3.*/X') -- start :snomagic command 1316 screen:expect([[ 1317 Inc substitution on | 1318 two lines | 1319 Inc substitution on | 1320 two lines | 1321 Line *.{20:X} here | 1322 {1:~ }|*4 1323 :%snomagic/3.*/X^ | 1324 ]]) 1325 end) 1326 1327 it('shows preview when cmd modifiers are present', function() 1328 -- one modifier 1329 feed(':keeppatterns %s/tw/to') 1330 screen:expect { any = [[{20:to}o lines]] } 1331 feed('<Esc>') 1332 screen:expect { any = [[two lines]] } 1333 1334 -- multiple modifiers 1335 feed(':keeppatterns silent %s/tw/to') 1336 screen:expect { any = [[{20:to}o lines]] } 1337 feed('<Esc>') 1338 screen:expect { any = [[two lines]] } 1339 1340 -- non-modifier prefix 1341 feed(':silent tabedit %s/tw/to') 1342 screen:expect([[ 1343 Inc substitution on | 1344 two lines | 1345 Inc substitution on | 1346 two lines | 1347 | 1348 {1:~ }|*2 1349 {3: }| 1350 :silent tabedit %s/t| 1351 w/to^ | 1352 ]]) 1353 end) 1354 1355 it('does not show window after toggling :set inccommand', function() 1356 feed(':%s/tw/OKOK') 1357 feed('<Esc>') 1358 command('set icm=split') 1359 feed(':%s/tw/OKOK') 1360 feed('<Esc>') 1361 command('set icm=nosplit') 1362 feed(':%s/tw/OKOK') 1363 poke_eventloop() 1364 screen:expect([[ 1365 Inc substitution on | 1366 {20:OKOK}o lines | 1367 Inc substitution on | 1368 {20:OKOK}o lines | 1369 | 1370 {1:~ }|*4 1371 :%s/tw/OKOK^ | 1372 ]]) 1373 end) 1374 1375 it('never shows preview buffer', function() 1376 command('set hlsearch') 1377 1378 feed(':%s/tw') 1379 screen:expect([[ 1380 Inc substitution on | 1381 {20:tw}o lines | 1382 Inc substitution on | 1383 {20:tw}o lines | 1384 | 1385 {1:~ }|*4 1386 :%s/tw^ | 1387 ]]) 1388 1389 feed('/BM') 1390 screen:expect([[ 1391 Inc substitution on | 1392 {20:BM}o lines | 1393 Inc substitution on | 1394 {20:BM}o lines | 1395 | 1396 {1:~ }|*4 1397 :%s/tw/BM^ | 1398 ]]) 1399 1400 feed('/') 1401 screen:expect([[ 1402 Inc substitution on | 1403 {20:BM}o lines | 1404 Inc substitution on | 1405 {20:BM}o lines | 1406 | 1407 {1:~ }|*4 1408 :%s/tw/BM/^ | 1409 ]]) 1410 1411 feed('<enter>') 1412 screen:expect([[ 1413 Inc substitution on | 1414 BMo lines | 1415 Inc substitution on | 1416 ^BMo lines | 1417 | 1418 {1:~ }|*4 1419 :%s/tw/BM/ | 1420 ]]) 1421 end) 1422 1423 it('clears preview if non-previewable command is edited', function() 1424 -- Put a non-previewable command in history. 1425 feed(":echo 'foo'<CR>") 1426 -- Start an incomplete :substitute command. 1427 feed(':1,2s/t/X') 1428 1429 screen:expect([[ 1430 Inc subs{20:X}itution on | 1431 {20:X}wo lines | 1432 Inc substitution on | 1433 two lines | 1434 | 1435 {1:~ }|*4 1436 :1,2s/t/X^ | 1437 ]]) 1438 1439 -- Select the previous command. 1440 feed('<C-P>') 1441 -- Assert that preview was cleared. 1442 screen:expect([[ 1443 Inc substitution on | 1444 two lines | 1445 Inc substitution on | 1446 two lines | 1447 | 1448 {1:~ }|*4 1449 :echo 'foo'^ | 1450 ]]) 1451 end) 1452 1453 it('does not execute trailing bar-separated commands #7494', function() 1454 feed(':%s/two/three/g|q!') 1455 screen:expect([[ 1456 Inc substitution on | 1457 {20:three} lines | 1458 Inc substitution on | 1459 {20:three} lines | 1460 | 1461 {1:~ }|*4 1462 :%s/two/three/g|q!^ | 1463 ]]) 1464 eq(eval('v:null'), eval('v:exiting')) 1465 end) 1466 1467 it('does not break bar-separated command #8796', function() 1468 source([[ 1469 function! F() 1470 if v:false | return | endif 1471 endfun 1472 ]]) 1473 command('call timer_start(10, {-> F()}, {"repeat":-1})') 1474 feed(':%s/') 1475 sleep(20) -- Allow some timer activity. 1476 screen:expect([[ 1477 Inc substitution on | 1478 two lines | 1479 Inc substitution on | 1480 two lines | 1481 | 1482 {1:~ }|*4 1483 :%s/^ | 1484 ]]) 1485 end) 1486 end) 1487 1488 describe(":substitute, 'inccommand' with a failing expression", function() 1489 local screen 1490 local cases = { '', 'split', 'nosplit' } 1491 1492 local function refresh(case) 1493 clear() 1494 screen = Screen.new(20, 10) 1495 common_setup(screen, case, default_text) 1496 end 1497 1498 it('in the pattern does nothing', function() 1499 for _, case in pairs(cases) do 1500 refresh(case) 1501 command('set inccommand=' .. case) 1502 feed(':silent! %s/tw\\(/LARD/') 1503 poke_eventloop() 1504 feed('<enter>') 1505 expect(default_text) 1506 end 1507 end) 1508 1509 it('in the replacement deletes the matches', function() 1510 for _, case in pairs(cases) do 1511 refresh(case) 1512 local replacements = { "\\='LARD", '\\=xx_novar__xx' } 1513 1514 for _, repl in pairs(replacements) do 1515 command('set inccommand=' .. case) 1516 feed(':silent! %s/tw/' .. repl .. '/') 1517 poke_eventloop() 1518 feed('<enter>') 1519 expect(default_text:gsub('tw', '')) 1520 command('undo') 1521 end 1522 end 1523 end) 1524 1525 it('in the range does not error #5912', function() 1526 for _, case in pairs(cases) do 1527 refresh(case) 1528 feed(':100s/') 1529 1530 screen:expect([[ 1531 Inc substitution on | 1532 two lines | 1533 | 1534 {1:~ }|*6 1535 :100s/^ | 1536 ]]) 1537 1538 feed('<enter>') 1539 screen:expect([[ 1540 Inc substitution on | 1541 two lines | 1542 ^ | 1543 {1:~ }|*6 1544 {9:E16: Invalid range} | 1545 ]]) 1546 end 1547 end) 1548 end) 1549 1550 describe("'inccommand' and :cnoremap", function() 1551 local cases = { '', 'split', 'nosplit' } 1552 local screen 1553 1554 local function refresh(case, visual) 1555 clear() 1556 screen = visual and Screen.new(80, 10) or nil 1557 common_setup(screen, case, default_text) 1558 end 1559 1560 it('work with remapped characters', function() 1561 for _, case in pairs(cases) do 1562 refresh(case) 1563 local cmd = '%s/lines/LINES/g' 1564 1565 for i = 1, string.len(cmd) do 1566 local c = string.sub(cmd, i, i) 1567 command('cnoremap ' .. c .. ' ' .. c) 1568 end 1569 1570 feed(':' .. cmd) 1571 poke_eventloop() 1572 feed('<CR>') 1573 expect([[ 1574 Inc substitution on 1575 two LINES 1576 ]]) 1577 end 1578 end) 1579 1580 it('work when mappings move the cursor', function() 1581 for _, case in pairs(cases) do 1582 refresh(case) 1583 command('cnoremap ,S LINES/<left><left><left><left><left><left>') 1584 1585 feed(':%s/lines/') 1586 poke_eventloop() 1587 feed(',S') 1588 poke_eventloop() 1589 feed('or three <enter>') 1590 poke_eventloop() 1591 expect([[ 1592 Inc substitution on 1593 two or three LINES 1594 ]]) 1595 1596 command('cnoremap ;S /X/<left><left><left>') 1597 feed(':%s/') 1598 poke_eventloop() 1599 feed(';S') 1600 poke_eventloop() 1601 feed('I<enter>') 1602 expect([[ 1603 Xnc substitution on 1604 two or three LXNES 1605 ]]) 1606 1607 command('cnoremap ,T //Y/<left><left><left>') 1608 feed(':%s') 1609 poke_eventloop() 1610 feed(',T') 1611 poke_eventloop() 1612 feed('X<enter>') 1613 expect([[ 1614 Ync substitution on 1615 two or three LYNES 1616 ]]) 1617 1618 command('cnoremap ;T s//Z/<left><left><left>') 1619 feed(':%') 1620 poke_eventloop() 1621 feed(';T') 1622 poke_eventloop() 1623 feed('Y<enter>') 1624 expect([[ 1625 Znc substitution on 1626 two or three LZNES 1627 ]]) 1628 end 1629 end) 1630 1631 it('still works with a broken mapping', function() 1632 for _, case in pairs(cases) do 1633 refresh(case, true) 1634 command("cnoremap <expr> x execute('bwipeout!')[-1].'x'") 1635 1636 api.nvim_set_vvar('errmsg', '') 1637 feed(':%s/tw/tox') 1638 -- error thrown b/c of the mapping 1639 matches('^E565:', api.nvim_get_vvar('errmsg')) 1640 1641 feed('<enter>') 1642 expect([[ 1643 Inc substitution on 1644 toxo lines 1645 ]]) 1646 end 1647 end) 1648 1649 it('work when temporarily moving the cursor', function() 1650 for _, case in pairs(cases) do 1651 refresh(case) 1652 command("cnoremap <expr> x cursor(1, 1)[-1].'x'") 1653 1654 feed(':%s/tw/tox') 1655 poke_eventloop() 1656 feed('/g<enter>') 1657 expect(default_text:gsub('tw', 'tox')) 1658 end 1659 end) 1660 1661 it("work when a mapping disables 'inccommand'", function() 1662 for _, case in pairs(cases) do 1663 refresh(case) 1664 command("cnoremap <expr> x execute('set inccommand=')[-1]") 1665 1666 feed(':%s/tw/tox') 1667 poke_eventloop() 1668 feed('a/g<enter>') 1669 expect(default_text:gsub('tw', 'toa')) 1670 end 1671 end) 1672 1673 it('work with a complex mapping', function() 1674 for _, case in pairs(cases) do 1675 refresh(case) 1676 source([[cnoremap x <C-\>eextend(g:, {'fo': getcmdline()}) 1677 \.fo<CR><C-c>:new<CR>:bw!<CR>:<C-r>=remove(g:, 'fo')<CR>x]]) 1678 1679 feed(':%s/tw/tox') 1680 poke_eventloop() 1681 feed('/<enter>') 1682 expect(default_text:gsub('tw', 'tox')) 1683 end 1684 end) 1685 end) 1686 1687 describe("'inccommand' autocommands", function() 1688 before_each(clear) 1689 1690 -- keys are events to be tested 1691 -- values are arrays like 1692 -- { open = { 1 }, close = { 2, 3} } 1693 -- which would mean that during the test below the event fires for 1694 -- buffer 1 when opening the preview window, and for buffers 2 and 3 1695 -- when closing the preview window 1696 local eventsExpected = { 1697 BufAdd = {}, 1698 BufDelete = {}, 1699 BufEnter = {}, 1700 BufFilePost = {}, 1701 BufFilePre = {}, 1702 BufHidden = {}, 1703 BufLeave = {}, 1704 BufNew = {}, 1705 BufNewFile = {}, 1706 BufRead = {}, 1707 BufReadCmd = {}, 1708 BufReadPre = {}, 1709 BufUnload = {}, 1710 BufWinEnter = {}, 1711 BufWinLeave = {}, 1712 BufWipeout = {}, 1713 BufWrite = {}, 1714 BufWriteCmd = {}, 1715 BufWritePost = {}, 1716 Syntax = {}, 1717 FileType = {}, 1718 WinEnter = {}, 1719 WinLeave = {}, 1720 CmdwinEnter = {}, 1721 CmdwinLeave = {}, 1722 } 1723 1724 local function bufferlist(q) 1725 local s = '' 1726 for _, buffer in pairs(q) do 1727 s = s .. ', ' .. tostring(buffer) 1728 end 1729 return s 1730 end 1731 1732 -- fill the table with default values 1733 for event, _ in pairs(eventsExpected) do 1734 eventsExpected[event].open = eventsExpected[event].open or {} 1735 eventsExpected[event].close = eventsExpected[event].close or {} 1736 end 1737 1738 local function register_autocmd(event) 1739 api.nvim_set_var(event .. '_fired', {}) 1740 command('autocmd ' .. event .. ' * call add(g:' .. event .. "_fired, expand('<abuf>'))") 1741 end 1742 1743 it('are not fired when splitting', function() 1744 common_setup(nil, 'split', default_text) 1745 1746 local eventsObserved = {} 1747 for event, _ in pairs(eventsExpected) do 1748 eventsObserved[event] = {} 1749 register_autocmd(event) 1750 end 1751 1752 feed(':%s/tw') 1753 1754 for event, _ in pairs(eventsExpected) do 1755 eventsObserved[event].open = api.nvim_get_var(event .. '_fired') 1756 api.nvim_set_var(event .. '_fired', {}) 1757 end 1758 1759 feed('/<enter>') 1760 1761 for event, _ in pairs(eventsExpected) do 1762 eventsObserved[event].close = api.nvim_get_var(event .. '_fired') 1763 end 1764 1765 for event, _ in pairs(eventsExpected) do 1766 eq( 1767 event .. bufferlist(eventsExpected[event].open), 1768 event .. bufferlist(eventsObserved[event].open) 1769 ) 1770 eq( 1771 event .. bufferlist(eventsExpected[event].close), 1772 event .. bufferlist(eventsObserved[event].close) 1773 ) 1774 end 1775 end) 1776 end) 1777 1778 describe("'inccommand' split windows", function() 1779 local screen 1780 local function refresh() 1781 clear() 1782 screen = Screen.new(40, 30) 1783 common_setup(screen, 'split', default_text) 1784 end 1785 1786 it('work after more splits', function() 1787 refresh() 1788 1789 feed('gg') 1790 command('vsplit') 1791 command('split') 1792 feed(':%s/tw') 1793 screen:expect([[ 1794 Inc substitution on │Inc substitution on| 1795 {20:tw}o lines │{20:tw}o lines | 1796 │ | 1797 {1:~ }│{1:~ }|*11 1798 {3:[No Name] [+] }│{1:~ }| 1799 Inc substitution on │{1:~ }| 1800 {20:tw}o lines │{1:~ }| 1801 │{1:~ }| 1802 {1:~ }│{1:~ }|*2 1803 {2:[No Name] [+] [No Name] [+] }| 1804 |2| {20:tw}o lines | 1805 {1:~ }|*6 1806 {2:[Preview] }| 1807 :%s/tw^ | 1808 ]]) 1809 1810 feed('<esc>') 1811 command('only') 1812 command('split') 1813 command('vsplit') 1814 1815 feed(':%s/tw') 1816 screen:expect([[ 1817 Inc substitution on │Inc substitution on| 1818 {20:tw}o lines │{20:tw}o lines | 1819 │ | 1820 {1:~ }│{1:~ }|*11 1821 {3:[No Name] [+] }{2:[No Name] [+] }| 1822 Inc substitution on | 1823 {20:tw}o lines | 1824 | 1825 {1:~ }|*2 1826 {2:[No Name] [+] }| 1827 |2| {20:tw}o lines | 1828 {1:~ }|*6 1829 {2:[Preview] }| 1830 :%s/tw^ | 1831 ]]) 1832 end) 1833 1834 local settings = { 1835 'splitbelow', 1836 'splitright', 1837 'noequalalways', 1838 'equalalways eadirection=ver', 1839 'equalalways eadirection=hor', 1840 'equalalways eadirection=both', 1841 } 1842 1843 it('are not affected by various settings', function() 1844 for _, setting in pairs(settings) do 1845 refresh() 1846 command('set ' .. setting) 1847 1848 feed(':%s/tw') 1849 1850 screen:expect([[ 1851 Inc substitution on | 1852 {20:tw}o lines | 1853 | 1854 {1:~ }|*17 1855 {3:[No Name] [+] }| 1856 |2| {20:tw}o lines | 1857 {1:~ }|*6 1858 {2:[Preview] }| 1859 :%s/tw^ | 1860 ]]) 1861 end 1862 end) 1863 1864 it("don't open if there's not enough room", function() 1865 refresh() 1866 screen:try_resize(40, 3) 1867 feed('gg:%s/tw') 1868 screen:expect([[ 1869 Inc substitution on | 1870 {20:tw}o lines | 1871 :%s/tw^ | 1872 ]]) 1873 end) 1874 end) 1875 1876 describe("'inccommand' with 'gdefault'", function() 1877 before_each(function() 1878 clear() 1879 end) 1880 1881 it('does not lock up #7244', function() 1882 common_setup(nil, 'nosplit', '{') 1883 command('set gdefault') 1884 feed(':s/{\\n') 1885 eq({ mode = 'c', blocking = false }, api.nvim_get_mode()) 1886 feed('/A<Enter>') 1887 expect('A') 1888 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 1889 end) 1890 1891 it('with multiline text and range, does not lock up #7244', function() 1892 common_setup(nil, 'nosplit', '{\n\n{') 1893 command('set gdefault') 1894 feed(':%s/{\\n') 1895 eq({ mode = 'c', blocking = false }, api.nvim_get_mode()) 1896 feed('/A<Enter>') 1897 expect('A\nA') 1898 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 1899 end) 1900 1901 it('does not crash on zero-width matches #7485', function() 1902 common_setup(nil, 'split', default_text) 1903 command('set gdefault') 1904 feed('gg') 1905 feed('Vj') 1906 feed(':s/\\%V') 1907 eq({ mode = 'c', blocking = false }, api.nvim_get_mode()) 1908 feed('<Esc>') 1909 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 1910 end) 1911 1912 it('removes highlights after abort for a zero-width match', function() 1913 local screen = Screen.new(30, 5) 1914 common_setup(screen, 'nosplit', default_text) 1915 command('set gdefault') 1916 1917 feed(':%s/\\%1c/a/') 1918 screen:expect([[ 1919 {20:a}Inc substitution on | 1920 {20:a}two lines | 1921 {20:a} | 1922 {1:~ }| 1923 :%s/\%1c/a/^ | 1924 ]]) 1925 1926 feed('<Esc>') 1927 screen:expect([[ 1928 Inc substitution on | 1929 two lines | 1930 ^ | 1931 {1:~ }| 1932 | 1933 ]]) 1934 end) 1935 end) 1936 1937 describe(':substitute', function() 1938 local screen 1939 before_each(function() 1940 clear() 1941 screen = Screen.new(30, 15) 1942 end) 1943 1944 it('inccommand=split, highlights multiline substitutions', function() 1945 common_setup(screen, 'split', multiline_text) 1946 feed('gg') 1947 1948 feed(':%s/2\\_.*X') 1949 screen:expect([[ 1950 1 {20:2 3} | 1951 {20:A B C} | 1952 {20:4 5 6} | 1953 {20:X} Y Z | 1954 7 8 9 | 1955 {3:[No Name] [+] }| 1956 |1| 1 {20:2 3} | 1957 |2|{20: A B C} | 1958 |3|{20: 4 5 6} | 1959 |4|{20: X} Y Z | 1960 {1:~ }|*3 1961 {2:[Preview] }| 1962 :%s/2\_.*X^ | 1963 ]]) 1964 1965 feed('/MMM') 1966 screen:expect([[ 1967 1 {20:MMM} Y Z | 1968 7 8 9 | 1969 | 1970 {1:~ }|*2 1971 {3:[No Name] [+] }| 1972 |1| 1 {20:MMM} Y Z | 1973 {1:~ }|*6 1974 {2:[Preview] }| 1975 :%s/2\_.*X/MMM^ | 1976 ]]) 1977 1978 feed('\\rK\\rLLL') 1979 screen:expect([[ 1980 1 {20:MMM} | 1981 {20:K} | 1982 {20:LLL} Y Z | 1983 7 8 9 | 1984 | 1985 {3:[No Name] [+] }| 1986 |1| 1 {20:MMM} | 1987 |2|{20: K} | 1988 |3|{20: LLL} Y Z | 1989 {1:~ }|*4 1990 {2:[Preview] }| 1991 :%s/2\_.*X/MMM\rK\rLLL^ | 1992 ]]) 1993 end) 1994 1995 it('inccommand=nosplit, highlights multiline substitutions', function() 1996 common_setup(screen, 'nosplit', multiline_text) 1997 feed('gg') 1998 1999 feed(':%s/2\\_.*X/MMM') 2000 screen:expect([[ 2001 1 {20:MMM} Y Z | 2002 7 8 9 | 2003 | 2004 {1:~ }|*11 2005 :%s/2\_.*X/MMM^ | 2006 ]]) 2007 2008 feed('\\rK\\rLLL') 2009 screen:expect([[ 2010 1 {20:MMM} | 2011 {20:K} | 2012 {20:LLL} Y Z | 2013 7 8 9 | 2014 | 2015 {1:~ }|*9 2016 :%s/2\_.*X/MMM\rK\rLLL^ | 2017 ]]) 2018 end) 2019 2020 it('inccommand=split, highlights multiple matches on a line', function() 2021 common_setup(screen, 'split', multimatch_text) 2022 command('set gdefault') 2023 feed('gg') 2024 2025 feed(':%s/a/XLK') 2026 screen:expect([[ 2027 {20:XLK} bdc e{20:XLK}e {20:XLK} fgl lzi{20:XLK} r| 2028 x | 2029 | 2030 {1:~ }|*2 2031 {3:[No Name] [+] }| 2032 |1| {20:XLK} bdc e{20:XLK}e {20:XLK} fgl lzi{20:X}| 2033 {20:LK} r | 2034 {1:~ }|*5 2035 {2:[Preview] }| 2036 :%s/a/XLK^ | 2037 ]]) 2038 end) 2039 2040 it('inccommand=nosplit, highlights multiple matches on a line', function() 2041 common_setup(screen, 'nosplit', multimatch_text) 2042 command('set gdefault') 2043 feed('gg') 2044 2045 feed(':%s/a/XLK') 2046 screen:expect([[ 2047 {20:XLK} bdc e{20:XLK}e {20:XLK} fgl lzi{20:XLK} r| 2048 x | 2049 | 2050 {1:~ }|*11 2051 :%s/a/XLK^ | 2052 ]]) 2053 end) 2054 2055 it('inccommand=split, with \\zs', function() 2056 common_setup(screen, 'split', multiline_text) 2057 feed('gg') 2058 2059 feed(':%s/[0-9]\\n\\zs[A-Z]/OKO') 2060 screen:expect([[ 2061 {20:OKO} B C | 2062 4 5 6 | 2063 {20:OKO} Y Z | 2064 7 8 9 | 2065 | 2066 {3:[No Name] [+] }| 2067 |1| 1 2 3 | 2068 |2| {20:OKO} B C | 2069 |3| 4 5 6 | 2070 |4| {20:OKO} Y Z | 2071 {1:~ }|*3 2072 {2:[Preview] }| 2073 :%s/[0-9]\n\zs[A-Z]/OKO^ | 2074 ]]) 2075 end) 2076 2077 it('inccommand=nosplit, with \\zs', function() 2078 common_setup(screen, 'nosplit', multiline_text) 2079 feed('gg') 2080 2081 feed(':%s/[0-9]\\n\\zs[A-Z]/OKO') 2082 screen:expect([[ 2083 1 2 3 | 2084 {20:OKO} B C | 2085 4 5 6 | 2086 {20:OKO} Y Z | 2087 7 8 9 | 2088 | 2089 {1:~ }|*8 2090 :%s/[0-9]\n\zs[A-Z]/OKO^ | 2091 ]]) 2092 end) 2093 2094 it('inccommand=split, substitutions of different length', function() 2095 common_setup(screen, 'split', 'T T123 T2T TTT T090804\nx') 2096 2097 feed(':%s/T\\([0-9]\\+\\)/\\1\\1/g') 2098 screen:expect([[ 2099 T {20:123123} {20:22}T TTT {20:090804090804} | 2100 x | 2101 {1:~ }|*3 2102 {3:[No Name] [+] }| 2103 |1| T {20:123123} {20:22}T TTT {20:090804090}| 2104 {20:804} | 2105 {1:~ }|*5 2106 {2:[Preview] }| 2107 :%s/T\([0-9]\+\)/\1\1/g^ | 2108 ]]) 2109 end) 2110 2111 it('inccommand=nosplit, substitutions of different length', function() 2112 common_setup(screen, 'nosplit', 'T T123 T2T TTT T090804\nx') 2113 2114 feed(':%s/T\\([0-9]\\+\\)/\\1\\1/g') 2115 screen:expect([[ 2116 T {20:123123} {20:22}T TTT {20:090804090804} | 2117 x | 2118 {1:~ }|*12 2119 :%s/T\([0-9]\+\)/\1\1/g^ | 2120 ]]) 2121 end) 2122 2123 it('inccommand=split, contraction of lines', function() 2124 local text = [[ 2125 T T123 T T123 T2T TT T23423424 2126 x 2127 afa Q 2128 adf la;lkd R 2129 alx 2130 ]] 2131 2132 common_setup(screen, 'split', text) 2133 feed(':%s/[QR]\\n') 2134 screen:expect([[ 2135 afa {20:Q} | 2136 adf la;lkd {20:R} | 2137 alx | 2138 | 2139 {1:~ }| 2140 {3:[No Name] [+] }| 2141 |3| afa {20:Q} | 2142 |4|{20: }adf la;lkd {20:R} | 2143 |5|{20: }alx | 2144 {1:~ }|*4 2145 {2:[Preview] }| 2146 :%s/[QR]\n^ | 2147 ]]) 2148 2149 feed('/KKK') 2150 screen:expect([[ 2151 T T123 T T123 T2T TT T23423424| 2152 x | 2153 afa {20:KKK}adf la;lkd {20:KKK}alx | 2154 | 2155 {1:~ }| 2156 {3:[No Name] [+] }| 2157 |3| afa {20:KKK}adf la;lkd {20:KKK}alx | 2158 {1:~ }|*6 2159 {2:[Preview] }| 2160 :%s/[QR]\n/KKK^ | 2161 ]]) 2162 end) 2163 2164 it('inccommand=nosplit, contraction of lines', function() 2165 local text = [[ 2166 T T123 T T123 T2T TT T23423424 2167 x 2168 afa Q 2169 adf la;lkd R 2170 alx 2171 ]] 2172 2173 common_setup(screen, 'nosplit', text) 2174 feed(':%s/[QR]\\n/KKK') 2175 screen:expect([[ 2176 T T123 T T123 T2T TT T23423424| 2177 x | 2178 afa {20:KKK}adf la;lkd {20:KKK}alx | 2179 | 2180 {1:~ }|*10 2181 :%s/[QR]\n/KKK^ | 2182 ]]) 2183 end) 2184 2185 it('inccommand=split, contraction of two subsequent NL chars', function() 2186 local text = [[ 2187 AAA AA 2188 2189 BBB BB 2190 2191 CCC CC 2192 2193 ]] 2194 2195 -- This used to crash, but more than 20 highlight entries are required 2196 -- to reproduce it (so that the marktree has multiple nodes) 2197 common_setup(screen, 'split', string.rep(text, 10)) 2198 feed(':%s/\\n\\n/<c-v><c-m>/g') 2199 screen:expect { 2200 grid = [[ 2201 CCC CC | 2202 AAA AA | 2203 BBB BB | 2204 CCC CC | 2205 | 2206 {3:[No Name] [+] }| 2207 | 1| AAA AA | 2208 | 2|{20: }BBB BB | 2209 | 3|{20: }CCC CC | 2210 | 4|{20: }AAA AA | 2211 | 5|{20: }BBB BB | 2212 | 6|{20: }CCC CC | 2213 | 7|{20: }AAA AA | 2214 {2:[Preview] }| 2215 :%s/\n\n/{18:^M}/g^ | 2216 ]], 2217 } 2218 assert_alive() 2219 end) 2220 2221 it('inccommand=nosplit, contraction of two subsequent NL chars', function() 2222 local text = [[ 2223 AAA AA 2224 2225 BBB BB 2226 2227 CCC CC 2228 2229 ]] 2230 2231 common_setup(screen, 'nosplit', string.rep(text, 10)) 2232 feed(':%s/\\n\\n/<c-v><c-m>/g') 2233 screen:expect { 2234 grid = [[ 2235 CCC CC | 2236 AAA AA | 2237 BBB BB | 2238 CCC CC | 2239 AAA AA | 2240 BBB BB | 2241 CCC CC | 2242 AAA AA | 2243 BBB BB | 2244 CCC CC | 2245 AAA AA | 2246 BBB BB | 2247 CCC CC | 2248 | 2249 :%s/\n\n/{18:^M}/g^ | 2250 ]], 2251 } 2252 assert_alive() 2253 end) 2254 2255 it('inccommand=split, multibyte text', function() 2256 common_setup(screen, 'split', multibyte_text) 2257 feed(':%s/£.*ѫ/X¥¥') 2258 screen:expect([[ 2259 a{20:X¥¥}¥KOL | 2260 £ ¥ libm | 2261 £ ¥ | 2262 | 2263 {1:~ }| 2264 {3:[No Name] [+] }| 2265 |1| {20:X¥¥} PEPPERS | 2266 |2| {20:X¥¥} | 2267 |3| a{20:X¥¥}¥KOL | 2268 {1:~ }|*4 2269 {2:[Preview] }| 2270 :%s/£.*ѫ/X¥¥^ | 2271 ]]) 2272 2273 feed('\\ra££ ¥') 2274 screen:expect([[ 2275 a{20:X¥¥} | 2276 {20:a££ ¥}¥KOL | 2277 £ ¥ libm | 2278 £ ¥ | 2279 | 2280 {3:[No Name] [+] }| 2281 |1| {20:X¥¥} | 2282 |2|{20: a££ ¥} PEPPERS | 2283 |3| {20:X¥¥} | 2284 |4|{20: a££ ¥} | 2285 |5| a{20:X¥¥} | 2286 |6|{20: a££ ¥}¥KOL | 2287 {1:~ }| 2288 {2:[Preview] }| 2289 :%s/£.*ѫ/X¥¥\ra££ ¥^ | 2290 ]]) 2291 end) 2292 2293 it('inccommand=nosplit, multibyte text', function() 2294 common_setup(screen, 'nosplit', multibyte_text) 2295 feed(':%s/£.*ѫ/X¥¥') 2296 screen:expect([[ 2297 {20:X¥¥} PEPPERS | 2298 {20:X¥¥} | 2299 a{20:X¥¥}¥KOL | 2300 £ ¥ libm | 2301 £ ¥ | 2302 | 2303 {1:~ }|*8 2304 :%s/£.*ѫ/X¥¥^ | 2305 ]]) 2306 2307 feed('\\ra££ ¥') 2308 screen:expect([[ 2309 {20:X¥¥} | 2310 {20:a££ ¥} PEPPERS | 2311 {20:X¥¥} | 2312 {20:a££ ¥} | 2313 a{20:X¥¥} | 2314 {20:a££ ¥}¥KOL | 2315 £ ¥ libm | 2316 £ ¥ | 2317 | 2318 {1:~ }|*5 2319 :%s/£.*ѫ/X¥¥\ra££ ¥^ | 2320 ]]) 2321 end) 2322 2323 it('inccommand=split, small cmdwinheight', function() 2324 common_setup(screen, 'split', long_multiline_text) 2325 command('set cmdwinheight=2') 2326 2327 feed(':%s/[a-z]') 2328 screen:expect([[ 2329 X Y Z | 2330 7 8 9 | 2331 K L M | 2332 {20:a} b c | 2333 {20:d} e f | 2334 {20:q} r s | 2335 {20:x} y z | 2336 £ {20:m} n | 2337 {20:t} œ ¥ | 2338 | 2339 {3:[No Name] [+] }| 2340 | 7| {20:a} b c | 2341 | 8| {20:d} e f | 2342 {2:[Preview] }| 2343 :%s/[a-z]^ | 2344 ]]) 2345 2346 feed('/JLKR £') 2347 screen:expect([[ 2348 X Y Z | 2349 7 8 9 | 2350 K L M | 2351 {20:JLKR £} b c | 2352 {20:JLKR £} e f | 2353 {20:JLKR £} r s | 2354 {20:JLKR £} y z | 2355 £ {20:JLKR £} n | 2356 {20:JLKR £} œ ¥ | 2357 | 2358 {3:[No Name] [+] }| 2359 | 7| {20:JLKR £} b c | 2360 | 8| {20:JLKR £} e f | 2361 {2:[Preview] }| 2362 :%s/[a-z]/JLKR £^ | 2363 ]]) 2364 2365 feed('\\rѫ ab \\rXXXX') 2366 screen:expect([[ 2367 7 8 9 | 2368 K L M | 2369 {20:JLKR £} | 2370 {20:ѫ ab } | 2371 {20:XXXX} b c | 2372 {20:JLKR £} | 2373 {20:ѫ ab } | 2374 {20:XXXX} e f | 2375 {20:JLKR £} | 2376 {20:ѫ ab } | 2377 {3:[No Name] [+] }| 2378 | 7| {20:JLKR £} | 2379 {3: }| 2380 :%s/[a-z]/JLKR £\rѫ ab \rXXX| 2381 X^ | 2382 ]]) 2383 end) 2384 2385 it('inccommand=split, large cmdwinheight', function() 2386 common_setup(screen, 'split', long_multiline_text) 2387 command('set cmdwinheight=11') 2388 2389 feed(':%s/. .$') 2390 screen:expect([[ 2391 t {20:œ ¥} | 2392 {3:[No Name] [+] }| 2393 | 1| 1 {20:2 3} | 2394 | 2| A {20:B C} | 2395 | 3| 4 {20:5 6} | 2396 | 4| X {20:Y Z} | 2397 | 5| 7 {20:8 9} | 2398 | 6| K {20:L M} | 2399 | 7| a {20:b c} | 2400 | 8| d {20:e f} | 2401 | 9| q {20:r s} | 2402 |10| x {20:y z} | 2403 |11| £ {20:m n} | 2404 {2:[Preview] }| 2405 :%s/. .$^ | 2406 ]]) 2407 2408 feed('/ YYY') 2409 screen:expect([[ 2410 t {20: YYY} | 2411 {3:[No Name] [+] }| 2412 | 1| 1 {20: YYY} | 2413 | 2| A {20: YYY} | 2414 | 3| 4 {20: YYY} | 2415 | 4| X {20: YYY} | 2416 | 5| 7 {20: YYY} | 2417 | 6| K {20: YYY} | 2418 | 7| a {20: YYY} | 2419 | 8| d {20: YYY} | 2420 | 9| q {20: YYY} | 2421 |10| x {20: YYY} | 2422 |11| £ {20: YYY} | 2423 {2:[Preview] }| 2424 :%s/. .$/ YYY^ | 2425 ]]) 2426 2427 feed('\\r KKK') 2428 screen:expect([[ 2429 a {20: YYY} | 2430 {3:[No Name] [+] }| 2431 | 1| 1 {20: YYY} | 2432 | 2|{20: KKK} | 2433 | 3| A {20: YYY} | 2434 | 4|{20: KKK} | 2435 | 5| 4 {20: YYY} | 2436 | 6|{20: KKK} | 2437 | 7| X {20: YYY} | 2438 | 8|{20: KKK} | 2439 | 9| 7 {20: YYY} | 2440 |10|{20: KKK} | 2441 |11| K {20: YYY} | 2442 {2:[Preview] }| 2443 :%s/. .$/ YYY\r KKK^ | 2444 ]]) 2445 end) 2446 2447 it('inccommand=split, lookaround', function() 2448 common_setup(screen, 'split', 'something\neverything\nsomeone') 2449 feed([[:%s/\(some\)\@<lt>=thing/one/]]) 2450 screen:expect([[ 2451 some{20:one} | 2452 everything | 2453 someone | 2454 {1:~ }|*2 2455 {3:[No Name] [+] }| 2456 |1| some{20:one} | 2457 {1:~ }|*6 2458 {2:[Preview] }| 2459 :%s/\(some\)\@<=thing/one/^ | 2460 ]]) 2461 2462 feed('<C-c>') 2463 feed('gg') 2464 poke_eventloop() 2465 feed([[:%s/\(some\)\@<lt>!thing/one/]]) 2466 screen:expect([[ 2467 something | 2468 every{20:one} | 2469 someone | 2470 {1:~ }|*2 2471 {3:[No Name] [+] }| 2472 |2| every{20:one} | 2473 {1:~ }|*6 2474 {2:[Preview] }| 2475 :%s/\(some\)\@<!thing/one/^ | 2476 ]]) 2477 2478 feed([[<C-c>]]) 2479 poke_eventloop() 2480 feed([[:%s/some\(thing\)\@=/every/]]) 2481 screen:expect([[ 2482 {20:every}thing | 2483 everything | 2484 someone | 2485 {1:~ }|*2 2486 {3:[No Name] [+] }| 2487 |1| {20:every}thing | 2488 {1:~ }|*6 2489 {2:[Preview] }| 2490 :%s/some\(thing\)\@=/every/^ | 2491 ]]) 2492 2493 feed([[<C-c>]]) 2494 poke_eventloop() 2495 feed([[:%s/some\(thing\)\@!/every/]]) 2496 screen:expect([[ 2497 something | 2498 everything | 2499 {20:every}one | 2500 {1:~ }|*2 2501 {3:[No Name] [+] }| 2502 |3| {20:every}one | 2503 {1:~ }|*6 2504 {2:[Preview] }| 2505 :%s/some\(thing\)\@!/every/^ | 2506 ]]) 2507 end) 2508 2509 it("doesn't prompt to swap cmd range", function() 2510 screen:try_resize(50, 8) -- wide to avoid hit-enter prompt 2511 common_setup(screen, 'split', default_text) 2512 feed(':2,1s/tw/MO/g') 2513 2514 -- substitution preview should have been made, without prompting 2515 screen:expect([[ 2516 {20:MO}o lines | 2517 {3:[No Name] [+] }| 2518 |2| {20:MO}o lines | 2519 {1:~ }|*3 2520 {2:[Preview] }| 2521 :2,1s/tw/MO/g^ | 2522 ]]) 2523 2524 -- but should be prompted on hitting enter 2525 feed('<CR>') 2526 screen:expect([[ 2527 {20:MO}o lines | 2528 {3:[No Name] [+] }| 2529 |2| {20:MO}o lines | 2530 {1:~ }|*3 2531 {2:[Preview] }| 2532 {6:Backwards range given, OK to swap (y/n)?}^ | 2533 ]]) 2534 2535 feed('y') 2536 screen:expect([[ 2537 Inc substitution on | 2538 ^MOo lines | 2539 | 2540 {1:~ }|*4 2541 {6:Backwards range given, OK to swap (y/n)?}y | 2542 ]]) 2543 end) 2544 end) 2545 2546 it(':substitute with inccommand during :terminal activity', function() 2547 if t.skip_fragile(pending) then 2548 return 2549 end 2550 retry(2, 40000, function() 2551 clear() 2552 local screen = Screen.new(30, 15) 2553 2554 command('set cmdwinheight=3') 2555 feed(([[:terminal "%s" REP 5000 xxx<cr>]]):format(testprg('shell-test'))) 2556 command('file term') 2557 feed('G') -- Follow :terminal output. 2558 command('new') 2559 common_setup(screen, 'split', 'foo bar baz\nbar baz fox\nbar foo baz') 2560 command('wincmd =') 2561 2562 feed('gg') 2563 feed(':%s/foo/ZZZ') 2564 sleep(20) -- Allow some terminal activity. 2565 poke_eventloop() 2566 screen:sleep(0) 2567 screen:expect_unchanged() 2568 end) 2569 end) 2570 2571 it(':substitute with inccommand, timer-induced :redraw #9777', function() 2572 clear() 2573 local screen = Screen.new(30, 12) 2574 command('set cmdwinheight=3') 2575 command('call timer_start(10, {-> execute("redraw")}, {"repeat":-1})') 2576 command('call timer_start(10, {-> execute("redrawstatus")}, {"repeat":-1})') 2577 common_setup(screen, 'split', 'foo bar baz\nbar baz fox\nbar foo baz') 2578 2579 feed('gg') 2580 feed(':%s/foo/ZZZ') 2581 sleep(20) -- Allow some timer activity. 2582 screen:expect([[ 2583 {20:ZZZ} bar baz | 2584 bar baz fox | 2585 bar {20:ZZZ} baz | 2586 {1:~ }|*3 2587 {3:[No Name] [+] }| 2588 |1| {20:ZZZ} bar baz | 2589 |3| bar {20:ZZZ} baz | 2590 {1:~ }| 2591 {2:[Preview] }| 2592 :%s/foo/ZZZ^ | 2593 ]]) 2594 2595 -- Also with nvim__redraw() 2596 command('call timer_start(10, {-> nvim__redraw(#{flush:1})}, {"repeat":-1})') 2597 command('call timer_start(10, {-> nvim__redraw(#{statusline:1})}, {"repeat":-1})') 2598 sleep(20) -- Allow some timer activity. 2599 screen:expect_unchanged() 2600 end) 2601 2602 it(':substitute with inccommand, allows :redraw before first separator is typed #18857', function() 2603 clear() 2604 local screen = Screen.new(30, 6) 2605 common_setup(screen, 'split', 'foo bar baz\nbar baz fox\nbar foo baz') 2606 command('hi! link NormalFloat CursorLine') 2607 local float_buf = api.nvim_create_buf(false, true) 2608 api.nvim_open_win(float_buf, false, { 2609 relative = 'editor', 2610 height = 1, 2611 width = 5, 2612 row = 3, 2613 col = 0, 2614 focusable = false, 2615 }) 2616 feed(':') 2617 screen:expect([[ 2618 foo bar baz | 2619 bar baz fox | 2620 bar foo baz | 2621 {21: }{1: }| 2622 {1:~ }| 2623 :^ | 2624 ]]) 2625 feed('%s') 2626 screen:expect([[ 2627 foo bar baz | 2628 bar baz fox | 2629 bar foo baz | 2630 {21: }{1: }| 2631 {1:~ }| 2632 :%s^ | 2633 ]]) 2634 api.nvim_buf_set_lines(float_buf, 0, -1, true, { 'foo' }) 2635 command('redraw') 2636 screen:expect([[ 2637 foo bar baz | 2638 bar baz fox | 2639 bar foo baz | 2640 {21:foo }{1: }| 2641 {1:~ }| 2642 :%s^ | 2643 ]]) 2644 end) 2645 2646 it(':substitute with inccommand, does not crash if range contains invalid marks', function() 2647 clear() 2648 local screen = Screen.new(30, 6) 2649 common_setup(screen, 'split', 'test') 2650 feed([[:'a,'bs]]) 2651 screen:expect([[ 2652 test | 2653 {1:~ }|*4 2654 :'a,'bs^ | 2655 ]]) 2656 -- v:errmsg shouldn't be set either before the first separator is typed 2657 eq('', eval('v:errmsg')) 2658 feed('/') 2659 screen:expect([[ 2660 test | 2661 {1:~ }|*4 2662 :'a,'bs/^ | 2663 ]]) 2664 end) 2665 2666 it(':substitute with inccommand, no unnecessary redraw if preview is not shown', function() 2667 clear() 2668 local screen = Screen.new(60, 6) 2669 common_setup(screen, 'split', 'test') 2670 feed(':ls<CR>') 2671 screen:expect([[ 2672 test | 2673 {1:~ }| 2674 {3: }| 2675 :ls | 2676 1 %a + "[No Name]" line 1 | 2677 {6:Press ENTER or type command to continue}^ | 2678 ]]) 2679 feed(':s') 2680 -- no unnecessary redraw, so messages are still shown 2681 screen:expect([[ 2682 test | 2683 {1:~ }| 2684 {3: }| 2685 :ls | 2686 1 %a + "[No Name]" line 1 | 2687 :s^ | 2688 ]]) 2689 feed('o') 2690 screen:expect([[ 2691 test | 2692 {1:~ }| 2693 {3: }| 2694 :ls | 2695 1 %a + "[No Name]" line 1 | 2696 :so^ | 2697 ]]) 2698 feed('<BS>') 2699 screen:expect([[ 2700 test | 2701 {1:~ }| 2702 {3: }| 2703 :ls | 2704 1 %a + "[No Name]" line 1 | 2705 :s^ | 2706 ]]) 2707 feed('/test') 2708 -- now inccommand is shown, so screen is redrawn 2709 screen:expect([[ 2710 {20:test} | 2711 {1:~ }|*4 2712 :s/test^ | 2713 ]]) 2714 end) 2715 2716 it(":substitute doesn't crash with inccommand, if undo is empty #12932", function() 2717 clear() 2718 local screen = Screen.new(10, 5) 2719 command('set undolevels=-1') 2720 common_setup(screen, 'split', 'test') 2721 feed(':%s/test') 2722 sleep(100) 2723 feed('/') 2724 sleep(100) 2725 feed('f') 2726 screen:expect([[ 2727 {20:f} | 2728 {1:~ }|*3 2729 :%s/test/f^ | 2730 ]]) 2731 assert_alive() 2732 end) 2733 2734 it(':substitute with inccommand works properly if undo is not synced #20029', function() 2735 clear() 2736 local screen = Screen.new(30, 6) 2737 common_setup(screen, 'nosplit', 'foo\nbar\nbaz') 2738 api.nvim_set_keymap('x', '<F2>', '<Esc>`<Oaaaaa asdf<Esc>`>obbbbb asdf<Esc>V`<k:s/asdf/', {}) 2739 feed('gg0<C-V>lljj<F2>') 2740 screen:expect([[ 2741 aaaaa | 2742 foo | 2743 bar | 2744 baz | 2745 bbbbb | 2746 :'<,'>s/asdf/^ | 2747 ]]) 2748 feed('hjkl') 2749 screen:expect([[ 2750 aaaaa {20:hjkl} | 2751 foo | 2752 bar | 2753 baz | 2754 bbbbb {20:hjkl} | 2755 :'<,'>s/asdf/hjkl^ | 2756 ]]) 2757 feed('<CR>') 2758 expect([[ 2759 aaaaa hjkl 2760 foo 2761 bar 2762 baz 2763 bbbbb hjkl]]) 2764 feed('u') 2765 expect([[ 2766 foo 2767 bar 2768 baz]]) 2769 end) 2770 2771 it(':substitute with inccommand does not unexpectedly change viewport #25697', function() 2772 clear() 2773 local screen = Screen.new(45, 5) 2774 common_setup(screen, 'nosplit', long_multiline_text) 2775 command('vnew | tabnew | tabclose') 2776 screen:expect([[ 2777 ^ │£ m n | 2778 {1:~ }│t œ ¥ | 2779 {1:~ }│ | 2780 {3:[No Name] }{2:[No Name] [+] }| 2781 | 2782 ]]) 2783 feed(':s/') 2784 screen:expect([[ 2785 │£ m n | 2786 {1:~ }│t œ ¥ | 2787 {1:~ }│ | 2788 {3:[No Name] }{2:[No Name] [+] }| 2789 :s/^ | 2790 ]]) 2791 feed('<Esc>') 2792 screen:expect([[ 2793 ^ │£ m n | 2794 {1:~ }│t œ ¥ | 2795 {1:~ }│ | 2796 {3:[No Name] }{2:[No Name] [+] }| 2797 | 2798 ]]) 2799 end) 2800 2801 it('long :%s/ with inccommand does not collapse cmdline', function() 2802 clear() 2803 local screen = Screen.new(10, 5) 2804 common_setup(screen, 'nosplit') 2805 feed( 2806 ':%s/AAAAAAA', 2807 'A', 2808 'A', 2809 'A', 2810 'A', 2811 'A', 2812 'A', 2813 'A', 2814 'A', 2815 'A', 2816 'A', 2817 'A', 2818 'A', 2819 'A', 2820 'A', 2821 'A', 2822 'A', 2823 'A', 2824 'A', 2825 'A', 2826 'A' 2827 ) 2828 screen:expect([[ 2829 | 2830 {3: }| 2831 :%s/AAAAAAAA| 2832 AAAAAAAAAAAA| 2833 AAAAAAA^ | 2834 ]]) 2835 end) 2836 2837 it("with 'inccommand' typing invalid `={expr}` does not show error", function() 2838 clear() 2839 local screen = Screen.new(30, 6) 2840 common_setup(screen, 'nosplit') 2841 feed(':edit `=`') 2842 screen:expect([[ 2843 | 2844 {1:~ }|*4 2845 :edit `=`^ | 2846 ]]) 2847 end) 2848 2849 it("with 'inccommand' typing :filter doesn't segfault or leak memory #19057", function() 2850 clear() 2851 common_setup(nil, 'nosplit') 2852 feed(':filter s') 2853 assert_alive() 2854 feed(' ') 2855 assert_alive() 2856 feed('h') 2857 assert_alive() 2858 feed('i') 2859 assert_alive() 2860 end) 2861 2862 it("'inccommand' cannot be changed during preview #23136", function() 2863 clear() 2864 local screen = Screen.new(30, 6) 2865 common_setup(screen, 'nosplit', 'foo\nbar\nbaz') 2866 source([[ 2867 function! IncCommandToggle() 2868 let prev = &inccommand 2869 2870 if &inccommand ==# 'split' 2871 set inccommand=nosplit 2872 elseif &inccommand ==# 'nosplit' 2873 set inccommand=split 2874 elseif &inccommand ==# '' 2875 set inccommand=nosplit 2876 else 2877 throw 'unknown inccommand' 2878 endif 2879 2880 return " \<BS>" 2881 endfun 2882 2883 cnoremap <expr> <C-E> IncCommandToggle() 2884 ]]) 2885 2886 feed(':%s/foo/bar<C-E><C-E><C-E>') 2887 assert_alive() 2888 end) 2889 2890 it("'inccommand' value can be changed multiple times #27086", function() 2891 clear() 2892 local screen = Screen.new(30, 20) 2893 common_setup(screen, 'split', 'foo1\nfoo2\nfoo3') 2894 for _ = 1, 3 do 2895 feed(':%s/foo/bar') 2896 screen:expect([[ 2897 {20:bar}1 | 2898 {20:bar}2 | 2899 {20:bar}3 | 2900 {1:~ }|*7 2901 {3:[No Name] [+] }| 2902 |1| {20:bar}1 | 2903 |2| {20:bar}2 | 2904 |3| {20:bar}3 | 2905 {1:~ }|*4 2906 {2:[Preview] }| 2907 :%s/foo/bar^ | 2908 ]]) 2909 feed('<Esc>') 2910 command('set inccommand=nosplit') 2911 feed(':%s/foo/bar') 2912 screen:expect([[ 2913 {20:bar}1 | 2914 {20:bar}2 | 2915 {20:bar}3 | 2916 {1:~ }|*16 2917 :%s/foo/bar^ | 2918 ]]) 2919 feed('<Esc>') 2920 command('set inccommand=split') 2921 end 2922 end) 2923 2924 it("'inccommand' disables preview if preview buffer can't be created #27086", function() 2925 clear() 2926 api.nvim_buf_set_name(0, '[Preview]') 2927 local screen = Screen.new(30, 20) 2928 common_setup(screen, 'split', 'foo1\nfoo2\nfoo3') 2929 eq('split', api.nvim_get_option_value('inccommand', {})) 2930 feed(':%s/foo/bar') 2931 screen:expect([[ 2932 {20:bar}1 | 2933 {20:bar}2 | 2934 {20:bar}3 | 2935 {1:~ }|*16 2936 :%s/foo/bar^ | 2937 ]]) 2938 eq('nosplit', api.nvim_get_option_value('inccommand', {})) 2939 end) 2940 2941 it("'inccommand' :substitute preview skips input() prompt #11940", function() 2942 clear() 2943 local screen = Screen.new(30, 3) 2944 common_setup(screen, 'split', 'foo') 2945 feed([[:s/f/\=input("sub: ")]]) 2946 screen:expect([[ 2947 oo | 2948 {1:~ }| 2949 :s/f/\=input("sub: ")^ | 2950 ]]) 2951 end)