wildmode_spec.lua (21119B)
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, command = n.clear, n.feed, n.command 6 local fn = n.fn 7 local api = n.api 8 local eq = t.eq 9 local eval = n.eval 10 local retry = t.retry 11 local testprg = n.testprg 12 local is_os = t.is_os 13 14 describe("'wildmenu'", function() 15 local screen 16 before_each(function() 17 clear() 18 screen = Screen.new(25, 5) 19 screen:add_extra_attr_ids { 20 [100] = { background = Screen.colors.Yellow1, foreground = Screen.colors.Black }, 21 } 22 end) 23 24 -- oldtest: Test_wildmenu_screendump() 25 it('works', function() 26 screen:add_extra_attr_ids { 27 [100] = { background = Screen.colors.Yellow1, foreground = Screen.colors.Black }, 28 } 29 -- Test simple wildmenu 30 feed(':sign <Tab>') 31 screen:expect { 32 grid = [[ 33 | 34 {1:~ }|*2 35 {100:define}{3: jump list > }| 36 :sign define^ | 37 ]], 38 } 39 40 feed('<Tab>') 41 screen:expect { 42 grid = [[ 43 | 44 {1:~ }|*2 45 {3:define }{100:jump}{3: list > }| 46 :sign jump^ | 47 ]], 48 } 49 50 feed('<Tab>') 51 screen:expect { 52 grid = [[ 53 | 54 {1:~ }|*2 55 {3:define jump }{100:list}{3: > }| 56 :sign list^ | 57 ]], 58 } 59 60 -- Looped back to the original value 61 feed('<Tab><Tab><Tab><Tab>') 62 screen:expect { 63 grid = [[ 64 | 65 {1:~ }|*2 66 {3:define jump list > }| 67 :sign ^ | 68 ]], 69 } 70 71 -- Test that the wild menu is cleared properly 72 feed('<Space>') 73 screen:expect { 74 grid = [[ 75 | 76 {1:~ }|*3 77 :sign ^ | 78 ]], 79 } 80 81 -- Test that a different wildchar still works 82 feed('<Esc>') 83 command('set wildchar=<Esc>') 84 feed(':sign <Esc>') 85 screen:expect { 86 grid = [[ 87 | 88 {1:~ }|*2 89 {100:define}{3: jump list > }| 90 :sign define^ | 91 ]], 92 } 93 94 -- Double-<Esc> is a hard-coded method to escape while wildchar=<Esc>. Make 95 -- sure clean up is properly done in edge case like this. 96 feed('<Esc>') 97 screen:expect { 98 grid = [[ 99 ^ | 100 {1:~ }|*3 101 | 102 ]], 103 } 104 end) 105 106 it('C-E to cancel wildmenu completion restore original input', function() 107 feed(':sign <tab>') 108 screen:expect([[ 109 | 110 {1:~ }|*2 111 {100:define}{3: jump list > }| 112 :sign define^ | 113 ]]) 114 feed('<C-E>') 115 screen:expect([[ 116 | 117 {1:~ }|*3 118 :sign ^ | 119 ]]) 120 end) 121 122 it('C-Y to apply selection and end wildmenu completion', function() 123 feed(':sign <tab>') 124 screen:expect([[ 125 | 126 {1:~ }|*2 127 {100:define}{3: jump list > }| 128 :sign define^ | 129 ]]) 130 feed('<tab><C-Y>') 131 screen:expect([[ 132 | 133 {1:~ }|*3 134 :sign jump^ | 135 ]]) 136 end) 137 138 it(':sign <tab> shows wildmenu completions', function() 139 command('set wildmenu wildmode=full') 140 feed(':sign <tab>') 141 screen:expect([[ 142 | 143 {1:~ }|*2 144 {100:define}{3: jump list > }| 145 :sign define^ | 146 ]]) 147 end) 148 149 it(':sign <tab> <space> hides wildmenu #8453', function() 150 command('set wildmode=full') 151 -- only a regression if status-line open 152 command('set laststatus=2') 153 command('set wildmenu') 154 feed(':sign <tab>') 155 screen:expect([[ 156 | 157 {1:~ }|*2 158 {100:define}{3: jump list > }| 159 :sign define^ | 160 ]]) 161 feed('<space>') 162 screen:expect([[ 163 | 164 {1:~ }|*2 165 {3:[No Name] }| 166 :sign define ^ | 167 ]]) 168 end) 169 170 it('does not crash after cycling back to original text', function() 171 command('set wildmode=full') 172 feed(':j<Tab><Tab><Tab>') 173 screen:expect([[ 174 | 175 {1:~ }|*2 176 {3:join jumps }| 177 :j^ | 178 ]]) 179 -- This would cause nvim to crash before #6650 180 feed('<BS><Tab>') 181 screen:expect([[ 182 | 183 {1:~ }|*2 184 {100:!}{3: # & < = > @ > }| 185 :!^ | 186 ]]) 187 end) 188 189 it('is preserved during :terminal activity', function() 190 command('set wildmenu wildmode=full') 191 command('set scrollback=4') 192 feed((':terminal "%s" REP 5000 !terminal_output!<cr>'):format(testprg('shell-test'))) 193 feed('G') -- Follow :terminal output. 194 feed([[:sign <Tab>]]) -- Invoke wildmenu. 195 screen:add_extra_attr_ids { 196 [100] = { foreground = Screen.colors.Black, background = Screen.colors.Yellow }, 197 [101] = { 198 bold = true, 199 foreground = Screen.colors.White, 200 background = Screen.colors.DarkGreen, 201 }, 202 } 203 -- NB: in earlier versions terminal output was redrawn during cmdline mode. 204 -- For now just assert that the screen remains unchanged. 205 screen:expect { any = '{100:define}{101: jump list > }|\n:sign define^ |' } 206 screen:expect_unchanged() 207 208 -- cmdline CTRL-D display should also be preserved. 209 feed([[<C-U>]]) 210 feed([[sign <C-D>]]) -- Invoke cmdline CTRL-D. 211 screen:expect { 212 grid = [[ 213 :sign | 214 define place | 215 jump undefine | 216 list unplace | 217 :sign ^ | 218 ]], 219 } 220 screen:expect_unchanged() 221 222 -- Exiting cmdline should show the buffer. 223 feed([[<C-\><C-N>]]) 224 screen:expect { any = [[!terminal_output!]] } 225 end) 226 227 it('ignores :redrawstatus called from a timer #7108', function() 228 command('set wildmenu wildmode=full') 229 command([[call timer_start(10, {->execute('redrawstatus')}, {'repeat':-1})]]) 230 feed([[<C-\><C-N>]]) 231 feed([[:sign <Tab>]]) -- Invoke wildmenu. 232 screen:expect { 233 grid = [[ 234 | 235 {1:~ }|*2 236 {100:define}{3: jump list > }| 237 :sign define^ | 238 ]], 239 } 240 screen:expect_unchanged() 241 end) 242 243 it('with laststatus=0, :vsplit, :term #2255', function() 244 if not is_os('win') then 245 command('set shell=sh') -- Need a predictable "$" prompt. 246 command('let $PS1 = "$"') 247 end 248 command('set laststatus=0') 249 command('vsplit') 250 command('term') 251 252 -- Check for a shell prompt to verify that the terminal loaded. 253 retry(nil, nil, function() 254 if is_os('win') then 255 eq('Microsoft', eval("matchstr(join(getline(1, '$')), 'Microsoft')")) 256 else 257 eq('$', eval([[matchstr(getline(1), '\$')]])) 258 end 259 end) 260 261 feed([[<C-\><C-N>]]) 262 feed([[:<Tab>]]) -- Invoke wildmenu. 263 screen:add_extra_attr_ids { 264 [100] = { foreground = Screen.colors.Black, background = Screen.colors.Yellow }, 265 [101] = { 266 bold = true, 267 foreground = Screen.colors.White, 268 background = Screen.colors.DarkGreen, 269 }, 270 } 271 -- Check only the last 2 lines, because the shell output is 272 -- system-dependent. 273 screen:expect { any = '{100:!}{101: # & < = > @ > }|\n:!^' } 274 -- Because this test verifies a _lack_ of activity, we must wait the full timeout. 275 -- So make it reasonable. 276 screen:expect_unchanged(false, 1000) 277 end) 278 279 it('wildmode=list,full and messages interaction #10092', function() 280 -- Need more than 5 rows, else tabline is covered and will be redrawn. 281 screen:try_resize(25, 7) 282 283 command('set wildmenu wildmode=list,full') 284 command('set showtabline=2') 285 feed(':set wildm<tab>') 286 screen:expect([[ 287 {5: [No Name] }{2: }| 288 | 289 {1:~ }| 290 {3: }| 291 :set wildm | 292 wildmenu wildmode | 293 :set wildm^ | 294 ]]) 295 feed('<tab>') -- trigger wildmode full 296 screen:expect([[ 297 {5: [No Name] }{2: }| 298 | 299 {3: }| 300 :set wildm | 301 wildmenu wildmode | 302 {100:wildmenu}{3: wildmode }| 303 :set wildmenu^ | 304 ]]) 305 feed('<Esc>') 306 screen:expect([[ 307 {5: [No Name] }{2: }| 308 ^ | 309 {1:~ }|*4 310 | 311 ]]) 312 end) 313 314 it('wildmode=longest,list', function() 315 -- Need more than 5 rows, else tabline is covered and will be redrawn. 316 screen:try_resize(25, 7) 317 318 command('set wildmenu wildmode=longest,list') 319 320 -- give wildmode-longest something to expand to 321 feed(':sign u<tab>') 322 screen:expect([[ 323 | 324 {1:~ }|*5 325 :sign un^ | 326 ]]) 327 feed('<tab>') -- trigger wildmode list 328 screen:expect([[ 329 | 330 {1:~ }|*2 331 {3: }| 332 :sign un | 333 undefine unplace | 334 :sign un^ | 335 ]]) 336 feed('<Esc>') 337 screen:expect([[ 338 ^ | 339 {1:~ }|*5 340 | 341 ]]) 342 343 -- give wildmode-longest something it cannot expand, use list 344 feed(':sign un<tab>') 345 screen:expect([[ 346 | 347 {1:~ }|*2 348 {3: }| 349 :sign un | 350 undefine unplace | 351 :sign un^ | 352 ]]) 353 feed('<tab>') 354 screen:expect_unchanged() 355 feed('<Esc>') 356 screen:expect([[ 357 ^ | 358 {1:~ }|*5 359 | 360 ]]) 361 end) 362 363 it('wildmode=list,longest', function() 364 -- Need more than 5 rows, else tabline is covered and will be redrawn. 365 screen:try_resize(25, 7) 366 367 command('set wildmenu wildmode=list,longest') 368 feed(':sign u<tab>') 369 screen:expect([[ 370 | 371 {1:~ }|*2 372 {3: }| 373 :sign u | 374 undefine unplace | 375 :sign u^ | 376 ]]) 377 feed('<tab>') -- trigger wildmode longest 378 screen:expect([[ 379 | 380 {1:~ }|*2 381 {3: }| 382 :sign u | 383 undefine unplace | 384 :sign un^ | 385 ]]) 386 feed('<Esc>') 387 screen:expect([[ 388 ^ | 389 {1:~ }|*5 390 | 391 ]]) 392 end) 393 394 it('multiple <C-D> renders correctly', function() 395 screen:try_resize(25, 7) 396 397 command('set laststatus=2') 398 feed(':set wildm') 399 feed('<c-d>') 400 screen:expect([[ 401 | 402 {1:~ }|*2 403 {3: }| 404 :set wildm | 405 wildmenu wildmode | 406 :set wildm^ | 407 ]]) 408 feed('<c-d>') 409 screen:expect([[ 410 | 411 {3: }| 412 :set wildm | 413 wildmenu wildmode | 414 :set wildm | 415 wildmenu wildmode | 416 :set wildm^ | 417 ]]) 418 feed('<Esc>') 419 screen:expect([[ 420 ^ | 421 {1:~ }|*4 422 {3:[No Name] }| 423 | 424 ]]) 425 end) 426 427 it("<C-D> doesn't make statuslines disappear with 'nowildmenu' #36053", function() 428 screen:try_resize(60, 10) 429 command('set laststatus=2 nowildmenu') 430 feed(':sign <C-D>') 431 screen:expect([[ 432 | 433 {1:~ }|*5 434 {3: }| 435 :sign | 436 define jump list place undefine unplace | 437 :sign ^ | 438 ]]) 439 feed('<Esc>') 440 screen:expect([[ 441 ^ | 442 {1:~ }|*7 443 {3:[No Name] }| 444 | 445 ]]) 446 command('mode') 447 screen:expect_unchanged() 448 feed('ifoobar<Esc>') 449 screen:expect([[ 450 fooba^r | 451 {1:~ }|*7 452 {3:[No Name] [+] }| 453 | 454 ]]) 455 end) 456 457 it('works with c_CTRL_Z standard mapping', function() 458 screen:add_extra_attr_ids { 459 [100] = { background = Screen.colors.Yellow1, foreground = Screen.colors.Black }, 460 } 461 462 -- Wildcharm? where we are going we aint't no need no wildcharm. 463 eq(0, api.nvim_get_option_value('wildcharm', {})) 464 -- Don't mess the defaults yet (neovim is about backwards compatibility) 465 eq(9, api.nvim_get_option_value('wildchar', {})) 466 -- Lol what is cnoremap? Some say it can define mappings. 467 command 'set wildchar=0' 468 eq(0, api.nvim_get_option_value('wildchar', {})) 469 470 command 'cnoremap <f2> <c-z>' 471 feed(':syntax <f2>') 472 screen:expect { 473 grid = [[ 474 | 475 {1:~ }|*2 476 {100:case}{3: clear cluster > }| 477 :syntax case^ | 478 ]], 479 } 480 feed '<esc>' 481 482 command 'set wildmode=longest:full,full' 483 -- this will get cleaner once we have native lua expr mappings: 484 command [[cnoremap <expr> <tab> luaeval("not rawset(_G, 'coin', not coin).coin") ? "<c-z>" : "c"]] 485 486 feed ':syntax <tab>' 487 screen:expect { 488 grid = [[ 489 | 490 {1:~ }|*3 491 :syntax c^ | 492 ]], 493 } 494 495 feed '<tab>' 496 screen:expect { 497 grid = [[ 498 | 499 {1:~ }|*2 500 {3:case clear cluster > }| 501 :syntax c^ | 502 ]], 503 } 504 505 feed '<tab>' 506 screen:expect { 507 grid = [[ 508 | 509 {1:~ }|*3 510 :syntax cc^ | 511 ]], 512 } 513 end) 514 end) 515 516 describe('command line completion', function() 517 local screen 518 before_each(function() 519 clear() 520 screen = Screen.new(40, 5) 521 screen:add_extra_attr_ids { 522 [100] = { background = Screen.colors.Yellow1, foreground = Screen.colors.Black }, 523 } 524 end) 525 after_each(function() 526 os.remove('Xtest-functional-viml-compl-dir') 527 end) 528 529 it('lists directories with empty PATH', function() 530 local tmp = fn.tempname() 531 command('e ' .. tmp) 532 command('cd %:h') 533 command("call mkdir('Xtest-functional-viml-compl-dir')") 534 command('let $PATH=""') 535 feed(':!<tab><bs>') 536 screen:expect([[ 537 | 538 {1:~ }|*3 539 :!Xtest-functional-viml-compl-dir^ | 540 ]]) 541 end) 542 543 it('completes env var names #9681', function() 544 command('let $XTEST_1 = "foo" | let $XTEST_2 = "bar"') 545 command('set wildmenu wildmode=full') 546 feed(':!echo $XTEST_<tab>') 547 screen:expect([[ 548 | 549 {1:~ }|*2 550 {100:XTEST_1}{3: XTEST_2 }| 551 :!echo $XTEST_1^ | 552 ]]) 553 end) 554 555 it('completes (multibyte) env var names #9655', function() 556 clear({ env = { 557 ['XTEST_1AaあB'] = 'foo', 558 ['XTEST_2'] = 'bar', 559 } }) 560 screen:attach() 561 command('set wildmenu wildmode=full') 562 feed(':!echo $XTEST_<tab>') 563 screen:expect([[ 564 | 565 {1:~ }|*2 566 {100:XTEST_1AaあB}{3: XTEST_2 }| 567 :!echo $XTEST_1AaあB^ | 568 ]]) 569 end) 570 571 it('does not leak memory with <S-Tab> with wildmenu and only one match #19874', function() 572 api.nvim_set_option_value('wildmenu', true, {}) 573 api.nvim_set_option_value('wildmode', 'full', {}) 574 api.nvim_set_option_value('wildoptions', 'pum', {}) 575 576 feed(':sign unpla<S-Tab>') 577 screen:expect([[ 578 | 579 {1:~ }|*3 580 :sign unplace^ | 581 ]]) 582 583 feed('<Space>buff<Tab>') 584 screen:expect([[ 585 | 586 {1:~ }|*3 587 :sign unplace buffer=^ | 588 ]]) 589 end) 590 591 it('does not show matches with <S-Tab> without wildmenu with wildmode=full', function() 592 api.nvim_set_option_value('wildmenu', false, {}) 593 api.nvim_set_option_value('wildmode', 'full', {}) 594 595 feed(':sign <S-Tab>') 596 screen:expect([[ 597 | 598 {1:~ }|*3 599 :sign unplace^ | 600 ]]) 601 end) 602 603 it('shows matches with <S-Tab> without wildmenu with wildmode=list', function() 604 api.nvim_set_option_value('wildmenu', false, {}) 605 api.nvim_set_option_value('wildmode', 'list', {}) 606 607 feed(':sign <S-Tab>') 608 screen:expect([[ 609 {3: }| 610 :sign define | 611 define list undefine | 612 jump place unplace | 613 :sign unplace^ | 614 ]]) 615 end) 616 end) 617 618 describe('ui/ext_wildmenu', function() 619 local screen 620 621 before_each(function() 622 clear() 623 screen = Screen.new(25, 5, { rgb = true, ext_wildmenu = true }) 624 end) 625 626 local function test_ext_wildmenu_sign_cmd() 627 local expected = { 628 'define', 629 'jump', 630 'list', 631 'place', 632 'undefine', 633 'unplace', 634 } 635 636 command('set wildmode=full') 637 command('set wildmenu') 638 feed(':sign <tab>') 639 screen:expect { 640 grid = [[ 641 | 642 {1:~ }|*3 643 :sign define^ | 644 ]], 645 wildmenu_items = expected, 646 wildmenu_pos = 0, 647 } 648 649 feed('<tab>') 650 screen:expect { 651 grid = [[ 652 | 653 {1:~ }|*3 654 :sign jump^ | 655 ]], 656 wildmenu_items = expected, 657 wildmenu_pos = 1, 658 } 659 660 feed('<left><left>') 661 screen:expect { 662 grid = [[ 663 | 664 {1:~ }|*3 665 :sign ^ | 666 ]], 667 wildmenu_items = expected, 668 wildmenu_pos = -1, 669 } 670 671 feed('<right>') 672 screen:expect { 673 grid = [[ 674 | 675 {1:~ }|*3 676 :sign define^ | 677 ]], 678 wildmenu_items = expected, 679 wildmenu_pos = 0, 680 } 681 682 feed('a') 683 screen:expect([[ 684 | 685 {1:~ }|*3 686 :sign definea^ | 687 ]]) 688 689 feed('<Esc>') 690 command('set wildmode=longest,full') 691 feed(':sign u<tab>') 692 screen:expect([[ 693 | 694 {1:~ }|*3 695 :sign un^ | 696 ]]) 697 698 feed('<tab>') 699 local s_undefine_unplace_0 = { 700 grid = [[ 701 | 702 {1:~ }|*3 703 :sign undefine^ | 704 ]], 705 wildmenu_items = { 'undefine', 'unplace' }, 706 wildmenu_pos = 0, 707 } 708 screen:expect(s_undefine_unplace_0) 709 710 feed('<Esc>') 711 screen:expect([[ 712 ^ | 713 {1:~ }|*3 714 | 715 ]]) 716 717 feed(':sign un<tab>') 718 screen:expect(s_undefine_unplace_0) 719 end 720 721 describe('works with :sign <tab>', function() 722 it('with wildoptions=pum', function() 723 command('set wildoptions=pum') 724 test_ext_wildmenu_sign_cmd() 725 end) 726 727 it('with wildoptions=', function() 728 command('set wildoptions=') 729 test_ext_wildmenu_sign_cmd() 730 end) 731 end) 732 end)