put_spec.lua (29023B)
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 insert = n.insert 7 local feed = n.feed 8 local expect = n.expect 9 local eq = t.eq 10 local map = vim.tbl_map 11 local filter = vim.tbl_filter 12 local command = n.command 13 local curbuf_contents = n.curbuf_contents 14 local fn = n.fn 15 local dedent = t.dedent 16 17 local function reset() 18 command('bwipe! | new') 19 insert([[ 20 Line of words 1 21 Line of words 2]]) 22 command('goto 1') 23 feed('itest_string.<esc>u') 24 fn.setreg('a', 'test_stringa', 'V') 25 fn.setreg('b', 'test_stringb\ntest_stringb\ntest_stringb', 'b') 26 fn.setreg('"', 'test_string"', 'v') 27 end 28 29 -- We check the last inserted register ". in each of these tests because it is 30 -- implemented completely differently in do_put(). 31 -- It is implemented differently so that control characters and imap'ped 32 -- characters work in the same manner when pasted as when inserted. 33 describe('put command', function() 34 clear() 35 before_each(reset) 36 37 local function visual_marks_zero() 38 for _, v in pairs(fn.getpos("'<")) do 39 if v ~= 0 then 40 return false 41 end 42 end 43 for _, v in pairs(fn.getpos("'>")) do 44 if v ~= 0 then 45 return false 46 end 47 end 48 return true 49 end 50 51 -- {{{ Where test definitions are run 52 local function run_test_variations(test_variations, extra_setup) 53 reset() 54 if extra_setup then 55 extra_setup() 56 end 57 local init_contents = curbuf_contents() 58 local init_cursorpos = fn.getcurpos() 59 local assert_no_change = function(exception_table, after_undo) 60 expect(init_contents) 61 -- When putting the ". register forwards, undo doesn't move 62 -- the cursor back to where it was before. 63 -- This is because it uses the command character 'a' to 64 -- start the insert, and undo after that leaves the cursor 65 -- one place to the right (unless we were at the end of the 66 -- line when we pasted). 67 if not (exception_table.undo_position and after_undo) then 68 eq(init_cursorpos, fn.getcurpos()) 69 end 70 end 71 72 for _, test in pairs(test_variations) do 73 it(test.description, function() 74 if extra_setup then 75 extra_setup() 76 end 77 local orig_dotstr = fn.getreg('.') 78 t.ok(visual_marks_zero()) 79 -- Make sure every test starts from the same conditions 80 assert_no_change(test.exception_table, false) 81 local was_cli = test.test_action() 82 test.test_assertions(test.exception_table, false) 83 -- Check that undo twice puts us back to the original conditions 84 -- (i.e. puts the cursor and text back to before) 85 feed('u') 86 assert_no_change(test.exception_table, true) 87 88 -- Should not have changed the ". register 89 -- If we paste the ". register with a count we can't avoid 90 -- changing this register, hence avoid this check. 91 if not test.exception_table.dot_reg_changed then 92 eq(orig_dotstr, fn.getreg('.')) 93 end 94 95 -- Doing something, undoing it, and then redoing it should 96 -- leave us in the same state as just doing it once. 97 -- For :ex actions we want '@:', for normal actions we want '.' 98 99 -- The '.' redo doesn't work for visual put so just exit if 100 -- it was tested. 101 -- We check that visual put was used by checking if the '< and 102 -- '> marks were changed. 103 if not visual_marks_zero() then 104 return 105 end 106 107 if test.exception_table.undo_position then 108 fn.setpos('.', init_cursorpos) 109 end 110 if was_cli then 111 feed('@:') 112 else 113 feed('.') 114 end 115 116 test.test_assertions(test.exception_table, true) 117 end) 118 end 119 end -- run_test_variations() 120 -- }}} 121 122 local function create_test_defs( 123 test_defs, 124 command_base, 125 command_creator, -- {{{ 126 expect_base, 127 expect_creator 128 ) 129 local rettab = {} 130 local exceptions 131 for _, v in pairs(test_defs) do 132 if v[4] then 133 exceptions = v[4] 134 else 135 exceptions = {} 136 end 137 table.insert(rettab, { 138 test_action = command_creator(command_base, v[1]), 139 test_assertions = expect_creator(expect_base, v[2]), 140 description = v[3], 141 exception_table = exceptions, 142 }) 143 end 144 return rettab 145 end -- create_test_defs() }}} 146 147 local function find_cursor_position(expect_string) -- {{{ 148 -- There must only be one occurrence of the character 'x' in 149 -- expect_string. 150 -- This function removes that occurrence, and returns the position that 151 -- it was in. 152 -- This returns the cursor position that would leave the 'x' in that 153 -- place if we feed 'ix<esc>' and the string existed before it. 154 for linenum, line in pairs(fn.split(expect_string, '\n', 1)) do 155 local column = line:find('x') 156 if column then 157 return { linenum, column }, expect_string:gsub('x', '') 158 end 159 end 160 end -- find_cursor_position() }}} 161 162 -- Action function creators {{{ 163 local function create_p_action(test_map, substitution) 164 local temp_val = test_map:gsub('p', substitution) 165 return function() 166 feed(temp_val) 167 return false 168 end 169 end 170 171 local function create_put_action(command_base, substitution) 172 local temp_val = command_base:gsub('put', substitution) 173 return function() 174 feed(':' .. temp_val .. '<CR>') 175 return true 176 end 177 end 178 -- }}} 179 180 -- Expect function creator {{{ 181 local function expect_creator(conversion_function, expect_base, conversion_table) 182 local temp_expect_string = conversion_function(expect_base, conversion_table) 183 local cursor_position, expect_string = find_cursor_position(temp_expect_string) 184 return function(exception_table, after_redo) 185 expect(expect_string) 186 187 -- Have to use getcurpos() instead of api.nvim_win_get_cursor(0) in 188 -- order to account for virtualedit. 189 -- We always want the curswant element in getcurpos(), which is 190 -- sometimes different to the column element in 191 -- api.nvim_win_get_cursor(0). 192 -- NOTE: The ".gp command leaves the cursor after the pasted text 193 -- when running, but does not when the command is redone with the 194 -- '.' command. 195 if not (exception_table.redo_position and after_redo) then 196 local actual_position = fn.getcurpos() 197 eq(cursor_position, { actual_position[2], actual_position[5] }) 198 end 199 end 200 end -- expect_creator() }}} 201 202 -- Test definitions {{{ 203 local function copy_def(def) 204 local rettab = { '', {}, '', nil } 205 rettab[1] = def[1] 206 for k, v in pairs(def[2]) do 207 rettab[2][k] = v 208 end 209 rettab[3] = def[3] 210 if def[4] then 211 rettab[4] = {} 212 for k, v in pairs(def[4]) do 213 rettab[4][k] = v 214 end 215 end 216 return rettab 217 end 218 219 local normal_command_defs = { 220 { 221 'p', 222 { cursor_after = false, put_backwards = false, dot_register = false }, 223 'pastes after cursor with p', 224 }, 225 { 226 'gp', 227 { cursor_after = true, put_backwards = false, dot_register = false }, 228 'leaves cursor after text with gp', 229 }, 230 { 231 '".p', 232 { cursor_after = false, put_backwards = false, dot_register = true }, 233 'works with the ". register', 234 }, 235 { 236 '".gp', 237 { cursor_after = true, put_backwards = false, dot_register = true }, 238 'gp works with the ". register', 239 { redo_position = true }, 240 }, 241 { 242 'P', 243 { cursor_after = false, put_backwards = true, dot_register = false }, 244 'pastes before cursor with P', 245 }, 246 { 247 'gP', 248 { cursor_after = true, put_backwards = true, dot_register = false }, 249 'gP pastes before cursor and leaves cursor after text', 250 }, 251 { 252 '".P', 253 { cursor_after = false, put_backwards = true, dot_register = true }, 254 'P works with ". register', 255 }, 256 { 257 '".gP', 258 { cursor_after = true, put_backwards = true, dot_register = true }, 259 'gP works with ". register', 260 { redo_position = true }, 261 }, 262 } 263 264 -- Add a definition applying a count for each definition above. 265 -- Could do this for each transformation (p -> P, p -> gp etc), but I think 266 -- it's neater this way (balance between being explicit and too verbose). 267 for i = 1, #normal_command_defs do 268 local cur = normal_command_defs[i] 269 270 -- Make modified copy of current definition that includes a count. 271 local newdef = copy_def(cur) 272 newdef[2].count = 2 273 cur[2].count = 1 274 newdef[1] = '2' .. newdef[1] 275 newdef[3] = 'double ' .. newdef[3] 276 277 if cur[2].dot_register then 278 if not cur[4] then 279 newdef[4] = {} 280 end 281 newdef[4].dot_reg_changed = true 282 end 283 284 normal_command_defs[#normal_command_defs + 1] = newdef 285 end 286 287 local ex_command_defs = { 288 { 289 'put', 290 { put_backwards = false, dot_register = false }, 291 'pastes linewise forwards with :put', 292 }, 293 { 294 'put!', 295 { put_backwards = true, dot_register = false }, 296 'pastes linewise backwards with :put!', 297 }, 298 { 299 'put .', 300 { put_backwards = false, dot_register = true }, 301 'pastes linewise with the dot register', 302 }, 303 { 304 'put! .', 305 { put_backwards = true, dot_register = true }, 306 'pastes linewise backwards with the dot register', 307 }, 308 } 309 310 local function non_dotdefs(def_table) 311 return filter(function(d) 312 return not d[2].dot_register 313 end, def_table) 314 end 315 316 -- }}} 317 318 -- Conversion functions {{{ 319 local function convert_charwise(expect_base, conversion_table, virtualedit_end, visual_put) 320 expect_base = dedent(expect_base) 321 -- There is no difference between 'P' and 'p' when VIsual_active 322 if not visual_put then 323 if conversion_table.put_backwards then 324 -- Special case for virtualedit at the end of a line. 325 local replace_string 326 if not virtualedit_end then 327 replace_string = 'test_stringx"%1' 328 else 329 replace_string = 'test_stringx"' 330 end 331 expect_base = expect_base:gsub('(.)test_stringx"', replace_string) 332 end 333 end 334 if conversion_table.count > 1 then 335 local rep_string = 'test_string"' 336 local extra_puts = rep_string:rep(conversion_table.count - 1) 337 expect_base = expect_base:gsub('test_stringx"', extra_puts .. 'test_stringx"') 338 end 339 if conversion_table.cursor_after then 340 expect_base = expect_base:gsub('test_stringx"', 'test_string"x') 341 end 342 if conversion_table.dot_register then 343 expect_base = expect_base:gsub('(test_stringx?)"', '%1.') 344 end 345 return expect_base 346 end -- convert_charwise() 347 348 local function make_back(string) 349 local prev_line 350 local rettab = {} 351 local string_found = false 352 for _, line in pairs(fn.split(string, '\n', 1)) do 353 if line:find('test_string') then 354 string_found = true 355 table.insert(rettab, line) 356 else 357 if string_found then 358 if prev_line then 359 table.insert(rettab, prev_line) 360 prev_line = nil 361 end 362 table.insert(rettab, line) 363 else 364 table.insert(rettab, prev_line) 365 prev_line = line 366 end 367 end 368 end 369 -- In case there are no lines after the text that was put. 370 if prev_line and string_found then 371 table.insert(rettab, prev_line) 372 end 373 return table.concat(rettab, '\n') 374 end -- make_back() 375 376 local function convert_linewise(expect_base, conversion_table, _, use_a, indent) 377 expect_base = dedent(expect_base) 378 if conversion_table.put_backwards then 379 expect_base = make_back(expect_base) 380 end 381 local p_str = 'test_string"' 382 if use_a then 383 p_str = 'test_stringa' 384 end 385 386 if conversion_table.dot_register then 387 expect_base = expect_base:gsub('x' .. p_str, 'xtest_string.') 388 p_str = 'test_string.' 389 end 390 391 if conversion_table.cursor_after then 392 expect_base = expect_base:gsub('x' .. p_str .. '\n', p_str .. '\nx') 393 end 394 395 -- The 'indent' argument is only used here because a single put with an 396 -- indent doesn't require special handling. It doesn't require special 397 -- handling because the cursor is never put before the indent, hence 398 -- the modification of 'test_stringx"' gives the same overall answer as 399 -- modifying ' test_stringx"'. 400 401 -- Only happens when using normal mode command actions. 402 if conversion_table.count and conversion_table.count > 1 then 403 if not indent then 404 indent = '' 405 end 406 local rep_string = indent .. p_str .. '\n' 407 local extra_puts = rep_string:rep(conversion_table.count - 1) 408 local orig_string, new_string 409 if conversion_table.cursor_after then 410 orig_string = indent .. p_str .. '\nx' 411 new_string = extra_puts .. orig_string 412 else 413 orig_string = indent .. 'x' .. p_str .. '\n' 414 new_string = orig_string .. extra_puts 415 end 416 expect_base = expect_base:gsub(orig_string, new_string) 417 end 418 return expect_base 419 end 420 421 local function put_x_last(orig_line, p_str) 422 local prev_end, cur_end, cur_start = 0, 0, 0 423 while cur_start do 424 prev_end = cur_end 425 cur_start, cur_end = orig_line:find(p_str, prev_end) 426 end 427 -- Assume (because that is the only way I call it) that p_str matches 428 -- the pattern 'test_string.' 429 return orig_line:sub(1, prev_end - 1) .. 'x' .. orig_line:sub(prev_end) 430 end 431 432 local function convert_blockwise( 433 expect_base, 434 conversion_table, 435 visual, 436 use_b, 437 trailing_whitespace 438 ) 439 expect_base = dedent(expect_base) 440 local p_str = 'test_string"' 441 if use_b then 442 p_str = 'test_stringb' 443 end 444 445 if conversion_table.dot_register then 446 expect_base = expect_base:gsub('(x?)' .. p_str, '%1test_string.') 447 -- Looks strange, but the dot is a special character in the pattern 448 -- and a literal character in the replacement. 449 expect_base = expect_base:gsub('test_stringx.', 'test_stringx.') 450 p_str = 'test_string.' 451 end 452 453 -- No difference between 'p' and 'P' in visual mode. 454 if not visual then 455 if conversion_table.put_backwards then 456 -- One for the line where the cursor is left, one for all other 457 -- lines. 458 expect_base = expect_base:gsub('([^x])' .. p_str, p_str .. '%1') 459 expect_base = expect_base:gsub('([^x])x' .. p_str, 'x' .. p_str .. '%1') 460 if not trailing_whitespace then 461 expect_base = expect_base:gsub(' \n', '\n') 462 expect_base = expect_base:gsub(' $', '') 463 end 464 end 465 end 466 467 if conversion_table.count and conversion_table.count > 1 then 468 local p_pattern = p_str:gsub('%.', '%%.') 469 expect_base = expect_base:gsub(p_pattern, p_str:rep(conversion_table.count)) 470 expect_base = 471 expect_base:gsub('test_stringx([b".])', p_str:rep(conversion_table.count - 1) .. '%0') 472 end 473 474 if conversion_table.cursor_after then 475 if not visual then 476 local prev_line 477 local rettab = {} 478 local prev_in_block = false 479 for _, line in pairs(fn.split(expect_base, '\n', 1)) do 480 if line:find('test_string') then 481 if prev_line then 482 prev_line = prev_line:gsub('x', '') 483 table.insert(rettab, prev_line) 484 end 485 prev_line = line 486 prev_in_block = true 487 else 488 if prev_in_block then 489 prev_line = put_x_last(prev_line, p_str) 490 table.insert(rettab, prev_line) 491 prev_in_block = false 492 end 493 table.insert(rettab, line) 494 end 495 end 496 if prev_line and prev_in_block then 497 table.insert(rettab, put_x_last(prev_line, p_str)) 498 end 499 500 expect_base = table.concat(rettab, '\n') 501 else 502 expect_base = expect_base:gsub('x(.)', '%1x') 503 end 504 end 505 506 return expect_base 507 end 508 -- }}} 509 510 -- Convenience functions {{{ 511 local function run_normal_mode_tests( 512 test_string, 513 base_map, 514 extra_setup, 515 virtualedit_end, 516 selection_string 517 ) 518 local function convert_closure(e, c) 519 return convert_charwise(e, c, virtualedit_end, selection_string) 520 end 521 local function expect_normal_creator(expect_base, conversion_table) 522 local test_expect = expect_creator(convert_closure, expect_base, conversion_table) 523 return function(exception_table, after_redo) 524 test_expect(exception_table, after_redo) 525 if selection_string then 526 if not conversion_table.put_backwards then 527 eq(selection_string, fn.getreg('"')) 528 end 529 else 530 eq('test_string"', fn.getreg('"')) 531 end 532 end 533 end 534 run_test_variations( 535 create_test_defs( 536 normal_command_defs, 537 base_map, 538 create_p_action, 539 test_string, 540 expect_normal_creator 541 ), 542 extra_setup 543 ) 544 end -- run_normal_mode_tests() 545 546 local function convert_linewiseer(expect_base, conversion_table) 547 return expect_creator(convert_linewise, expect_base, conversion_table) 548 end 549 550 local function run_linewise_tests(expect_base, base_command, extra_setup) 551 local linewise_test_defs = create_test_defs( 552 ex_command_defs, 553 base_command, 554 create_put_action, 555 expect_base, 556 convert_linewiseer 557 ) 558 run_test_variations(linewise_test_defs, extra_setup) 559 end -- run_linewise_tests() 560 -- }}} 561 562 -- Actual tests 563 describe('default pasting', function() 564 local expect_string = [[ 565 Ltest_stringx"ine of words 1 566 Line of words 2]] 567 run_normal_mode_tests(expect_string, 'p') 568 569 run_linewise_tests( 570 [[ 571 Line of words 1 572 xtest_string" 573 Line of words 2]], 574 'put' 575 ) 576 end) 577 578 describe('linewise register', function() 579 -- put with 'p' 580 local local_ex_command_defs = non_dotdefs(normal_command_defs) 581 local base_expect_string = [[ 582 Line of words 1 583 xtest_stringa 584 Line of words 2]] 585 local function local_convert_linewise(expect_base, conversion_table) 586 return convert_linewise(expect_base, conversion_table, nil, true) 587 end 588 local function expect_lineput(expect_base, conversion_table) 589 return expect_creator(local_convert_linewise, expect_base, conversion_table) 590 end 591 run_test_variations( 592 create_test_defs( 593 local_ex_command_defs, 594 '"ap', 595 create_p_action, 596 base_expect_string, 597 expect_lineput 598 ) 599 ) 600 601 -- put with :put 602 local linewise_put_defs = non_dotdefs(ex_command_defs) 603 base_expect_string = [[ 604 Line of words 1 605 xtest_stringa 606 Line of words 2]] 607 run_test_variations( 608 create_test_defs( 609 linewise_put_defs, 610 'put a', 611 create_put_action, 612 base_expect_string, 613 convert_linewiseer 614 ) 615 ) 616 end) 617 618 describe('blockwise register', function() 619 local blockwise_put_defs = non_dotdefs(normal_command_defs) 620 local test_base = [[ 621 Lxtest_stringbine of words 1 622 Ltest_stringbine of words 2 623 test_stringb]] 624 625 local function expect_block_creator(expect_base, conversion_table) 626 return expect_creator(function(e, c) 627 return convert_blockwise(e, c, nil, true) 628 end, expect_base, conversion_table) 629 end 630 631 run_test_variations( 632 create_test_defs(blockwise_put_defs, '"bp', create_p_action, test_base, expect_block_creator) 633 ) 634 end) 635 636 it('adds correct indentation when put with [p and ]p', function() 637 feed('G>>"a]pix<esc>') 638 -- luacheck: ignore 639 expect([[ 640 Line of words 1 641 Line of words 2 642 xtest_stringa]]) 643 feed('uu"a[pix<esc>') 644 -- luacheck: ignore 645 expect([[ 646 Line of words 1 647 xtest_stringa 648 Line of words 2]]) 649 end) 650 651 describe('linewise paste with autoindent', function() 652 -- luacheck: ignore 653 run_linewise_tests( 654 [[ 655 Line of words 1 656 Line of words 2 657 xtest_string"]], 658 'put', 659 function() 660 fn.setline('$', ' Line of words 2') 661 -- Set curswant to '8' to be at the end of the tab character 662 -- This is where the cursor is put back after the 'u' command. 663 fn.setpos('.', { 0, 2, 1, 0, 8 }) 664 command('set autoindent') 665 end 666 ) 667 end) 668 669 describe('put inside tabs with virtualedit', function() 670 local test_string = [[ 671 Line of words 1 672 test_stringx" Line of words 2]] 673 run_normal_mode_tests(test_string, 'p', function() 674 fn.setline('$', ' Line of words 2') 675 command('setlocal virtualedit=all') 676 fn.setpos('.', { 0, 2, 1, 2, 3 }) 677 end) 678 end) 679 680 describe('put after the line with virtualedit', function() 681 -- luacheck: ignore 621 682 local test_string = [[ 683 Line of words 1 test_stringx" 684 Line of words 2]] 685 run_normal_mode_tests(test_string, 'p', function() 686 fn.setline('$', ' Line of words 2') 687 command('setlocal virtualedit=all') 688 fn.setpos('.', { 0, 1, 16, 1, 17 }) 689 end, true) 690 end) 691 692 describe('Visual put', function() 693 describe('basic put', function() 694 local test_string = [[ 695 test_stringx" words 1 696 Line of words 2]] 697 run_normal_mode_tests(test_string, 'v2ep', nil, nil, 'Line of') 698 end) 699 describe('over trailing newline', function() 700 local test_string = 'Line of test_stringx"Line of words 2' 701 run_normal_mode_tests(test_string, 'v$p', function() 702 fn.setpos('.', { 0, 1, 9, 0, 9 }) 703 end, nil, 'words 1\n') 704 end) 705 describe('linewise mode', function() 706 local test_string = [[ 707 xtest_string" 708 Line of words 2]] 709 local function expect_vis_linewise(expect_base, conversion_table) 710 return expect_creator(function(e, c) 711 return convert_linewise(e, c, nil, nil) 712 end, expect_base, conversion_table) 713 end 714 run_test_variations( 715 create_test_defs( 716 normal_command_defs, 717 'Vp', 718 create_p_action, 719 test_string, 720 expect_vis_linewise 721 ), 722 function() 723 fn.setpos('.', { 0, 1, 1, 0, 1 }) 724 end 725 ) 726 727 describe('with whitespace at bol', function() 728 local function expect_vis_lineindented(expect_base, conversion_table) 729 local test_expect = expect_creator(function(e, c) 730 return convert_linewise(e, c, nil, nil, ' ') 731 end, expect_base, conversion_table) 732 return function(exception_table, after_redo) 733 test_expect(exception_table, after_redo) 734 if not conversion_table.put_backwards then 735 eq('Line of words 1\n', fn.getreg('"')) 736 end 737 end 738 end 739 local base_expect_string = [[ 740 xtest_string" 741 Line of words 2]] 742 run_test_variations( 743 create_test_defs( 744 normal_command_defs, 745 'Vp', 746 create_p_action, 747 base_expect_string, 748 expect_vis_lineindented 749 ), 750 function() 751 feed('i test_string.<esc>u') 752 fn.setreg('"', ' test_string"', 'v') 753 end 754 ) 755 end) 756 end) 757 758 describe('blockwise visual mode', function() 759 local test_base = [[ 760 test_stringx"e of words 1 761 test_string"e of words 2]] 762 763 local function expect_block_creator(expect_base, conversion_table) 764 local test_expect = expect_creator(function(e, c) 765 return convert_blockwise(e, c, true) 766 end, expect_base, conversion_table) 767 return function(e, c) 768 test_expect(e, c) 769 if not conversion_table.put_backwards then 770 eq('Lin\nLin', fn.getreg('"')) 771 end 772 end 773 end 774 775 local select_down_test_defs = create_test_defs( 776 normal_command_defs, 777 '<C-v>jllp', 778 create_p_action, 779 test_base, 780 expect_block_creator 781 ) 782 run_test_variations(select_down_test_defs) 783 784 -- Undo and redo of a visual block put leave the cursor in the top 785 -- left of the visual block area no matter where the cursor was 786 -- when it started. 787 local undo_redo_no = map(function(table) 788 local rettab = copy_def(table) 789 if not rettab[4] then 790 rettab[4] = {} 791 end 792 rettab[4].undo_position = true 793 rettab[4].redo_position = true 794 return rettab 795 end, normal_command_defs) 796 797 -- Selection direction doesn't matter 798 run_test_variations( 799 create_test_defs( 800 undo_redo_no, 801 '<C-v>kllp', 802 create_p_action, 803 test_base, 804 expect_block_creator 805 ), 806 function() 807 fn.setpos('.', { 0, 2, 1, 0, 1 }) 808 end 809 ) 810 811 describe('blockwise cursor after undo', function() 812 -- A bit of a hack of the reset above. 813 -- In the tests that selection direction doesn't matter, we 814 -- don't check the undo/redo position because it doesn't fit 815 -- the same pattern as everything else. 816 -- Here we fix this by directly checking the undo/redo position 817 -- in the test_assertions of our test definitions. 818 local function assertion_creator(_, _) 819 return function(_, _) 820 feed('u') 821 -- Have to use feed('u') here to set curswant, because 822 -- ex_undo() doesn't do that. 823 eq({ 0, 1, 1, 0, 1 }, fn.getcurpos()) 824 feed('<C-r>') 825 eq({ 0, 1, 1, 0, 1 }, fn.getcurpos()) 826 end 827 end 828 829 run_test_variations( 830 create_test_defs(undo_redo_no, '<C-v>kllp', create_p_action, test_base, assertion_creator), 831 function() 832 fn.setpos('.', { 0, 2, 1, 0, 1 }) 833 end 834 ) 835 end) 836 end) 837 838 describe("with 'virtualedit'", function() 839 describe('splitting a tab character', function() 840 local base_expect_string = [[ 841 Line of words 1 842 test_stringx" Line of words 2]] 843 run_normal_mode_tests(base_expect_string, 'vp', function() 844 fn.setline('$', ' Line of words 2') 845 command('setlocal virtualedit=all') 846 fn.setpos('.', { 0, 2, 1, 2, 3 }) 847 end, nil, ' ') 848 end) 849 describe('after end of line', function() 850 local base_expect_string = [[ 851 Line of words 1 test_stringx" 852 Line of words 2]] 853 run_normal_mode_tests(base_expect_string, 'vp', function() 854 command('setlocal virtualedit=all') 855 fn.setpos('.', { 0, 1, 16, 2, 18 }) 856 end, true, ' ') 857 end) 858 end) 859 end) 860 861 describe('. register special tests', function() 862 -- luacheck: ignore 621 863 before_each(reset) 864 it('applies control character actions', function() 865 feed('i<C-t><esc>u') 866 expect([[ 867 Line of words 1 868 Line of words 2]]) 869 feed('".p') 870 expect([[ 871 Line of words 1 872 Line of words 2]]) 873 feed('u1go<C-v>j".p') 874 eq( 875 [[ 876 ine of words 1 877 ine of words 2]], 878 curbuf_contents() 879 ) 880 end) 881 882 local screen 883 setup(function() 884 screen = Screen.new() 885 end) 886 887 local function bell_test(actions, should_ring) 888 if should_ring then 889 -- check bell is not set by nvim before the action 890 screen:sleep(50) 891 end 892 t.ok(not screen.bell and not screen.visualbell) 893 actions() 894 screen:expect { 895 condition = function() 896 if should_ring then 897 if not screen.bell and not screen.visualbell then 898 error('Bell was not rung after action') 899 end 900 else 901 if screen.bell or screen.visualbell then 902 error('Bell was rung after action') 903 end 904 end 905 end, 906 unchanged = not should_ring, 907 } 908 screen.bell = false 909 screen.visualbell = false 910 end 911 912 it('should not ring the bell with gp at end of line', function() 913 bell_test(function() 914 feed('$".gp') 915 end) 916 917 -- Even if the last character is a multibyte character. 918 reset() 919 fn.setline(1, 'helloม') 920 bell_test(function() 921 feed('$".gp') 922 end) 923 end) 924 925 it('should not ring the bell with gp and end of file', function() 926 fn.setpos('.', { 0, 2, 1, 0 }) 927 bell_test(function() 928 feed('$vl".gp') 929 end) 930 end) 931 932 it('should ring the bell when deleting if not appropriate', function() 933 t.skip(t.is_os('bsd'), 'crashes on freebsd') 934 935 command('goto 2') 936 feed('i<bs><esc>') 937 expect([[ 938 ine of words 1 939 Line of words 2]]) 940 bell_test(function() 941 feed('".P') 942 end, true) 943 end) 944 945 it('should restore cursor position after undo of ".p', function() 946 local origpos = fn.getcurpos() 947 feed('".pu') 948 eq(origpos, fn.getcurpos()) 949 end) 950 951 it("should be unaffected by 'autoindent' with V\".2p", function() 952 command('set autoindent') 953 feed('i test_string.<esc>u') 954 feed('V".2p') 955 expect([[ 956 test_string. 957 test_string. 958 Line of words 2]]) 959 end) 960 end) 961 end)