cursor_spec.lua (57972B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local tt = require('test.functional.testterm') 4 5 local feed, clear = n.feed, n.clear 6 local testprg, command = n.testprg, n.command 7 local eq, eval = t.eq, n.eval 8 local api = n.api 9 local exec_lua = n.exec_lua 10 local matches = t.matches 11 local call = n.call 12 local hide_cursor = tt.hide_cursor 13 local show_cursor = tt.show_cursor 14 local retry = t.retry 15 local is_os = t.is_os 16 local skip = t.skip 17 18 describe(':terminal cursor', function() 19 local screen 20 21 local terminal_mode_idx ---@type number 22 23 before_each(function() 24 clear() 25 screen = tt.setup_screen() 26 27 if terminal_mode_idx == nil then 28 for i, v in ipairs(screen._mode_info) do 29 if v.name == 'terminal' then 30 terminal_mode_idx = i 31 end 32 end 33 assert(terminal_mode_idx) 34 end 35 end) 36 37 it('moves the screen cursor when focused', function() 38 tt.feed_data('testing cursor') 39 screen:expect([[ 40 tty ready | 41 testing cursor^ | 42 |*4 43 {5:-- TERMINAL --} | 44 ]]) 45 end) 46 47 it('is highlighted when not focused', function() 48 feed('<c-\\><c-n>') 49 screen:expect([[ 50 tty ready | 51 ^ | 52 |*5 53 ]]) 54 end) 55 56 describe('with number column', function() 57 before_each(function() 58 feed('<c-\\><c-n>:set number<cr>') 59 end) 60 61 it('is positioned correctly when unfocused', function() 62 screen:expect([[ 63 {121: 1 }tty ready | 64 {121: 2 }^rows: 6, cols: 46 | 65 {121: 3 } | 66 {121: 4 } | 67 {121: 5 } | 68 {121: 6 } | 69 :set number | 70 ]]) 71 end) 72 73 it('is positioned correctly when focused', function() 74 screen:expect([[ 75 {121: 1 }tty ready | 76 {121: 2 }^rows: 6, cols: 46 | 77 {121: 3 } | 78 {121: 4 } | 79 {121: 5 } | 80 {121: 6 } | 81 :set number | 82 ]]) 83 feed('i') 84 n.poke_eventloop() 85 screen:expect([[ 86 {121: 1 }tty ready | 87 {121: 2 }rows: 6, cols: 46 | 88 {121: 3 }^ | 89 {121: 4 } | 90 {121: 5 } | 91 {121: 6 } | 92 {5:-- TERMINAL --} | 93 ]]) 94 end) 95 end) 96 97 describe('when invisible', function() 98 it('is not highlighted', function() 99 hide_cursor() 100 screen:expect([[ 101 tty ready | 102 |*5 103 {5:-- TERMINAL --} | 104 ]]) 105 show_cursor() 106 screen:expect([[ 107 tty ready | 108 ^ | 109 |*4 110 {5:-- TERMINAL --} | 111 ]]) 112 -- same for when the terminal is unfocused 113 feed('<c-\\><c-n>') 114 hide_cursor() 115 screen:expect([[ 116 tty ready | 117 ^ | 118 |*5 119 ]]) 120 show_cursor() 121 screen:expect_unchanged() 122 end) 123 124 it('becomes visible when exiting Terminal mode', function() 125 hide_cursor() 126 screen:expect([[ 127 tty ready | 128 |*5 129 {5:-- TERMINAL --} | 130 ]]) 131 feed('<c-\\><c-n>') 132 screen:expect([[ 133 tty ready | 134 ^ | 135 |*5 136 ]]) 137 feed('i') 138 screen:expect([[ 139 tty ready | 140 |*5 141 {5:-- TERMINAL --} | 142 ]]) 143 144 -- Cursor is hidden; now request to show it while in a TermLeave autocmd. 145 -- Process events (via :sleep) to handle the escape sequence now. 146 n.exec([[ 147 autocmd TermLeave * ++once call chansend(&channel, "\e[?25h") | sleep 1m 148 \ | let g:did_termleave = 1]]) 149 feed([[<C-\><C-N>]]) -- Exit terminal mode; cursor should not remain hidden 150 screen:expect([[ 151 tty ready | 152 ^ | 153 |*5 154 ]]) 155 -- Wait for the TermLeave autocommand to finish. Sometimes :sleep can be slow. 156 retry(nil, 1000, function() 157 eq(1, api.nvim_get_var('did_termleave')) 158 end) 159 160 command('bwipeout! | let chan = nvim_open_term(0, {})') 161 feed('i') 162 -- Hide the cursor, switch to a non-terminal buffer, then show the cursor; it shouldn't remain 163 -- hidden after we're kicked out of terminal mode in the new buffer. 164 -- Must ensure these actions happen within the same terminal_execute call. The stream is 165 -- internal, so polling the event loop isn't necessary (terminal_receive is directly called). 166 command([[call chansend(chan, "\e[?25l") | new floob | call chansend(chan, "\e[?25h")]]) 167 screen:expect([[ 168 ^ | 169 {100:~ }| 170 {3:floob }| 171 |*2 172 {119:[Scratch] [-] }| 173 | 174 ]]) 175 176 feed('<C-W>pi') 177 screen:expect([[ 178 | 179 {100:~ }| 180 {2:floob }| 181 ^ | 182 | 183 {120:[Scratch] [-] }| 184 {5:-- TERMINAL --} | 185 ]]) 186 end) 187 188 it('becomes visible on TermLeave if hidden immediately by events #32456', function() 189 -- Reproducing the issue is quite fragile; it's easiest done in a lone test case like this 190 -- with no prior commands. 191 feed([[<C-\><C-N>]]) 192 screen:expect([[ 193 tty ready | 194 ^ | 195 |*5 196 ]]) 197 198 -- Hide the cursor such that the escape sequence is processed as a side effect of showmode in 199 -- terminal_enter handling events (skip_showmode -> char_avail -> vpeekc -> os_breakcheck). 200 -- This requires a particular set of actions; :startinsert repros better than feed('i') here. 201 hide_cursor() 202 command('mode | startinsert') 203 screen:expect([[ 204 tty ready | 205 |*5 206 {5:-- TERMINAL --} | 207 ]]) 208 209 feed([[<C-\><C-N>]]) 210 screen:expect([[ 211 tty ready | 212 ^ | 213 |*5 214 ]]) 215 end) 216 end) 217 218 it('can be modified by application #3681 #31685', function() 219 local states = { 220 [1] = { blink = true, shape = 'block' }, 221 [2] = { blink = false, shape = 'block' }, 222 [3] = { blink = true, shape = 'horizontal' }, 223 [4] = { blink = false, shape = 'horizontal' }, 224 [5] = { blink = true, shape = 'vertical' }, 225 [6] = { blink = false, shape = 'vertical' }, 226 } 227 228 for k, v in pairs(states) do 229 tt.feed_csi(('%d q'):format(k)) 230 screen:expect({ 231 grid = [[ 232 tty ready | 233 ^ | 234 |*4 235 {5:-- TERMINAL --} | 236 ]], 237 condition = function() 238 if v.blink then 239 eq(500, screen._mode_info[terminal_mode_idx].blinkon) 240 eq(500, screen._mode_info[terminal_mode_idx].blinkoff) 241 else 242 eq(0, screen._mode_info[terminal_mode_idx].blinkon) 243 eq(0, screen._mode_info[terminal_mode_idx].blinkoff) 244 end 245 246 eq(v.shape, screen._mode_info[terminal_mode_idx].cursor_shape) 247 248 -- Cell percentages are hard coded for each shape in terminal.c 249 if v.shape == 'horizontal' then 250 eq(20, screen._mode_info[terminal_mode_idx].cell_percentage) 251 elseif v.shape == 'vertical' then 252 eq(25, screen._mode_info[terminal_mode_idx].cell_percentage) 253 end 254 end, 255 }) 256 end 257 258 feed([[<C-\><C-N>]]) 259 260 screen:expect([[ 261 tty ready | 262 ^ | 263 |*5 264 ]]) 265 266 -- Cursor returns to default on TermLeave 267 eq(500, screen._mode_info[terminal_mode_idx].blinkon) 268 eq(500, screen._mode_info[terminal_mode_idx].blinkoff) 269 eq('block', screen._mode_info[terminal_mode_idx].cursor_shape) 270 end) 271 272 it('can be modified per terminal', function() 273 -- Set cursor to vertical bar with blink 274 tt.feed_csi('5 q') 275 screen:expect({ 276 grid = [[ 277 tty ready | 278 ^ | 279 |*4 280 {5:-- TERMINAL --} | 281 ]], 282 condition = function() 283 eq(500, screen._mode_info[terminal_mode_idx].blinkon) 284 eq(500, screen._mode_info[terminal_mode_idx].blinkoff) 285 eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape) 286 end, 287 }) 288 289 tt.hide_cursor() 290 screen:expect({ 291 grid = [[ 292 tty ready | 293 |*5 294 {5:-- TERMINAL --} | 295 ]], 296 condition = function() 297 eq(500, screen._mode_info[terminal_mode_idx].blinkon) 298 eq(500, screen._mode_info[terminal_mode_idx].blinkoff) 299 eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape) 300 end, 301 }) 302 303 -- Exit terminal mode to reset terminal cursor settings to default and 304 -- create a new terminal window 305 feed([[<C-\><C-N>]]) 306 command('set statusline=~~~') 307 command('new') 308 call('jobstart', { testprg('tty-test') }, { term = true }) 309 feed('i') 310 screen:expect({ 311 grid = [[ 312 tty ready | 313 ^ | 314 {120:~~~ }| 315 rows: 2, cols: 50 | 316 | 317 {119:~~~ }| 318 {5:-- TERMINAL --} | 319 ]], 320 condition = function() 321 -- New terminal, cursor resets to defaults 322 eq(500, screen._mode_info[terminal_mode_idx].blinkon) 323 eq(500, screen._mode_info[terminal_mode_idx].blinkoff) 324 eq('block', screen._mode_info[terminal_mode_idx].cursor_shape) 325 end, 326 }) 327 328 -- Set cursor to underline, no blink 329 tt.feed_csi('4 q') 330 screen:expect({ 331 grid = [[ 332 tty ready | 333 ^ | 334 {120:~~~ }| 335 rows: 2, cols: 50 | 336 | 337 {119:~~~ }| 338 {5:-- TERMINAL --} | 339 ]], 340 condition = function() 341 eq(0, screen._mode_info[terminal_mode_idx].blinkon) 342 eq(0, screen._mode_info[terminal_mode_idx].blinkoff) 343 eq('horizontal', screen._mode_info[terminal_mode_idx].cursor_shape) 344 end, 345 }) 346 347 -- Switch back to first terminal, cursor should still be hidden 348 command('wincmd p') 349 screen:expect({ 350 grid = [[ 351 tty ready | 352 | 353 {119:~~~ }| 354 rows: 2, cols: 50 | 355 | 356 {120:~~~ }| 357 {5:-- TERMINAL --} | 358 ]], 359 condition = function() 360 eq(500, screen._mode_info[terminal_mode_idx].blinkon) 361 eq(500, screen._mode_info[terminal_mode_idx].blinkoff) 362 eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape) 363 end, 364 }) 365 end) 366 367 it('can be positioned arbitrarily', function() 368 clear() 369 screen = tt.setup_child_nvim({ 370 '-u', 371 'NONE', 372 '-i', 373 'NONE', 374 '--cmd', 375 n.nvim_set .. ' noshowmode', 376 }) 377 screen:expect([[ 378 ^ | 379 ~ |*4 380 | 381 {5:-- TERMINAL --} | 382 ]]) 383 384 feed('i<Tab>') 385 screen:expect([[ 386 ^ | 387 ~ |*4 388 | 389 {5:-- TERMINAL --} | 390 ]]) 391 end) 392 393 it('preserves guicursor value on TermLeave #31612', function() 394 eq(3, screen._mode_info[terminal_mode_idx].hl_id) 395 396 -- Change 'guicursor' while terminal mode is active 397 command('set guicursor+=t:Error') 398 399 local error_hl_id = call('hlID', 'Error') 400 401 screen:expect({ 402 condition = function() 403 eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id) 404 end, 405 }) 406 407 -- Exit terminal mode 408 feed([[<C-\><C-N>]]) 409 410 screen:expect([[ 411 tty ready | 412 ^ | 413 |*5 414 ]]) 415 416 eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id) 417 end) 418 419 it('uses the correct attributes', function() 420 feed([[<C-\><C-N>]]) 421 command([[ 422 bwipeout! 423 let chan1 = nvim_open_term(0, {}) 424 vnew 425 let chan2 = nvim_open_term(0, {}) 426 ]]) 427 feed('i') 428 screen:expect([[ 429 ^ │ | 430 │ |*4 431 {120:[Scratch] [-] }{119:[Scratch] [-] }| 432 {5:-- TERMINAL --} | 433 ]]) 434 eq('block', screen._mode_info[terminal_mode_idx].cursor_shape) 435 eq(500, screen._mode_info[terminal_mode_idx].blinkon) 436 eq(500, screen._mode_info[terminal_mode_idx].blinkoff) 437 438 -- Modify cursor in the non-current terminal; should not affect this cursor. 439 command([[call chansend(chan1, "\e[4 q")]]) 440 screen:expect_unchanged() 441 eq('block', screen._mode_info[terminal_mode_idx].cursor_shape) 442 eq(500, screen._mode_info[terminal_mode_idx].blinkon) 443 eq(500, screen._mode_info[terminal_mode_idx].blinkoff) 444 445 -- Modify cursor in the current terminal. 446 command([[call chansend(chan2, "\e[6 q")]]) 447 screen:expect_unchanged() 448 eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape) 449 eq(0, screen._mode_info[terminal_mode_idx].blinkon) 450 eq(0, screen._mode_info[terminal_mode_idx].blinkoff) 451 452 -- Check the cursor in the other terminal reflects our changes from before. 453 command('wincmd p') 454 screen:expect([[ 455 │^ | 456 │ |*4 457 {119:[Scratch] [-] }{120:[Scratch] [-] }| 458 {5:-- TERMINAL --} | 459 ]]) 460 eq('horizontal', screen._mode_info[terminal_mode_idx].cursor_shape) 461 eq(0, screen._mode_info[terminal_mode_idx].blinkon) 462 eq(0, screen._mode_info[terminal_mode_idx].blinkoff) 463 end) 464 465 it('position correct within events', function() 466 local term, term_unfocused = exec_lua(function() 467 vim.cmd 'bwipeout!' 468 local term_unfocused = vim.api.nvim_open_term(0, {}) 469 vim.cmd.vnew() 470 vim.cmd.wincmd '|' 471 local term = vim.api.nvim_open_term(0, {}) 472 -- We'll use this keymap to pause the main loop while we send events, as we want the test to 473 -- run within the same terminal_execute call (while using test suite facilities like retry). 474 vim.keymap.set('t', '<F1>', '<Cmd>let g:sleepy = 1 | sleep 5000 | let g:sleepy = 0<CR>') 475 return term, term_unfocused 476 end) 477 feed('i<F1>') 478 479 local function check_pos(expected_pos, expected_virtcol, chan, data) 480 api.nvim_chan_send(chan, data) -- Using nvim_chan_send so terminal_receive is immediate. 481 482 -- Results won't be visible until refresh_terminal is called, which happens on a timer. 483 retry(nil, nil, function() 484 eq(expected_pos, eval("getpos('.')[1:]")) 485 end) 486 eq(expected_virtcol, eval("virtcol('.', 1)")) 487 eq(1, eval('g:sleepy')) -- :sleep shouldn't have timed out. 488 end 489 490 check_pos({ 1, 4, 0 }, { 4, 4 }, term, 'foo') 491 -- double-width char at end (3 bytes) 492 check_pos({ 2, 13, 0 }, { 12, 12 }, term, '\r\nbarbaaaar哦') 493 -- Move to 1,12 (beyond eol; sets coladd) 494 check_pos({ 1, 4, 8 }, { 12, 12 }, term, '\27[1;12H') 495 -- Move to 4,1 496 check_pos({ 4, 1, 0 }, { 1, 1 }, term, '\27[4;1H') 497 -- Move to 4,5 (beyond eol; sets coladd) 498 check_pos({ 4, 1, 4 }, { 5, 5 }, term, '\27[4;5H') 499 -- Move to 2,10 (head of wide char) 500 check_pos({ 2, 10, 0 }, { 10, 11 }, term, '\27[2;10H') 501 -- Move to 2,11 (non-head of wide char) 502 check_pos({ 2, 10, 0 }, { 10, 11 }, term, '\27[2;11H') 503 -- Move to 2,12 (after wide char) 504 check_pos({ 2, 13, 0 }, { 12, 12 }, term, '\27[2;12H') 505 -- Move to 2,13 (beyond eol; sets coladd) 506 check_pos({ 2, 13, 1 }, { 13, 13 }, term, '\27[2;13H') 507 -- Cursor movement in unfocused terminal shouldn't affect us 508 check_pos({ 2, 13, 1 }, { 13, 13 }, term_unfocused, 'amogus') 509 end) 510 end) 511 512 describe('buffer cursor position is correct in terminal without number column', function() 513 local screen 514 515 local function setup_ex_register(str) 516 screen = tt.setup_child_nvim({ 517 '-u', 518 'NONE', 519 '-i', 520 'NONE', 521 '-E', 522 '--cmd', 523 string.format('let @r = "%s"', str), 524 -- <Left> and <Right> don't always work 525 '--cmd', 526 'cnoremap <C-X> <Left>', 527 '--cmd', 528 'cnoremap <C-O> <Right>', 529 '--cmd', 530 'set notermguicolors', 531 }, { 532 cols = 70, 533 }) 534 screen:expect([[ 535 |*3 536 {2: }| 537 Entering Ex mode. Type "visual" to go to Normal mode. | 538 :^ | 539 {5:-- TERMINAL --} | 540 ]]) 541 end 542 543 before_each(function() 544 clear() 545 command('autocmd! nvim.terminal TermOpen') 546 end) 547 548 describe('in a line with no multibyte chars or trailing spaces,', function() 549 before_each(function() 550 setup_ex_register('aaaaaaaa') 551 end) 552 553 it('at the end', function() 554 feed('<C-R>r') 555 screen:expect([[ 556 |*3 557 {2: }| 558 Entering Ex mode. Type "visual" to go to Normal mode. | 559 :aaaaaaaa^ | 560 {5:-- TERMINAL --} | 561 ]]) 562 eq({ 6, 9 }, eval('nvim_win_get_cursor(0)')) 563 feed([[<C-\><C-N>]]) 564 screen:expect([[ 565 |*3 566 {2: }| 567 Entering Ex mode. Type "visual" to go to Normal mode. | 568 :aaaaaaa^a | 569 | 570 ]]) 571 eq({ 6, 8 }, eval('nvim_win_get_cursor(0)')) 572 end) 573 574 it('near the end', function() 575 feed('<C-R>r<C-X><C-X>') 576 screen:expect([[ 577 |*3 578 {2: }| 579 Entering Ex mode. Type "visual" to go to Normal mode. | 580 :aaaaaa^aa | 581 {5:-- TERMINAL --} | 582 ]]) 583 eq({ 6, 7 }, eval('nvim_win_get_cursor(0)')) 584 feed([[<C-\><C-N>]]) 585 screen:expect([[ 586 |*3 587 {2: }| 588 Entering Ex mode. Type "visual" to go to Normal mode. | 589 :aaaaa^aaa | 590 | 591 ]]) 592 eq({ 6, 6 }, eval('nvim_win_get_cursor(0)')) 593 end) 594 595 it('near the start', function() 596 feed('<C-R>r<C-B><C-O>') 597 screen:expect([[ 598 |*3 599 {2: }| 600 Entering Ex mode. Type "visual" to go to Normal mode. | 601 :a^aaaaaaa | 602 {5:-- TERMINAL --} | 603 ]]) 604 eq({ 6, 2 }, eval('nvim_win_get_cursor(0)')) 605 feed([[<C-\><C-N>]]) 606 screen:expect([[ 607 |*3 608 {2: }| 609 Entering Ex mode. Type "visual" to go to Normal mode. | 610 :^aaaaaaaa | 611 | 612 ]]) 613 eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) 614 end) 615 end) 616 617 describe('in a line with single-cell multibyte chars and no trailing spaces,', function() 618 before_each(function() 619 setup_ex_register('µµµµµµµµ') 620 end) 621 622 it('at the end', function() 623 feed('<C-R>r') 624 screen:expect([[ 625 |*3 626 {2: }| 627 Entering Ex mode. Type "visual" to go to Normal mode. | 628 :µµµµµµµµ^ | 629 {5:-- TERMINAL --} | 630 ]]) 631 eq({ 6, 17 }, eval('nvim_win_get_cursor(0)')) 632 feed([[<C-\><C-N>]]) 633 screen:expect([[ 634 |*3 635 {2: }| 636 Entering Ex mode. Type "visual" to go to Normal mode. | 637 :µµµµµµµ^µ | 638 | 639 ]]) 640 eq({ 6, 15 }, eval('nvim_win_get_cursor(0)')) 641 end) 642 643 it('near the end', function() 644 feed('<C-R>r<C-X><C-X>') 645 screen:expect([[ 646 |*3 647 {2: }| 648 Entering Ex mode. Type "visual" to go to Normal mode. | 649 :µµµµµµ^µµ | 650 {5:-- TERMINAL --} | 651 ]]) 652 eq({ 6, 13 }, eval('nvim_win_get_cursor(0)')) 653 feed([[<C-\><C-N>]]) 654 screen:expect([[ 655 |*3 656 {2: }| 657 Entering Ex mode. Type "visual" to go to Normal mode. | 658 :µµµµµ^µµµ | 659 | 660 ]]) 661 eq({ 6, 11 }, eval('nvim_win_get_cursor(0)')) 662 end) 663 664 it('near the start', function() 665 feed('<C-R>r<C-B><C-O>') 666 screen:expect([[ 667 |*3 668 {2: }| 669 Entering Ex mode. Type "visual" to go to Normal mode. | 670 :µ^µµµµµµµ | 671 {5:-- TERMINAL --} | 672 ]]) 673 eq({ 6, 3 }, eval('nvim_win_get_cursor(0)')) 674 feed([[<C-\><C-N>]]) 675 screen:expect([[ 676 |*3 677 {2: }| 678 Entering Ex mode. Type "visual" to go to Normal mode. | 679 :^µµµµµµµµ | 680 | 681 ]]) 682 eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) 683 end) 684 end) 685 686 describe('in a line with single-cell composed multibyte chars and no trailing spaces,', function() 687 before_each(function() 688 setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') 689 end) 690 691 it('at the end', function() 692 feed('<C-R>r') 693 screen:expect([[ 694 |*3 695 {2: }| 696 Entering Ex mode. Type "visual" to go to Normal mode. | 697 :µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳^ | 698 {5:-- TERMINAL --} | 699 ]]) 700 eq({ 6, 33 }, eval('nvim_win_get_cursor(0)')) 701 feed([[<C-\><C-N>]]) 702 screen:expect([[ 703 |*3 704 {2: }| 705 Entering Ex mode. Type "visual" to go to Normal mode. | 706 :µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳ | 707 | 708 ]]) 709 eq({ 6, 29 }, eval('nvim_win_get_cursor(0)')) 710 end) 711 712 it('near the end', function() 713 skip(is_os('win')) 714 feed('<C-R>r<C-X><C-X>') 715 screen:expect([[ 716 |*3 717 {2: }| 718 Entering Ex mode. Type "visual" to go to Normal mode. | 719 :µ̳µ̳µ̳µ̳µ̳µ̳^µ̳µ̳ | 720 {5:-- TERMINAL --} | 721 ]]) 722 eq({ 6, 25 }, eval('nvim_win_get_cursor(0)')) 723 feed([[<C-\><C-N>]]) 724 screen:expect([[ 725 |*3 726 {2: }| 727 Entering Ex mode. Type "visual" to go to Normal mode. | 728 :µ̳µ̳µ̳µ̳µ̳^µ̳µ̳µ̳ | 729 | 730 ]]) 731 eq({ 6, 21 }, eval('nvim_win_get_cursor(0)')) 732 end) 733 734 it('near the start', function() 735 skip(is_os('win')) 736 feed('<C-R>r<C-B><C-O>') 737 screen:expect([[ 738 |*3 739 {2: }| 740 Entering Ex mode. Type "visual" to go to Normal mode. | 741 :µ̳^µ̳µ̳µ̳µ̳µ̳µ̳µ̳ | 742 {5:-- TERMINAL --} | 743 ]]) 744 eq({ 6, 5 }, eval('nvim_win_get_cursor(0)')) 745 feed([[<C-\><C-N>]]) 746 screen:expect([[ 747 |*3 748 {2: }| 749 Entering Ex mode. Type "visual" to go to Normal mode. | 750 :^µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳ | 751 | 752 ]]) 753 eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) 754 end) 755 end) 756 757 describe('in a line with double-cell multibyte chars and no trailing spaces,', function() 758 before_each(function() 759 setup_ex_register('哦哦哦哦哦哦哦哦') 760 end) 761 762 it('at the end', function() 763 feed('<C-R>r') 764 screen:expect([[ 765 |*3 766 {2: }| 767 Entering Ex mode. Type "visual" to go to Normal mode. | 768 :哦哦哦哦哦哦哦哦^ | 769 {5:-- TERMINAL --} | 770 ]]) 771 eq({ 6, 25 }, eval('nvim_win_get_cursor(0)')) 772 feed([[<C-\><C-N>]]) 773 screen:expect([[ 774 |*3 775 {2: }| 776 Entering Ex mode. Type "visual" to go to Normal mode. | 777 :哦哦哦哦哦哦哦^哦 | 778 | 779 ]]) 780 eq({ 6, 22 }, eval('nvim_win_get_cursor(0)')) 781 end) 782 783 it('near the end', function() 784 feed('<C-R>r<C-X><C-X>') 785 screen:expect([[ 786 |*3 787 {2: }| 788 Entering Ex mode. Type "visual" to go to Normal mode. | 789 :哦哦哦哦哦哦^哦哦 | 790 {5:-- TERMINAL --} | 791 ]]) 792 eq({ 6, 19 }, eval('nvim_win_get_cursor(0)')) 793 feed([[<C-\><C-N>]]) 794 screen:expect([[ 795 |*3 796 {2: }| 797 Entering Ex mode. Type "visual" to go to Normal mode. | 798 :哦哦哦哦哦^哦哦哦 | 799 | 800 ]]) 801 eq({ 6, 16 }, eval('nvim_win_get_cursor(0)')) 802 end) 803 804 it('near the start', function() 805 feed('<C-R>r<C-B><C-O>') 806 screen:expect([[ 807 |*3 808 {2: }| 809 Entering Ex mode. Type "visual" to go to Normal mode. | 810 :哦^哦哦哦哦哦哦哦 | 811 {5:-- TERMINAL --} | 812 ]]) 813 eq({ 6, 4 }, eval('nvim_win_get_cursor(0)')) 814 feed([[<C-\><C-N>]]) 815 screen:expect([[ 816 |*3 817 {2: }| 818 Entering Ex mode. Type "visual" to go to Normal mode. | 819 :^哦哦哦哦哦哦哦哦 | 820 | 821 ]]) 822 eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) 823 end) 824 end) 825 826 it('at the end of a line with trailing spaces #16234', function() 827 setup_ex_register('aaaaaaaa ') 828 feed('<C-R>r') 829 screen:expect([[ 830 |*3 831 {2: }| 832 Entering Ex mode. Type "visual" to go to Normal mode. | 833 :aaaaaaaa ^ | 834 {5:-- TERMINAL --} | 835 ]]) 836 matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()')) 837 eq({ 6, 13 }, eval('nvim_win_get_cursor(0)')) 838 feed([[<C-\><C-N>]]) 839 screen:expect([[ 840 |*3 841 {2: }| 842 Entering Ex mode. Type "visual" to go to Normal mode. | 843 :aaaaaaaa ^ | 844 | 845 ]]) 846 eq({ 6, 12 }, eval('nvim_win_get_cursor(0)')) 847 end) 848 end) 849 850 describe('buffer cursor position is correct in terminal with number column', function() 851 local screen 852 853 local function setup_ex_register(str) 854 screen = tt.setup_child_nvim({ 855 '-u', 856 'NONE', 857 '-i', 858 'NONE', 859 '-E', 860 '--cmd', 861 string.format('let @r = "%s"', str), 862 -- <Left> and <Right> don't always work 863 '--cmd', 864 'cnoremap <C-X> <Left>', 865 '--cmd', 866 'cnoremap <C-O> <Right>', 867 '--cmd', 868 'set notermguicolors', 869 }, { 870 cols = 70, 871 }) 872 screen:expect([[ 873 {121: 1 } | 874 {121: 2 } | 875 {121: 3 } | 876 {121: 4 }{2: }| 877 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 878 {121: 6 }:^ | 879 {5:-- TERMINAL --} | 880 ]]) 881 end 882 883 before_each(function() 884 clear() 885 command('autocmd! nvim.terminal TermOpen') 886 -- 'number' should be set before the terminal process starts, otherwise the resize 887 -- from setting 'number' may cause a redraw that removes the "Entering Ex mode". 888 command('set number') 889 end) 890 891 describe('in a line with no multibyte chars or trailing spaces,', function() 892 before_each(function() 893 setup_ex_register('aaaaaaaa') 894 end) 895 896 it('at the end', function() 897 feed('<C-R>r') 898 screen:expect([[ 899 {121: 1 } | 900 {121: 2 } | 901 {121: 3 } | 902 {121: 4 }{2: }| 903 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 904 {121: 6 }:aaaaaaaa^ | 905 {5:-- TERMINAL --} | 906 ]]) 907 eq({ 6, 9 }, eval('nvim_win_get_cursor(0)')) 908 feed([[<C-\><C-N>]]) 909 screen:expect([[ 910 {121: 1 } | 911 {121: 2 } | 912 {121: 3 } | 913 {121: 4 }{2: }| 914 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 915 {121: 6 }:aaaaaaa^a | 916 | 917 ]]) 918 eq({ 6, 8 }, eval('nvim_win_get_cursor(0)')) 919 end) 920 921 it('near the end', function() 922 feed('<C-R>r<C-X><C-X>') 923 screen:expect([[ 924 {121: 1 } | 925 {121: 2 } | 926 {121: 3 } | 927 {121: 4 }{2: }| 928 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 929 {121: 6 }:aaaaaa^aa | 930 {5:-- TERMINAL --} | 931 ]]) 932 eq({ 6, 7 }, eval('nvim_win_get_cursor(0)')) 933 feed([[<C-\><C-N>]]) 934 screen:expect([[ 935 {121: 1 } | 936 {121: 2 } | 937 {121: 3 } | 938 {121: 4 }{2: }| 939 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 940 {121: 6 }:aaaaa^aaa | 941 | 942 ]]) 943 eq({ 6, 6 }, eval('nvim_win_get_cursor(0)')) 944 end) 945 946 it('near the start', function() 947 feed('<C-R>r<C-B><C-O>') 948 screen:expect([[ 949 {121: 1 } | 950 {121: 2 } | 951 {121: 3 } | 952 {121: 4 }{2: }| 953 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 954 {121: 6 }:a^aaaaaaa | 955 {5:-- TERMINAL --} | 956 ]]) 957 eq({ 6, 2 }, eval('nvim_win_get_cursor(0)')) 958 feed([[<C-\><C-N>]]) 959 screen:expect([[ 960 {121: 1 } | 961 {121: 2 } | 962 {121: 3 } | 963 {121: 4 }{2: }| 964 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 965 {121: 6 }:^aaaaaaaa | 966 | 967 ]]) 968 eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) 969 end) 970 end) 971 972 describe('in a line with single-cell multibyte chars and no trailing spaces,', function() 973 before_each(function() 974 setup_ex_register('µµµµµµµµ') 975 end) 976 977 it('at the end', function() 978 feed('<C-R>r') 979 screen:expect([[ 980 {121: 1 } | 981 {121: 2 } | 982 {121: 3 } | 983 {121: 4 }{2: }| 984 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 985 {121: 6 }:µµµµµµµµ^ | 986 {5:-- TERMINAL --} | 987 ]]) 988 eq({ 6, 17 }, eval('nvim_win_get_cursor(0)')) 989 feed([[<C-\><C-N>]]) 990 screen:expect([[ 991 {121: 1 } | 992 {121: 2 } | 993 {121: 3 } | 994 {121: 4 }{2: }| 995 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 996 {121: 6 }:µµµµµµµ^µ | 997 | 998 ]]) 999 eq({ 6, 15 }, eval('nvim_win_get_cursor(0)')) 1000 end) 1001 1002 it('near the end', function() 1003 feed('<C-R>r<C-X><C-X>') 1004 screen:expect([[ 1005 {121: 1 } | 1006 {121: 2 } | 1007 {121: 3 } | 1008 {121: 4 }{2: }| 1009 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1010 {121: 6 }:µµµµµµ^µµ | 1011 {5:-- TERMINAL --} | 1012 ]]) 1013 eq({ 6, 13 }, eval('nvim_win_get_cursor(0)')) 1014 feed([[<C-\><C-N>]]) 1015 screen:expect([[ 1016 {121: 1 } | 1017 {121: 2 } | 1018 {121: 3 } | 1019 {121: 4 }{2: }| 1020 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1021 {121: 6 }:µµµµµ^µµµ | 1022 | 1023 ]]) 1024 eq({ 6, 11 }, eval('nvim_win_get_cursor(0)')) 1025 end) 1026 1027 it('near the start', function() 1028 feed('<C-R>r<C-B><C-O>') 1029 screen:expect([[ 1030 {121: 1 } | 1031 {121: 2 } | 1032 {121: 3 } | 1033 {121: 4 }{2: }| 1034 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1035 {121: 6 }:µ^µµµµµµµ | 1036 {5:-- TERMINAL --} | 1037 ]]) 1038 eq({ 6, 3 }, eval('nvim_win_get_cursor(0)')) 1039 feed([[<C-\><C-N>]]) 1040 screen:expect([[ 1041 {121: 1 } | 1042 {121: 2 } | 1043 {121: 3 } | 1044 {121: 4 }{2: }| 1045 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1046 {121: 6 }:^µµµµµµµµ | 1047 | 1048 ]]) 1049 eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) 1050 end) 1051 end) 1052 1053 describe('in a line with single-cell composed multibyte chars and no trailing spaces,', function() 1054 before_each(function() 1055 setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') 1056 end) 1057 1058 it('at the end', function() 1059 feed('<C-R>r') 1060 screen:expect([[ 1061 {121: 1 } | 1062 {121: 2 } | 1063 {121: 3 } | 1064 {121: 4 }{2: }| 1065 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1066 {121: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳^ | 1067 {5:-- TERMINAL --} | 1068 ]]) 1069 eq({ 6, 33 }, eval('nvim_win_get_cursor(0)')) 1070 feed([[<C-\><C-N>]]) 1071 screen:expect([[ 1072 {121: 1 } | 1073 {121: 2 } | 1074 {121: 3 } | 1075 {121: 4 }{2: }| 1076 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1077 {121: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳ | 1078 | 1079 ]]) 1080 eq({ 6, 29 }, eval('nvim_win_get_cursor(0)')) 1081 end) 1082 1083 it('near the end', function() 1084 skip(is_os('win')) 1085 feed('<C-R>r<C-X><C-X>') 1086 screen:expect([[ 1087 {121: 1 } | 1088 {121: 2 } | 1089 {121: 3 } | 1090 {121: 4 }{2: }| 1091 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1092 {121: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳^µ̳µ̳ | 1093 {5:-- TERMINAL --} | 1094 ]]) 1095 eq({ 6, 25 }, eval('nvim_win_get_cursor(0)')) 1096 feed([[<C-\><C-N>]]) 1097 screen:expect([[ 1098 {121: 1 } | 1099 {121: 2 } | 1100 {121: 3 } | 1101 {121: 4 }{2: }| 1102 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1103 {121: 6 }:µ̳µ̳µ̳µ̳µ̳^µ̳µ̳µ̳ | 1104 | 1105 ]]) 1106 eq({ 6, 21 }, eval('nvim_win_get_cursor(0)')) 1107 end) 1108 1109 it('near the start', function() 1110 skip(is_os('win')) 1111 feed('<C-R>r<C-B><C-O>') 1112 screen:expect([[ 1113 {121: 1 } | 1114 {121: 2 } | 1115 {121: 3 } | 1116 {121: 4 }{2: }| 1117 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1118 {121: 6 }:µ̳^µ̳µ̳µ̳µ̳µ̳µ̳µ̳ | 1119 {5:-- TERMINAL --} | 1120 ]]) 1121 eq({ 6, 5 }, eval('nvim_win_get_cursor(0)')) 1122 feed([[<C-\><C-N>]]) 1123 screen:expect([[ 1124 {121: 1 } | 1125 {121: 2 } | 1126 {121: 3 } | 1127 {121: 4 }{2: }| 1128 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1129 {121: 6 }:^µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳ | 1130 | 1131 ]]) 1132 eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) 1133 end) 1134 end) 1135 1136 describe('in a line with double-cell multibyte chars and no trailing spaces,', function() 1137 before_each(function() 1138 setup_ex_register('哦哦哦哦哦哦哦哦') 1139 end) 1140 1141 it('at the end', function() 1142 feed('<C-R>r') 1143 screen:expect([[ 1144 {121: 1 } | 1145 {121: 2 } | 1146 {121: 3 } | 1147 {121: 4 }{2: }| 1148 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1149 {121: 6 }:哦哦哦哦哦哦哦哦^ | 1150 {5:-- TERMINAL --} | 1151 ]]) 1152 eq({ 6, 25 }, eval('nvim_win_get_cursor(0)')) 1153 feed([[<C-\><C-N>]]) 1154 screen:expect([[ 1155 {121: 1 } | 1156 {121: 2 } | 1157 {121: 3 } | 1158 {121: 4 }{2: }| 1159 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1160 {121: 6 }:哦哦哦哦哦哦哦^哦 | 1161 | 1162 ]]) 1163 eq({ 6, 22 }, eval('nvim_win_get_cursor(0)')) 1164 end) 1165 1166 it('near the end', function() 1167 feed('<C-R>r<C-X><C-X>') 1168 screen:expect([[ 1169 {121: 1 } | 1170 {121: 2 } | 1171 {121: 3 } | 1172 {121: 4 }{2: }| 1173 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1174 {121: 6 }:哦哦哦哦哦哦^哦哦 | 1175 {5:-- TERMINAL --} | 1176 ]]) 1177 eq({ 6, 19 }, eval('nvim_win_get_cursor(0)')) 1178 feed([[<C-\><C-N>]]) 1179 screen:expect([[ 1180 {121: 1 } | 1181 {121: 2 } | 1182 {121: 3 } | 1183 {121: 4 }{2: }| 1184 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1185 {121: 6 }:哦哦哦哦哦^哦哦哦 | 1186 | 1187 ]]) 1188 eq({ 6, 16 }, eval('nvim_win_get_cursor(0)')) 1189 end) 1190 1191 it('near the start', function() 1192 feed('<C-R>r<C-B><C-O>') 1193 screen:expect([[ 1194 {121: 1 } | 1195 {121: 2 } | 1196 {121: 3 } | 1197 {121: 4 }{2: }| 1198 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1199 {121: 6 }:哦^哦哦哦哦哦哦哦 | 1200 {5:-- TERMINAL --} | 1201 ]]) 1202 eq({ 6, 4 }, eval('nvim_win_get_cursor(0)')) 1203 feed([[<C-\><C-N>]]) 1204 screen:expect([[ 1205 {121: 1 } | 1206 {121: 2 } | 1207 {121: 3 } | 1208 {121: 4 }{2: }| 1209 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1210 {121: 6 }:^哦哦哦哦哦哦哦哦 | 1211 | 1212 ]]) 1213 eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) 1214 end) 1215 end) 1216 1217 it('at the end of a line with trailing spaces #16234', function() 1218 setup_ex_register('aaaaaaaa ') 1219 feed('<C-R>r') 1220 screen:expect([[ 1221 {121: 1 } | 1222 {121: 2 } | 1223 {121: 3 } | 1224 {121: 4 }{2: }| 1225 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1226 {121: 6 }:aaaaaaaa ^ | 1227 {5:-- TERMINAL --} | 1228 ]]) 1229 matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()')) 1230 eq({ 6, 13 }, eval('nvim_win_get_cursor(0)')) 1231 feed([[<C-\><C-N>]]) 1232 screen:expect([[ 1233 {121: 1 } | 1234 {121: 2 } | 1235 {121: 3 } | 1236 {121: 4 }{2: }| 1237 {121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | 1238 {121: 6 }:aaaaaaaa ^ | 1239 | 1240 ]]) 1241 eq({ 6, 12 }, eval('nvim_win_get_cursor(0)')) 1242 end) 1243 end)