prompt_buffer_spec.lua (31159B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local feed = n.feed 6 local fn = n.call 7 local source = n.source 8 local clear = n.clear 9 local command = n.command 10 local expect = n.expect 11 local poke_eventloop = n.poke_eventloop 12 local api = n.api 13 local eq = t.eq 14 local neq = t.neq 15 local exec_lua = n.exec_lua 16 17 describe('prompt buffer', function() 18 local screen 19 20 before_each(function() 21 clear() 22 screen = Screen.new(25, 10) 23 command('set laststatus=0 nohidden') 24 end) 25 26 local function source_script() 27 source([[ 28 func TextEntered(text) 29 if a:text == "exit" 30 " Reset &modified to allow the buffer to be closed. 31 set nomodified 32 stopinsert 33 close 34 else 35 " Add the output above the current prompt. 36 call append(line("$") - 1, split('Command: "' . a:text . '"', '\n')) 37 " Reset &modified to allow the buffer to be closed. 38 set nomodified 39 call timer_start(20, {id -> TimerFunc(a:text)}) 40 endif 41 endfunc 42 43 func TimerFunc(text) 44 " Add the output above the current prompt. 45 call append(line("$") - 1, split('Result: "' . a:text .'"', '\n')) 46 " Reset &modified to allow the buffer to be closed. 47 set nomodified 48 endfunc 49 50 func SwitchWindows() 51 call timer_start(0, {-> execute("wincmd p", "")}) 52 endfunc 53 54 call setline(1, "other buffer") 55 set nomodified 56 new 57 set buftype=prompt 58 call prompt_setcallback(bufnr(''), function("TextEntered")) 59 eval bufnr("")->prompt_setprompt("cmd: ") 60 startinsert 61 ]]) 62 screen:expect([[ 63 cmd: ^ | 64 {1:~ }|*3 65 {3:[Prompt] [+] }| 66 other buffer | 67 {1:~ }|*3 68 {5:-- INSERT --} | 69 ]]) 70 end 71 72 -- oldtest: Test_prompt_basic() 73 it('works', function() 74 source_script() 75 feed('hello\n') 76 screen:expect([[ 77 cmd: hello | 78 Command: "hello" | 79 Result: "hello" | 80 cmd: ^ | 81 {3:[Prompt] }| 82 other buffer | 83 {1:~ }|*3 84 {5:-- INSERT --} | 85 ]]) 86 feed('exit\n') 87 screen:expect([[ 88 ^other buffer | 89 {1:~ }|*8 90 | 91 ]]) 92 end) 93 94 -- oldtest: Test_prompt_editing() 95 it('editing', function() 96 source_script() 97 feed('hello<BS><BS>') 98 screen:expect([[ 99 cmd: hel^ | 100 {1:~ }|*3 101 {3:[Prompt] [+] }| 102 other buffer | 103 {1:~ }|*3 104 {5:-- INSERT --} | 105 ]]) 106 feed('<Left><Left><Left><BS>-') 107 screen:expect([[ 108 cmd: -^hel | 109 {1:~ }|*3 110 {3:[Prompt] [+] }| 111 other buffer | 112 {1:~ }|*3 113 {5:-- INSERT --} | 114 ]]) 115 feed('<C-O>lz') 116 screen:expect([[ 117 cmd: -hz^el | 118 {1:~ }|*3 119 {3:[Prompt] [+] }| 120 other buffer | 121 {1:~ }|*3 122 {5:-- INSERT --} | 123 ]]) 124 feed('<End>x') 125 screen:expect([[ 126 cmd: -hzelx^ | 127 {1:~ }|*3 128 {3:[Prompt] [+] }| 129 other buffer | 130 {1:~ }|*3 131 {5:-- INSERT --} | 132 ]]) 133 134 -- :edit doesn't apply on prompt buffer 135 eq('Vim(edit):cannot :edit a prompt buffer', t.pcall_err(api.nvim_command, 'edit')) 136 137 feed('<C-U>exit\n') 138 screen:expect([[ 139 ^other buffer | 140 {1:~ }|*8 141 | 142 ]]) 143 end) 144 145 -- oldtest: Test_prompt_switch_windows() 146 it('switch windows', function() 147 source_script() 148 feed('<C-O>:call SwitchWindows()<CR>') 149 screen:expect([[ 150 cmd: | 151 {1:~ }|*3 152 {2:[Prompt] [+] }| 153 ^other buffer | 154 {1:~ }|*3 155 | 156 ]]) 157 feed('<C-O>:call SwitchWindows()<CR>') 158 screen:expect([[ 159 cmd: ^ | 160 {1:~ }|*3 161 {3:[Prompt] [+] }| 162 other buffer | 163 {1:~ }|*3 164 {5:-- INSERT --} | 165 ]]) 166 feed('<Esc>') 167 screen:expect([[ 168 cmd:^ | 169 {1:~ }|*3 170 {3:[Prompt] [+] }| 171 other buffer | 172 {1:~ }|*3 173 | 174 ]]) 175 end) 176 177 -- oldtest: Test_prompt_while_writing_to_hidden_buffer() 178 it('keeps insert mode after aucmd_restbuf in callback', function() 179 source_script() 180 source [[ 181 let s:buf = nvim_create_buf(1, 1) 182 call timer_start(0, {-> nvim_buf_set_lines(s:buf, -1, -1, 0, ['walrus'])}) 183 ]] 184 poke_eventloop() 185 eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) 186 end) 187 188 -- oldtest: Test_prompt_appending_while_hidden() 189 it('accessing hidden prompt buffer does not start insert mode', function() 190 local prev_win = api.nvim_get_current_win() 191 source([[ 192 new prompt 193 set buftype=prompt 194 set bufhidden=hide 195 196 func s:TextEntered(text) 197 if a:text == 'exit' 198 close 199 endif 200 let g:entered = a:text 201 endfunc 202 call prompt_setcallback(bufnr(), function('s:TextEntered')) 203 204 func DoAppend(cmd_before = '') 205 exe a:cmd_before 206 call appendbufline('prompt', '$', 'Test') 207 return '' 208 endfunc 209 210 autocmd User SwitchTabPages tabprevious | tabnext 211 func DoAutoAll(cmd_before = '') 212 exe a:cmd_before 213 doautoall User SwitchTabPages 214 return '' 215 endfunc 216 ]]) 217 feed('asomething<CR>') 218 eq('something', api.nvim_get_var('entered')) 219 neq(prev_win, api.nvim_get_current_win()) 220 feed('exit<CR>') 221 eq(prev_win, api.nvim_get_current_win()) 222 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 223 command('call DoAppend()') 224 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 225 feed('i') 226 eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) 227 command('call DoAppend()') 228 eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) 229 command("call DoAppend('stopinsert')") 230 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 231 command("call DoAppend('startreplace')") 232 eq({ mode = 'R', blocking = false }, api.nvim_get_mode()) 233 feed('<Esc>') 234 command('tabnew') 235 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 236 command("call DoAutoAll('startinsert')") 237 eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) 238 command("call DoAutoAll('stopinsert')") 239 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 240 end) 241 242 -- oldtest: Test_prompt_leave_modify_hidden() 243 it('modifying hidden buffer does not prevent prompt buffer mode change', function() 244 source([[ 245 file hidden 246 set bufhidden=hide 247 enew 248 new prompt 249 set buftype=prompt 250 251 inoremap <buffer> w <Cmd>wincmd w<CR> 252 inoremap <buffer> q <Cmd>bwipe!<CR> 253 autocmd BufLeave prompt call appendbufline('hidden', '$', 'Leave') 254 autocmd BufEnter prompt call appendbufline('hidden', '$', 'Enter') 255 autocmd BufWinLeave prompt call appendbufline('hidden', '$', 'Close') 256 ]]) 257 feed('a') 258 eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) 259 feed('w') 260 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 261 feed('<C-W>w') 262 eq({ mode = 'i', blocking = false }, api.nvim_get_mode()) 263 feed('q') 264 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 265 command('bwipe!') 266 expect([[ 267 268 Leave 269 Enter 270 Leave 271 Close]]) 272 end) 273 274 it('can insert multiline text', function() 275 source_script() 276 local buf = api.nvim_get_current_buf() 277 278 feed('line 1<s-cr>line 2<s-cr>line 3') 279 screen:expect([[ 280 cmd: line 1 | 281 line 2 | 282 line 3^ | 283 {1:~ }| 284 {3:[Prompt] [+] }| 285 other buffer | 286 {1:~ }|*3 287 {5:-- INSERT --} | 288 ]]) 289 290 -- prompt_getinput works with multiline input 291 eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf)) 292 293 feed('<cr>') 294 -- submitting multiline text works 295 screen:expect([[ 296 Result: "line 1 | 297 line 2 | 298 line 3" | 299 cmd: ^ | 300 {3:[Prompt] }| 301 other buffer | 302 {1:~ }|*3 303 {5:-- INSERT --} | 304 ]]) 305 306 eq('', fn('prompt_getinput', buf)) 307 308 -- % prompt is not repeated with formatoptions+=r 309 source([[ 310 bwipeout! 311 set formatoptions+=r 312 call prompt_setprompt(bufnr(), "% ") 313 set buftype=prompt 314 ]]) 315 feed('iline1<s-cr>line2') 316 screen:expect([[ 317 other buffer | 318 % line1 | 319 line2^ | 320 {1:~ }|*6 321 {5:-- INSERT --} | 322 ]]) 323 324 -- ensure cursor gets placed on first line of user input. 325 -- when insert mode is entered from read-only region of prompt buffer. 326 local prompt_pos = api.nvim_buf_get_mark(0, ':') 327 feed('<esc>') 328 -- works before prompt 329 api.nvim_win_set_cursor(0, { prompt_pos[1] - 1, 0 }) 330 screen:expect([[ 331 ^other buffer | 332 % line1 | 333 line2 | 334 {1:~ }|*6 335 | 336 ]]) 337 feed('a') 338 feed('<esc>') 339 screen:expect([[ 340 other buffer | 341 %^ line1 | 342 line2 | 343 {1:~ }|*6 344 | 345 ]]) 346 -- works on prompt 347 api.nvim_win_set_cursor(0, { prompt_pos[1], 0 }) 348 screen:expect([[ 349 other buffer | 350 ^% line1 | 351 line2 | 352 {1:~ }|*6 353 | 354 ]]) 355 feed('a') 356 feed('<esc>') 357 screen:expect([[ 358 other buffer | 359 %^ line1 | 360 line2 | 361 {1:~ }|*6 362 | 363 ]]) 364 365 -- i_<Left> i_<C-Left> i_<Home> i_<End> keys on prompt-line doesn't put cursor 366 -- at end of text 367 feed('a<Left><C-Left>') 368 screen:expect([[ 369 other buffer | 370 % ^line1 | 371 line2 | 372 {1:~ }|*6 373 {5:-- INSERT --} | 374 ]]) 375 376 feed('<End>') 377 screen:expect([[ 378 other buffer | 379 % line1^ | 380 line2 | 381 {1:~ }|*6 382 {5:-- INSERT --} | 383 ]]) 384 385 feed('<Home>') 386 screen:expect([[ 387 other buffer | 388 % ^line1 | 389 line2 | 390 {1:~ }|*6 391 {5:-- INSERT --} | 392 ]]) 393 end) 394 395 it('can put (p) multiline text', function() 396 source_script() 397 local buf = api.nvim_get_current_buf() 398 399 fn('setreg', 'a', 'line 1\nline 2\nline 3') 400 feed('<esc>"ap') 401 screen:expect([[ 402 cmd: ^line 1 | 403 line 2 | 404 line 3 | 405 {1:~ }| 406 {3:[Prompt] [+] }| 407 other buffer | 408 {1:~ }|*3 409 | 410 ]]) 411 412 -- prompt_getinput works with pasted input 413 eq('line 1\nline 2\nline 3', fn('prompt_getinput', buf)) 414 415 feed('i<cr>') 416 screen:expect([[ 417 Result: "line 1 | 418 line 2 | 419 line 3" | 420 cmd: ^ | 421 {3:[Prompt] }| 422 other buffer | 423 {1:~ }|*3 424 {5:-- INSERT --} | 425 ]]) 426 end) 427 428 it('can put multiline text with nvim_paste', function() 429 source_script() 430 api.nvim_paste('line 1\nline 2\nline 3', false, -1) 431 screen:expect([[ 432 cmd: line 1 | 433 line 2 | 434 line 3^ | 435 {1:~ }| 436 {3:[Prompt] [+] }| 437 other buffer | 438 {1:~ }|*3 439 {5:-- INSERT --} | 440 ]]) 441 end) 442 443 it('can undo current prompt', function() 444 source_script() 445 local buf = api.nvim_get_current_buf() 446 447 -- text editing allowed in current prompt 448 feed('tests-initial<esc>') 449 feed('bimiddle-<esc>') 450 screen:expect([[ 451 cmd: tests-middle^-initial| 452 {1:~ }|*3 453 {3:[Prompt] [+] }| 454 other buffer | 455 {1:~ }|*3 456 | 457 ]]) 458 459 feed('Fdx') 460 screen:expect([[ 461 cmd: tests-mid^le-initial | 462 {1:~ }|*3 463 {3:[Prompt] [+] }| 464 other buffer | 465 {1:~ }|*3 466 | 467 ]]) 468 469 -- can undo edits until prompt has been submitted 470 feed('u') 471 screen:expect([[ 472 cmd: tests-mid^dle-initial| 473 {1:~ }|*3 474 {3:[Prompt] [+] }| 475 other buffer | 476 {1:~ }|*3 477 1 change; {MATCH:.*} | 478 ]]) 479 480 -- undo is reflected in prompt_getinput 481 eq('tests-middle-initial', fn('prompt_getinput', buf)) 482 483 feed('u') 484 screen:expect([[ 485 cmd: tests-^initial | 486 {1:~ }|*3 487 {3:[Prompt] [+] }| 488 other buffer | 489 {1:~ }|*3 490 1 change; {MATCH:.*} | 491 ]]) 492 493 feed('i<cr><esc>') 494 screen:expect([[ 495 cmd: tests-initial | 496 Command: "tests-initial" | 497 Result: "tests-initial" | 498 cmd:^ | 499 {3:[Prompt] }| 500 other buffer | 501 {1:~ }|*3 502 | 503 ]]) 504 505 -- after submit undo does nothing 506 feed('u') 507 screen:expect([[ 508 cmd: tests-initial | 509 Command: "tests-initial" | 510 cmd:^ | 511 {1:~ }| 512 {3:[Prompt] [+] }| 513 other buffer | 514 {1:~ }|*3 515 1 line {MATCH:.*} | 516 ]]) 517 518 -- "S" does not clear undo 519 feed('ihello<Esc>S') 520 screen:expect([[ 521 cmd: tests-initial | 522 Command: "tests-initial" | 523 cmd: ^ | 524 {1:~ }| 525 {3:[Prompt] [+] }| 526 other buffer | 527 {1:~ }|*3 528 {5:-- INSERT --} | 529 ]]) 530 feed('<Esc>u') 531 screen:expect([[ 532 cmd: tests-initial | 533 Command: "tests-initial" | 534 ^cmd: hello | 535 {1:~ }| 536 {3:[Prompt] [+] }| 537 other buffer | 538 {1:~ }|*3 539 1 change; {MATCH:.*} | 540 ]]) 541 542 -- undo cleared if prompt changes 543 -- (otherwise undoing would abort it and append a new prompt, which isn't useful) 544 fn('prompt_setprompt', '', 'cmd > ') 545 feed('u') 546 screen:expect([[ 547 cmd: tests-initial | 548 Command: "tests-initial" | 549 c^md > hello | 550 {1:~ }| 551 {3:[Prompt] [+] }| 552 other buffer | 553 {1:~ }|*3 554 Already at oldest change | 555 ]]) 556 557 -- new prompt line appended to fix missing prompt also clears undo 558 feed('A there') 559 fn('setpos', "':", { 0, fn('line', '.'), 99, 0 }) 560 feed('<Esc>u') 561 screen:expect([[ 562 cmd: tests-initial | 563 Command: "tests-initial" | 564 cmd > hello there | 565 cmd >^ | 566 {3:[Prompt] [+] }| 567 other buffer | 568 {1:~ }|*3 569 Already at oldest change | 570 ]]) 571 end) 572 573 it('o/O can create new lines', function() 574 source_script() 575 local buf = api.nvim_get_current_buf() 576 577 feed('line 1<s-cr>line 2<s-cr>line 3') 578 screen:expect([[ 579 cmd: line 1 | 580 line 2 | 581 line 3^ | 582 {1:~ }| 583 {3:[Prompt] [+] }| 584 other buffer | 585 {1:~ }|*3 586 {5:-- INSERT --} | 587 ]]) 588 589 feed('<esc>koafter') 590 screen:expect([[ 591 cmd: line 1 | 592 line 2 | 593 after^ | 594 line 3 | 595 {3:[Prompt] [+] }| 596 other buffer | 597 {1:~ }|*3 598 {5:-- INSERT --} | 599 ]]) 600 601 -- newline created with o is reflected in prompt_getinput 602 eq('line 1\nline 2\nafter\nline 3', fn('prompt_getinput', buf)) 603 604 feed('<esc>kObefore') 605 606 screen:expect([[ 607 cmd: line 1 | 608 before^ | 609 line 2 | 610 after | 611 {3:[Prompt] [+] }| 612 other buffer | 613 {1:~ }|*3 614 {5:-- INSERT --} | 615 ]]) 616 617 -- newline created with O is reflected in prompt_getinput 618 eq('line 1\nbefore\nline 2\nafter\nline 3', fn('prompt_getinput', buf)) 619 620 feed('<cr>') 621 screen:expect([[ 622 line 2 | 623 after | 624 line 3" | 625 cmd: ^ | 626 {3:[Prompt] }| 627 other buffer | 628 {1:~ }|*3 629 {5:-- INSERT --} | 630 ]]) 631 632 feed('line 4<s-cr>line 5') 633 634 feed('<esc>k0oafter prompt') 635 screen:expect([[ 636 after | 637 line 3" | 638 cmd: line 4 | 639 after prompt^ | 640 {3:[Prompt] [+] }| 641 other buffer | 642 {1:~ }|*3 643 {5:-- INSERT --} | 644 ]]) 645 646 feed('<esc>k0Oat prompt') 647 screen:expect([[ 648 after | 649 line 3" | 650 cmd: at prompt^ | 651 line 4 | 652 {3:[Prompt] [+] }| 653 other buffer | 654 {1:~ }|*3 655 {5:-- INSERT --} | 656 ]]) 657 658 feed('<cr>') 659 screen:expect([[ 660 line 4 | 661 after prompt | 662 line 5" | 663 cmd: ^ | 664 {3:[Prompt] }| 665 other buffer | 666 {1:~ }|*3 667 {5:-- INSERT --} | 668 ]]) 669 end) 670 671 it('deleting prompt adds it back on insert', function() 672 source_script() 673 feed('asdf') 674 screen:expect([[ 675 cmd: asdf^ | 676 {1:~ }|*3 677 {3:[Prompt] [+] }| 678 other buffer | 679 {1:~ }|*3 680 {5:-- INSERT --} | 681 ]]) 682 683 feed('<esc>ddi') 684 screen:expect([[ 685 cmd: ^ | 686 {1:~ }|*3 687 {3:[Prompt] [+] }| 688 other buffer | 689 {1:~ }|*3 690 {5:-- INSERT --} | 691 ]]) 692 693 feed('asdf') 694 screen:expect([[ 695 cmd: asdf^ | 696 {1:~ }|*3 697 {3:[Prompt] [+] }| 698 other buffer | 699 {1:~ }|*3 700 {5:-- INSERT --} | 701 ]]) 702 703 feed('<esc>cc') 704 screen:expect([[ 705 cmd: ^ | 706 {1:~ }|*3 707 {3:[Prompt] [+] }| 708 other buffer | 709 {1:~ }|*3 710 {5:-- INSERT --} | 711 ]]) 712 end) 713 714 it("sets the ': mark", function() 715 api.nvim_set_option_value('buftype', 'prompt', { buf = 0 }) 716 exec_lua(function() 717 local buf = vim.api.nvim_get_current_buf() 718 vim.fn.prompt_setprompt(buf, 'cmd > ') 719 vim.fn.prompt_setcallback(buf, function(str) 720 local last_line = vim.api.nvim_buf_line_count(buf) 721 vim.api.nvim_buf_set_lines(buf, last_line - 1, last_line - 1, true, vim.split(str, '\n')) 722 end) 723 end) 724 725 feed('asdf') 726 eq({ 1, 6 }, api.nvim_buf_get_mark(0, ':')) 727 feed('<cr>') 728 eq({ 3, 6 }, api.nvim_buf_get_mark(0, ':')) 729 -- Multiline prompt. 730 feed('<s-cr>line1<s-cr>line2<s-cr>line3<cr>') 731 eq({ 11, 6 }, api.nvim_buf_get_mark(0, ':')) 732 733 -- ': mark is only available in prompt buffer. 734 api.nvim_set_option_value('buftype', '', { buf = 0 }) 735 eq("Invalid mark name: ':'", t.pcall_err(api.nvim_buf_get_mark, 0, ':')) 736 737 -- mark can be moved 738 api.nvim_set_option_value('buftype', 'prompt', { buf = 0 }) 739 local last_line = api.nvim_buf_line_count(0) 740 eq({ last_line, 6 }, api.nvim_buf_get_mark(0, ':')) 741 eq(true, api.nvim_buf_set_mark(0, ':', 1, 5, {})) 742 eq({ 1, 5 }, api.nvim_buf_get_mark(0, ':')) 743 744 -- No crash from invalid col. 745 eq(true, api.nvim_buf_set_mark(0, ':', fn('line', '.'), 999, {})) 746 eq({ 12, 6 }, api.nvim_buf_get_mark(0, ':')) 747 748 -- Clamps lnum to at least 1. Do in one event to repro the leak. 749 exec_lua(function() 750 vim.fn.setpos("':", { 0, 0, 0, 0 }) 751 vim.fn.prompt_setprompt('', 'bar > ') 752 end) 753 eq({ 1, 6 }, api.nvim_buf_get_mark(0, ':')) 754 755 -- No ml_get error from invalid lnum. 756 command('set messagesopt+=wait:0 messagesopt-=hit-enter') 757 fn('setpos', "':", { 0, 999, 7, 0 }) 758 eq('', api.nvim_get_vvar('errmsg')) 759 command('set messagesopt&') 760 eq({ 13, 6 }, api.nvim_buf_get_mark(0, ':')) 761 end) 762 763 describe('prompt_getinput', function() 764 it('returns current prompts text', function() 765 command('new') 766 local bufnr = fn('bufnr') 767 api.nvim_set_option_value('buftype', 'prompt', { buf = 0 }) 768 eq('', fn('prompt_getinput', bufnr)) 769 feed('iasdf') 770 eq('asdf', fn('prompt_getinput', bufnr)) 771 feed('<esc>dd') 772 eq('', fn('prompt_getinput', bufnr)) 773 feed('iasdf2') 774 eq('asdf2', fn('prompt_getinput', bufnr)) 775 776 -- returns empty string when called from non prompt buffer 777 api.nvim_set_option_value('buftype', '', { buf = 0 }) 778 eq('', fn('prompt_getinput', bufnr)) 779 end) 780 end) 781 782 it('programmatic (non-user) edits', function() 783 api.nvim_set_option_value('buftype', 'prompt', { buf = 0 }) 784 785 -- with nvim_buf_set_lines 786 exec_lua([[ 787 local buf = vim.api.nvim_get_current_buf() 788 vim.fn.prompt_setcallback(buf, function(text) 789 vim.api.nvim_buf_set_lines(buf, -2, -2, true, vim.split(text, '\n')) 790 end) 791 ]]) 792 feed('iset_lines<cr>') 793 feed('set_lines2<cr>') 794 screen:expect([[ 795 % set_lines | 796 set_lines | 797 % set_lines2 | 798 set_lines2 | 799 % ^ | 800 {1:~ }|*4 801 {5:-- INSERT --} | 802 ]]) 803 804 feed('set_lines3(multi-1)<s-cr>set_lines3(multi-2)<cr>') 805 screen:expect([[ 806 % set_lines | 807 set_lines | 808 % set_lines2 | 809 set_lines2 | 810 % set_lines3(multi-1) | 811 set_lines3(multi-2) | 812 set_lines3(multi-1) | 813 set_lines3(multi-2) | 814 % ^ | 815 {5:-- INSERT --} | 816 ]]) 817 -- with nvim_buf_set_text 818 source('bwipeout!') 819 api.nvim_set_option_value('buftype', 'prompt', { buf = 0 }) 820 exec_lua([[ 821 local buf = vim.api.nvim_get_current_buf() 822 vim.fn.prompt_setcallback(buf, function(text) 823 local lines = vim.split(text, '\n') 824 if lines[#lines] ~= '' then 825 table.insert(lines, '') 826 end 827 vim.api.nvim_buf_set_text(buf, -1, 0, -1, 0, lines) 828 end) 829 ]]) 830 feed('set_text<cr>') 831 feed('set_text2<cr>') 832 screen:expect([[ 833 % set_text | 834 set_text | 835 % set_text2 | 836 set_text2 | 837 % ^ | 838 {1:~ }|*4 839 {5:-- INSERT --} | 840 ]]) 841 842 feed('set_text3(multi-1)<s-cr>set_text3(multi-2)<cr>') 843 screen:expect([[ 844 % set_text | 845 set_text | 846 % set_text2 | 847 set_text2 | 848 % set_text3(multi-1) | 849 set_text3(multi-2) | 850 set_text3(multi-1) | 851 set_text3(multi-2) | 852 % ^ | 853 {5:-- INSERT --} | 854 ]]) 855 end) 856 857 it('works correctly with empty string as prompt', function() 858 api.nvim_set_option_value('buftype', 'prompt', { buf = 0 }) 859 exec_lua(function() 860 local buf = vim.api.nvim_get_current_buf() 861 vim.fn.prompt_setprompt(buf, '') 862 end) 863 864 source('startinsert') 865 866 -- mark correctly set 867 eq({ 1, 0 }, api.nvim_buf_get_mark(0, ':')) 868 869 feed('asdf') 870 screen:expect([[ 871 asdf^ | 872 {1:~ }|*8 873 {5:-- INSERT --} | 874 ]]) 875 876 -- can clear all of it 877 feed('<backspace><backspace><backspace><backspace>') 878 screen:expect([[ 879 ^ | 880 {1:~ }|*8 881 {5:-- INSERT --} | 882 ]]) 883 feed('<cr>') 884 885 eq({ 2, 0 }, api.nvim_buf_get_mark(0, ':')) 886 end) 887 888 it('prompt can be changed without interrupting user input', function() 889 api.nvim_set_option_value('buftype', 'prompt', { buf = 0 }) 890 local buf = api.nvim_get_current_buf() 891 892 local function set_prompt(prompt, b) 893 fn('prompt_setprompt', b or buf, prompt) 894 end 895 896 set_prompt('> ') 897 898 source('startinsert') 899 900 feed('user input') 901 -- Move the cursor a bit to check cursor maintaining position 902 feed('<esc>hhi') 903 904 screen:expect([[ 905 > user in^put | 906 {1:~ }|*8 907 {5:-- INSERT --} | 908 ]]) 909 910 eq({ 1, 2 }, api.nvim_buf_get_mark(0, ':')) 911 912 set_prompt('new-prompt > ') 913 914 screen:expect([[ 915 new-prompt > user in^put | 916 {1:~ }|*8 917 {5:-- INSERT --} | 918 ]]) 919 920 eq({ 1, 13 }, api.nvim_buf_get_mark(0, ':')) 921 922 set_prompt('new-prompt(status) > ') 923 924 screen:expect([[ 925 new-prompt(status) > user| 926 in^put | 927 {1:~ }|*7 928 {5:-- INSERT --} | 929 ]]) 930 eq({ 1, 21 }, api.nvim_buf_get_mark(0, ':')) 931 932 set_prompt('new-prompt > ') 933 934 screen:expect([[ 935 new-prompt > user in^put | 936 {1:~ }|*8 937 {5:-- INSERT --} | 938 ]]) 939 eq({ 1, 13 }, api.nvim_buf_get_mark(0, ':')) 940 941 -- Cursor not moved when not on the prompt line. 942 feed('<CR>user input<Esc>k') 943 screen:expect([[ 944 new-prompt > user inpu^t | 945 new-prompt > user input | 946 {1:~ }|*7 947 | 948 ]]) 949 set_prompt('<>< ') 950 screen:expect([[ 951 new-prompt > user inpu^t | 952 <>< user input | 953 {1:~ }|*7 954 | 955 ]]) 956 -- Correct col when prompt has multi-cell chars. 957 feed('i<Left><Left>') 958 screen:expect([[ 959 new-prompt > user input | 960 <>< user inp^ut | 961 {1:~ }|*7 962 {5:-- INSERT --} | 963 ]]) 964 set_prompt('\t > ') 965 screen:expect([[ 966 new-prompt > user input | 967 > user inp^ut | 968 {1:~ }|*7 969 {5:-- INSERT --} | 970 ]]) 971 -- Works with 'virtualedit': coladd remains sensible. Cursor is redrawn correctly. 972 -- Tab size visually changes due to multiples of 'tabstop'. 973 command('set virtualedit=all') 974 feed('<C-O>Sa<Tab>b<C-O>3h') 975 screen:expect([[ 976 new-prompt > user input | 977 > a ^ b | 978 {1:~ }|*7 979 {5:-- INSERT --} | 980 ]]) 981 set_prompt('😊 > ') 982 screen:expect([[ 983 new-prompt > user input | 984 😊 > a ^ b | 985 {1:~ }|*7 986 {5:-- INSERT --} | 987 ]]) 988 -- Minimum col should be 1. Same event to avoid corrections from the state loop. 989 feed('<Esc>0') 990 local colnr = exec_lua(function() 991 vim.fn.prompt_setprompt('', '') 992 return vim.fn.col('.') 993 end) 994 eq(1, colnr) 995 -- Correct cursor adjustment when old ': col and old prompt length differs. 996 set_prompt('foo > ') 997 fn('setpos', "':", { 0, fn('line', '.'), 10, 0 }) 998 fn('setline', '.', ' foo > hello') 999 feed('fh') 1000 screen:expect([[ 1001 new-prompt > user input | 1002 foo > ^hello | 1003 {1:~ }|*7 1004 | 1005 ]]) 1006 set_prompt('bar > ') 1007 screen:expect([[ 1008 new-prompt > user input | 1009 bar > ^hello | 1010 {1:~ }|*7 1011 | 1012 ]]) 1013 1014 -- No crash when setting shorter prompt than curbuf's in other buffer. 1015 feed('ztA') 1016 command('set virtualedit& | new | setlocal buftype=prompt') 1017 set_prompt('looooooooooooooooooooooooooooooooooooooooooooong > ', '') -- curbuf 1018 set_prompt('foo > ') 1019 screen:expect([[ 1020 loooooooooooooooooooooooo| 1021 ooooooooooooooooooooong >| 1022 ^ | 1023 {1:~ }| 1024 {3:[Prompt] [+] }| 1025 foo > hello | 1026 {1:~ }|*3 1027 {5:-- INSERT --} | 1028 ]]) 1029 1030 -- No prompt_setprompt crash from invalid ': col. Must happen in the same event. 1031 exec_lua(function() 1032 vim.cmd 'bwipeout!' 1033 vim.api.nvim_buf_set_mark(0, ':', vim.fn.line('.'), 999, {}) 1034 vim.fn.prompt_setprompt('', 'new-prompt > ') 1035 end) 1036 screen:expect([[ 1037 new-prompt > ^ | 1038 {1:~ }|*8 1039 {5:-- INSERT --} | 1040 ]]) 1041 1042 -- No leak if prompt_setprompt called for unloaded prompt buffer. 1043 local unloaded_buf = fn('bufadd', '') 1044 api.nvim_set_option_value('buftype', 'prompt', { buf = unloaded_buf }) 1045 fn('prompt_setprompt', unloaded_buf, 'hello unloaded! > ') 1046 eq('hello unloaded! > ', fn('prompt_getprompt', unloaded_buf)) 1047 end) 1048 end)