messages2_spec.lua (37944B)
1 -- Tests for (protocol-driven) ui2, intended to replace the legacy message grid UI. 2 3 local t = require('test.testutil') 4 local n = require('test.functional.testnvim')() 5 local Screen = require('test.functional.ui.screen') 6 7 local api, clear, command, exec_lua, feed = n.api, n.clear, n.command, n.exec_lua, n.feed 8 9 local msg_timeout = 200 10 local function set_msg_target_zero_ch() 11 exec_lua(function() 12 require('vim._core.ui2').enable({ msg = { target = 'msg', timeout = msg_timeout } }) 13 vim.o.cmdheight = 0 14 end) 15 end 16 17 describe('messages2', function() 18 local screen 19 before_each(function() 20 clear() 21 screen = Screen.new() 22 screen:add_extra_attr_ids({ 23 [100] = { foreground = Screen.colors.Magenta1, bold = true }, 24 }) 25 exec_lua(function() 26 require('vim._core.ui2').enable({}) 27 end) 28 end) 29 after_each(function() 30 -- Since ui2 module lasts until Nvim exits, there may be unfinished timers. 31 -- Close unfinished timers to avoid 2s delay on exit with ASAN or TSAN. 32 exec_lua(function() 33 vim.uv.walk(function(handle) 34 if not handle:is_closing() then 35 handle:close() 36 end 37 end) 38 end) 39 end) 40 41 it('multiline messages and pager', function() 42 command('echo "foo\nbar"') 43 screen:expect([[ 44 ^ | 45 {1:~ }|*10 46 {3: }| 47 foo | 48 bar | 49 ]]) 50 command('set ruler showcmd noshowmode') 51 feed('g<lt>') 52 screen:expect([[ 53 | 54 {1:~ }|*9 55 {3: }| 56 ^foo | 57 bar | 58 1,1 All| 59 ]]) 60 -- Multiple messages in same event loop iteration are appended and shown in full. 61 feed([[q:echo "foo" | echo "bar\nbaz\n"->repeat(&lines)<CR>]]) 62 screen:expect([[ 63 ^ | 64 {1:~ }|*5 65 {3: }| 66 foo | 67 bar | 68 baz | 69 bar | 70 baz | 71 bar | 72 baz [+23] | 73 ]]) 74 -- Any key press resizes the cmdline and updates the spill indicator. 75 feed('j') 76 screen:expect([[ 77 ^ | 78 {1:~ }|*12 79 foo [+29] 0,0-1 All| 80 ]]) 81 command('echo "foo"') 82 -- New message clears spill indicator. 83 screen:expect([[ 84 ^ | 85 {1:~ }|*12 86 foo 0,0-1 All| 87 ]]) 88 -- No error for ruler virt_text msg_row exceeding buffer length. 89 command([[map Q <cmd>echo "foo\nbar" <bar> ls<CR>]]) 90 feed('Q') 91 screen:expect([[ 92 ^ | 93 {1:~ }|*9 94 {3: }| 95 foo | 96 bar | 97 1 %a "[No Name]" line 1 | 98 ]]) 99 feed('<C-L>') 100 screen:expect([[ 101 ^ | 102 {1:~ }|*12 103 0,0-1 All| 104 ]]) 105 -- g< shows messages from last command 106 feed('g<lt>') 107 screen:expect([[ 108 | 109 {1:~ }|*8 110 {3: }| 111 ^foo | 112 bar | 113 1 %a "[No Name]" line 1 | 114 1,1 All| 115 ]]) 116 -- edit_unputchar() does not clear already updated screen #34515. 117 feed('qix<Esc>dwi<C-r>') 118 screen:expect([[ 119 {18:^"} | 120 {1:~ }|*12 121 ^R 1,1 All| 122 ]]) 123 feed('-<Esc>') 124 screen:expect([[ 125 ^x | 126 {1:~ }|*12 127 1,1 All| 128 ]]) 129 -- Switching tabpage closes expanded cmdline #37659. 130 command('tabnew | echo "foo\nbar"') 131 screen:expect([[ 132 {24: + [No Name] }{5: }{100:2}{5: [No Name] }{2: }{24:X}| 133 ^ | 134 {1:~ }|*9 135 {3: }| 136 foo | 137 bar | 138 ]]) 139 feed('gt') 140 screen:expect([[ 141 {5: + [No Name] }{24: [No Name] }{2: }{24:X}| 142 ^x | 143 {1:~ }|*11 144 foo [+1] 1,1 All| 145 ]]) 146 -- Changing 'laststatus' reveals the global statusline with a pager height 147 -- exceeding the available lines: #38008. 148 command('tabonly | call nvim_echo([["foo\n"]]->repeat(&lines), 1, {})') 149 screen:expect([[ 150 ^x | 151 {1:~ }|*5 152 {3: }| 153 foo |*6 154 foo [+8] | 155 ]]) 156 feed('<CR>') 157 screen:expect([[ 158 {3: }| 159 ^foo | 160 foo |*11 161 1,1 Top| 162 ]]) 163 command('set laststatus=3') 164 screen:expect([[ 165 {3: }| 166 ^foo | 167 foo |*10 168 {3:[Pager] 1,1 Top}| 169 | 170 ]]) 171 end) 172 173 it('new buffer, window and options after closing a buffer', function() 174 command('set nomodifiable | echom "foo" | messages') 175 screen:expect([[ 176 | 177 {1:~ }|*10 178 {3: }| 179 ^foo | 180 foo | 181 ]]) 182 command('bdelete | messages') 183 screen:expect_unchanged() 184 end) 185 186 it('screenclear and empty message clears messages', function() 187 command('echo "foo"') 188 screen:expect([[ 189 ^ | 190 {1:~ }|*12 191 foo | 192 ]]) 193 command('mode') 194 screen:expect([[ 195 ^ | 196 {1:~ }|*12 197 | 198 ]]) 199 command('echo "foo"') 200 screen:expect([[ 201 ^ | 202 {1:~ }|*12 203 foo | 204 ]]) 205 command('echo ""') 206 screen:expect([[ 207 ^ | 208 {1:~ }|*12 209 | 210 ]]) 211 -- A redraw indicates the start of messages in the cmdline, which empty should clear. 212 command('echo "foo" | redraw | echo "bar"') 213 screen:expect([[ 214 ^ | 215 {1:~ }|*12 216 bar | 217 ]]) 218 command('echo "foo" | redraw | echo ""') 219 screen:expect([[ 220 ^ | 221 {1:~ }|*12 222 | 223 ]]) 224 set_msg_target_zero_ch() 225 command('echo "foo"') 226 screen:expect([[ 227 ^ | 228 {1:~ }|*12 229 {1:~ }{4:foo}| 230 ]]) 231 command('mode') 232 screen:expect([[ 233 ^ | 234 {1:~ }|*13 235 ]]) 236 -- But not with target='msg' 237 command('echo "foo"') 238 screen:expect([[ 239 ^ | 240 {1:~ }|*12 241 {1:~ }{4:foo}| 242 ]]) 243 command('echo ""') 244 screen:expect_unchanged() 245 -- Or a screen resize 246 screen:try_resize(screen._width, screen._height - 1) 247 screen:expect([[ 248 ^ | 249 {1:~ }|*11 250 {1:~ }{4:foo}| 251 ]]) 252 -- Moved up when opening cmdline 253 feed(':') 254 screen:expect([[ 255 | 256 {1:~ }|*10 257 {1:~ }{4:foo}| 258 {16::}^ | 259 ]]) 260 -- Highlighter disabled when message is moved to cmdline #34884 261 feed([[echo "bar\n"->repeat(&lines)<CR>]]) 262 screen:expect([[ 263 ^ | 264 {1:~ }|*4 265 {3: }| 266 foo | 267 bar |*5 268 bar [+8] | 269 ]]) 270 end) 271 272 it("deleting buffer restores 'buftype'", function() 273 feed(':%bdelete<CR>') 274 screen:expect([[ 275 ^ | 276 {1:~ }|*12 277 5 buffers deleted | 278 ]]) 279 -- Would trigger changed dialog if 'buftype' was not restored. 280 command('%bdelete') 281 screen:expect_unchanged() 282 end) 283 284 it('showmode does not overwrite important messages', function() 285 command('set readonly') 286 feed('i') 287 screen:expect([[ 288 ^ | 289 {1:~ }|*12 290 {19:W10: Warning: Changing a readonly file} | 291 ]]) 292 feed('<Esc>Qi') 293 screen:expect([[ 294 ^ | 295 {1:~ }|*12 296 {9:E354: Invalid register name: '^@'} | 297 ]]) 298 end) 299 300 it('hit-enter prompt does not error for invalid window #35095', function() 301 command('echo "foo\nbar"') 302 screen:expect([[ 303 ^ | 304 {1:~ }|*10 305 {3: }| 306 foo | 307 bar | 308 ]]) 309 feed('<C-w>o') 310 screen:expect([[ 311 ^ | 312 {1:~ }|*12 313 | 314 ]]) 315 end) 316 317 it('not restoring already open hit-enter-prompt config #35298', function() 318 command('echo "foo\nbar"') 319 screen:expect([[ 320 ^ | 321 {1:~ }|*10 322 {3: }| 323 foo | 324 bar | 325 ]]) 326 command('echo "foo\nbar"') 327 screen:expect_unchanged() 328 feed(':') 329 screen:expect([[ 330 | 331 {1:~ }|*12 332 {16::}^ | 333 ]]) 334 end) 335 336 it('paging prompt dialog #35191', function() 337 screen:try_resize(71, screen._height) 338 local top = [[ 339 | 340 {1:~ }|*4 341 {3: f/d/j: screen/page/line down, b/u/k: up, <Esc>: stop paging }| 342 0 | 343 1 | 344 2 | 345 3 | 346 4 | 347 5 | 348 6 [+93] | 349 Type number and <Enter> or click with the mouse (q or empty cancels): ^ | 350 ]] 351 feed(':call inputlist(range(100))<CR>') 352 screen:expect(top) 353 feed('j') 354 screen:expect([[ 355 | 356 {1:~ }|*4 357 {3: f/d/j: screen/page/line down, b/u/k: up, <Esc>: stop paging }| 358 1 [+1] | 359 2 | 360 3 | 361 4 | 362 5 | 363 6 | 364 7 [+92] | 365 Type number and <Enter> or click with the mouse (q or empty cancels): ^ | 366 ]]) 367 feed('k') 368 screen:expect(top) 369 feed('d') 370 screen:expect([[ 371 | 372 {1:~ }|*4 373 {3: f/d/j: screen/page/line down, b/u/k: up, <Esc>: stop paging }| 374 3 [+3] | 375 4 | 376 5 | 377 6 | 378 7 | 379 8 | 380 9 [+90] | 381 Type number and <Enter> or click with the mouse (q or empty cancels): ^ | 382 ]]) 383 feed('u') 384 screen:expect(top) 385 feed('f') 386 screen:expect([[ 387 | 388 {1:~ }|*4 389 {3: f/d/j: screen/page/line down, b/u/k: up, <Esc>: stop paging }| 390 5 [+5] | 391 6 | 392 7 | 393 8 | 394 9 | 395 10 | 396 11 [+88] | 397 Type number and <Enter> or click with the mouse (q or empty cancels): ^ | 398 ]]) 399 feed('b') 400 screen:expect(top) 401 feed('G') 402 screen:expect([[ 403 | 404 {1:~ }|*4 405 {3: f/d/j: screen/page/line down, b/u/k: up, <Esc>: stop paging }| 406 93 [+93] | 407 94 | 408 95 | 409 96 | 410 97 | 411 98 | 412 99 | 413 Type number and <Enter> or click with the mouse (q or empty cancels): ^ | 414 ]]) 415 -- No scrolling beyond end of buffer #36114 416 feed('f') 417 screen:expect([[ 418 | 419 {1:~ }|*3 420 {3: f/d/j: screen/page/line down, b/u/k: up, <Esc>: stop paging }| 421 93 [+93] | 422 94 | 423 95 | 424 96 | 425 97 | 426 98 | 427 99 | 428 Type number and <Enter> or click with the mouse (q or empty cancels): f| 429 ^ | 430 ]]) 431 feed('<Backspace>g') 432 screen:expect(top) 433 feed('<Esc>f') 434 screen:expect([[ 435 | 436 {1:~ }|*3 437 {3: }| 438 0 | 439 1 | 440 2 | 441 3 | 442 4 | 443 5 | 444 6 [+93] | 445 Type number and <Enter> or click with the mouse (q or empty cancels): f| 446 ^ | 447 ]]) 448 end) 449 450 it('FileType is fired after default options are set', function() 451 n.exec([[ 452 let g:set = {} 453 au FileType pager set nowrap 454 au OptionSet * let g:set[expand('<amatch>')] = g:set->get(expand('<amatch>'), 0) + 1 455 echom 'foo'->repeat(&columns) 456 messages 457 ]]) 458 screen:expect([[ 459 | 460 {1:~ }|*10 461 {3: }| 462 ^foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofo| 463 | 464 ]]) 465 t.eq(5, n.eval('g:set').filetype) -- still fires for 'filetype' 466 end) 467 468 it('Search highlights only apply to pager', function() 469 screen:add_extra_attr_ids({ 470 [100] = { background = Screen.colors.Blue1, foreground = Screen.colors.Red }, 471 [101] = { background = Screen.colors.Red1, foreground = Screen.colors.Blue1 }, 472 }) 473 command('hi MsgArea guifg=Red guibg=Blue') 474 command('hi Search guifg=Blue guibg=Red') 475 command('set hlsearch shortmess+=s') 476 feed('/foo<CR>') 477 screen:expect([[ 478 ^ | 479 {1:~ }|*12 480 {9:E486: Pattern not found: foo}{100: }| 481 ]]) 482 set_msg_target_zero_ch() 483 command('echo "foo"') 484 screen:expect([[ 485 ^ | 486 {1:~ }|*12 487 {1:~ }{4:foo}| 488 ]]) 489 feed('g<lt>') 490 screen:expect([[ 491 | 492 {1:~ }|*11 493 {3: }| 494 {101:^foo}{100: }| 495 ]]) 496 end) 497 498 it(':echon appends message', function() 499 command([[echo 1 | echon 2]]) 500 screen:expect([[ 501 ^ | 502 {1:~ }|*12 503 12 | 504 ]]) 505 feed('g<lt>') 506 screen:expect([[ 507 | 508 {1:~ }|*10 509 {3: }| 510 ^12 | 511 | 512 ]]) 513 feed([[q:echo 1 | echon 2 | echon 2 | echon 3<CR>]]) 514 screen:expect([[ 515 ^ | 516 {1:~ }|*12 517 1223 | 518 ]]) 519 feed('g<lt>') 520 screen:expect([[ 521 | 522 {1:~ }|*10 523 {3: }| 524 ^1223 | 525 | 526 ]]) 527 end) 528 529 it('shows message from still running command', function() 530 exec_lua(function() 531 vim.schedule(function() 532 print('foo') 533 vim.fn.getchar() 534 end) 535 end) 536 screen:expect([[ 537 ^ | 538 {1:~ }|*12 539 foo | 540 ]]) 541 end) 542 543 it('properly formatted carriage return messages', function() 544 screen:try_resize(screen._width, 20) 545 command([[echon "\r" | echon "Hello" | echon " " | echon "World"]]) 546 screen:expect([[ 547 ^ | 548 {1:~ }|*18 549 Hello World | 550 ]]) 551 exec_lua(function() 552 vim.api.nvim_echo({ { 'fooo\nbarbaz\n\nlol', 'statement' }, { '\rbar' } }, true, {}) 553 vim.api.nvim_echo({ { 'foooooooo', 'statement' }, { 'baz\rb', 'error' } }, true, {}) 554 vim.api.nvim_echo({ { 'fooobar', 'statement' }, { '\rbaz\n' } }, true, {}) 555 vim.api.nvim_echo({ { 'fooobar', 'statement' }, { '\rbaz\rb', 'error' } }, true, {}) 556 vim.api.nvim_echo({ { 'fooo\rbar', 'statement' }, { 'baz', 'error' } }, true, {}) 557 end) 558 screen:expect([[ 559 ^ | 560 {1:~ }|*9 561 {3: }| 562 {15:fooo} | 563 {15:barbaz} | 564 | 565 bar | 566 {9:b}{15:oooooooo}{9:baz} | 567 baz{15:obar} | 568 | 569 {9:baz}{15:obar} | 570 {15:bar}{9:baz} | 571 ]]) 572 end) 573 574 it('can show message during textlock', function() 575 exec_lua(function() 576 _G.omnifunc = function() 577 print('x!') 578 vim.cmd.sleep('100m') 579 end 580 vim.bo.omnifunc = 'v:lua.omnifunc' 581 end) 582 feed('i<C-X>') 583 screen:expect([[ 584 ^ | 585 {1:~ }|*12 586 {5:-- ^X mode (^]^D^E^F^I^K^L^N^O^P^Rs^U^V^Y)} | 587 ]]) 588 feed('<C-O>') 589 screen:expect([[ 590 ^ | 591 {1:~ }|*12 592 x! | 593 ]]) 594 screen:expect([[ 595 ^ | 596 {1:~ }|*12 597 {5:-- Omni completion (^O^N^P) }{9:Pattern not found} | 598 ]]) 599 exec_lua(function() 600 vim.keymap.set('n', '<F1>', function() 601 print('i hate locks so much!!!!') 602 vim.cmd.messages() 603 end, { expr = true }) 604 end) 605 feed('<Esc><F1>') 606 screen:expect([[ 607 | 608 {1:~ }|*8 609 {3: }| 610 ^x! | 611 x! | 612 i hate locks so much!!!! |*2 613 ]]) 614 end) 615 616 it('replace by message ID', function() 617 exec_lua(function() 618 vim.api.nvim_echo({ { 'foo' } }, true, { id = 1 }) 619 vim.api.nvim_echo({ { 'bar\nbaz' } }, true, { id = 2 }) 620 vim.api.nvim_echo({ { 'foo' } }, true, { id = 3 }) 621 vim.keymap.set('n', 'Q', function() 622 vim.api.nvim_echo({ { 'Syntax', 23 }, { '\n - ', 0 }, { 'cCommentL', 439 } }, false, {}) 623 end) 624 end) 625 screen:expect([[ 626 ^ | 627 {1:~ }|*8 628 {3: }| 629 foo | 630 bar | 631 baz | 632 foo | 633 ]]) 634 api.nvim_echo({ { 'foo' } }, true, { id = 2 }) 635 screen:expect([[ 636 ^ | 637 {1:~ }|*9 638 {3: }| 639 foo |*3 640 ]]) 641 api.nvim_echo({ { 'bar\nbaz' } }, true, { id = 1 }) 642 screen:expect([[ 643 ^ | 644 {1:~ }|*8 645 {3: }| 646 bar | 647 baz | 648 foo |*2 649 ]]) 650 -- Pressing a key immediately dismisses an expanded cmdline, and 651 -- replacing a multiline, multicolored message doesn't error due 652 -- to unnecessarily inserted lines #37994. 653 feed('Q') 654 screen:expect([[ 655 ^ | 656 {1:~ }|*10 657 {3: }| 658 {100:Syntax} | 659 - cCommentL | 660 ]]) 661 feed('Q') 662 screen:expect_unchanged() 663 feed('<C-L>') -- close expanded cmdline 664 set_msg_target_zero_ch() 665 api.nvim_echo({ { 'foo' } }, true, { id = 1 }) 666 api.nvim_echo({ { 'bar\nbaz' } }, true, { id = 2 }) 667 api.nvim_echo({ { 'foo' } }, true, { id = 3 }) 668 screen:expect([[ 669 ^ | 670 {1:~ }|*9 671 {1:~ }{4:foo}| 672 {1:~ }{4:bar}| 673 {1:~ }{4:baz}| 674 {1:~ }{4:foo}| 675 ]]) 676 api.nvim_echo({ { 'foo' } }, true, { id = 2 }) 677 screen:expect([[ 678 ^ | 679 {1:~ }|*10 680 {1:~ }{4:foo}|*3 681 ]]) 682 api.nvim_echo({ { 'f', 'Conceal' }, { 'oo\nbar' } }, true, { id = 3 }) 683 screen:expect([[ 684 ^ | 685 {1:~ }|*9 686 {1:~ }{4:foo}|*2 687 {1:~ }{14:f}{4:oo}| 688 {1:~ }{4:bar}| 689 ]]) 690 -- No error expanding the cmdline when trying to copy over message span marks #37672. 691 screen:try_resize(screen._width, 6) 692 command('ls!') 693 screen:expect([[ 694 ^ | 695 {1:~ }| 696 {3: }| 697 foo |*2 698 {14:f}oo [+6] | 699 ]]) 700 feed('<Esc>') 701 screen:expect([[ 702 ^ | 703 {1:~ }|*5 704 ]]) 705 end) 706 707 it('while cmdline is open', function() 708 command('cnoremap <C-A> <Cmd>lua error("foo")<CR>') 709 feed(':echo "bar"<C-A>') 710 screen:expect([[ 711 | 712 {1:~ }|*7 713 {3: }| 714 {9:E5108: Lua: [string ":lua"]:1: foo} | 715 {9:stack traceback:} | 716 {9: [C]: in function 'error'} | 717 {9: [string ":lua"]:1: in main chunk} | 718 {16::}{15:echo} {26:"bar"}^ | 719 ]]) 720 feed('<CR>') 721 screen:expect([[ 722 ^ | 723 {1:~ }|*12 724 bar | 725 ]]) 726 set_msg_target_zero_ch() 727 feed([[:call confirm("foo\nbar")<C-A>]]) 728 screen:expect([[ 729 | 730 {1:~ }|*8 731 {1:~ }{9:E5108: Lua: [string ":lua"]:1: foo}{4: }| 732 {1:~ }{9:stack traceback:}{4: }| 733 {1:~ }{9: [C]: in function 'error'}{4: }| 734 {1:~ }{9: [string ":lua"]:1: in main chunk}| 735 {16::}{15:call} {25:confirm}{16:(}{26:"foo\nbar"}{16:)}^ | 736 ]]) 737 feed('<CR>') 738 screen:expect([[ 739 | 740 {1:~ }|*8 741 {1:~ }{9:E5108: Lua: [string ":lua"]:1: foo}{4: }| 742 {3: }| 743 {6:foo} | 744 {6:bar} | 745 {6:[O]k: }^ | 746 ]]) 747 end) 748 749 it('no search_cmd with cmdheight=0', function() 750 set_msg_target_zero_ch() 751 feed('ifoo<Esc>?foo<CR>') 752 screen:expect([[ 753 {10:^foo} | 754 {1:~ }|*13 755 ]]) 756 end) 757 758 it('closed msg window timer removes empty lines', function() 759 set_msg_target_zero_ch() 760 command('echo "foo" | echo "bar\n"') 761 screen:expect([[ 762 ^ | 763 {1:~ }|*10 764 {1:~ }{4:foo}| 765 {1:~ }{4:bar}| 766 {1:~ }{4: }| 767 ]]) 768 command('fclose!') 769 screen:sleep(msg_timeout + 50) 770 command('echo "baz"') 771 screen:expect([[ 772 ^ | 773 {1:~ }|*12 774 {1:~ }{4:baz}| 775 ]]) 776 -- Last message line is at bottom of window after closing it. 777 screen:try_resize(screen._width, 8) 778 command('mode | echo "1\n" | echo "2\n" | echo "3\n" | echo "4\n"') 779 screen:expect([[ 780 ^ | 781 {1:~ }|*3 782 {1:~ }{4:3}| 783 {1:~ }{4: }| 784 {1:~ }{4:4}| 785 {1:~ }{4: }| 786 ]]) 787 command('fclose!') 788 screen:expect([[ 789 ^ | 790 {1:~ }|*7 791 ]]) 792 command('echo "5\n"') 793 screen:expect([[ 794 ^ | 795 {1:~ }|*3 796 {1:~ }{4:4}| 797 {1:~ }{4: }| 798 {1:~ }{4:5}| 799 {1:~ }{4: }| 800 ]]) 801 end) 802 803 it('configured targets per kind', function() 804 exec_lua(function() 805 local cfg = { msg = { targets = { echo = 'msg', list_cmd = 'pager' } } } 806 require('vim._core.ui2').enable(cfg) 807 print('foo') -- "lua_print" kind goes to cmd 808 vim.cmd.echo('"bar"') -- "echo" kind goes to msg 809 vim.cmd.highlight('VisualNC') -- "list_cmd" kind goes to pager 810 end) 811 screen:expect([[ 812 | 813 {1:~ }|*10 814 {3: }| 815 ^VisualNC xxx cleared {4:bar}| 816 foo | 817 ]]) 818 command('hi VisualNC') -- cursor moved to last message in pager 819 screen:expect([[ 820 | 821 {1:~ }|*9 822 {3: }| 823 VisualNC xxx cleared | 824 ^VisualNC xxx cleared {4:bar}| 825 foo | 826 ]]) 827 end) 828 end)