messages_spec.lua (129264B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local clear, feed = n.clear, n.feed 6 local eval = n.eval 7 local eq = t.eq 8 local neq = t.neq 9 local command = n.command 10 local set_method_error = n.set_method_error 11 local api = n.api 12 local async_meths = n.async_meths 13 local nvim_prog = n.nvim_prog 14 local testprg = n.testprg 15 local exec = n.exec 16 local exec_capture = n.exec_capture 17 local exc_exec = n.exc_exec 18 local exec_lua = n.exec_lua 19 local poke_eventloop = n.poke_eventloop 20 local assert_alive = n.assert_alive 21 local retry = t.retry 22 local is_os = t.is_os 23 local fn = n.fn 24 local skip = t.skip 25 26 describe('ui/ext_messages', function() 27 local screen 28 local fname = 'Xtest_functional_ui_messages_spec' 29 30 before_each(function() 31 clear() 32 screen = Screen.new(25, 5, { rgb = true, ext_messages = true, ext_popupmenu = true }) 33 screen:add_extra_attr_ids { 34 [100] = { undercurl = true, special = Screen.colors.Red }, 35 [101] = { foreground = Screen.colors.Magenta1, bold = true }, 36 } 37 end) 38 after_each(function() 39 os.remove(fname) 40 end) 41 42 it('msg_show kinds', function() 43 feed('iline 1\nline 2<esc>') 44 45 -- confirm is now cmdline prompt 46 feed(':echo confirm("test")<cr>') 47 screen:expect({ 48 grid = [[ 49 line 1 | 50 line ^2 | 51 {1:~ }|*3 52 ]], 53 cmdline = { { content = { { '' } }, hl = 'MoreMsg', pos = 0, prompt = '[O]k: ' } }, 54 messages = { { content = { { 'test', 6, 'MoreMsg' } }, kind = 'confirm' } }, 55 }) 56 feed('<cr>') 57 screen:expect({ 58 grid = [[ 59 line 1 | 60 line ^2 | 61 {1:~ }|*3 62 ]], 63 messages = { { content = { { '1' } }, kind = 'echo' } }, 64 }) 65 66 -- :substitute confirm is now cmdline prompt 67 feed(':%s/i/X/gc<cr>') 68 screen:expect({ 69 grid = [[ 70 l{2:i}ne 1 | 71 l{10:i}ne ^2 | 72 {1:~ }|*3 73 ]], 74 cmdline = { 75 { 76 content = { { '' } }, 77 hl = 'Question', 78 pos = 0, 79 prompt = 'replace with X? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)', 80 }, 81 }, 82 }) 83 feed('nq') 84 85 -- kind=wmsg (editing readonly file) 86 local byte = t.is_os('win') and 16 or 14 87 local writemsg = ('"Xtest_functional_ui_messages_spec" [New] 2L, %dB written'):format(byte) 88 command('write ' .. fname) 89 command('set readonly nohls') 90 feed('G$x') 91 screen:expect({ 92 grid = [[ 93 line 1 | 94 line^ | 95 {1:~ }|*3 96 ]], 97 messages = { 98 { content = { { writemsg } }, history = true, kind = 'bufwrite' }, 99 { 100 content = { { 'W10: Warning: Changing a readonly file', 19, 'WarningMsg' } }, 101 history = true, 102 kind = 'wmsg', 103 }, 104 }, 105 }) 106 107 -- kind=wmsg ('wrapscan' after search reaches EOF) 108 command('silent undo') 109 feed('G$/i<CR>G$') 110 screen:expect { 111 grid = [[ 112 line 1 | 113 line ^2 | 114 {1:~ }|*3 115 ]], 116 messages = { 117 { content = { { '/i ' } }, kind = 'search_cmd' }, 118 { 119 content = { { 'search hit BOTTOM, continuing at TOP', 19, 'WarningMsg' } }, 120 kind = 'wmsg', 121 }, 122 }, 123 } 124 125 -- kind=emsg after :throw 126 feed(':throw "foo"<cr>') 127 screen:expect { 128 grid = [[ 129 line 1 | 130 line ^2 | 131 {1:~ }|*3 132 ]], 133 messages = { 134 { 135 content = { { 'Error in :\nE605: Exception not caught: foo', 9, 'ErrorMsg' } }, 136 history = true, 137 kind = 'emsg', 138 }, 139 }, 140 } 141 142 -- kind=quickfix after :cnext 143 command("caddexpr [expand('%').':1:line1',expand('%').':2:line2']") 144 feed(':cnext<CR>$') 145 screen:expect { 146 grid = [[ 147 line 1 | 148 line ^2 | 149 {1:~ }|*3 150 ]], 151 messages = { 152 { content = { { '(2 of 2): line2' } }, history = true, kind = 'quickfix' }, 153 }, 154 } 155 156 -- search_cmd 157 feed('?line<CR>G$') 158 screen:expect({ 159 grid = [[ 160 line 1 | 161 line ^2 | 162 {1:~ }|*3 163 ]], 164 messages = { { content = { { '?line ' } }, kind = 'search_cmd' } }, 165 }) 166 167 -- highlight 168 feed('G$:filter character highlight<CR>') 169 screen:expect({ 170 grid = [[ 171 line 1 | 172 line ^2 | 173 {1:~ }|*3 174 ]], 175 messages = { 176 { 177 content = { 178 { '@character ' }, 179 { 'xxx', 26, '@character' }, 180 { ' ' }, 181 { 'links to', 18, 'Directory' }, 182 { ' Character\n@character.special ' }, 183 { 'xxx', 16, '@character.special' }, 184 { ' ' }, 185 { 'links to', 18, 'Directory' }, 186 { ' SpecialChar' }, 187 }, 188 kind = 'list_cmd', 189 }, 190 }, 191 }) 192 193 -- undo 194 feed('u') 195 screen:expect({ 196 grid = [[ 197 ^ | 198 {1:~ }|*4 199 ]], 200 condition = function() 201 local msg = screen.messages[1].content[1][2]:gsub('%d seconds?', '0 seconds') 202 eq('2 fewer lines; before #1 0 seconds ago', msg) 203 eq('undo', screen.messages[1].kind) 204 screen.messages = {} 205 end, 206 }) 207 feed('u') 208 screen:expect({ 209 grid = [[ 210 ^ | 211 {1:~ }|*4 212 ]], 213 messages = { 214 { content = { { 'Already at oldest change' } }, history = true, kind = 'undo' }, 215 }, 216 }) 217 218 command('silent redo | silent redo | redo') 219 screen:expect({ 220 grid = [[ 221 line 1 | 222 line^ | 223 {1:~ }|*3 224 ]], 225 messages = { 226 { content = { { 'Already at newest change' } }, history = true, kind = 'undo' }, 227 }, 228 }) 229 230 -- kind=completion 231 command('set noshowmode') 232 feed('i<C-n>') 233 screen:expect({ 234 grid = [[ 235 line 1 | 236 line^ | 237 {1:~ }|*3 238 ]], 239 messages = { { content = { { 'The only match' } }, kind = 'completion' } }, 240 }) 241 feed('<Esc>l') 242 command('set showmode') 243 244 -- kind=echoerr for nvim_echo() err 245 feed(':call nvim_echo([["Error"], ["Message", "Special"]], 1, #{ err:1 })<CR>') 246 screen:expect({ 247 grid = [[ 248 line 1 | 249 line^ | 250 {1:~ }|*3 251 ]], 252 messages = { 253 { 254 content = { { 'Error', 9, 'ErrorMsg' }, { 'Message', 16, 'Special' } }, 255 history = true, 256 kind = 'echoerr', 257 }, 258 }, 259 }) 260 261 -- kind=verbose for nvim_echo() verbose 262 feed(':call nvim_echo([["Verbose Message"]], 1, #{ verbose:1 })<CR>') 263 screen:expect({ 264 grid = [[ 265 line 1 | 266 line^ | 267 {1:~ }|*3 268 ]], 269 messages = { 270 { content = { { 'Verbose Message' } }, history = true, kind = 'verbose' }, 271 }, 272 }) 273 274 feed(':call nvim_echo([["Foo"]], 1, #{ kind:"list_cmd" })<CR>') 275 screen:expect({ 276 grid = [[ 277 line 1 | 278 line^ | 279 {1:~ }|*3 280 ]], 281 messages = { { content = { { 'Foo' } }, history = true, kind = 'list_cmd' } }, 282 }) 283 284 -- kind=verbose for :verbose messages 285 feed(':1verbose filter Diff[AC] hi<CR>') 286 screen:expect({ 287 grid = [[ 288 line 1 | 289 line^ | 290 {1:~ }|*3 291 ]], 292 messages = { 293 { 294 content = { 295 { 'DiffAdd ' }, 296 { 'xxx', 22, 'DiffAdd' }, 297 { ' ' }, 298 { 'ctermbg=', 18, 'Directory' }, 299 { '81 ' }, 300 { 'guibg=', 18, 'Directory' }, 301 { 302 'LightBlue\n\tLast set from Lua (run Nvim with -V1 for more details)\nDiffChange ', 303 }, 304 { 'xxx', 4, 'DiffChange' }, 305 { ' ' }, 306 { 'ctermbg=', 18, 'Directory' }, 307 { '225 ' }, 308 { 'guibg=', 18, 'Directory' }, 309 { 'LightMagenta\n\tLast set from Lua (run Nvim with -V1 for more details)' }, 310 }, 311 kind = 'list_cmd', 312 }, 313 }, 314 }) 315 316 exec([[ 317 set verbose=9 318 augroup group1 319 autocmd BufEnter * echoh "BufEnter" 320 autocmd BufWinEnter * bdelete 321 autocmd ExitPre pat1 foo 322 autocmd ExitPre pat2 bar 323 augroup END 324 augroup group2 325 autocmd ExitPre pat1 foo 326 autocmd ExitPre pat2 bar 327 augroup END 328 ]]) 329 feed(':edit! foo<CR>') 330 screen:expect({ 331 grid = [[ 332 line 1 | 333 ^line | 334 {1:~ }|*3 335 ]], 336 messages = { 337 { content = { { 'finished sourcing nvim_exec2()' } }, history = true, kind = 'verbose' }, 338 { 339 content = { { 'Executing BufEnter Autocommands for "*"' } }, 340 history = true, 341 kind = 'verbose', 342 }, 343 { content = { { 'autocommand echoh "BufEnter"' } }, history = true, kind = 'verbose' }, 344 { content = { { '\n' } }, kind = '' }, 345 { 346 content = { { 'Executing BufWinEnter Autocommands for "*"' } }, 347 history = true, 348 kind = 'verbose', 349 }, 350 { content = { { 'autocommand bdelete' } }, history = true, kind = 'verbose' }, 351 { content = { { '\n' } }, kind = '' }, 352 }, 353 }) 354 feed(':au ExitPre<CR>') 355 screen:expect({ 356 grid = [[ 357 line 1 | 358 ^line | 359 {1:~ }|*3 360 ]], 361 messages = { 362 { 363 content = { 364 { '--- Autocommands ---', 101, 'Title' }, 365 { '\n' }, 366 { 'group1', 101, 'Title' }, 367 { ' ' }, 368 { 'ExitPre', 101, 'Title' }, 369 { 370 '\n pat1 foo\n\tLast set from anonymous :source line 5\n pat2 bar\n\tLast set from anonymous :source line 6\n', 371 }, 372 { 'group2', 101, 'Title' }, 373 { ' ' }, 374 { 'ExitPre', 101, 'Title' }, 375 { 376 '\n pat1 foo\n\tLast set from anonymous :source line 9\n pat2 bar\n\tLast set from anonymous :source line 10', 377 }, 378 }, 379 kind = 'list_cmd', 380 }, 381 }, 382 }) 383 command('autocmd! group1') 384 command('autocmd! group2') 385 command('augroup! group1') 386 command('augroup! group2') 387 command('set verbose=0') 388 389 n.add_builddir_to_rtp() 390 feed(':help<CR>:tselect<CR>') 391 screen:expect({ 392 grid = [[ 393 ^*help.txt* Nvim | 394 | 395 {3:help.txt [Help][-][RO] }| 396 line | 397 {2:<i_messages_spec [+][RO] }| 398 ]], 399 cmdline = { 400 { 401 content = { { '' } }, 402 pos = 0, 403 prompt = 'Type number and <Enter> (q or empty cancels): ', 404 }, 405 }, 406 -- Message depends on runtimepath, only test the static text... 407 condition = function() 408 for _, msg in ipairs(screen.messages) do 409 eq(false, msg.history) 410 eq('confirm', msg.kind) 411 eq(' # pri kind tag', msg.content[1][2]) 412 eq('\n ', msg.content[2][2]) 413 eq('file\n', msg.content[3][2]) 414 eq('> 1 F ', msg.content[4][2]) 415 eq('help.txt', msg.content[5][2]) 416 eq(' \n ', msg.content[6][2]) 417 eq('\n *help.txt*', msg.content[#msg.content][2]) 418 end 419 screen.messages = {} 420 end, 421 }) 422 feed('<CR>:bdelete<CR>$') 423 424 -- kind=shell for :!cmd messages 425 local cmd = t.is_os('win') and 'echo stdout& echo stderr>&2& exit 3' 426 or '{ echo stdout; echo stderr >&2; exit 3; }' 427 feed((':!%s<CR>'):format(cmd)) 428 screen:expect({ 429 grid = [[ 430 line 1 | 431 line^ | 432 {1:~ }|*3 433 ]], 434 messages = { 435 { 436 content = { { (':!%s\r\n[No write since last change]\n'):format(cmd) } }, 437 kind = 'shell_cmd', 438 }, 439 { 440 content = { { ('stdout%s\n'):format(t.is_os('win') and '\r' or ''), 'StdoutMsg' } }, 441 kind = 'shell_out', 442 }, 443 { 444 content = { { ('stderr%s\n'):format(t.is_os('win') and '\r' or ''), 9, 'StderrMsg' } }, 445 kind = 'shell_err', 446 }, 447 { content = { { '\nshell returned 3\n' } }, kind = 'shell_ret' }, 448 }, 449 }) 450 451 feed(':registers .<CR>') 452 screen:expect({ 453 grid = [[ 454 line 1 | 455 line^ | 456 {1:~ }|*3 457 ]], 458 messages = { 459 { 460 content = { { 'Type Name Content', 101, 'Title' }, { '\n c ". ' } }, 461 kind = 'list_cmd', 462 }, 463 }, 464 }) 465 466 feed(':au ChanInfo * foo<CR>:au ChanInfo<CR>') 467 screen:expect({ 468 grid = [[ 469 line 1 | 470 line^ | 471 {1:~ }|*3 472 ]], 473 messages = { 474 { 475 content = { 476 { '--- Autocommands ---', 101, 'Title' }, 477 { '\n' }, 478 { 'ChanInfo', 101, 'Title' }, 479 { '\n * foo' }, 480 }, 481 kind = 'list_cmd', 482 }, 483 }, 484 }) 485 486 feed(':1,2p<CR>') 487 screen:expect({ 488 grid = [[ 489 line 1 | 490 ^line | 491 {1:~ }|*3 492 ]], 493 messages = { { content = { { 'line 1\nline ' } }, kind = 'list_cmd' } }, 494 }) 495 496 -- single message for :global command #37726 497 feed(':g/line<CR>') 498 screen:expect({ 499 grid = [[ 500 line 1 | 501 ^line | 502 {1:~ }|*3 503 ]], 504 messages = { { content = { { 'line 1\nline ' } }, kind = 'list_cmd' } }, 505 }) 506 507 command('command Foo Bar') 508 feed(':command<CR>') 509 screen:expect({ 510 grid = [[ 511 line 1 | 512 ^line | 513 {1:~ }|*3 514 ]], 515 messages = { 516 { 517 content = { 518 { ' Name Args Address Complete Definition', 101, 'Title' }, 519 { '\n ' }, 520 { 'Foo', 18, 'Directory' }, 521 { ' 0 Bar' }, 522 }, 523 kind = 'list_cmd', 524 }, 525 }, 526 }) 527 528 feed(':colorscheme<CR>') 529 screen:expect({ 530 grid = [[ 531 line 1 | 532 ^line | 533 {1:~ }|*3 534 ]], 535 messages = { { content = { { 'default' } }, history = true, kind = 'list_cmd' } }, 536 }) 537 538 feed(':version<CR>') 539 screen:expect({ 540 grid = [[ 541 line 1 | 542 ^line | 543 {1:~ }|*3 544 ]], 545 condition = function() 546 eq('list_cmd', screen.messages[1].kind) 547 screen.messages = {} -- Ignore build dependent :version content 548 end, 549 }) 550 551 -- 3 empty message events, not for an empty chunk after a non-printable character 552 feed(':echo "foo\\n" | echo "" | echom "" | lua print()<CR>') 553 screen:expect({ 554 grid = [[ 555 line 1 | 556 ^line | 557 {1:~ }|*3 558 ]], 559 messages = { 560 { content = { { 'foo\n' } }, kind = 'echo' }, 561 { content = {}, kind = 'empty' }, 562 { content = {}, kind = 'empty' }, 563 { content = {}, kind = 'empty' }, 564 }, 565 }) 566 567 -- No empty message event for empty option value 568 feed(':set foldclose<CR>') 569 screen:expect({ 570 grid = [[ 571 line 1 | 572 ^line | 573 {1:~ }|*3 574 ]], 575 messages = { { content = { { ' foldclose=' } }, history = true, kind = 'list_cmd' } }, 576 }) 577 end) 578 579 it(':echoerr', function() 580 feed(':echoerr "raa"<cr>') 581 screen:expect { 582 grid = [[ 583 ^ | 584 {1:~ }|*4 585 ]], 586 messages = { { content = { { 'raa', 9, 'ErrorMsg' } }, history = true, kind = 'echoerr' } }, 587 } 588 589 -- cmdline in a later input cycle clears error message 590 feed(':') 591 screen:expect { 592 grid = [[ 593 ^ | 594 {1:~ }|*4 595 ]], 596 cmdline = { { firstc = ':', content = { { '' } }, pos = 0 } }, 597 } 598 599 feed('echoerr "bork" | echoerr "fail"<cr>') 600 screen:expect { 601 grid = [[ 602 ^ | 603 {1:~ }|*4 604 ]], 605 messages = { 606 { content = { { 'bork', 9, 'ErrorMsg' } }, history = true, kind = 'echoerr' }, 607 { content = { { 'fail', 9, 'ErrorMsg' } }, history = true, kind = 'echoerr' }, 608 }, 609 } 610 611 feed(':echoerr "extrafail"<cr>') 612 screen:expect { 613 grid = [[ 614 ^ | 615 {1:~ }|*4 616 ]], 617 messages = { 618 { 619 content = { { 'extrafail', 9, 'ErrorMsg' } }, 620 history = true, 621 kind = 'echoerr', 622 }, 623 }, 624 } 625 626 -- cmdline without interleaving wait/display keeps the error message 627 feed(':echoerr "problem" | let x = input("foo> ")<cr>') 628 screen:expect { 629 grid = [[ 630 ^ | 631 {1:~ }|*4 632 ]], 633 messages = { 634 { content = { { 'problem', 9, 'ErrorMsg' } }, history = true, kind = 'echoerr' }, 635 }, 636 cmdline = { { prompt = 'foo> ', content = { { '' } }, pos = 0 } }, 637 } 638 639 feed('solution<cr>') 640 screen:expect_unchanged() 641 eq('solution', eval('x')) 642 643 feed(':messages<cr>') 644 screen:expect { 645 grid = [[ 646 ^ | 647 {1:~ }|*4 648 ]], 649 msg_history = { 650 { kind = 'echoerr', content = { { 'raa', 9, 'ErrorMsg' } } }, 651 { kind = 'echoerr', content = { { 'bork', 9, 'ErrorMsg' } } }, 652 { kind = 'echoerr', content = { { 'fail', 9, 'ErrorMsg' } } }, 653 { kind = 'echoerr', content = { { 'extrafail', 9, 'ErrorMsg' } } }, 654 { kind = 'echoerr', content = { { 'problem', 9, 'ErrorMsg' } } }, 655 }, 656 } 657 end) 658 659 it(':echoerr multiline', function() 660 exec_lua([[vim.g.multi = table.concat({ "bork", "fail" }, "\n")]]) 661 feed(':echoerr g:multi<cr>') 662 screen:expect { 663 grid = [[ 664 ^ | 665 {1:~ }|*4 666 ]], 667 messages = { 668 { 669 content = { { 'bork\nfail', 9, 'ErrorMsg' } }, 670 history = true, 671 kind = 'echoerr', 672 }, 673 }, 674 } 675 676 feed(':messages<cr>') 677 screen:expect { 678 grid = [[ 679 ^ | 680 {1:~ }|*4 681 ]], 682 msg_history = { { content = { { 'bork\nfail', 9, 'ErrorMsg' } }, kind = 'echoerr' } }, 683 } 684 end) 685 686 it('shortmess-=S', function() 687 command('set shortmess-=S') 688 feed('iline 1\nline 2<esc>') 689 690 feed('/line<cr>') 691 screen:expect { 692 grid = [[ 693 {10:^line} 1 | 694 {10:line} 2 | 695 {1:~ }|*3 696 ]], 697 messages = { { content = { { '/line W [1/2]' } }, kind = 'search_count' } }, 698 } 699 700 feed('n') 701 screen:expect { 702 grid = [[ 703 {10:line} 1 | 704 {10:^line} 2 | 705 {1:~ }|*3 706 ]], 707 messages = { { content = { { '/line [2/2]' } }, kind = 'search_count' } }, 708 } 709 end) 710 711 it("doesn't crash with column adjustment #10069", function() 712 feed(':let [x,y] = [1,2]<cr>') 713 feed(':let x y<cr>') 714 screen:expect({ 715 grid = [[ 716 ^ | 717 {1:~ }|*4 718 ]], 719 messages = { 720 { 721 content = { { 'x #1\ny #2' } }, 722 kind = 'list_cmd', 723 }, 724 }, 725 }) 726 end) 727 728 it('&showmode', function() 729 command('imap <f2> <cmd>echomsg "stuff"<cr>') 730 feed('i') 731 screen:expect { 732 grid = [[ 733 ^ | 734 {1:~ }|*4 735 ]], 736 showmode = { { '-- INSERT --', 5, 'ModeMsg' } }, 737 } 738 739 feed('alphpabet<cr>alphanum<cr>') 740 screen:expect { 741 grid = [[ 742 alphpabet | 743 alphanum | 744 ^ | 745 {1:~ }|*2 746 ]], 747 showmode = { { '-- INSERT --', 5, 'ModeMsg' } }, 748 } 749 750 feed('<c-x>') 751 screen:expect { 752 grid = [[ 753 alphpabet | 754 alphanum | 755 ^ | 756 {1:~ }|*2 757 ]], 758 showmode = { { '-- ^X mode (^]^D^E^F^I^K^L^N^O^P^Rs^U^V^Y)', 5, 'ModeMsg' } }, 759 } 760 761 feed('<c-p>') 762 screen:expect { 763 grid = [[ 764 alphpabet | 765 alphanum | 766 alphanum^ | 767 {1:~ }|*2 768 ]], 769 popupmenu = { 770 anchor = { 1, 2, 0 }, 771 items = { { 'alphpabet', '', '', '' }, { 'alphanum', '', '', '' } }, 772 pos = 1, 773 }, 774 showmode = { 775 { '-- Keyword Local completion (^N^P) ', 5, 'ModeMsg' }, 776 { 'match 1 of 2', 6, 'Question' }, 777 }, 778 } 779 780 -- echomsg and showmode don't overwrite each other, this is the same 781 -- as the TUI behavior with cmdheight=2 or larger. 782 feed('<f2>') 783 screen:expect { 784 grid = [[ 785 alphpabet | 786 alphanum | 787 alphanum^ | 788 {1:~ }|*2 789 ]], 790 popupmenu = { 791 anchor = { 1, 2, 0 }, 792 items = { { 'alphpabet', '', '', '' }, { 'alphanum', '', '', '' } }, 793 pos = 1, 794 }, 795 messages = { { content = { { 'stuff' } }, history = true, kind = 'echomsg' } }, 796 showmode = { 797 { '-- Keyword Local completion (^N^P) ', 5, 'ModeMsg' }, 798 { 'match 1 of 2', 6, 'Question' }, 799 }, 800 } 801 802 feed('<c-p>') 803 screen:expect { 804 grid = [[ 805 alphpabet | 806 alphanum | 807 alphpabet^ | 808 {1:~ }|*2 809 ]], 810 popupmenu = { 811 anchor = { 1, 2, 0 }, 812 items = { { 'alphpabet', '', '', '' }, { 'alphanum', '', '', '' } }, 813 pos = 0, 814 }, 815 showmode = { 816 { '-- Keyword Local completion (^N^P) ', 5, 'ModeMsg' }, 817 { 'match 2 of 2', 6, 'Question' }, 818 }, 819 } 820 821 feed('<esc>:messages<cr>') 822 screen:expect { 823 grid = [[ 824 alphpabet | 825 alphanum | 826 alphpabe^t | 827 {1:~ }|*2 828 ]], 829 msg_history = { { content = { { 'stuff' } }, kind = 'echomsg' } }, 830 } 831 end) 832 833 it('&showmode with macro-recording message', function() 834 feed('qq') 835 screen:expect { 836 grid = [[ 837 ^ | 838 {1:~ }|*4 839 ]], 840 showmode = { { 'recording @q', 5, 'ModeMsg' } }, 841 } 842 843 feed('i') 844 screen:expect { 845 grid = [[ 846 ^ | 847 {1:~ }|*4 848 ]], 849 showmode = { { '-- INSERT --recording @q', 5, 'ModeMsg' } }, 850 } 851 852 feed('<esc>') 853 screen:expect { 854 grid = [[ 855 ^ | 856 {1:~ }|*4 857 ]], 858 showmode = { { 'recording @q', 5, 'ModeMsg' } }, 859 } 860 861 feed('q') 862 screen:expect([[ 863 ^ | 864 {1:~ }|*4 865 ]]) 866 end) 867 868 it('shows macro-recording message with &noshowmode', function() 869 command('set noshowmode') 870 feed('qq') 871 -- also check mode to avoid immediate success 872 screen:expect { 873 grid = [[ 874 ^ | 875 {1:~ }|*4 876 ]], 877 showmode = { { 'recording @q', 5, 'ModeMsg' } }, 878 mode = 'normal', 879 } 880 881 feed('i') 882 screen:expect { 883 grid = [[ 884 ^ | 885 {1:~ }|*4 886 ]], 887 showmode = { { 'recording @q', 5, 'ModeMsg' } }, 888 mode = 'insert', 889 } 890 891 feed('<esc>') 892 screen:expect { 893 grid = [[ 894 ^ | 895 {1:~ }|*4 896 ]], 897 showmode = { { 'recording @q', 5, 'ModeMsg' } }, 898 mode = 'normal', 899 } 900 901 feed('q') 902 screen:expect { 903 grid = [[ 904 ^ | 905 {1:~ }|*4 906 ]], 907 mode = 'normal', 908 } 909 end) 910 911 it("supports 'showcmd' and 'ruler(format)'", function() 912 command('set showcmd ruler') 913 command('hi link MsgArea ErrorMsg') 914 screen:expect({ 915 grid = [[ 916 ^ | 917 {1:~ }|*4 918 ]], 919 ruler = { { '0,0-1 All', 9, 'MsgArea' } }, 920 }) 921 command('hi clear MsgArea') 922 feed('i') 923 screen:expect { 924 grid = [[ 925 ^ | 926 {1:~ }|*4 927 ]], 928 showmode = { { '-- INSERT --', 5, 'ModeMsg' } }, 929 ruler = { { '0,1 All', 'MsgArea' } }, 930 } 931 feed('abcde<cr>12345<esc>') 932 screen:expect { 933 grid = [[ 934 abcde | 935 1234^5 | 936 {1:~ }|*3 937 ]], 938 ruler = { { '2,5 All', 'MsgArea' } }, 939 } 940 feed('d') 941 screen:expect { 942 grid = [[ 943 abcde | 944 1234^5 | 945 {1:~ }|*3 946 ]], 947 showcmd = { { 'd' } }, 948 ruler = { { '2,5 All', 'MsgArea' } }, 949 } 950 feed('<esc>^') 951 screen:expect { 952 grid = [[ 953 abcde | 954 ^12345 | 955 {1:~ }|*3 956 ]], 957 ruler = { { '2,1 All', 'MsgArea' } }, 958 } 959 feed('<c-v>k2l') 960 screen:expect({ 961 grid = [[ 962 {17:ab}^cde | 963 {17:123}45 | 964 {1:~ }|*3 965 ]], 966 showmode = { { '-- VISUAL BLOCK --', 5, 'ModeMsg' } }, 967 showcmd = { { '2x3' } }, 968 ruler = { { '1,3 All', 'MsgArea' } }, 969 }) 970 feed('o<esc>d') 971 screen:expect { 972 grid = [[ 973 abcde | 974 ^12345 | 975 {1:~ }|*3 976 ]], 977 showcmd = { { 'd' } }, 978 ruler = { { '2,1 All', 'MsgArea' } }, 979 } 980 feed('i') 981 screen:expect { 982 grid = [[ 983 abcde | 984 ^12345 | 985 {1:~ }|*3 986 ]], 987 showcmd = { { 'di' } }, 988 ruler = { { '2,1 All', 'MsgArea' } }, 989 } 990 feed('w') 991 screen:expect { 992 grid = [[ 993 abcde | 994 ^ | 995 {1:~ }|*3 996 ]], 997 ruler = { { '2,0-1 All', 'MsgArea' } }, 998 } 999 command('set rulerformat=Foo%#ErrorMsg#Bar') 1000 screen:expect({ 1001 grid = [[ 1002 abcde | 1003 ^ | 1004 {1:~ }|*3 1005 ]], 1006 ruler = { { 'Foo', 'MsgArea' }, { 'Bar', 9, 'ErrorMsg' } }, 1007 }) 1008 command('set rulerformat=') 1009 1010 -- when ruler is part of statusline it is not externalized. 1011 -- this will be added as part of future ext_statusline support 1012 command('set laststatus=2') 1013 screen:expect([[ 1014 abcde | 1015 ^ | 1016 {1:~ }|*2 1017 {3:<] [+] 2,0-1 All}| 1018 ]]) 1019 -- ruler of float is not part of statusline and is cleared when leaving the float #37649. 1020 command('set rulerformat=foo') 1021 api.nvim_open_win(0, true, { relative = 'editor', row = 1, col = 1, width = 10, height = 10 }) 1022 screen:expect({ 1023 grid = [[ 1024 a{4:abcde } | 1025 {4:^ } | 1026 {1:~}{11:~ }{1: }|*2 1027 {2:[}{11:~ }{2:+] foo}| 1028 ]], 1029 ruler = { { 'foo', 'MsgArea' } }, 1030 }) 1031 command('wincmd p') 1032 screen:expect([[ 1033 a{4:abcde } | 1034 ^ {4: } | 1035 {1:~}{11:~ }{1: }|*2 1036 {3:[}{11:~ }{3:+] foo}| 1037 ]]) 1038 end) 1039 1040 it('keeps history of message of different kinds', function() 1041 feed(':echomsg "howdy"<cr>') 1042 screen:expect { 1043 grid = [[ 1044 ^ | 1045 {1:~ }|*4 1046 ]], 1047 messages = { { content = { { 'howdy' } }, history = true, kind = 'echomsg' } }, 1048 } 1049 1050 -- always test a message without kind. If this one gets promoted to a 1051 -- category, add a new message without kind. 1052 feed('<c-c>') 1053 screen:expect { 1054 grid = [[ 1055 ^ | 1056 {1:~ }|*4 1057 ]], 1058 messages = { 1059 { 1060 content = { { 'Type :qa and press <Enter> to exit Nvim' } }, 1061 history = true, 1062 kind = '', 1063 }, 1064 }, 1065 } 1066 1067 feed(':echoerr "bork"<cr>') 1068 screen:expect { 1069 grid = [[ 1070 ^ | 1071 {1:~ }|*4 1072 ]], 1073 messages = { 1074 { content = { { 'bork', 9, 'ErrorMsg' } }, history = true, kind = 'echoerr' }, 1075 }, 1076 } 1077 1078 feed(':echo "xyz"<cr>') 1079 screen:expect { 1080 grid = [[ 1081 ^ | 1082 {1:~ }|*4 1083 ]], 1084 messages = { { content = { { 'xyz' } }, kind = 'echo' } }, 1085 } 1086 1087 feed(':call nosuchfunction()<cr>') 1088 screen:expect { 1089 grid = [[ 1090 ^ | 1091 {1:~ }|*4 1092 ]], 1093 messages = { 1094 { 1095 content = { { 'E117: Unknown function: nosuchfunction', 9, 'ErrorMsg' } }, 1096 history = true, 1097 kind = 'emsg', 1098 }, 1099 }, 1100 } 1101 1102 feed(':messages<cr>') 1103 screen:expect { 1104 grid = [[ 1105 ^ | 1106 {1:~ }|*4 1107 ]], 1108 msg_history = { 1109 { kind = 'echomsg', content = { { 'howdy' } } }, 1110 { kind = '', content = { { 'Type :qa and press <Enter> to exit Nvim' } } }, 1111 { kind = 'echoerr', content = { { 'bork', 9, 'ErrorMsg' } } }, 1112 { 1113 kind = 'emsg', 1114 content = { { 'E117: Unknown function: nosuchfunction', 9, 'ErrorMsg' } }, 1115 }, 1116 }, 1117 } 1118 end) 1119 1120 it("implies ext_cmdline but allows changing 'cmdheight'", function() 1121 eq(0, eval('&cmdheight')) 1122 feed(':set cmdheight=1') 1123 screen:expect { 1124 grid = [[ 1125 ^ | 1126 {1:~ }|*4 1127 ]], 1128 cmdline = { { content = { { 'set cmdheight=1' } }, firstc = ':', pos = 15 } }, 1129 } 1130 1131 feed('<cr>') 1132 screen:expect([[ 1133 ^ | 1134 {1:~ }|*3 1135 | 1136 ]]) 1137 eq(1, eval('&cmdheight')) 1138 1139 feed(':set cmdheight=0') 1140 screen:expect { 1141 grid = [[ 1142 ^ | 1143 {1:~ }|*3 1144 | 1145 ]], 1146 cmdline = { { content = { { 'set cmdheight=0' } }, firstc = ':', pos = 15 } }, 1147 } 1148 feed('<cr>') 1149 screen:expect([[ 1150 ^ | 1151 {1:~ }|*4 1152 ]]) 1153 eq(0, eval('&cmdheight')) 1154 end) 1155 1156 it('supports multiline messages from lua', function() 1157 feed(':lua error("such\\nmultiline\\nerror")<cr>') 1158 screen:expect { 1159 grid = [[ 1160 ^ | 1161 {1:~ }|*4 1162 ]], 1163 messages = { 1164 { 1165 content = { 1166 { 1167 [[E5108: Lua: [string ":lua"]:1: such 1168 multiline 1169 error 1170 stack traceback: 1171 [C]: in function 'error' 1172 [string ":lua"]:1: in main chunk]], 1173 9, 1174 'ErrorMsg', 1175 }, 1176 }, 1177 history = true, 1178 kind = 'lua_error', 1179 }, 1180 }, 1181 } 1182 end) 1183 1184 it('supports multiline messages from rpc', function() 1185 feed(':call rpcrequest(1, "test_method")<cr>') 1186 1187 screen:expect { 1188 grid = [[ 1189 ^ | 1190 {1:~ }|*4 1191 ]], 1192 messages = { 1193 { 1194 content = { 1195 { 1196 "Invoking 'test_method' on channel 1:\ncomplete\nerror\n\nmessage", 1197 9, 1198 'ErrorMsg', 1199 }, 1200 }, 1201 history = true, 1202 kind = 'rpc_error', 1203 }, 1204 }, 1205 request_cb = function(name) 1206 if name == 'test_method' then 1207 set_method_error('complete\nerror\n\nmessage') 1208 end 1209 end, 1210 } 1211 end) 1212 1213 it('supports multiline messages for :map', function() 1214 command('mapclear') 1215 command('nmap Y y$') 1216 command('nmap Q @@') 1217 command('nnoremap j k') 1218 feed(':map<cr>') 1219 1220 screen:expect { 1221 grid = [[ 1222 ^ | 1223 {1:~ }|*4 1224 ]], 1225 messages = { 1226 { 1227 content = { 1228 { 'n Q @@\nn Y y$\nn j ' }, 1229 { '*', 18, 'SpecialKey' }, 1230 { ' k' }, 1231 }, 1232 kind = 'list_cmd', 1233 }, 1234 }, 1235 } 1236 end) 1237 1238 it('wildmode=list', function() 1239 screen:try_resize(25, 7) 1240 screen:set_option('ext_popupmenu', false) 1241 1242 command('set wildmenu wildmode=list') 1243 feed(':set wildm<tab>') 1244 screen:expect { 1245 grid = [[ 1246 ^ | 1247 {1:~ }|*6 1248 ]], 1249 messages = { { content = { { 'wildmenu wildmode\n' } }, kind = 'wildlist' } }, 1250 cmdline = { { firstc = ':', content = { { 'set wildm' } }, pos = 9 } }, 1251 } 1252 end) 1253 1254 it('hides prompt_for_number messages', function() 1255 command('set spell') 1256 feed('ihelllo<esc>') 1257 1258 feed('z=') 1259 screen:expect({ 1260 grid = [[ 1261 {100:helll^o} | 1262 {1:~ }|*4 1263 ]], 1264 cmdline = { 1265 { 1266 content = { { '' } }, 1267 pos = 0, 1268 prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels): ', 1269 }, 1270 }, 1271 messages = { 1272 { 1273 content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"' } }, 1274 kind = 'confirm', 1275 }, 1276 }, 1277 }) 1278 1279 feed('1') 1280 screen:expect({ 1281 grid = [[ 1282 {100:helll^o} | 1283 {1:~ }|*4 1284 ]], 1285 cmdline = { 1286 { 1287 content = { { '1' } }, 1288 pos = 1, 1289 prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels): ', 1290 }, 1291 }, 1292 }) 1293 1294 feed('<cr>') 1295 screen:expect([[ 1296 ^Hello | 1297 {1:~ }|*4 1298 ]]) 1299 1300 async_meths.nvim_command("let g:n = inputlist(['input0', 'input1'])") 1301 screen:expect({ 1302 grid = [[ 1303 ^Hello | 1304 {1:~ }|*4 1305 ]], 1306 cmdline = { 1307 { 1308 content = { { '' } }, 1309 pos = 0, 1310 prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels): ', 1311 }, 1312 }, 1313 messages = { { content = { { 'input0\ninput1' } }, kind = 'confirm' } }, 1314 }) 1315 1316 feed('42<CR>') 1317 screen:expect({ 1318 grid = [[ 1319 ^Hello | 1320 {1:~ }|*4 1321 ]], 1322 unchanged = true, 1323 }) 1324 eq(42, eval('g:n')) 1325 end) 1326 1327 it('supports nvim_echo messages with multiple attrs', function() 1328 local chunks = { { 'wow, ', 'Search' }, { 'such\n\nvery ', 'ErrorMsg' }, { 'color', 'LineNr' } } 1329 async_meths.nvim_echo(chunks, true, {}) 1330 screen:expect { 1331 grid = [[ 1332 ^ | 1333 {1:~ }|*4 1334 ]], 1335 messages = { 1336 { 1337 content = { 1338 { 'wow, ', 10, 'Search' }, 1339 { 'such\n\nvery ', 9, 'ErrorMsg' }, 1340 { 'color', 8, 'LineNr' }, 1341 }, 1342 history = true, 1343 kind = 'echomsg', 1344 }, 1345 }, 1346 } 1347 1348 feed ':ls<cr>' 1349 screen:expect { 1350 grid = [[ 1351 ^ | 1352 {1:~ }|*4 1353 ]], 1354 messages = { 1355 { content = { { ' 1 %a "[No Name]" line 1' } }, kind = 'list_cmd' }, 1356 }, 1357 } 1358 1359 feed ':messages<cr>' 1360 screen:expect { 1361 grid = [[ 1362 ^ | 1363 {1:~ }|*4 1364 ]], 1365 msg_history = { 1366 { 1367 content = { 1368 { 'wow, ', 10, 'Search' }, 1369 { 'such\n\nvery ', 9, 'ErrorMsg' }, 1370 { 'color', 8, 'LineNr' }, 1371 }, 1372 kind = 'echomsg', 1373 }, 1374 }, 1375 } 1376 end) 1377 1378 it('does not truncate messages', function() 1379 command('write ' .. fname) 1380 screen:expect({ 1381 messages = { 1382 { 1383 content = { { string.format('"%s" [New] 0L, 0B written', fname) } }, 1384 kind = 'bufwrite', 1385 history = true, 1386 }, 1387 }, 1388 }) 1389 end) 1390 1391 it('does not do showmode unnecessarily #29086', function() 1392 local screen_showmode = screen._handle_msg_showmode 1393 local showmode = 0 1394 screen._handle_msg_showmode = function(...) 1395 screen_showmode(...) 1396 showmode = showmode + 1 1397 end 1398 screen:expect([[ 1399 ^ | 1400 {1:~ }|*4 1401 ]]) 1402 eq(showmode, 0) 1403 feed('i') 1404 screen:expect({ 1405 grid = [[ 1406 ^ | 1407 {1:~ }|*4 1408 ]], 1409 showmode = { { '-- INSERT --', 5, 'ModeMsg' } }, 1410 }) 1411 eq(showmode, 2) 1412 command('set noshowmode') 1413 feed('<Esc>') 1414 screen:expect([[ 1415 ^ | 1416 {1:~ }|*4 1417 ]]) 1418 eq(showmode, 3) 1419 feed('i') 1420 screen:expect_unchanged() 1421 eq(showmode, 3) 1422 end) 1423 1424 it('emits single message for multiline print())', function() 1425 exec_lua([[print("foo\nbar\nbaz")]]) 1426 screen:expect({ 1427 grid = [[ 1428 ^ | 1429 {1:~ }|*4 1430 ]], 1431 messages = { { content = { { 'foo\nbar\nbaz' } }, history = true, kind = 'lua_print' } }, 1432 }) 1433 exec_lua([[print(vim.inspect({ foo = "bar" }))]]) 1434 screen:expect({ 1435 grid = [[ 1436 ^ | 1437 {1:~ }|*4 1438 ]], 1439 messages = { 1440 { content = { { '{\n foo = "bar"\n}' } }, history = true, kind = 'lua_print' }, 1441 }, 1442 }) 1443 exec_lua([[vim.print({ foo = "bar" })]]) 1444 screen:expect({ 1445 grid = [[ 1446 ^ | 1447 {1:~ }|*4 1448 ]], 1449 messages = { 1450 { content = { { '{\n foo = "bar"\n}' } }, history = true, kind = 'lua_print' }, 1451 }, 1452 }) 1453 end) 1454 1455 it('ruler redraw does not crash due to double grid_line_start()', function() 1456 exec_lua([[ 1457 local ns = vim.api.nvim_create_namespace('') 1458 vim.ui_attach(ns, { ext_messages = true }, function(event, ...) 1459 if event == 'msg_ruler' then 1460 vim.api.nvim__redraw({ flush = true }) 1461 end 1462 end) 1463 vim.o.ruler = true 1464 vim.o.laststatus = 0 1465 ]]) 1466 feed('i') 1467 n.assert_alive() 1468 end) 1469 1470 it(':digraph contains newlines', function() 1471 command('digraph') 1472 screen:expect({ 1473 condition = function() 1474 local nl = 0 1475 eq('list_cmd', screen.messages[1].kind) 1476 for _, chunk in ipairs(screen.messages[1].content) do 1477 nl = nl + (chunk[2]:find('\n') and 1 or 0) 1478 end 1479 eq(683, nl) 1480 screen.messages = {} 1481 end, 1482 }) 1483 end) 1484 1485 it('g< mapping shows recent messages', function() 1486 feed(':echo "foo" | echo "bar" | echon "baz"<CR>') 1487 screen:expect({ 1488 grid = [[ 1489 ^ | 1490 {1:~ }|*4 1491 ]], 1492 messages = { 1493 { content = { { 'foo' } }, kind = 'echo' }, 1494 { content = { { 'bar' } }, kind = 'echo' }, 1495 { content = { { 'baz' } }, kind = 'echo', append = true }, 1496 }, 1497 }) 1498 feed('g<lt>') 1499 screen:expect({ 1500 grid = [[ 1501 ^ | 1502 {1:~ }|*4 1503 ]], 1504 msg_history = { 1505 prev_cmd = true, 1506 { content = { { 'foo' } }, kind = 'echo' }, 1507 { content = { { 'bar' } }, kind = 'echo' }, 1508 { content = { { 'baz' } }, kind = 'echo', append = true }, 1509 }, 1510 }) 1511 feed('Q') 1512 screen:expect({ 1513 grid = [[ 1514 ^ | 1515 {1:~ }|*4 1516 ]], 1517 messages = { 1518 { 1519 content = { { "E354: Invalid register name: '^@'", 9, 'ErrorMsg' } }, 1520 history = true, 1521 kind = 'emsg', 1522 }, 1523 }, 1524 }) 1525 feed('g<lt>') 1526 screen:expect({ 1527 grid = [[ 1528 ^ | 1529 {1:~ }|*4 1530 ]], 1531 msg_history = { 1532 prev_cmd = true, 1533 { content = { { "E354: Invalid register name: '^@'", 9, 'ErrorMsg' } }, kind = 'emsg' }, 1534 }, 1535 }) 1536 end) 1537 1538 it('single event for multiple :set options', function() 1539 command('set sw ts sts') 1540 screen:expect({ 1541 grid = [[ 1542 ^ | 1543 {1:~ }|*4 1544 ]], 1545 messages = { 1546 { content = { { ' shiftwidth=8\n tabstop=8\n softtabstop=0' } }, kind = 'list_cmd' }, 1547 }, 1548 }) 1549 end) 1550 1551 it('clears showmode after insert_expand mode', function() 1552 feed('i<C-N>') 1553 screen:expect({ 1554 grid = [[ 1555 ^ | 1556 {1:~ }|*4 1557 ]], 1558 showmode = { 1559 { '-- Keyword completion (^N^P) ', 5, 'ModeMsg' }, 1560 { 'Pattern not found', 9, 'ErrorMsg' }, 1561 }, 1562 }) 1563 feed('<Esc>') 1564 screen:expect([[ 1565 ^ | 1566 {1:~ }|*4 1567 ]]) 1568 end) 1569 1570 it(':echon sets append', function() 1571 command('echo "foo" | echon " bar" | echon " baz"') 1572 screen:expect({ 1573 grid = [[ 1574 ^ | 1575 {1:~ }|*4 1576 ]], 1577 messages = { 1578 { content = { { 'foo' } }, kind = 'echo' }, 1579 { content = { { ' bar' } }, kind = 'echo', append = true }, 1580 { content = { { ' baz' } }, kind = 'echo', append = true }, 1581 }, 1582 }) 1583 end) 1584 1585 it('can capture execute("messages"))', function() 1586 feed('Q') 1587 screen:expect({ 1588 grid = [[ 1589 ^ | 1590 {1:~ }|*4 1591 ]], 1592 messages = { 1593 { 1594 content = { { "E354: Invalid register name: '^@'", 9, 'ErrorMsg' } }, 1595 history = true, 1596 kind = 'emsg', 1597 }, 1598 }, 1599 }) 1600 feed(':let msg = execute("messages")<CR>') 1601 screen:expect_unchanged() 1602 eq("E354: Invalid register name: '^@'", eval('msg'):gsub('\n', '')) 1603 end) 1604 1605 it('single event for multi-expr :echo', function() 1606 command('echo 1 2 | echon 1 2') 1607 screen:expect({ 1608 grid = [[ 1609 ^ | 1610 {1:~ }|*4 1611 ]], 1612 messages = { 1613 { content = { { '1 2' } }, kind = 'echo' }, 1614 { content = { { '12' } }, kind = 'echo', append = true }, 1615 }, 1616 }) 1617 end) 1618 1619 it('completion message overwrites previous', function() 1620 command('set shortmess-=C | edit foo | edit bar | edit baz') 1621 feed('i<C-N>') 1622 screen:expect({ 1623 grid = [[ 1624 ^ | 1625 {1:~ }|*4 1626 ]], 1627 messages = { 1628 { 1629 content = { { 'Scanning tags.', 6, 'Question' } }, 1630 kind = 'completion', 1631 }, 1632 }, 1633 showmode = { 1634 { '-- Keyword completion (^N^P) ', 5, 'ModeMsg' }, 1635 { 'Pattern not found', 9, 'ErrorMsg' }, 1636 }, 1637 }) 1638 end) 1639 end) 1640 1641 describe('ui/builtin messages', function() 1642 local screen 1643 before_each(function() 1644 clear() 1645 screen = Screen.new(60, 7, { rgb = true, ext_popupmenu = true }) 1646 screen:add_extra_attr_ids { 1647 [100] = { background = Screen.colors.LightRed }, 1648 [101] = { background = Screen.colors.Grey20 }, 1649 [102] = { foreground = Screen.colors.Magenta1, bold = true }, 1650 } 1651 end) 1652 1653 it('supports multiline messages from rpc', function() 1654 feed(':call rpcrequest(1, "test_method")<cr>') 1655 1656 screen:expect { 1657 grid = [[ 1658 {3: }| 1659 {9:Invoking 'test_method' on channel 1:} | 1660 {9:complete} | 1661 {9:error} | 1662 | 1663 {9:message} | 1664 {6:Press ENTER or type command to continue}^ | 1665 ]], 1666 request_cb = function(name) 1667 if name == 'test_method' then 1668 set_method_error('complete\nerror\n\nmessage') 1669 end 1670 end, 1671 } 1672 end) 1673 1674 it(':hi Group output', function() 1675 screen:try_resize(70, 7) 1676 feed(':hi ErrorMsg<cr>') 1677 screen:expect([[ 1678 | 1679 {1:~ }|*2 1680 {3: }| 1681 :hi ErrorMsg | 1682 ErrorMsg {9:xxx} {18:ctermfg=}15 {18:ctermbg=}1 {18:guifg=}White {18:guibg=}Red | 1683 {6:Press ENTER or type command to continue}^ | 1684 ]]) 1685 1686 feed('<cr>') 1687 screen:try_resize(30, 7) 1688 feed(':hi ErrorMsg<cr>') 1689 screen:expect([[ 1690 :hi ErrorMsg | 1691 ErrorMsg {9:xxx} {18:ctermfg=}15 | 1692 {18:ctermbg=}1 | 1693 {18:guifg=}White| 1694 {18:guibg=}Red | 1695 {6:Press ENTER or type command to}| 1696 {6: continue}^ | 1697 ]]) 1698 feed('<cr>') 1699 1700 -- screen size doesn't affect internal output #10285 1701 eq('ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red', exec_capture('hi ErrorMsg')) 1702 end) 1703 1704 it(':syntax list langGroup output', function() 1705 command('syntax on') 1706 exec([[ 1707 syn match vimComment excludenl +\s"[^\-:.%#=*].*$+lc=1 contains=@vimCommentGroup,vimCommentString 1708 syn match vimComment +\<endif\s\+".*$+lc=5 contains=@vimCommentGroup,vimCommentString 1709 syn match vimComment +\<else\s\+".*$+lc=4 contains=@vimCommentGroup,vimCommentString 1710 hi link vimComment Comment 1711 ]]) 1712 screen:try_resize(110, 7) 1713 feed(':syntax list vimComment<cr>') 1714 screen:expect([[ 1715 {102:--- Syntax items ---} | 1716 vimComment {18:xxx} {18:match} /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 {18:excludenl} {18:contains}=@vimCommentGroup,vimCommentString | 1717 | 1718 {18:match} /\<endif\s\+".*$/ms=s+5,lc=5 {18:contains}=@vimCommentGroup,vimCommentString | 1719 {18:match} /\<else\s\+".*$/ms=s+4,lc=4 {18:contains}=@vimCommentGroup,vimCommentString | 1720 {18:links to} Comment | 1721 {6:Press ENTER or type command to continue}^ | 1722 ]]) 1723 1724 feed('<cr>') 1725 screen:try_resize(55, 7) 1726 feed(':syntax list vimComment<cr>') 1727 screen:expect([[ 1728 | 1729 {18:match} /\<endif\s\+".*$/ms=s+5,lc=5 | 1730 {18:contains}=@vimCommentGroup,vimCommentString | 1731 {18:match} /\<else\s\+".*$/ms=s+4,lc=4 {18:c}| 1732 {18:ontains}=@vimCommentGroup,vimCommentString | 1733 {18:links to} Comment | 1734 {6:Press ENTER or type command to continue}^ | 1735 ]]) 1736 feed('<cr>') 1737 1738 -- ignore final whitespace inside string 1739 -- luacheck: push ignore 1740 eq( 1741 [[--- Syntax items --- 1742 vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vimCommentGroup,vimCommentString 1743 match /\<endif\s\+".*$/ms=s+5,lc=5 contains=@vimCommentGroup,vimCommentString 1744 match /\<else\s\+".*$/ms=s+4,lc=4 contains=@vimCommentGroup,vimCommentString 1745 links to Comment]], 1746 exec_capture('syntax list vimComment') 1747 ) 1748 -- luacheck: pop 1749 end) 1750 1751 it('no empty line after :silent #12099', function() 1752 exec([[ 1753 func T1() 1754 silent !echo 1755 echo "message T1" 1756 endfunc 1757 func T2() 1758 silent lua print("lua message") 1759 echo "message T2" 1760 endfunc 1761 func T3() 1762 silent call nvim_out_write("api message\n") 1763 echo "message T3" 1764 endfunc 1765 ]]) 1766 feed(':call T1()<CR>') 1767 screen:expect([[ 1768 ^ | 1769 {1:~ }|*5 1770 message T1 | 1771 ]]) 1772 feed(':call T2()<CR>') 1773 screen:expect([[ 1774 ^ | 1775 {1:~ }|*5 1776 message T2 | 1777 ]]) 1778 feed(':call T3()<CR>') 1779 screen:expect([[ 1780 ^ | 1781 {1:~ }|*5 1782 message T3 | 1783 ]]) 1784 end) 1785 1786 it('supports ruler with laststatus=0', function() 1787 command('set ruler laststatus=0') 1788 screen:expect([[ 1789 ^ | 1790 {1:~ }|*5 1791 0,0-1 All | 1792 ]]) 1793 1794 command('hi MsgArea guibg=#333333') 1795 screen:expect([[ 1796 ^ | 1797 {1:~ }|*5 1798 {101: 0,0-1 All }| 1799 ]]) 1800 1801 command('set rulerformat=%15(%c%V\\ %p%%%)') 1802 screen:expect([[ 1803 ^ | 1804 {1:~ }|*5 1805 {101: 0,0-1 100% }| 1806 ]]) 1807 1808 -- Ruler is cleared when it is no longer drawn. 1809 command('set noruler') 1810 screen:expect([[ 1811 ^ | 1812 {1:~ }|*5 1813 {101: }| 1814 ]]) 1815 end) 1816 1817 it('supports echo with CRLF line separators', function() 1818 feed(':echo "line 1\\r\\nline 2"<cr>') 1819 screen:expect([[ 1820 | 1821 {1:~ }|*2 1822 {3: }| 1823 line 1 | 1824 line 2 | 1825 {6:Press ENTER or type command to continue}^ | 1826 ]]) 1827 1828 feed('<cr>:echo "abc\\rz"<cr>') 1829 screen:expect([[ 1830 ^ | 1831 {1:~ }|*5 1832 zbc | 1833 ]]) 1834 end) 1835 1836 it('redraws UPD_NOT_VALID correctly after message', function() 1837 -- edge case: only one window was set UPD_NOT_VALID. Original report 1838 -- used :make, but fake it using one command to set the current 1839 -- window UPD_NOT_VALID and another to show a long message. 1840 command('set more') 1841 feed(':new<cr><c-w><c-w>') 1842 screen:expect([[ 1843 | 1844 {1:~ }| 1845 {2:[No Name] }| 1846 ^ | 1847 {1:~ }| 1848 {3:[No Name] }| 1849 :new | 1850 ]]) 1851 1852 feed(':set colorcolumn=10 | digraphs<cr>') 1853 screen:expect([[ 1854 :set colorcolumn=10 | digraphs | 1855 NU {18:^@} 10 SH {18:^A} 1 SX {18:^B} 2 EX {18:^C} 3 | 1856 ET {18:^D} 4 EQ {18:^E} 5 AK {18:^F} 6 BL {18:^G} 7 | 1857 BS {18:^H} 8 HT {18:^I} 9 LF {18:^@} 10 VT {18:^K} 11 | 1858 FF {18:^L} 12 CR {18:^M} 13 SO {18:^N} 14 SI {18:^O} 15 | 1859 DL {18:^P} 16 D1 {18:^Q} 17 D2 {18:^R} 18 D3 {18:^S} 19 | 1860 {6:-- More --}^ | 1861 ]]) 1862 1863 feed('q') 1864 screen:expect([[ 1865 | 1866 {1:~ }| 1867 {2:[No Name] }| 1868 ^ {100: } | 1869 {1:~ }| 1870 {3:[No Name] }| 1871 | 1872 ]]) 1873 1874 -- edge case: just covers statusline 1875 feed(':set colorcolumn=5 | lua error("x\\n\\nx")<cr>') 1876 screen:expect([[ 1877 {9:E5108: Lua: [string ":lua"]:1: x} | 1878 | 1879 {9:x} | 1880 {9:stack traceback:} | 1881 {9: [C]: in function 'error'} | 1882 {9: [string ":lua"]:1: in main chunk} | 1883 {6:Press ENTER or type command to continue}^ | 1884 ]]) 1885 1886 feed('<cr>') 1887 screen:expect([[ 1888 | 1889 {1:~ }| 1890 {2:[No Name] }| 1891 ^ {100: } | 1892 {1:~ }| 1893 {3:[No Name] }| 1894 | 1895 ]]) 1896 1897 -- edge case: just covers lowest window line 1898 feed(':set colorcolumn=5 | lua error("x\\n\\n\\nx")<cr>') 1899 screen:expect([[ 1900 {9:E5108: Lua: [string ":lua"]:1: x} | 1901 |*2 1902 {9:x} | 1903 {9:stack traceback:} | 1904 {9: [C]: in function 'error'} | 1905 {6:-- More --}^ | 1906 ]]) 1907 1908 feed('<cr>') 1909 screen:expect([[ 1910 |*2 1911 {9:x} | 1912 {9:stack traceback:} | 1913 {9: [C]: in function 'error'} | 1914 {9: [string ":lua"]:1: in main chunk} | 1915 {6:Press ENTER or type command to continue}^ | 1916 ]]) 1917 end) 1918 1919 it('supports nvim_echo messages with multiple attrs', function() 1920 local chunks = { { 'wow, ', 'Search' }, { 'such\n\nvery ', 'ErrorMsg' }, { 'color', 'LineNr' } } 1921 async_meths.nvim_echo(chunks, true, {}) 1922 screen:expect([[ 1923 | 1924 {1:~ }| 1925 {3: }| 1926 {10:wow, }{9:such} | 1927 | 1928 {9:very }{8:color} | 1929 {6:Press ENTER or type command to continue}^ | 1930 ]]) 1931 1932 feed '<cr>' 1933 screen:expect([[ 1934 ^ | 1935 {1:~ }|*5 1936 | 1937 ]]) 1938 1939 feed ':messages<cr>' 1940 screen:expect([[ 1941 | 1942 {1:~ }| 1943 {3: }| 1944 {10:wow, }{9:such} | 1945 | 1946 {9:very }{8:color} | 1947 {6:Press ENTER or type command to continue}^ | 1948 ]]) 1949 end) 1950 1951 it('supports nvim_echo messages with emoji', function() 1952 -- stylua: ignore 1953 async_meths.nvim_echo({ { 'wow, 🏳️⚧️🧑🌾❤️😂🏴☠️\nvariant ❤️ one\nvariant ❤ two' } }, true, {}) 1954 1955 screen:expect([[ 1956 | 1957 {1:~ }| 1958 {3: }| 1959 wow, 🏳️⚧️🧑🌾❤️😂🏴☠️ | 1960 variant ❤️ one | 1961 variant ❤ two | 1962 {6:Press ENTER or type command to continue}^ | 1963 ]]) 1964 1965 feed '<cr>' 1966 screen:expect([[ 1967 ^ | 1968 {1:~ }|*5 1969 | 1970 ]]) 1971 1972 feed ':messages<cr>' 1973 screen:expect([[ 1974 | 1975 {1:~ }| 1976 {3: }| 1977 wow, 🏳️⚧️🧑🌾❤️😂🏴☠️ | 1978 variant ❤️ one | 1979 variant ❤ two | 1980 {6:Press ENTER or type command to continue}^ | 1981 ]]) 1982 end) 1983 1984 it('prints lines in Ex mode correctly with a burst of carriage returns #19341', function() 1985 command('set number') 1986 api.nvim_buf_set_lines(0, 0, 0, true, { 'aaa', 'bbb', 'ccc' }) 1987 feed('gggQ<CR><CR>1<CR><CR>vi') 1988 screen:expect([[ 1989 Entering Ex mode. Type "visual" to go to Normal mode. | 1990 {8: 2 }bbb | 1991 {8: 3 }ccc | 1992 :1 | 1993 {8: 1 }aaa | 1994 {8: 2 }bbb | 1995 :vi^ | 1996 ]]) 1997 feed('<CR>') 1998 screen:expect([[ 1999 {8: 1 }aaa | 2000 {8: 2 }^bbb | 2001 {8: 3 }ccc | 2002 {8: 4 } | 2003 {1:~ }|*2 2004 | 2005 ]]) 2006 end) 2007 2008 describe('echo messages are shown when immediately followed by', function() 2009 --- @param to_block string command to cause a blocking wait 2010 --- @param to_unblock number|string number: timeout for blocking screen 2011 --- string: keys to stop the blocking wait 2012 local function test_flush_before_block(to_block, to_unblock) 2013 local timeout = type(to_unblock) == 'number' and to_unblock or nil 2014 exec(([[ 2015 func PrintAndWait() 2016 echon "aaa\nbbb" 2017 %s 2018 echon "\nccc" 2019 endfunc 2020 ]]):format(to_block)) 2021 feed(':call PrintAndWait()') 2022 screen:expect([[ 2023 | 2024 {1:~ }|*5 2025 :call PrintAndWait()^ | 2026 ]]) 2027 feed('<CR>') 2028 screen:expect { 2029 grid = [[ 2030 | 2031 {1:~ }|*3 2032 {3: }| 2033 aaa | 2034 bbb^ | 2035 ]], 2036 timeout = timeout, 2037 } 2038 if type(to_unblock) == 'string' then 2039 feed(to_unblock) 2040 end 2041 screen:expect([[ 2042 | 2043 {1:~ }| 2044 {3: }| 2045 aaa | 2046 bbb | 2047 ccc | 2048 {6:Press ENTER or type command to continue}^ | 2049 ]]) 2050 end 2051 2052 it('getchar()', function() 2053 test_flush_before_block([[call getchar()]], 'k') 2054 end) 2055 2056 it('wait()', function() 2057 test_flush_before_block([[call wait(300, '0')]], 100) 2058 end) 2059 2060 it('lua vim.wait()', function() 2061 test_flush_before_block([[lua vim.wait(300, function() end)]], 100) 2062 end) 2063 end) 2064 2065 it('consecutive calls to win_move_statusline() work after multiline message #21014', function() 2066 async_meths.nvim_exec( 2067 [[ 2068 echo "\n" 2069 call win_move_statusline(0, -4) 2070 call win_move_statusline(0, 4) 2071 ]], 2072 false 2073 ) 2074 screen:expect([[ 2075 | 2076 {1:~ }|*3 2077 {3: }| 2078 | 2079 {6:Press ENTER or type command to continue}^ | 2080 ]]) 2081 feed('<CR>') 2082 screen:expect([[ 2083 ^ | 2084 {1:~ }|*5 2085 | 2086 ]]) 2087 eq(1, api.nvim_get_option_value('cmdheight', {})) 2088 end) 2089 2090 it('using nvim_echo in VimResized does not cause hit-enter prompt #26139', function() 2091 command([[au VimResized * lua vim.api.nvim_echo({ { '123456' } }, true, {})]]) 2092 screen:try_resize(60, 5) 2093 screen:expect([[ 2094 ^ | 2095 {1:~ }|*3 2096 | 2097 ]]) 2098 eq({ mode = 'n', blocking = false }, api.nvim_get_mode()) 2099 end) 2100 2101 it('bottom of screen is cleared after increasing &cmdheight #20360', function() 2102 command('set laststatus=2') 2103 screen:expect([[ 2104 ^ | 2105 {1:~ }|*4 2106 {3:[No Name] }| 2107 | 2108 ]]) 2109 command('set cmdheight=4') 2110 screen:expect([[ 2111 ^ | 2112 {1:~ }| 2113 {3:[No Name] }| 2114 |*4 2115 ]]) 2116 end) 2117 2118 it('supports :intro with cmdheight=0 #26505', function() 2119 screen:try_resize(80, 24) 2120 command('set cmdheight=0') 2121 feed(':intro<CR>') 2122 screen:expect([[ 2123 |*5 2124 {MATCH:.*}| 2125 | 2126 Nvim is open source and freely distributable | 2127 https://neovim.io/#chat | 2128 | 2129 type :help nvim{18:<Enter>} if you are new! | 2130 type :checkhealth{18:<Enter>} to optimize Nvim | 2131 type :q{18:<Enter>} to exit | 2132 type :help{18:<Enter>} for help | 2133 | 2134 {MATCH: +}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+ +}| 2135 | 2136 Help poor children in Uganda! | 2137 type :help Kuwasha{18:<Enter>} for information | 2138 |*4 2139 ^ | 2140 ]]) 2141 feed('<CR>') 2142 assert_alive() 2143 end) 2144 2145 it('no wait return before delayed exception error message', function() 2146 screen:try_resize(70, 7) 2147 feed('ia<esc>:lua vim.cmd.quit()<CR>') 2148 screen:expect({ 2149 any = { 2150 '{9:.*Vim:E37: No write since.*}', 2151 '{6:Press ENTER or type command to continue}^', 2152 }, 2153 }) 2154 end) 2155 end) 2156 2157 it('calling screenstring() after redrawing between messages without UI #20999', function() 2158 clear() 2159 exec([[ 2160 echo repeat('a', 100) 2161 redraw 2162 echo "\n" 2163 call screenstring(1, 1) 2164 ]]) 2165 assert_alive() 2166 end) 2167 2168 describe('ui/ext_messages', function() 2169 local screen 2170 2171 before_each(function() 2172 clear { args_rm = { '--headless' }, args = { '--cmd', 'set shortmess-=I' } } 2173 screen = Screen.new(80, 24, { rgb = true, ext_messages = true, ext_popupmenu = true }) 2174 end) 2175 2176 it('supports intro screen', function() 2177 -- intro message is not externalized. But check that it still works. 2178 -- Note parts of it depends on version or is indeterministic. We ignore those parts. 2179 local introscreen = [[ 2180 ^ | 2181 {1:~ }|*4 2182 {MATCH:.*}| 2183 {1:~ }| 2184 {1:~ }Nvim is open source and freely distributable{1: }| 2185 {1:~ }https://neovim.io/#chat{1: }| 2186 {1:~ }| 2187 {1:~ }type :help nvim{18:<Enter>} if you are new! {1: }| 2188 {1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }| 2189 {1:~ }type :q{18:<Enter>} to exit {1: }| 2190 {1:~ }type :help{18:<Enter>} for help {1: }| 2191 {1:~ }| 2192 {1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}| 2193 {1:~ }| 2194 {1:~ }Help poor children in Uganda!{1: }| 2195 {1:~ }type :help Kuwasha{18:<Enter>} for information {1: }| 2196 {1:~ }|*5 2197 ]] 2198 local showmode = { { '-- INSERT --', 5, 'ModeMsg' } } 2199 screen:expect(introscreen) 2200 2201 -- <c-l> (same as :mode) does _not_ clear intro message 2202 feed('<c-l>i') 2203 screen:expect { grid = introscreen, showmode = showmode } 2204 2205 -- opening a float without focus also does not 2206 local win = api.nvim_open_win(api.nvim_create_buf(false, false), false, { 2207 relative = 'editor', 2208 height = 1, 2209 width = 5, 2210 row = 1, 2211 col = 5, 2212 }) 2213 screen:expect { 2214 grid = [[ 2215 ^ | 2216 {1:~ }{4: }{1: }| 2217 {1:~ }|*3 2218 {MATCH:.*}| 2219 {1:~ }| 2220 {1:~ }Nvim is open source and freely distributable{1: }| 2221 {1:~ }https://neovim.io/#chat{1: }| 2222 {1:~ }| 2223 {1:~ }type :help nvim{18:<Enter>} if you are new! {1: }| 2224 {1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }| 2225 {1:~ }type :q{18:<Enter>} to exit {1: }| 2226 {1:~ }type :help{18:<Enter>} for help {1: }| 2227 {1:~ }| 2228 {1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}| 2229 {1:~ }| 2230 {1:~ }Help poor children in Uganda!{1: }| 2231 {1:~ }type :help Kuwasha{18:<Enter>} for information {1: }| 2232 {1:~ }|*5 2233 ]], 2234 showmode = showmode, 2235 } 2236 2237 api.nvim_win_close(win, true) 2238 screen:expect { grid = introscreen, showmode = showmode } 2239 2240 -- but editing text does.. 2241 feed('x') 2242 screen:expect { 2243 grid = [[ 2244 x^ | 2245 {1:~ }|*23 2246 ]], 2247 showmode = showmode, 2248 } 2249 2250 feed('<esc>:intro<cr>') 2251 screen:expect { 2252 grid = [[ 2253 ^ | 2254 |*4 2255 {MATCH:.*}| 2256 | 2257 Nvim is open source and freely distributable | 2258 https://neovim.io/#chat | 2259 | 2260 type :help nvim{18:<Enter>} if you are new! | 2261 type :checkhealth{18:<Enter>} to optimize Nvim | 2262 type :q{18:<Enter>} to exit | 2263 type :help{18:<Enter>} for help | 2264 | 2265 {MATCH: +}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+ +}| 2266 | 2267 Help poor children in Uganda! | 2268 type :help Kuwasha{18:<Enter>} for information | 2269 |*5 2270 ]], 2271 } 2272 2273 feed('<cr>') 2274 screen:expect([[ 2275 ^x | 2276 {1:~ }|*23 2277 ]]) 2278 end) 2279 2280 it('clears intro screen when new buffer is active', function() 2281 api.nvim_set_current_buf(api.nvim_create_buf(true, false)) 2282 screen:expect([[ 2283 ^ | 2284 {1:~ }|*23 2285 ]]) 2286 end) 2287 2288 it('clears intro screen when new buffer is active in floating window', function() 2289 local win_opts = { relative = 'editor', height = 1, width = 5, row = 1, col = 5 } 2290 api.nvim_open_win(api.nvim_create_buf(false, false), true, win_opts) 2291 screen:expect([[ 2292 | 2293 {1:~ }{4:^ }{1: }| 2294 {1:~ }|*22 2295 ]]) 2296 end) 2297 2298 it('clears intro screen when initial buffer is active in floating window', function() 2299 local win_opts = { relative = 'editor', height = 1, width = 5, row = 1, col = 5 } 2300 api.nvim_open_win(api.nvim_get_current_buf(), true, win_opts) 2301 screen:expect([[ 2302 | 2303 {1:~ }{4:^ }{1: }| 2304 {1:~ }|*22 2305 ]]) 2306 end) 2307 2308 it('clears intro screen when initial window is converted to be floating', function() 2309 exec_lua([[ 2310 local init_win_id = vim.api.nvim_get_current_win() 2311 vim.cmd('split') 2312 local win_opts = { relative = 'editor', height = 1, width = 5, row = 1, col = 5 } 2313 vim.api.nvim_win_set_config(init_win_id, win_opts) 2314 vim.api.nvim_set_current_win(init_win_id) 2315 ]]) 2316 screen:expect([[ 2317 | 2318 {1:~ }{4:^ }{1: }| 2319 {1:~ }|*21 2320 {2:[No Name] }| 2321 ]]) 2322 end) 2323 2324 it('supports global statusline', function() 2325 feed(':set laststatus=3<cr>') 2326 feed(':sp<cr>') 2327 feed(':set cmdheight<cr>') 2328 screen:expect({ 2329 grid = [[ 2330 ^ | 2331 {1:~ }|*10 2332 ────────────────────────────────────────────────────────────────────────────────| 2333 | 2334 {1:~ }|*10 2335 {3:[No Name] }| 2336 ]], 2337 messages = { { content = { { ' cmdheight=0' } }, kind = 'list_cmd' } }, 2338 }) 2339 2340 feed('<c-w>+') 2341 feed(':set laststatus<cr>') 2342 screen:expect({ 2343 grid = [[ 2344 ^ | 2345 {1:~ }|*11 2346 ────────────────────────────────────────────────────────────────────────────────| 2347 | 2348 {1:~ }|*9 2349 {3:[No Name] }| 2350 ]], 2351 messages = { { content = { { ' laststatus=3' } }, kind = 'list_cmd' } }, 2352 }) 2353 2354 feed(':set mouse=a<cr>') 2355 api.nvim_input_mouse('left', 'press', '', 0, 12, 10) 2356 poke_eventloop() 2357 api.nvim_input_mouse('left', 'drag', '', 0, 11, 10) 2358 feed('<c-l>') 2359 feed(':set cmdheight<cr>') 2360 screen:expect({ 2361 grid = [[ 2362 ^ | 2363 {1:~ }|*10 2364 ────────────────────────────────────────────────────────────────────────────────| 2365 | 2366 {1:~ }|*10 2367 {3:[No Name] }| 2368 ]], 2369 messages = { { content = { { ' cmdheight=0' } }, kind = 'list_cmd' } }, 2370 }) 2371 end) 2372 end) 2373 2374 it('ui/ext_multigrid supports intro screen', function() 2375 clear { args_rm = { '--headless' }, args = { '--cmd', 'set shortmess-=I' } } 2376 local screen = Screen.new(80, 24, { rgb = true, ext_multigrid = true }) 2377 2378 screen:expect { 2379 grid = [[ 2380 ## grid 1 2381 [2:--------------------------------------------------------------------------------]|*23 2382 [3:--------------------------------------------------------------------------------]| 2383 ## grid 2 2384 ^ | 2385 {1:~ }|*4 2386 {MATCH:.*}| 2387 {1:~ }| 2388 {1:~ }Nvim is open source and freely distributable{1: }| 2389 {1:~ }https://neovim.io/#chat{1: }| 2390 {1:~ }| 2391 {1:~ }type :help nvim{18:<Enter>} if you are new! {1: }| 2392 {1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }| 2393 {1:~ }type :q{18:<Enter>} to exit {1: }| 2394 {1:~ }type :help{18:<Enter>} for help {1: }| 2395 {1:~ }| 2396 {1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}| 2397 {1:~ }| 2398 {1:~ }Help poor children in Uganda!{1: }| 2399 {1:~ }type :help Kuwasha{18:<Enter>} for information {1: }| 2400 {1:~ }|*4 2401 ## grid 3 2402 | 2403 ]], 2404 win_viewport = { 2405 [2] = { 2406 win = 1000, 2407 topline = 0, 2408 botline = 2, 2409 curline = 0, 2410 curcol = 0, 2411 linecount = 1, 2412 sum_scroll_delta = 0, 2413 }, 2414 }, 2415 } 2416 2417 feed 'ix' 2418 screen:expect { 2419 grid = [[ 2420 ## grid 1 2421 [2:--------------------------------------------------------------------------------]|*23 2422 [3:--------------------------------------------------------------------------------]| 2423 ## grid 2 2424 x^ | 2425 {1:~ }|*22 2426 ## grid 3 2427 {5:-- INSERT --} | 2428 ]], 2429 win_viewport = { 2430 [2] = { 2431 win = 1000, 2432 topline = 0, 2433 botline = 2, 2434 curline = 0, 2435 curcol = 1, 2436 linecount = 1, 2437 sum_scroll_delta = 0, 2438 }, 2439 }, 2440 } 2441 end) 2442 2443 describe('ui/msg_puts_printf', function() 2444 it('output multibyte characters correctly', function() 2445 skip(not t.translations_enabled(), 'Nvim not built with ENABLE_TRANSLATIONS') 2446 local screen 2447 local cmd = '' 2448 local build_dir = t.paths.test_build_dir 2449 local locale_dir = build_dir .. '/share/locale/ja/LC_MESSAGES' 2450 2451 clear({ env = { LANG = 'ja_JP.UTF-8' } }) 2452 screen = Screen.new(25, 5) 2453 2454 if is_os('win') then 2455 if os.execute('chcp 932 > NUL 2>&1') ~= 0 then 2456 pending('missing japanese language features', function() end) 2457 return 2458 else 2459 cmd = 'chcp 932 > NUL & ' 2460 end 2461 else 2462 if exc_exec('lang ja_JP.UTF-8') ~= 0 then 2463 pending('Locale ja_JP.UTF-8 not supported', function() end) 2464 return 2465 end 2466 end 2467 2468 fn.mkdir(locale_dir, 'p') 2469 fn.filecopy(build_dir .. '/src/nvim/po/ja.mo', locale_dir .. '/nvim.mo') 2470 finally(function() 2471 n.rmdir(vim.fs.dirname(locale_dir)) 2472 end) 2473 2474 cmd = cmd .. '"' .. nvim_prog .. '" -u NONE -i NONE -Es -V1' 2475 command([[call jobstart(']] .. cmd .. [[',{'term':v:true})]]) 2476 screen:expect([[ 2477 ^Exモードに入ります。ノー | 2478 マルモードに戻るには "vis| 2479 ual" と入力してください。| 2480 : | 2481 | 2482 ]]) 2483 end) 2484 end) 2485 2486 describe('pager', function() 2487 local screen 2488 2489 before_each(function() 2490 clear() 2491 screen = Screen.new(35, 8) 2492 screen:set_default_attr_ids({ 2493 [1] = { bold = true, foreground = Screen.colors.Blue1 }, 2494 [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, 2495 [3] = { 2496 foreground = Screen.colors.Grey100, 2497 background = Screen.colors.Red, 2498 special = Screen.colors.Yellow, 2499 }, 2500 [4] = { bold = true, foreground = Screen.colors.SeaGreen4 }, 2501 [5] = { special = Screen.colors.Yellow }, 2502 [6] = { special = Screen.colors.Yellow, bold = true, foreground = Screen.colors.SeaGreen4 }, 2503 [7] = { foreground = Screen.colors.Grey0, background = Screen.colors.Grey100 }, 2504 [8] = { foreground = Screen.colors.Gray90, background = Screen.colors.Grey100 }, 2505 [9] = { foreground = tonumber('0x00000c'), background = Screen.colors.Grey100 }, 2506 [10] = { background = Screen.colors.Grey100, bold = true, foreground = tonumber('0xe5e5ff') }, 2507 [11] = { background = Screen.colors.Grey100, bold = true, foreground = tonumber('0x2b8452') }, 2508 [12] = { bold = true, reverse = true }, 2509 [13] = { foreground = Screen.colors.Grey0 }, 2510 [14] = { foreground = Screen.colors.Grey90 }, 2511 [15] = { foreground = tonumber('0x00000c') }, 2512 [16] = { bold = true, foreground = tonumber('0xe5e5ff') }, 2513 [17] = { bold = true, foreground = tonumber('0x2b8452') }, 2514 }) 2515 command('set more') 2516 2517 exec_lua( 2518 '_G.x = ...', 2519 [[ 2520 Lorem ipsum dolor sit amet, consectetur 2521 adipisicing elit, sed do eiusmod tempor 2522 incididunt ut labore et dolore magna aliqua. 2523 Ut enim ad minim veniam, quis nostrud xercitation 2524 ullamco laboris nisi ut 2525 aliquip ex ea commodo consequat.]] 2526 ) 2527 end) 2528 2529 it('can be quit with echon', function() 2530 screen:try_resize(25, 5) 2531 feed(':echon join(map(range(0, &lines*10), "v:val"), "\\n")<cr>') 2532 screen:expect([[ 2533 0 | 2534 1 | 2535 2 | 2536 3 | 2537 {4:-- More --}^ | 2538 ]]) 2539 feed('q') 2540 screen:expect([[ 2541 ^ | 2542 {1:~ }|*3 2543 | 2544 ]]) 2545 end) 2546 2547 it('can be quit with Lua #11224 #16537', function() 2548 screen:try_resize(40, 5) 2549 feed(':lua for i=0,10 do print(i) end<cr>') 2550 screen:expect([[ 2551 0 | 2552 1 | 2553 2 | 2554 3 | 2555 {4:-- More --}^ | 2556 ]]) 2557 feed('q') 2558 screen:expect([[ 2559 ^ | 2560 {1:~ }|*3 2561 | 2562 ]]) 2563 feed(':mess<cr>') 2564 screen:expect([[ 2565 0 | 2566 1 | 2567 2 | 2568 3 | 2569 {4:-- More --}^ | 2570 ]]) 2571 feed('G') 2572 screen:expect([[ 2573 7 | 2574 8 | 2575 9 | 2576 10 | 2577 {4:Press ENTER or type command to continue}^ | 2578 ]]) 2579 feed('<cr>') 2580 end) 2581 2582 it('handles wrapped lines with line scroll', function() 2583 feed(':lua error(_G.x)<cr>') 2584 screen:expect([[ 2585 {2:E5108: Lua: [string ":lua"]:1: Lore}| 2586 {2:m ipsum dolor sit amet, consectetur}| 2587 | 2588 {2:adipisicing elit, sed do eiusmod te}| 2589 {2:mpor} | 2590 {2:incididunt ut labore et dolore magn}| 2591 {2:a aliqua.} | 2592 {4:-- More --}^ | 2593 ]]) 2594 2595 feed('j') 2596 screen:expect([[ 2597 {2:m ipsum dolor sit amet, consectetur}| 2598 | 2599 {2:adipisicing elit, sed do eiusmod te}| 2600 {2:mpor} | 2601 {2:incididunt ut labore et dolore magn}| 2602 {2:a aliqua.} | 2603 {2:Ut enim ad minim veniam, quis nostr}| 2604 {4:-- More --}^ | 2605 ]]) 2606 2607 feed('k') 2608 screen:expect([[ 2609 {2:E5108: Lua: [string ":lua"]:1: Lore}| 2610 {2:m ipsum dolor sit amet, consectetur}| 2611 | 2612 {2:adipisicing elit, sed do eiusmod te}| 2613 {2:mpor} | 2614 {2:incididunt ut labore et dolore magn}| 2615 {2:a aliqua.} | 2616 {4:-- More --}^ | 2617 ]]) 2618 2619 feed('j') 2620 screen:expect([[ 2621 {2:m ipsum dolor sit amet, consectetur}| 2622 | 2623 {2:adipisicing elit, sed do eiusmod te}| 2624 {2:mpor} | 2625 {2:incididunt ut labore et dolore magn}| 2626 {2:a aliqua.} | 2627 {2:Ut enim ad minim veniam, quis nostr}| 2628 {4:-- More --}^ | 2629 ]]) 2630 end) 2631 2632 it('handles wrapped lines with page scroll', function() 2633 feed(':lua error(_G.x)<cr>') 2634 screen:expect([[ 2635 {2:E5108: Lua: [string ":lua"]:1: Lore}| 2636 {2:m ipsum dolor sit amet, consectetur}| 2637 | 2638 {2:adipisicing elit, sed do eiusmod te}| 2639 {2:mpor} | 2640 {2:incididunt ut labore et dolore magn}| 2641 {2:a aliqua.} | 2642 {4:-- More --}^ | 2643 ]]) 2644 feed('d') 2645 screen:expect([[ 2646 {2:mpor} | 2647 {2:incididunt ut labore et dolore magn}| 2648 {2:a aliqua.} | 2649 {2:Ut enim ad minim veniam, quis nostr}| 2650 {2:ud xercitation} | 2651 {2:ullamco laboris nisi ut} | 2652 {2:aliquip ex ea commodo consequat.} | 2653 {4:-- More --}^ | 2654 ]]) 2655 feed('u') 2656 screen:expect([[ 2657 {2:E5108: Lua: [string ":lua"]:1: Lore}| 2658 {2:m ipsum dolor sit amet, consectetur}| 2659 | 2660 {2:adipisicing elit, sed do eiusmod te}| 2661 {2:mpor} | 2662 {2:incididunt ut labore et dolore magn}| 2663 {2:a aliqua.} | 2664 {4:-- More --}^ | 2665 ]]) 2666 feed('d') 2667 screen:expect([[ 2668 {2:mpor} | 2669 {2:incididunt ut labore et dolore magn}| 2670 {2:a aliqua.} | 2671 {2:Ut enim ad minim veniam, quis nostr}| 2672 {2:ud xercitation} | 2673 {2:ullamco laboris nisi ut} | 2674 {2:aliquip ex ea commodo consequat.} | 2675 {4:-- More --}^ | 2676 ]]) 2677 end) 2678 2679 it('handles wrapped lines with line scroll and MsgArea highlight', function() 2680 command('hi MsgArea guisp=Yellow') 2681 2682 feed(':lua error(_G.x)<cr>') 2683 screen:expect([[ 2684 {3:E5108: Lua: [string ":lua"]:1: Lore}| 2685 {3:m ipsum dolor sit amet, consectetur}| 2686 {5: }| 2687 {3:adipisicing elit, sed do eiusmod te}| 2688 {3:mpor}{5: }| 2689 {3:incididunt ut labore et dolore magn}| 2690 {3:a aliqua.}{5: }| 2691 {6:-- More --}{5:^ }| 2692 ]]) 2693 2694 feed('j') 2695 screen:expect([[ 2696 {3:m ipsum dolor sit amet, consectetur}| 2697 {5: }| 2698 {3:adipisicing elit, sed do eiusmod te}| 2699 {3:mpor}{5: }| 2700 {3:incididunt ut labore et dolore magn}| 2701 {3:a aliqua.}{5: }| 2702 {3:Ut enim ad minim veniam, quis nostr}| 2703 {6:-- More --}{5:^ }| 2704 ]]) 2705 2706 feed('k') 2707 screen:expect([[ 2708 {3:E5108: Lua: [string ":lua"]:1: Lore}| 2709 {3:m ipsum dolor sit amet, consectetur}| 2710 {5: }| 2711 {3:adipisicing elit, sed do eiusmod te}| 2712 {3:mpor}{5: }| 2713 {3:incididunt ut labore et dolore magn}| 2714 {3:a aliqua.}{5: }| 2715 {6:-- More --}{5:^ }| 2716 ]]) 2717 2718 feed('j') 2719 screen:expect([[ 2720 {3:m ipsum dolor sit amet, consectetur}| 2721 {5: }| 2722 {3:adipisicing elit, sed do eiusmod te}| 2723 {3:mpor}{5: }| 2724 {3:incididunt ut labore et dolore magn}| 2725 {3:a aliqua.}{5: }| 2726 {3:Ut enim ad minim veniam, quis nostr}| 2727 {6:-- More --}{5:^ }| 2728 ]]) 2729 end) 2730 2731 it('handles wrapped lines with page scroll and MsgArea highlight', function() 2732 command('hi MsgArea guisp=Yellow') 2733 feed(':lua error(_G.x)<cr>') 2734 screen:expect([[ 2735 {3:E5108: Lua: [string ":lua"]:1: Lore}| 2736 {3:m ipsum dolor sit amet, consectetur}| 2737 {5: }| 2738 {3:adipisicing elit, sed do eiusmod te}| 2739 {3:mpor}{5: }| 2740 {3:incididunt ut labore et dolore magn}| 2741 {3:a aliqua.}{5: }| 2742 {6:-- More --}{5:^ }| 2743 ]]) 2744 feed('d') 2745 screen:expect([[ 2746 {3:mpor}{5: }| 2747 {3:incididunt ut labore et dolore magn}| 2748 {3:a aliqua.}{5: }| 2749 {3:Ut enim ad minim veniam, quis nostr}| 2750 {3:ud xercitation}{5: }| 2751 {3:ullamco laboris nisi ut}{5: }| 2752 {3:aliquip ex ea commodo consequat.}{5: }| 2753 {6:-- More --}{5:^ }| 2754 ]]) 2755 feed('u') 2756 screen:expect([[ 2757 {3:E5108: Lua: [string ":lua"]:1: Lore}| 2758 {3:m ipsum dolor sit amet, consectetur}| 2759 {5: }| 2760 {3:adipisicing elit, sed do eiusmod te}| 2761 {3:mpor}{5: }| 2762 {3:incididunt ut labore et dolore magn}| 2763 {3:a aliqua.}{5: }| 2764 {6:-- More --}{5:^ }| 2765 ]]) 2766 feed('d') 2767 screen:expect([[ 2768 {3:mpor}{5: }| 2769 {3:incididunt ut labore et dolore magn}| 2770 {3:a aliqua.}{5: }| 2771 {3:Ut enim ad minim veniam, quis nostr}| 2772 {3:ud xercitation}{5: }| 2773 {3:ullamco laboris nisi ut}{5: }| 2774 {3:aliquip ex ea commodo consequat.}{5: }| 2775 {6:-- More --}{5:^ }| 2776 ]]) 2777 end) 2778 2779 it('preserves MsgArea highlighting after more prompt', function() 2780 screen:try_resize(70, 6) 2781 command('hi MsgArea guisp=Yellow') 2782 command('map x Lorem ipsum labore et dolore magna aliqua') 2783 command('map y adipisicing elit') 2784 command('map z incididunt ut') 2785 command('map a labore et dolore') 2786 command('map b ex ea commodo') 2787 command('map xx yy') 2788 command('map xy yz') 2789 feed(':map<cr>') 2790 screen:expect([[ 2791 {5: a labore et dolore }| 2792 {5: b ex ea commodo }| 2793 {5: xy yz }| 2794 {5: xx yy }| 2795 {5: x Lorem ipsum labore et dolore magna aliqua }| 2796 {6:-- More --}{5:^ }| 2797 ]]) 2798 feed('j') 2799 screen:expect([[ 2800 {5: b ex ea commodo }| 2801 {5: xy yz }| 2802 {5: xx yy }| 2803 {5: x Lorem ipsum labore et dolore magna aliqua }| 2804 {5: y adipisicing elit }| 2805 {6:-- More --}{5:^ }| 2806 ]]) 2807 feed('j') 2808 screen:expect([[ 2809 {5: xy yz }| 2810 {5: xx yy }| 2811 {5: x Lorem ipsum labore et dolore magna aliqua }| 2812 {5: y adipisicing elit }| 2813 {5: z incididunt ut }| 2814 {6:Press ENTER or type command to continue}{5:^ }| 2815 ]]) 2816 end) 2817 2818 it('clears "-- more --" message', function() 2819 command('hi MsgArea guisp=Yellow blend=10') 2820 feed(':echon join(range(20), "\\n")<cr>') 2821 screen:expect([[ 2822 {13:0}{14: }| 2823 {15:1}{16: }| 2824 {15:2}{16: }| 2825 {15:3}{16: }| 2826 {15:4}{16: }| 2827 {15:5}{16: }| 2828 {15:6}{16: }| 2829 {17:--}{14: }{17:More}{14: }{17:--}{14:^ }| 2830 ]]) 2831 2832 feed('j') 2833 screen:expect([[ 2834 {13:1}{14: }| 2835 {15:2}{16: }| 2836 {15:3}{16: }| 2837 {15:4}{16: }| 2838 {15:5}{16: }| 2839 {15:6}{16: }| 2840 {15:7}{16: }| 2841 {17:--}{14: }{17:More}{14: }{17:--}{14:^ }| 2842 ]]) 2843 2844 feed('k') 2845 screen:expect([[ 2846 {13:0}{14: }| 2847 {15:1}{16: }| 2848 {15:2}{16: }| 2849 {15:3}{16: }| 2850 {15:4}{16: }| 2851 {15:5}{16: }| 2852 {15:6}{16: }| 2853 {17:--}{14: }{17:More}{14: }{17:--}{14:^ }| 2854 ]]) 2855 2856 feed('j') 2857 screen:expect([[ 2858 {13:1}{14: }| 2859 {15:2}{16: }| 2860 {15:3}{16: }| 2861 {15:4}{16: }| 2862 {15:5}{16: }| 2863 {15:6}{16: }| 2864 {15:7}{16: }| 2865 {17:--}{14: }{17:More}{14: }{17:--}{14:^ }| 2866 ]]) 2867 end) 2868 2869 it('with :!cmd does not crash on resize', function() 2870 skip(fn.executable('sleep') == 0, 'missing "sleep" command') 2871 feed(':!sleep 1<cr>') 2872 screen:expect([[ 2873 | 2874 {1:~ }|*4 2875 {12: }| 2876 :!sleep 1 | 2877 | 2878 ]]) 2879 2880 -- not processed while command is executing 2881 async_meths.nvim_ui_try_resize(35, 5) 2882 2883 -- TODO(bfredl): ideally it should be processed just 2884 -- before the "press ENTER" prompt though 2885 screen:expect([[ 2886 | 2887 {1:~ }|*2 2888 {12: }| 2889 :!sleep 1 | 2890 | 2891 {4:Press ENTER or type command to cont}| 2892 {4:inue}^ | 2893 ]]) 2894 2895 feed('<cr>') 2896 screen:expect([[ 2897 ^ | 2898 {1:~ }|*3 2899 | 2900 ]]) 2901 end) 2902 2903 it('can be resized', function() 2904 feed(':lua error(_G.x)<cr>') 2905 screen:expect([[ 2906 {2:E5108: Lua: [string ":lua"]:1: Lore}| 2907 {2:m ipsum dolor sit amet, consectetur}| 2908 | 2909 {2:adipisicing elit, sed do eiusmod te}| 2910 {2:mpor} | 2911 {2:incididunt ut labore et dolore magn}| 2912 {2:a aliqua.} | 2913 {4:-- More --}^ | 2914 ]]) 2915 2916 -- responds to resize, but text is not reflown 2917 screen:try_resize(45, 5) 2918 screen:expect([[ 2919 {2:adipisicing elit, sed do eiusmod te} | 2920 {2:mpor} | 2921 {2:incididunt ut labore et dolore magn} | 2922 {2:a aliqua.} | 2923 {4:-- More --}^ | 2924 ]]) 2925 2926 -- can create empty space, as the command hasn't output the text below yet. 2927 -- text is not reflown; existing lines get cut 2928 screen:try_resize(30, 12) 2929 screen:expect([[ 2930 :lua error(_G.x) | 2931 {2:E5108: Lua: [string ":lua"]:1:}| 2932 {2:m ipsum dolor sit amet, consec}| 2933 {2:tetur} | 2934 {2:adipisicing elit, sed do eiusm}| 2935 {2:mpore} | 2936 {2:incididunt ut labore et dolore}| 2937 {2:a aliqua.} | 2938 |*3 2939 {4:-- More --}^ | 2940 ]]) 2941 2942 -- continues in a mostly consistent state, but only new lines are 2943 -- wrapped at the new screen size. 2944 feed('<cr>') 2945 screen:expect([[ 2946 {2:E5108: Lua: [string ":lua"]:1:}| 2947 {2:m ipsum dolor sit amet, consec}| 2948 {2:tetur} | 2949 {2:adipisicing elit, sed do eiusm}| 2950 {2:mpore} | 2951 {2:incididunt ut labore et dolore}| 2952 {2:a aliqua.} | 2953 {2:Ut enim ad minim veniam, quis }| 2954 {2:nostrud xercitation} | 2955 {2:ullamco laboris nisi ut} | 2956 {2:aliquip ex ea commodo consequa}| 2957 {4:-- More --}^ | 2958 ]]) 2959 2960 feed('<cr>') 2961 screen:expect([[ 2962 {2:m ipsum dolor sit amet, consec}| 2963 {2:tetur} | 2964 {2:adipisicing elit, sed do eiusm}| 2965 {2:mpore} | 2966 {2:incididunt ut labore et dolore}| 2967 {2:a aliqua.} | 2968 {2:Ut enim ad minim veniam, quis }| 2969 {2:nostrud xercitation} | 2970 {2:ullamco laboris nisi ut} | 2971 {2:aliquip ex ea commodo consequa}| 2972 {2:t.} | 2973 {4:-- More --}^ | 2974 ]]) 2975 2976 feed('q') 2977 screen:expect([[ 2978 ^ | 2979 {1:~ }|*10 2980 | 2981 ]]) 2982 end) 2983 2984 it('with cmdheight=0 does not crash with g<', function() 2985 command('set cmdheight=0') 2986 feed(':ls<cr>') 2987 screen:expect([[ 2988 | 2989 {1:~ }| 2990 {12: }| 2991 :ls | 2992 1 %a "[No Name]" | 2993 line 1 | 2994 {4:Press ENTER or type command to cont}| 2995 {4:inue}^ | 2996 ]]) 2997 2998 feed('<cr>') 2999 screen:expect([[ 3000 ^ | 3001 {1:~ }|*7 3002 ]]) 3003 3004 feed('g<lt>') 3005 screen:expect([[ 3006 | 3007 {1:~ }| 3008 {12: }| 3009 :ls | 3010 1 %a "[No Name]" | 3011 line 1 | 3012 {4:Press ENTER or type command to cont}| 3013 {4:inue}^ | 3014 ]]) 3015 3016 feed('<cr>') 3017 screen:expect([[ 3018 ^ | 3019 {1:~ }|*7 3020 ]]) 3021 end) 3022 3023 it('g< shows blank line from :echo properly', function() 3024 screen:try_resize(60, 8) 3025 feed([[:echo 1 | echo "\n" | echo 2<CR>]]) 3026 screen:expect([[ 3027 | 3028 {1:~ }|*2 3029 {12: }| 3030 1 | 3031 | 3032 2 | 3033 {4:Press ENTER or type command to continue}^ | 3034 ]]) 3035 3036 feed('<CR>') 3037 screen:expect([[ 3038 ^ | 3039 {1:~ }|*6 3040 | 3041 ]]) 3042 3043 feed('g<lt>') 3044 screen:expect([[ 3045 | 3046 {1:~ }| 3047 {12: }| 3048 :echo 1 | echo "\n" | echo 2 | 3049 1 | 3050 | 3051 2 | 3052 {4:Press ENTER or type command to continue}^ | 3053 ]]) 3054 3055 feed('<CR>') 3056 screen:expect([[ 3057 ^ | 3058 {1:~ }|*6 3059 | 3060 ]]) 3061 end) 3062 3063 it('scrolling works properly when :echo output ends with newline', function() 3064 screen:try_resize(60, 6) 3065 feed([[:echo range(100)->join("\n") .. "\n"<CR>]]) 3066 screen:expect([[ 3067 0 | 3068 1 | 3069 2 | 3070 3 | 3071 4 | 3072 {4:-- More --}^ | 3073 ]]) 3074 feed('G') 3075 screen:expect([[ 3076 96 | 3077 97 | 3078 98 | 3079 99 | 3080 | 3081 {4:Press ENTER or type command to continue}^ | 3082 ]]) 3083 for _ = 1, 3 do 3084 feed('k') 3085 screen:expect([[ 3086 95 | 3087 96 | 3088 97 | 3089 98 | 3090 99 | 3091 {4:-- More --}^ | 3092 ]]) 3093 feed('k') 3094 screen:expect([[ 3095 94 | 3096 95 | 3097 96 | 3098 97 | 3099 98 | 3100 {4:-- More --}^ | 3101 ]]) 3102 feed('j') 3103 screen:expect([[ 3104 95 | 3105 96 | 3106 97 | 3107 98 | 3108 99 | 3109 {4:-- More --}^ | 3110 ]]) 3111 feed('j') 3112 screen:expect([[ 3113 96 | 3114 97 | 3115 98 | 3116 99 | 3117 | 3118 {4:-- More --}^ | 3119 ]]) 3120 feed('j') 3121 screen:expect([[ 3122 96 | 3123 97 | 3124 98 | 3125 99 | 3126 | 3127 {4:Press ENTER or type command to continue}^ | 3128 ]]) 3129 end 3130 end) 3131 3132 it('scrolling works properly when :!cmd output ends with newline #27902', function() 3133 screen:try_resize(60, 6) 3134 api.nvim_set_option_value('shell', testprg('shell-test'), {}) 3135 api.nvim_set_option_value('shellcmdflag', 'REP 100', {}) 3136 api.nvim_set_option_value('shellxquote', '', {}) -- win: avoid extra quotes 3137 feed([[:!foo<CR>]]) 3138 screen:expect([[ 3139 96: foo | 3140 97: foo | 3141 98: foo | 3142 99: foo | 3143 | 3144 {4:Press ENTER or type command to continue}^ | 3145 ]]) 3146 for _ = 1, 3 do 3147 feed('k') 3148 screen:expect([[ 3149 95: foo | 3150 96: foo | 3151 97: foo | 3152 98: foo | 3153 99: foo | 3154 {4:-- More --}^ | 3155 ]]) 3156 feed('k') 3157 screen:expect([[ 3158 94: foo | 3159 95: foo | 3160 96: foo | 3161 97: foo | 3162 98: foo | 3163 {4:-- More --}^ | 3164 ]]) 3165 feed('j') 3166 screen:expect([[ 3167 95: foo | 3168 96: foo | 3169 97: foo | 3170 98: foo | 3171 99: foo | 3172 {4:-- More --}^ | 3173 ]]) 3174 feed('j') 3175 screen:expect([[ 3176 96: foo | 3177 97: foo | 3178 98: foo | 3179 99: foo | 3180 | 3181 {4:-- More --}^ | 3182 ]]) 3183 feed('j') 3184 screen:expect([[ 3185 96: foo | 3186 97: foo | 3187 98: foo | 3188 99: foo | 3189 | 3190 {4:Press ENTER or type command to continue}^ | 3191 ]]) 3192 end 3193 end) 3194 end) 3195 3196 it('pager works in headless mode with UI attached', function() 3197 clear() 3198 local child_server = assert(n.new_pipename()) 3199 fn.jobstart({ nvim_prog, '--clean', '--headless', '--listen', child_server }) 3200 retry(nil, nil, function() 3201 neq(nil, vim.uv.fs_stat(child_server)) 3202 end) 3203 3204 local child_session = n.connect(child_server) 3205 local child_screen = Screen.new(40, 6, nil, child_session) 3206 child_screen._default_attr_ids = nil -- TODO: unskip with new color scheme 3207 3208 child_session:notify('nvim_command', [[echo range(100)->join("\n")]]) 3209 child_screen:expect([[ 3210 0 | 3211 1 | 3212 2 | 3213 3 | 3214 4 | 3215 -- More --^ | 3216 ]]) 3217 3218 child_session:request('nvim_input', 'G') 3219 child_screen:expect([[ 3220 95 | 3221 96 | 3222 97 | 3223 98 | 3224 99 | 3225 Press ENTER or type command to continue^ | 3226 ]]) 3227 3228 child_session:request('nvim_input', 'g') 3229 child_screen:expect([[ 3230 0 | 3231 1 | 3232 2 | 3233 3 | 3234 4 | 3235 -- More --^ | 3236 ]]) 3237 end) 3238 3239 describe('progress-message', function() 3240 local screen 3241 3242 local function setup_autocmd(pattern) 3243 exec_lua(function() 3244 local grp = vim.api.nvim_create_augroup('ProgressListener', { clear = true }) 3245 _G.progress_autocmd_result = nil 3246 vim.api.nvim_create_autocmd('Progress', { 3247 pattern = pattern, 3248 group = grp, 3249 callback = function(ev) 3250 _G.progress_autocmd_result = ev.data 3251 end, 3252 }) 3253 end) 3254 end 3255 3256 local function assert_progress_autocmd(expected, context) 3257 local progress_autocmd_result = exec_lua(function() 3258 return _G.progress_autocmd_result 3259 end) 3260 eq(expected, progress_autocmd_result, context) 3261 exec_lua(function() 3262 _G.progress_autocmd_result = nil 3263 end) 3264 end 3265 3266 local function setup_screen(with_ext_msg) 3267 if with_ext_msg then 3268 screen = Screen.new(25, 5, { ext_messages = true }) 3269 screen:add_extra_attr_ids { 3270 [100] = { undercurl = true, special = Screen.colors.Red }, 3271 [101] = { foreground = Screen.colors.Magenta1, bold = true }, 3272 [102] = { foreground = Screen.colors.NvimDarkGreen }, 3273 } 3274 else 3275 screen = Screen.new(40, 5) 3276 end 3277 end 3278 3279 before_each(function() 3280 clear() 3281 setup_screen(true) 3282 setup_autocmd() 3283 end) 3284 3285 it('emitted by nvim_echo', function() 3286 local id = api.nvim_echo( 3287 { { 'test-message' } }, 3288 true, 3289 { kind = 'progress', title = 'testsuit', percent = 10, status = 'running' } 3290 ) 3291 3292 screen:expect({ 3293 grid = [[ 3294 ^ | 3295 {1:~ }|*4 3296 ]], 3297 messages = { 3298 { 3299 content = { 3300 { 'testsuit', 6, 'MoreMsg' }, 3301 { ': ' }, 3302 { ' 10% ', 19, 'WarningMsg' }, 3303 { 'test-message' }, 3304 }, 3305 history = true, 3306 id = 1, 3307 kind = 'progress', 3308 }, 3309 }, 3310 }) 3311 assert_progress_autocmd({ 3312 text = { 'test-message' }, 3313 percent = 10, 3314 status = 'running', 3315 title = 'testsuit', 3316 id = 1, 3317 data = {}, 3318 }, 'progress autocmd receives progress messages') 3319 3320 -- can update progress messages 3321 api.nvim_echo( 3322 { { 'test-message-updated' } }, 3323 true, 3324 { id = id, kind = 'progress', title = 'TestSuit', percent = 50, status = 'running' } 3325 ) 3326 screen:expect({ 3327 grid = [[ 3328 ^ | 3329 {1:~ }|*4 3330 ]], 3331 messages = { 3332 { 3333 content = { 3334 { 'TestSuit', 6, 'MoreMsg' }, 3335 { ': ' }, 3336 { ' 50% ', 19, 'WarningMsg' }, 3337 { 'test-message-updated' }, 3338 }, 3339 history = true, 3340 id = 1, 3341 kind = 'progress', 3342 }, 3343 }, 3344 }) 3345 3346 assert_progress_autocmd({ 3347 text = { 'test-message-updated' }, 3348 percent = 50, 3349 status = 'running', 3350 title = 'TestSuit', 3351 id = 1, 3352 data = {}, 3353 }, 'Progress autocmd receives progress update') 3354 3355 -- success status 3356 api.nvim_echo( 3357 { { 'test-message (success)' } }, 3358 true, 3359 { kind = 'progress', title = 'TestSuit', percent = 100, status = 'success' } 3360 ) 3361 screen:expect({ 3362 grid = [[ 3363 ^ | 3364 {1:~ }|*4 3365 ]], 3366 messages = { 3367 { 3368 content = { 3369 { 'TestSuit', 102, 'OkMsg' }, 3370 { ': ' }, 3371 { '100% ', 19, 'WarningMsg' }, 3372 { 'test-message (success)' }, 3373 }, 3374 history = true, 3375 id = 2, 3376 kind = 'progress', 3377 }, 3378 }, 3379 }) 3380 3381 -- failed status 3382 api.nvim_echo( 3383 { { 'test-message (fail)' } }, 3384 true, 3385 { kind = 'progress', title = 'TestSuit', percent = 35, status = 'failed' } 3386 ) 3387 screen:expect({ 3388 grid = [[ 3389 ^ | 3390 {1:~ }|*4 3391 ]], 3392 messages = { 3393 { 3394 content = { 3395 { 'TestSuit', 9, 'ErrorMsg' }, 3396 { ': ' }, 3397 { ' 35% ', 19, 'WarningMsg' }, 3398 { 'test-message (fail)' }, 3399 }, 3400 history = true, 3401 id = 3, 3402 kind = 'progress', 3403 }, 3404 }, 3405 }) 3406 3407 -- cancel status 3408 api.nvim_echo( 3409 { { 'test-message (cancel)' } }, 3410 true, 3411 { kind = 'progress', title = 'TestSuit', percent = 30, status = 'cancel' } 3412 ) 3413 screen:expect({ 3414 grid = [[ 3415 ^ | 3416 {1:~ }|*4 3417 ]], 3418 messages = { 3419 { 3420 content = { 3421 { 'TestSuit', 19, 'WarningMsg' }, 3422 { ': ' }, 3423 { ' 30% ', 19, 'WarningMsg' }, 3424 { 'test-message (cancel)' }, 3425 }, 3426 history = true, 3427 id = 4, 3428 kind = 'progress', 3429 }, 3430 }, 3431 }) 3432 3433 -- without title and percent 3434 api.nvim_echo( 3435 { { 'test-message (no-tile or percent)' } }, 3436 true, 3437 { kind = 'progress', status = 'cancel' } 3438 ) 3439 screen:expect({ 3440 grid = [[ 3441 ^ | 3442 {1:~ }|*4 3443 ]], 3444 messages = { 3445 { 3446 content = { { 'test-message (no-tile or percent)' } }, 3447 history = true, 3448 id = 5, 3449 kind = 'progress', 3450 }, 3451 }, 3452 }) 3453 3454 -- progress event can filter by title 3455 setup_autocmd('Special Title') 3456 api.nvim_echo( 3457 { { 'test-message-updated' } }, 3458 true, 3459 { id = id, kind = 'progress', percent = 80, status = 'running' } 3460 ) 3461 assert_progress_autocmd(nil, 'No progress message with Special Title yet') 3462 3463 api.nvim_echo( 3464 { { 'test-message-updated' } }, 3465 true, 3466 { id = id, kind = 'progress', title = 'Special Title', percent = 100, status = 'success' } 3467 ) 3468 assert_progress_autocmd({ 3469 text = { 'test-message-updated' }, 3470 percent = 100, 3471 status = 'success', 3472 title = 'Special Title', 3473 id = 1, 3474 data = {}, 3475 }, 'Progress autocmd receives progress update') 3476 end) 3477 3478 it('user-defined data in `data` field', function() 3479 api.nvim_echo({ { 'test-message' } }, true, { 3480 kind = 'progress', 3481 title = 'TestSuit', 3482 percent = 10, 3483 status = 'running', 3484 data = { test_attribute = 1 }, 3485 }) 3486 3487 screen:expect({ 3488 grid = [[ 3489 ^ | 3490 {1:~ }|*4 3491 ]], 3492 messages = { 3493 { 3494 content = { 3495 { 'TestSuit', 6, 'MoreMsg' }, 3496 { ': ' }, 3497 { ' 10% ', 19, 'WarningMsg' }, 3498 { 'test-message' }, 3499 }, 3500 history = true, 3501 id = 1, 3502 kind = 'progress', 3503 }, 3504 }, 3505 }) 3506 assert_progress_autocmd({ 3507 text = { 'test-message' }, 3508 percent = 10, 3509 status = 'running', 3510 title = 'TestSuit', 3511 id = 1, 3512 data = { test_attribute = 1 }, 3513 }, 'Progress autocmd receives progress messages') 3514 end) 3515 3516 it('validates', function() 3517 -- throws error if title, status, percent, data is used in non progress message 3518 eq( 3519 'title, status, percent and data fields can only be used with progress messages', 3520 t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { title = 'TestSuit' }) 3521 ) 3522 3523 eq( 3524 'title, status, percent and data fields can only be used with progress messages', 3525 t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { status = 'running' }) 3526 ) 3527 3528 eq( 3529 'title, status, percent and data fields can only be used with progress messages', 3530 t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { percent = 10 }) 3531 ) 3532 3533 eq( 3534 'title, status, percent and data fields can only be used with progress messages', 3535 t.pcall_err(api.nvim_echo, { { 'test-message' } }, false, { data = { tag = 'test' } }) 3536 ) 3537 3538 -- throws error if anything other then running/success/failed/cancel is used in status 3539 eq( 3540 "Invalid 'status': expected success|failed|running|cancel, got live", 3541 t.pcall_err( 3542 api.nvim_echo, 3543 { { 'test-message' } }, 3544 false, 3545 { kind = 'progress', status = 'live' } 3546 ) 3547 ) 3548 3549 -- throws error if parcent is not in 0-100 3550 eq( 3551 "Invalid 'percent': out of range", 3552 t.pcall_err( 3553 api.nvim_echo, 3554 { { 'test-message' } }, 3555 false, 3556 { kind = 'progress', status = 'running', percent = -1 } 3557 ) 3558 ) 3559 3560 eq( 3561 "Invalid 'percent': out of range", 3562 t.pcall_err( 3563 api.nvim_echo, 3564 { { 'test-message' } }, 3565 false, 3566 { kind = 'progress', status = 'running', percent = 101 } 3567 ) 3568 ) 3569 3570 -- throws error if data is not a dictionary 3571 eq( 3572 "Invalid 'data': expected Dict, got String", 3573 t.pcall_err( 3574 api.nvim_echo, 3575 { { 'test-message' } }, 3576 false, 3577 { kind = 'progress', title = 'TestSuit', percent = 10, status = 'running', data = 'test' } 3578 ) 3579 ) 3580 end) 3581 3582 it('gets placed in history', function() 3583 local id = api.nvim_echo( 3584 { { 'test-message 10' } }, 3585 true, 3586 { kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' } 3587 ) 3588 eq('TestSuit: 10% test-message 10', exec_capture('messages')) 3589 3590 api.nvim_echo( 3591 { { 'test-message 20' } }, 3592 true, 3593 { id = id, kind = 'progress', title = 'TestSuit', percent = 20, status = 'running' } 3594 ) 3595 eq('TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20', exec_capture('messages')) 3596 3597 api.nvim_echo({ { 'middle msg' } }, true, {}) 3598 eq( 3599 'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg', 3600 exec_capture('messages') 3601 ) 3602 api.nvim_echo( 3603 { { 'test-message 30' } }, 3604 true, 3605 { id = id, kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } 3606 ) 3607 eq( 3608 'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg\nTestSuit: 30% test-message 30', 3609 exec_capture('messages') 3610 ) 3611 3612 api.nvim_echo( 3613 { { 'test-message 50' } }, 3614 true, 3615 { id = id, kind = 'progress', title = 'TestSuit', percent = 50, status = 'running' } 3616 ) 3617 eq( 3618 'TestSuit: 10% test-message 10\nTestSuit: 20% test-message 20\nmiddle msg\nTestSuit: 30% test-message 30\nTestSuit: 50% test-message 50', 3619 exec_capture('messages') 3620 ) 3621 end) 3622 3623 it('sets msg-id correctly', function() 3624 local id1 = api.nvim_echo( 3625 { { 'test-message 10' } }, 3626 true, 3627 { kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' } 3628 ) 3629 eq(1, id1) 3630 3631 local id2 = api.nvim_echo( 3632 { { 'test-message 20' } }, 3633 true, 3634 { kind = 'progress', title = 'TestSuit', percent = 20, status = 'running' } 3635 ) 3636 eq(2, id2) 3637 3638 local id3 = api.nvim_echo({ { 'normal message' } }, true, {}) 3639 eq(3, id3) 3640 3641 local id4 = api.nvim_echo({ { 'without history' } }, false, {}) 3642 eq(4, id4) 3643 3644 local id5 = api.nvim_echo( 3645 { { 'test-message 30' } }, 3646 true, 3647 { id = 10, kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } 3648 ) 3649 eq(10, id5) 3650 3651 -- updating progress message does not create new msg-id 3652 local id5_update = api.nvim_echo( 3653 { { 'test-message 40' } }, 3654 true, 3655 { id = id5, kind = 'progress', title = 'TestSuit', percent = 40, status = 'running' } 3656 ) 3657 eq(id5, id5_update) 3658 3659 local id6 = api.nvim_echo( 3660 { { 'test-message 30' } }, 3661 true, 3662 { kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } 3663 ) 3664 eq(11, id6) 3665 3666 local id7 = api.nvim_echo( 3667 { { 'supports str-id' } }, 3668 true, 3669 { id = 'str-id', kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } 3670 ) 3671 eq('str-id', id7) 3672 3673 -- internal messages are also assigned an ID (and thus advance the next progress ID) 3674 feed('Q') 3675 local id8 = api.nvim_echo( 3676 { { 'test-message 30' } }, 3677 true, 3678 { kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } 3679 ) 3680 eq(13, id8) 3681 end) 3682 3683 it('supports string ids', function() 3684 -- string id works 3685 local id = api.nvim_echo( 3686 { { 'supports str-id' } }, 3687 true, 3688 { id = 'str-id', kind = 'progress', title = 'TestSuit', percent = 30, status = 'running' } 3689 ) 3690 eq('str-id', id) 3691 3692 screen:expect({ 3693 grid = [[ 3694 ^ | 3695 {1:~ }|*4 3696 ]], 3697 messages = { 3698 { 3699 content = { 3700 { 'TestSuit', 6, 'MoreMsg' }, 3701 { ': ' }, 3702 { ' 30% ', 19, 'WarningMsg' }, 3703 { 'supports str-id' }, 3704 }, 3705 history = true, 3706 id = 'str-id', 3707 kind = 'progress', 3708 }, 3709 }, 3710 }) 3711 3712 local id_update = api.nvim_echo( 3713 { { 'supports str-id updated' } }, 3714 true, 3715 { id = id, kind = 'progress', title = 'testsuit', percent = 40, status = 'running' } 3716 ) 3717 eq(id, id_update) 3718 assert_progress_autocmd({ 3719 text = { 'supports str-id updated' }, 3720 percent = 40, 3721 status = 'running', 3722 title = 'testsuit', 3723 id = 'str-id', 3724 data = {}, 3725 }) 3726 end) 3727 3728 it('tui displays progress message in proper format', function() 3729 clear() 3730 setup_screen(false) 3731 api.nvim_echo( 3732 { { 'test-message' } }, 3733 true, 3734 { kind = 'progress', title = 'TestSuit', percent = 10, status = 'running' } 3735 ) 3736 screen:expect([[ 3737 ^ | 3738 {1:~ }|*3 3739 {6:TestSuit}: {19: 10% }test-message | 3740 ]]) 3741 end) 3742 3743 it('works with history off', function() 3744 api.nvim_echo({ { 'test-message' } }, false, { 3745 kind = 'progress', 3746 title = 'TestSuit', 3747 percent = 10, 3748 status = 'running', 3749 }) 3750 3751 screen:expect({ 3752 grid = [[ 3753 ^ | 3754 {1:~ }|*4 3755 ]], 3756 messages = { 3757 { 3758 content = { 3759 { 'TestSuit', 6, 'MoreMsg' }, 3760 { ': ' }, 3761 { ' 10% ', 19, 'WarningMsg' }, 3762 { 'test-message' }, 3763 }, 3764 id = 1, 3765 kind = 'progress', 3766 }, 3767 }, 3768 }) 3769 3770 assert_progress_autocmd({ 3771 text = { 'test-message' }, 3772 percent = 10, 3773 status = 'running', 3774 title = 'TestSuit', 3775 id = 1, 3776 data = {}, 3777 }, 'progress autocmd receives progress messages') 3778 end) 3779 end)