startup_spec.lua (56309B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local assert_alive = n.assert_alive 6 local assert_log = t.assert_log 7 local clear = n.clear 8 local command = n.command 9 local ok = t.ok 10 local eq = t.eq 11 local matches = t.matches 12 local eval = n.eval 13 local exec = n.exec 14 local exec_capture = n.exec_capture 15 local exec_lua = n.exec_lua 16 local feed = n.feed 17 local fn = n.fn 18 local pesc = vim.pesc 19 local mkdir = t.mkdir 20 local mkdir_p = n.mkdir_p 21 local nvim_prog = n.nvim_prog 22 local nvim_set = n.nvim_set 23 local read_file = t.read_file 24 local retry = t.retry 25 local rmdir = n.rmdir 26 local sleep = vim.uv.sleep 27 local startswith = vim.startswith 28 local write_file = t.write_file 29 local api = n.api 30 local is_os = t.is_os 31 local dedent = t.dedent 32 local tbl_map = vim.tbl_map 33 local tbl_filter = vim.tbl_filter 34 local endswith = vim.endswith 35 local check_close = n.check_close 36 37 local testlog = 'Xtest-startupspec-log' 38 39 describe('startup', function() 40 it('--clean', function() 41 clear() 42 matches( 43 vim.pesc(t.fix_slashes(fn.stdpath('config'))), 44 t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) 45 ) 46 47 clear('--clean') 48 ok( 49 not t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) 50 :match(vim.pesc(t.fix_slashes(fn.stdpath('config')))) 51 ) 52 end) 53 54 it('prevents remote UI infinite loop', function() 55 clear() 56 local screen = Screen.new(84, 3) 57 fn.jobstart( 58 { nvim_prog, '-u', 'NONE', '--server', eval('v:servername'), '--remote-ui' }, 59 { term = true } 60 ) 61 screen:expect([[ 62 ^Cannot attach UI of :terminal child to its parent. (Unset $NVIM to skip this check) | 63 |*2 64 ]]) 65 end) 66 67 it('--startuptime', function() 68 local testfile = 'Xtest_startuptime' 69 finally(function() 70 os.remove(testfile) 71 end) 72 clear({ args = { '--startuptime', testfile } }) 73 assert_log('Embedded', testfile, 100) 74 assert_log('sourcing', testfile, 100) 75 assert_log("require%('vim%._core.editor'%)", testfile, 100) 76 end) 77 78 it('--startuptime does not crash on error #31125', function() 79 local p = n.spawn_wait('--startuptime', '.', '-c', '42cquit') 80 eq("E484: Can't open file .", p.stderr) 81 eq(42, p.status) 82 end) 83 84 it('-D does not hang #12647', function() 85 clear() 86 local screen = Screen.new(60, 7) 87 -- not the same colors on windows for some reason 88 screen._default_attr_ids = nil 89 local id = fn.jobstart({ 90 nvim_prog, 91 '-u', 92 'NONE', 93 '-i', 94 'NONE', 95 '--cmd', 96 'set noruler', 97 '-D', 98 }, { 99 term = true, 100 env = { 101 VIMRUNTIME = os.getenv('VIMRUNTIME'), 102 }, 103 }) 104 screen:expect({ any = pesc('Entering Debug mode. Type "cont" to continue.') }) 105 fn.chansend(id, 'cont\n') 106 screen:expect([[ 107 ^ | 108 ~ |*3 109 [No Name] | 110 |*2 111 ]]) 112 end) 113 114 it(':filetype detect enables filetype detection with -u NONE', function() 115 clear() 116 eq('filetype detection:OFF plugin:OFF indent:OFF', exec_capture('filetype')) 117 command('filetype detect') 118 eq('filetype detection:ON plugin:OFF indent:OFF', exec_capture('filetype')) 119 end) 120 end) 121 122 describe('startup', function() 123 before_each(clear) 124 125 after_each(function() 126 check_close() 127 os.remove(testlog) 128 end) 129 130 describe('-l Lua', function() 131 local function assert_l_out(expected, nvim_args, lua_args, script, input) 132 local args = { nvim_prog } 133 vim.list_extend(args, nvim_args or {}) 134 vim.list_extend(args, { '-l', (script or 'test/functional/fixtures/startup.lua') }) 135 vim.list_extend(args, lua_args or {}) 136 local out = fn.system(args, input):gsub('\r\n', '\n') 137 if type(expected) == 'function' then 138 return expected(out) 139 else 140 return eq(dedent(expected), out) 141 end 142 end 143 144 it('failure modes', function() 145 -- nvim -l <empty> 146 local proc = n.spawn_wait('-l') 147 matches('nvim%.?e?x?e?: Argument missing after: "%-l"', proc.stderr) 148 eq(1, proc.status) 149 end) 150 151 it('os.exit() sets Nvim exitcode', function() 152 -- tricky: LeakSanitizer triggers on os.exit() and disrupts the return value, disable it 153 exec_lua [[ 154 local asan_options = os.getenv('ASAN_OPTIONS') or '' 155 if asan_options ~= '' then 156 asan_options = asan_options .. ':' 157 end 158 vim.uv.os_setenv('ASAN_OPTIONS', asan_options .. ':detect_leaks=0') 159 ]] 160 -- nvim -l foo.lua -arg1 -- a b c 161 assert_l_out( 162 [[ 163 bufs: 164 nvim args: 7 165 lua args: { "-arg1", "--exitcode", "73", "--arg2", 166 [0] = "test/functional/fixtures/startup.lua" 167 }]], 168 {}, 169 { '-arg1', '--exitcode', '73', '--arg2' } 170 ) 171 eq(73, eval('v:shell_error')) 172 end) 173 174 it('Lua-error sets Nvim exitcode', function() 175 local proc = n.spawn_wait('-l', 'test/functional/fixtures/startup-fail.lua') 176 matches('E5113: .* my pearls!!', proc:output()) 177 eq(0, proc.signal) 178 eq(1, proc.status) 179 180 eq(0, eval('v:shell_error')) 181 matches( 182 'E5113: .* %[string "error%("whoa"%)"%]:1: whoa', 183 fn.system({ nvim_prog, '-l', '-' }, 'error("whoa")') 184 ) 185 eq(1, eval('v:shell_error')) 186 end) 187 188 it('executes stdin "-"', function() 189 assert_l_out( 190 'arg0=- args=2 whoa\n', 191 nil, 192 { 'arg1', 'arg 2' }, 193 '-', 194 "print(('arg0=%s args=%d %s'):format(_G.arg[0], #_G.arg, 'whoa'))" 195 ) 196 assert_l_out( 197 'biiig input: 1000042\n', 198 nil, 199 nil, 200 '-', 201 ('print("biiig input: "..("%s"):len())'):format(string.rep('x', (1000 * 1000) + 42)) 202 ) 203 eq(0, eval('v:shell_error')) 204 end) 205 206 it('does not truncate long print() message', function() 207 assert_l_out(('k'):rep(1234) .. '\n', nil, nil, '-', "print(('k'):rep(1234))") 208 end) 209 210 it('does not add newline when unnecessary', function() 211 assert_l_out('', nil, nil, '-', '') 212 assert_l_out('foobar\n', nil, nil, '-', [[print('foobar\n')]]) 213 end) 214 215 it('sets _G.arg', function() 216 -- nvim -l foo.lua 217 assert_l_out( 218 [[ 219 bufs: 220 nvim args: 3 221 lua args: { 222 [0] = "test/functional/fixtures/startup.lua" 223 } 224 ]], 225 {}, 226 {} 227 ) 228 eq(0, eval('v:shell_error')) 229 230 -- nvim -l foo.lua [args] 231 assert_l_out( 232 [[ 233 bufs: 234 nvim args: 7 235 lua args: { "-arg1", "--arg2", "--", "arg3", 236 [0] = "test/functional/fixtures/startup.lua" 237 } 238 ]], 239 {}, 240 { '-arg1', '--arg2', '--', 'arg3' } 241 ) 242 eq(0, eval('v:shell_error')) 243 244 -- nvim file1 file2 -l foo.lua -arg1 -- file3 file4 245 assert_l_out( 246 [[ 247 bufs: file1 file2 248 nvim args: 10 249 lua args: { "-arg1", "arg 2", "--", "file3", "file4", 250 [0] = "test/functional/fixtures/startup.lua" 251 } 252 ]], 253 { 'file1', 'file2' }, 254 { '-arg1', 'arg 2', '--', 'file3', 'file4' } 255 ) 256 eq(0, eval('v:shell_error')) 257 258 -- nvim -l foo.lua <vim args> 259 assert_l_out( 260 [[ 261 bufs: 262 nvim args: 5 263 lua args: { "-c", "set wrap?", 264 [0] = "test/functional/fixtures/startup.lua" 265 } 266 ]], 267 {}, 268 { '-c', 'set wrap?' } 269 ) 270 eq(0, eval('v:shell_error')) 271 272 -- nvim <vim args> -l foo.lua <vim args> 273 assert_l_out( 274 [[ 275 wrap 276 bufs: 277 nvim args: 7 278 lua args: { "-c", "set wrap?", 279 [0] = "test/functional/fixtures/startup.lua" 280 } 281 ]], 282 { '-c', 'set wrap?' }, 283 { '-c', 'set wrap?' } 284 ) 285 eq(0, eval('v:shell_error')) 286 end) 287 288 it('disables swapfile/shada/config/plugins unless overridden', function() 289 local script = [[print(('updatecount=%d shadafile=%s loadplugins=%s scripts=%d'):format( 290 vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]] 291 finally(function() 292 os.remove('Xtest_shada') 293 end) 294 295 assert_l_out( 296 'updatecount=0 shadafile=NONE loadplugins=false scripts=1\n', 297 nil, 298 nil, 299 '-', 300 script 301 ) 302 303 -- User can override. 304 assert_l_out( 305 function(out) 306 return matches('updatecount=99 shadafile=Xtest_shada loadplugins=true scripts=2%d\n', out) 307 end, 308 { '+set updatecount=99', '-i', 'Xtest_shada', '+set loadplugins', '-u', 'NORC' }, 309 nil, 310 '-', 311 script 312 ) 313 end) 314 end) 315 316 it('--cmd/-c/+ do not truncate long Lua print() message with --headless', function() 317 local out = fn.system({ 318 nvim_prog, 319 '-u', 320 'NONE', 321 '-i', 322 'NONE', 323 '--headless', 324 '--cmd', 325 'lua print(("A"):rep(1234))', 326 '-c', 327 'lua print(("B"):rep(1234))', 328 '+lua print(("C"):rep(1234))', 329 '+q', 330 }) 331 eq(('A'):rep(1234) .. '\r\n' .. ('B'):rep(1234) .. '\r\n' .. ('C'):rep(1234), out) 332 end) 333 334 it('pipe at both ends: has("ttyin")==0 has("ttyout")==0', function() 335 -- system() puts a pipe at both ends. 336 local out = fn.system({ 337 nvim_prog, 338 '-u', 339 'NONE', 340 '-i', 341 'NONE', 342 '--headless', 343 '--cmd', 344 nvim_set, 345 '-c', 346 [[echo has('ttyin') has('ttyout')]], 347 '+q', 348 }) 349 eq('0 0', out) 350 end) 351 352 it('with --embed: has("ttyin")==0 has("ttyout")==0', function() 353 local screen = Screen.new(25, 3) 354 -- Remote UI connected by --embed. 355 -- TODO: a lot of tests in this file already use the new default color scheme. 356 -- once we do the batch update of tests to use it, remove this workaround 357 screen._default_attr_ids = nil 358 command([[echo has('ttyin') has('ttyout')]]) 359 screen:expect([[ 360 ^ | 361 ~ | 362 0 0 | 363 ]]) 364 end) 365 366 it('in a TTY: has("ttyin")==1 has("ttyout")==1', function() 367 local screen = Screen.new(25, 4) 368 screen._default_attr_ids = nil 369 if is_os('win') then 370 command([[set shellcmdflag=/s\ /c shellxquote=\"]]) 371 end 372 -- Running in :terminal 373 fn.jobstart({ 374 nvim_prog, 375 '-u', 376 'NONE', 377 '-i', 378 'NONE', 379 '--cmd', 380 nvim_set, 381 '-c', 382 'echo has("ttyin") has("ttyout")', 383 }, { 384 term = true, 385 env = { 386 VIMRUNTIME = os.getenv('VIMRUNTIME'), 387 }, 388 }) 389 screen:expect([[ 390 ^ | 391 ~ | 392 1 1 | 393 | 394 ]]) 395 end) 396 397 it('output to pipe: has("ttyin")==1 has("ttyout")==0', function() 398 clear({ env = { NVIM_LOG_FILE = testlog } }) 399 if is_os('win') then 400 command([[set shellcmdflag=/s\ /c shellxquote=\"]]) 401 end 402 os.remove('Xtest_startup_ttyout') 403 finally(function() 404 os.remove('Xtest_startup_ttyout') 405 end) 406 -- Running in :terminal 407 fn.jobstart( 408 ( 409 [["%s" -u NONE -i NONE --cmd "%s"]] 410 .. [[ -c "call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')"]] 411 .. [[ -c q | cat -v]] 412 ):format(nvim_prog, nvim_set), 413 { 414 term = true, 415 env = { 416 VIMRUNTIME = os.getenv('VIMRUNTIME'), 417 }, 418 } 419 ) 420 retry(nil, 3000, function() 421 sleep(1) 422 eq( 423 '1\n0\n', -- stdin is a TTY, stdout is a pipe 424 read_file('Xtest_startup_ttyout') 425 ) 426 end) 427 end) 428 429 it('input from pipe: has("ttyin")==0 has("ttyout")==1', function() 430 clear({ env = { NVIM_LOG_FILE = testlog } }) 431 if is_os('win') then 432 command([[set shellcmdflag=/s\ /c shellxquote=\"]]) 433 end 434 os.remove('Xtest_startup_ttyout') 435 finally(function() 436 os.remove('Xtest_startup_ttyout') 437 end) 438 -- Running in :terminal 439 fn.jobstart( 440 ( 441 [[echo foo | ]] -- Input from a pipe. 442 .. [["%s" -u NONE -i NONE --cmd "%s"]] 443 .. [[ -c "call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')"]] 444 .. [[ -c q -- -]] 445 ):format(nvim_prog, nvim_set), 446 { 447 term = true, 448 env = { 449 VIMRUNTIME = os.getenv('VIMRUNTIME'), 450 }, 451 } 452 ) 453 retry(nil, 3000, function() 454 sleep(1) 455 eq( 456 '0\n1\n', -- stdin is a pipe, stdout is a TTY 457 read_file('Xtest_startup_ttyout') 458 ) 459 end) 460 end) 461 462 it('input from pipe (implicit) #7679', function() 463 clear({ env = { NVIM_LOG_FILE = testlog } }) 464 local screen = Screen.new(25, 4) 465 screen._default_attr_ids = nil 466 if is_os('win') then 467 command([[set shellcmdflag=/s\ /c shellxquote=\"]]) 468 end 469 -- Running in :terminal 470 fn.jobstart( 471 ( 472 [[echo foo | ]] 473 .. [["%s" -u NONE -i NONE --cmd "%s"]] 474 .. [[ -c "echo has('ttyin') has('ttyout')"]] 475 ):format(nvim_prog, nvim_set), 476 { 477 term = true, 478 env = { 479 VIMRUNTIME = os.getenv('VIMRUNTIME'), 480 }, 481 } 482 ) 483 screen:expect([[ 484 ^foo | 485 ~ | 486 0 1 | 487 | 488 ]]) 489 if not is_os('win') then 490 assert_log('Failed to get flags on descriptor 3: Bad file descriptor', testlog, 100) 491 end 492 end) 493 494 it('input from pipe + file args #7679', function() 495 eq( 496 'ohyeah\r\n0 0 bufs=3', 497 fn.system({ 498 nvim_prog, 499 '-n', 500 '-u', 501 'NONE', 502 '-i', 503 'NONE', 504 '--headless', 505 '+.print', 506 "+echo has('ttyin') has('ttyout') 'bufs='.bufnr('$')", 507 '+qall!', 508 '-', 509 'test/functional/fixtures/tty-test.c', 510 'test/functional/fixtures/shell-test.c', 511 }, { 'ohyeah', '' }) 512 ) 513 end) 514 515 it('if stdin is empty: selects buffer 2, deletes buffer 1 #8561', function() 516 eq( 517 '\r\n 2 %a "file1" line 0\r\n 3 "file2" line 0', 518 fn.system({ 519 nvim_prog, 520 '-n', 521 '-u', 522 'NONE', 523 '-i', 524 'NONE', 525 '--headless', 526 '+ls!', 527 '+qall!', 528 '-', 529 'file1', 530 'file2', 531 }, { '' }) 532 ) 533 end) 534 535 it('if stdin is empty and - is last: selects buffer 1, deletes buffer 3 #35269', function() 536 eq( 537 '\r\n 1 %a "file1" line 0\r\n 2 "file2" line 0', 538 fn.system({ 539 nvim_prog, 540 '-n', 541 '-u', 542 'NONE', 543 '-i', 544 'NONE', 545 '--headless', 546 '+ls!', 547 '+qall!', 548 'file1', 549 'file2', 550 '-', 551 }, { '' }) 552 ) 553 end) 554 555 it("empty stdin with terminal split doesn't crash #35681", function() 556 eq( 557 'nocrash', 558 fn.system({ 559 nvim_prog, 560 '-n', 561 '-u', 562 'NONE', 563 '-i', 564 'NONE', 565 '--headless', 566 '--cmd', 567 'term', 568 '+split', 569 '+quit!', 570 '+bw!', 571 '+bw!', 572 '+echo "nocrash"', 573 "+call timer_start(1, { -> execute('qa') })", -- need to let event handling happen 574 '-', 575 }, { '' }) 576 ) 577 end) 578 579 it('stdin with -es/-Es #7679', function() 580 local input = { 'append', 'line1', 'line2', '.', '%print', '' } 581 local inputstr = table.concat(input, '\n') 582 583 -- 584 -- -Es: read stdin as text 585 -- 586 eq( 587 'partylikeits1999\n', 588 fn.system({ 589 nvim_prog, 590 '-n', 591 '-u', 592 'NONE', 593 '-i', 594 'NONE', 595 '-Es', 596 '+.print', 597 'test/functional/fixtures/tty-test.c', 598 }, { 'partylikeits1999', '' }) 599 ) 600 eq(inputstr, fn.system({ nvim_prog, '-i', 'NONE', '-Es', '+%print', '-' }, input)) 601 -- with `-u NORC` 602 eq( 603 'thepartycontinues\n', 604 fn.system({ nvim_prog, '-n', '-u', 'NORC', '-Es', '+.print' }, { 'thepartycontinues', '' }) 605 ) 606 -- without `-u` 607 eq( 608 'thepartycontinues\n', 609 fn.system({ nvim_prog, '-n', '-Es', '+.print' }, { 'thepartycontinues', '' }) 610 ) 611 612 -- 613 -- -es: read stdin as ex-commands 614 -- 615 eq( 616 ' encoding=utf-8\n', 617 fn.system({ 618 nvim_prog, 619 '-n', 620 '-u', 621 'NONE', 622 '-i', 623 'NONE', 624 '-es', 625 'test/functional/fixtures/tty-test.c', 626 }, { 'set encoding', '' }) 627 ) 628 eq('line1\nline2\n', fn.system({ nvim_prog, '-i', 'NONE', '-es', '-' }, input)) 629 -- with `-u NORC` 630 eq( 631 ' encoding=utf-8\n', 632 fn.system({ nvim_prog, '-n', '-u', 'NORC', '-es' }, { 'set encoding', '' }) 633 ) 634 -- without `-u` 635 eq(' encoding=utf-8\n', fn.system({ nvim_prog, '-n', '-es' }, { 'set encoding', '' })) 636 end) 637 638 it('-es/-Es disables swapfile/shada/config #8540', function() 639 for _, arg in ipairs({ '-es', '-Es' }) do 640 local out = fn.system({ 641 nvim_prog, 642 arg, 643 '+set updatecount? shadafile? loadplugins?', 644 '+put =map(getscriptinfo(), {-> v:val.name})', 645 '+%print', 646 }) 647 local line1 = string.match(out, '^.-\n') 648 -- updatecount=0 means swapfile was disabled. 649 eq(' updatecount=0 shadafile=NONE loadplugins\n', line1) 650 -- Standard plugins were loaded, but not user config. #31878 651 local nrlines = #vim.split(out, '\n') 652 ok(nrlines > 20, '>20', nrlines) 653 ok(string.find(out, 'man.lua') ~= nil) 654 ok(string.find(out, 'init.vim') == nil) 655 end 656 end) 657 658 it('fails on --embed with -es/-Es/-l', function() 659 matches( 660 'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', 661 n.spawn_wait('--embed', '-es').stderr 662 ) 663 matches( 664 'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', 665 n.spawn_wait('--embed', '-Es').stderr 666 ) 667 matches( 668 'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', 669 n.spawn_wait('--embed', '-l', 'foo.lua').stderr 670 ) 671 end) 672 673 it('-es does not exit early with closed stdin', function() 674 write_file('Xinput.txt', 'line1\nline2\nline3\nline4\n') 675 write_file('Xoutput.txt', 'OUT\n') 676 finally(function() 677 os.remove('Xinput.txt') 678 os.remove('Xoutput.txt') 679 end) 680 -- Use system() without input so that stdin is closed. 681 fn.system({ 682 nvim_prog, 683 '--clean', 684 '-es', 685 '-c', 686 -- 'showcmd' leads to a char_avail() call just after the 'Q' (no more input). 687 [[set showcmd | exe "g/^/vi|Vgg:w>>Xoutput.txt\rgQ"]], 688 'Xinput.txt', 689 }) 690 eq( 691 'OUT\nline1\nline1\nline2\nline1\nline2\nline3\nline1\nline2\nline3\nline4\n', 692 read_file('Xoutput.txt') 693 ) 694 end) 695 696 it('ENTER dismisses early message #7967', function() 697 local screen 698 screen = Screen.new(60, 6) 699 screen._default_attr_ids = nil 700 local id = fn.jobstart({ 701 nvim_prog, 702 '-u', 703 'NONE', 704 '-i', 705 'NONE', 706 '--cmd', 707 'set noruler', 708 '--cmd', 709 'let g:foo = g:bar', 710 }, { 711 term = true, 712 env = { 713 VIMRUNTIME = os.getenv('VIMRUNTIME'), 714 }, 715 }) 716 screen:expect([[ 717 ^ | 718 | 719 Error in pre-vimrc command line: | 720 E121: Undefined variable: g:bar | 721 Press ENTER or type command to continue | 722 | 723 ]]) 724 fn.chansend(id, '\n') 725 screen:expect([[ 726 ^ | 727 ~ |*2 728 [No Name] | 729 |*2 730 ]]) 731 end) 732 733 it('-r works without --headless in PTY #23294', function() 734 exec([[ 735 func Normalize(data) abort 736 " Windows: remove ^M and term escape sequences 737 return mapnew(a:data, 'substitute(substitute(v:val, "\r", "", "g"), "\x1b\\%(\\]\\d\\+;.\\{-}\x07\\|\\[.\\{-}[\x40-\x7E]\\)", "", "g")') 738 endfunc 739 func OnOutput(id, data, event) dict 740 let g:stdout = Normalize(a:data) 741 endfunc 742 call jobstart([v:progpath, '-u', 'NONE', '-i', 'NONE', '-r'], { 743 \ 'pty': v:true, 744 \ 'stdout_buffered': v:true, 745 \ 'on_stdout': function('OnOutput'), 746 \ }) 747 ]]) 748 retry(nil, nil, function() 749 eq('Swap files found:', eval('g:stdout[0]')) 750 end) 751 end) 752 753 it('fixed hang issue with --headless (#11386)', function() 754 local expected = '' 755 local period = 100 756 for i = 1, period - 1 do 757 expected = expected .. i .. '\r\n' 758 end 759 expected = expected .. period 760 eq( 761 expected, 762 -- FIXME(codehex): We should really set a timeout for the system function. 763 -- If this test fails, there will be a waiting input state. 764 fn.system({ 765 nvim_prog, 766 '-u', 767 'NONE', 768 '-c', 769 'for i in range(1, 100) | echo i | endfor | quit', 770 '--headless', 771 }) 772 ) 773 end) 774 775 it('get command line arguments from v:argv', function() 776 local p = n.spawn_wait('--cmd', nvim_set, '-c', [[echo v:argv[-1:] len(v:argv) > 1]], '+q') 777 eq("['+q'] 1", p.stderr) 778 end) 779 end) 780 781 describe('startup', function() 782 it('-e/-E interactive #7679', function() 783 clear('-e') 784 local screen = Screen.new(25, 3) 785 feed("put ='from -e'<CR>") 786 screen:expect([[ 787 :put ='from -e' | 788 from -e | 789 :^ | 790 ]]) 791 792 clear('-E') 793 screen = Screen.new(25, 3) 794 feed("put ='from -E'<CR>") 795 screen:expect([[ 796 :put ='from -E' | 797 from -E | 798 :^ | 799 ]]) 800 end) 801 802 it('-e sets ex mode', function() 803 clear('-e') 804 local screen = Screen.new(25, 3) 805 -- Verify we set the proper mode both before and after :vi. 806 feed('put =mode(1)<CR>vi<CR>:put =mode(1)<CR>') 807 screen:expect([[ 808 cv | 809 ^n | 810 :put =mode(1) | 811 ]]) 812 813 eq('cv\n', fn.system({ nvim_prog, '-n', '-es' }, { 'put =mode(1)', 'print', '' })) 814 end) 815 816 it('-d does not diff non-arglist windows #13720 #21289', function() 817 write_file( 818 'Xdiff.vim', 819 [[ 820 let bufnr = nvim_create_buf(0, 1) 821 let config = { 822 \ 'relative': 'editor', 823 \ 'focusable': v:false, 824 \ 'width': 1, 825 \ 'height': 1, 826 \ 'row': 3, 827 \ 'col': 3 828 \ } 829 autocmd WinEnter * let g:float_win = nvim_open_win(bufnr, v:false, config) 830 ]] 831 ) 832 finally(function() 833 os.remove('Xdiff.vim') 834 end) 835 clear { args = { '-u', 'Xdiff.vim', '-d', 'Xdiff.vim', 'Xdiff.vim' } } 836 eq(true, api.nvim_get_option_value('diff', { win = fn.win_getid(1) })) 837 eq(true, api.nvim_get_option_value('diff', { win = fn.win_getid(2) })) 838 local float_win = eval('g:float_win') 839 eq('editor', api.nvim_win_get_config(float_win).relative) 840 eq(false, api.nvim_get_option_value('diff', { win = float_win })) 841 end) 842 843 it('still opens arglist windows if init only opens floating windows', function() 844 write_file( 845 'Xfloat.vim', 846 [[ 847 let config = { 848 \ 'relative': 'editor', 849 \ 'focusable': v:false, 850 \ 'width': 1, 851 \ 'height': 1, 852 \ 'row': 1, 853 \ 'col': 1 854 \ } 855 call nvim_open_win(0, v:false, config) 856 ]] 857 ) 858 finally(function() 859 os.remove('Xfloat.vim') 860 end) 861 clear { args = { '-u', 'Xfloat.vim', '-d', 'Xfloat.vim', 'foo' } } 862 local screen = Screen.new(40, 5) 863 screen:add_extra_attr_ids({ 864 [101] = { 865 bold = true, 866 background = Screen.colors.LightBlue, 867 foreground = Screen.colors.Brown, 868 }, 869 [102] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.LightBlue }, 870 [103] = { foreground = Screen.colors.Magenta, background = Screen.colors.LightBlue }, 871 [104] = { foreground = Screen.colors.DarkCyan, background = Screen.colors.LightBlue }, 872 }) 873 screen:expect([[ 874 {7: }{101:^let}{22: config }{101:=}{22: }{102:{}{22: }│{23:-}| 875 {7: }{101:l}{22: }{102:\}{22: }{103:'relative'}{22:: }{103:'editor'}{22:, }│{23:-}| 876 {7: }{22: }{102:\}{22: }{103:'focusable'}{22:: }{104:v:}{22:false, }│ | 877 {3:Xfloat.vim }{2:<}| 878 | 879 ]]) 880 end) 881 882 it("default 'diffopt' is applied with -d", function() 883 clear({ 884 args = { 885 '-d', 886 'test/functional/fixtures/diff/startup_old.txt', 887 'test/functional/fixtures/diff/startup_new.txt', 888 '--cmd', 889 'set laststatus=0', 890 }, 891 }) 892 local screen = Screen.new(80, 24) 893 screen:expect([[ 894 {7:+ }{13:^+-- 15 lines: package main············}│{7:+ }{13:+-- 15 lines: package main···········}| 895 {7: } │{7: } | 896 {7: }func printCharacters(str string) strin│{7: }func printCharacters(str string) stri| 897 {7: } return str │{7: } return str | 898 {7: }} │{7: }} | 899 {7: } │{7: } | 900 {7: }func main() { │{7: }func main() { | 901 {7: }{23:--------------------------------------}│{7: }{22: hello := "Hello, World!" }| 902 {7: }{23:--------------------------------------}│{7: }{22: }| 903 {7: }{4: fmt.Print}{27:Ln}{4:(compressString("aa}│{7: }{4: fmt.Print(compressString("aaa}| 904 {7: }{4: fmt.Print}{27:Ln}{4:(compressString("go}│{7: }{4: fmt.Print(compressString("gol}| 905 {7: }{4: fmt.Print}{27:Ln}{4:(removeDuplicate("a}│{7: }{4: fmt.Print(removeDuplicate("aa}| 906 {7: }{4: fmt.Print}{27:Ln}{4:(reverseAndDouble("}│{7: }{4: fmt.Print(reverseAndDouble("a}| 907 {7: }{4: fmt.Print}{27:Ln}{4:(printCharacters("g}│{7: }{4: fmt.Print(printCharacters("go}| 908 {7: }{23:--------------------------------------}│{7: }{22: }| 909 {7: }{23:--------------------------------------}│{7: }{22: fmt.Println(hello) }| 910 {7: }} │{7: }} | 911 {1:~ }│{1:~ }|*6 912 | 913 ]]) 914 command('let &diffopt = &diffopt') 915 screen:expect_unchanged() 916 end) 917 918 it('does not crash if --embed is given twice', function() 919 clear { args = { '--embed' } } 920 assert_alive() 921 end) 922 923 it('does not crash when expanding cdpath during early_init', function() 924 clear { env = { CDPATH = '~doesnotexist' } } 925 assert_alive() 926 eq(',~doesnotexist', eval('&cdpath')) 927 end) 928 929 it("sets 'shortmess' when loading other tabs", function() 930 clear({ args = { '-p', 'a', 'b', 'c' } }) 931 local screen = Screen.new(25, 4) 932 screen:expect([[ 933 {5: a }{24: b c }{2: }{24:X}| 934 ^ | 935 {1:~ }| 936 | 937 ]]) 938 end) 939 940 describe('opening a terminal before buffers are loaded #30765', function() 941 local lines = {} --- @type string[] 942 for i = 1, 50 do 943 lines[#lines + 1] = ('line%d'):format(i) 944 end 945 946 setup(function() 947 write_file('Xsomefile', table.concat(lines, '\n') .. '\n') 948 end) 949 950 teardown(function() 951 os.remove('Xsomefile') 952 end) 953 954 it('sends buffer content to terminal with nvim_open_term()', function() 955 clear({ 956 args_rm = { '--headless' }, 957 args = { 958 'Xsomefile', 959 '--cmd', 960 'let g:chan = nvim_open_term(0, {}) | startinsert', 961 '--cmd', 962 'call chansend(g:chan, "new_line1\nnew_line2\nnew_line3")', 963 }, 964 }) 965 local screen = Screen.new(50, 7) 966 screen:expect([[ 967 line48 | 968 line49 | 969 line50 | 970 new_line1 | 971 new_line2 | 972 new_line3^ | 973 {5:-- TERMINAL --} | 974 ]]) 975 eq(lines, api.nvim_buf_get_lines(0, 0, #lines, true)) 976 end) 977 978 it('does not error with jobstart(…,{term=true})', function() 979 clear({ 980 args_rm = { '--headless' }, 981 args = { 982 'Xsomefile', 983 '--cmd', 984 ('lua vim.fn.jobstart({%q}, {term = true})'):format(n.testprg('tty-test')), 985 }, 986 }) 987 local screen = Screen.new(50, 7) 988 screen:expect([[ 989 ^tty ready | 990 |*6 991 ]]) 992 end) 993 end) 994 end) 995 996 describe('startup', function() 997 local function pack_clear(cmd) 998 -- add packages after config dir in rtp but before config/after 999 clear { 1000 args = { 1001 '--cmd', 1002 'set packpath=test/functional/fixtures', 1003 '--cmd', 1004 'let paths=split(&rtp, ",")', 1005 '--cmd', 1006 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', 1007 '--cmd', 1008 cmd, 1009 }, 1010 env = { XDG_CONFIG_HOME = 'test/functional/fixtures/' }, 1011 args_rm = { 'runtimepath' }, 1012 } 1013 end 1014 1015 it('handles &packpath during startup', function() 1016 pack_clear [[ 1017 let g:x = bar#test() 1018 let g:y = leftpad#pad("heyya") 1019 ]] 1020 eq(-3, eval 'g:x') 1021 eq(' heyya', eval 'g:y') 1022 1023 pack_clear [[ lua _G.y = require'bar'.doit() _G.z = require'leftpad''howdy' ]] 1024 eq({ 9003, '\thowdy' }, exec_lua [[ return { _G.y, _G.z } ]]) 1025 end) 1026 1027 it('handles require from &packpath in an async handler', function() 1028 -- NO! you cannot just speed things up by calling async functions during startup! 1029 -- It doesn't make anything actually faster! NOOOO! 1030 pack_clear [[ lua require'async_leftpad'('brrrr', 'async_res') ]] 1031 1032 -- haha, async leftpad go brrrrr 1033 eq('\tbrrrr', exec_lua [[ return _G.async_res ]]) 1034 end) 1035 1036 it('handles :packadd during startup', function() 1037 -- control group: opt/bonus is not available by default 1038 pack_clear [[ 1039 try 1040 let g:x = bonus#secret() 1041 catch 1042 let g:err = v:exception 1043 endtry 1044 ]] 1045 eq('Vim(let):E117: Unknown function: bonus#secret', eval 'g:err') 1046 1047 pack_clear [[ lua _G.test = {pcall(function() require'bonus'.launch() end)} ]] 1048 eq( 1049 { false, [[[string ":lua"]:1: module 'bonus' not found:]] }, 1050 exec_lua [[ _G.test[2] = string.gsub(_G.test[2], '[\r\n].*', '') return _G.test ]] 1051 ) 1052 1053 -- ok, time to launch the nukes: 1054 pack_clear [[ packadd! bonus | let g:x = bonus#secret() ]] 1055 eq('halloj', eval 'g:x') 1056 1057 pack_clear [[ packadd! bonus | lua _G.y = require'bonus'.launch() ]] 1058 eq('CPE 1704 TKS', exec_lua [[ return _G.y ]]) 1059 end) 1060 1061 it('handles the correct order with start packages and after/', function() 1062 pack_clear [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]] 1063 eq( 1064 { 'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after' }, 1065 exec_lua [[ return _G.test_loadorder ]] 1066 ) 1067 end) 1068 1069 it('handles the correct order with start packages and after/ after startup', function() 1070 pack_clear [[ lua _G.test_loadorder = {} ]] 1071 command [[ runtime! filen.lua ]] 1072 eq( 1073 { 'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after' }, 1074 exec_lua [[ return _G.test_loadorder ]] 1075 ) 1076 end) 1077 1078 it('handles the correct order with globpath(&rtp, ...)', function() 1079 pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] 1080 command [[ 1081 for x in globpath(&rtp, "filen.lua",1,1) 1082 call v:lua.dofile(x) 1083 endfor 1084 ]] 1085 eq( 1086 { 'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after' }, 1087 exec_lua [[ return _G.test_loadorder ]] 1088 ) 1089 1090 local rtp = api.nvim_get_option_value('rtp', {}) 1091 ok( 1092 startswith( 1093 rtp, 1094 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,' 1095 ), 1096 'startswith(…)', 1097 'rtp=' .. rtp 1098 ) 1099 end) 1100 1101 it('handles the correct order with opt packages and after/', function() 1102 pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! superspecial\nruntime! filen.lua" ]] 1103 eq({ 1104 'ordinary', 1105 'SuperSpecial', 1106 'FANCY', 1107 'mittel', 1108 'FANCY after', 1109 'SuperSpecial after', 1110 'ordinary after', 1111 }, exec_lua [[ return _G.test_loadorder ]]) 1112 end) 1113 1114 it('handles the correct order with opt packages and after/ after startup', function() 1115 pack_clear [[ lua _G.test_loadorder = {} ]] 1116 command [[ 1117 packadd! superspecial 1118 runtime! filen.lua 1119 ]] 1120 eq({ 1121 'ordinary', 1122 'SuperSpecial', 1123 'FANCY', 1124 'mittel', 1125 'FANCY after', 1126 'SuperSpecial after', 1127 'ordinary after', 1128 }, exec_lua [[ return _G.test_loadorder ]]) 1129 end) 1130 1131 it('does an incremental update for packadd', function() 1132 pack_clear [[ lua _G.test_loadorder = {} ]] 1133 command [[ 1134 " need to use the runtime to make the initial cache: 1135 runtime! non_exist_ent 1136 " this should now incrementally update it: 1137 packadd! superspecial 1138 ]] 1139 1140 local check = api.nvim__runtime_inspect() 1141 local check_copy = vim.deepcopy(check) 1142 local any_incremental = false 1143 for _, item in ipairs(check_copy) do 1144 any_incremental = any_incremental or item.pack_inserted 1145 item.pack_inserted = nil 1146 end 1147 eq(true, any_incremental, 'no pack_inserted in ' .. vim.inspect(check)) 1148 1149 command [[ 1150 let &rtp = &rtp 1151 runtime! phantom_ghost 1152 ]] 1153 1154 local new_check = api.nvim__runtime_inspect() 1155 eq(check_copy, new_check) 1156 1157 command [[ runtime! filen.lua ]] 1158 eq({ 1159 'ordinary', 1160 'SuperSpecial', 1161 'FANCY', 1162 'mittel', 1163 'FANCY after', 1164 'SuperSpecial after', 1165 'ordinary after', 1166 }, exec_lua [[ return _G.test_loadorder ]]) 1167 end) 1168 1169 it('handles the correct order with opt packages and globpath(&rtp, ...)', function() 1170 pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] 1171 command [[ 1172 packadd! superspecial 1173 for x in globpath(&rtp, "filen.lua",1,1) 1174 call v:lua.dofile(x) 1175 endfor 1176 ]] 1177 eq({ 1178 'ordinary', 1179 'SuperSpecial', 1180 'FANCY', 1181 'mittel', 1182 'SuperSpecial after', 1183 'FANCY after', 1184 'ordinary after', 1185 }, exec_lua [[ return _G.test_loadorder ]]) 1186 end) 1187 1188 it('handles the correct order with a package that changes packpath', function() 1189 pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! funky\nruntime! filen.lua" ]] 1190 eq( 1191 { 'ordinary', 'funky!', 'FANCY', 'mittel', 'FANCY after', 'ordinary after' }, 1192 exec_lua [[ return _G.test_loadorder ]] 1193 ) 1194 eq({ 'ordinary', 'funky!', 'mittel', 'ordinary after' }, exec_lua [[ return _G.nested_order ]]) 1195 end) 1196 1197 it('handles the correct order when prepending packpath', function() 1198 clear { 1199 args = { 1200 '--cmd', 1201 'set packpath^=test/functional/fixtures', 1202 '--cmd', 1203 [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]], 1204 }, 1205 env = { XDG_CONFIG_HOME = 'test/functional/fixtures/' }, 1206 } 1207 eq( 1208 { 'ordinary', 'FANCY', 'FANCY after', 'ordinary after' }, 1209 exec_lua [[ return _G.test_loadorder ]] 1210 ) 1211 end) 1212 1213 it('window widths are correct when modelines set &columns with tabpages', function() 1214 write_file('Xtab1.noft', 'vim: columns=81') 1215 write_file('Xtab2.noft', 'vim: columns=81') 1216 finally(function() 1217 os.remove('Xtab1.noft') 1218 os.remove('Xtab2.noft') 1219 end) 1220 clear({ args = { '-p', 'Xtab1.noft', 'Xtab2.noft' } }) 1221 eq(81, api.nvim_win_get_width(0)) 1222 command('tabnext') 1223 eq(81, api.nvim_win_get_width(0)) 1224 end) 1225 end) 1226 1227 describe('sysinit', function() 1228 local xdgdir = 'Xxdg' 1229 local vimdir = 'Xvim' 1230 local xhome = 'Xhome' 1231 local pathsep = n.get_pathsep() 1232 1233 before_each(function() 1234 rmdir(xdgdir) 1235 rmdir(vimdir) 1236 rmdir(xhome) 1237 1238 mkdir(xdgdir) 1239 mkdir(xdgdir .. pathsep .. 'nvim') 1240 write_file( 1241 table.concat({ xdgdir, 'nvim', 'sysinit.vim' }, pathsep), 1242 [[ 1243 let g:loaded = get(g:, "loaded", 0) + 1 1244 let g:xdg = 1 1245 ]] 1246 ) 1247 1248 mkdir(vimdir) 1249 write_file( 1250 table.concat({ vimdir, 'sysinit.vim' }, pathsep), 1251 [[ 1252 let g:loaded = get(g:, "loaded", 0) + 1 1253 let g:vim = 1 1254 ]] 1255 ) 1256 1257 mkdir(xhome) 1258 end) 1259 after_each(function() 1260 rmdir(xdgdir) 1261 rmdir(vimdir) 1262 rmdir(xhome) 1263 end) 1264 1265 it('prefers XDG_CONFIG_DIRS over VIM', function() 1266 clear { 1267 args = { '--cmd', 'set nomore undodir=. directory=. belloff=' }, 1268 args_rm = { '-u', '--cmd' }, 1269 env = { HOME = xhome, XDG_CONFIG_DIRS = xdgdir, VIM = vimdir }, 1270 } 1271 eq( 1272 'loaded 1 xdg 1 vim 0', 1273 eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))') 1274 ) 1275 end) 1276 1277 it('uses VIM if XDG_CONFIG_DIRS unset', function() 1278 clear { 1279 args = { '--cmd', 'set nomore undodir=. directory=. belloff=' }, 1280 args_rm = { '-u', '--cmd' }, 1281 env = { HOME = xhome, XDG_CONFIG_DIRS = '', VIM = vimdir }, 1282 } 1283 eq( 1284 'loaded 1 xdg 0 vim 1', 1285 eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))') 1286 ) 1287 end) 1288 1289 it('respects NVIM_APPNAME in XDG_CONFIG_DIRS', function() 1290 local appname = 'mysysinitapp' 1291 mkdir(xdgdir .. pathsep .. appname) 1292 write_file( 1293 table.concat({ xdgdir, appname, 'sysinit.vim' }, pathsep), 1294 [[let g:appname_sysinit = 1]] 1295 ) 1296 clear { 1297 args_rm = { '-u' }, 1298 env = { HOME = xhome, XDG_CONFIG_DIRS = xdgdir, NVIM_APPNAME = appname }, 1299 } 1300 eq(1, eval('g:appname_sysinit')) 1301 -- Should not load from nvim/ subdir (which has the default sysinit.vim from before_each) 1302 eq(0, eval('get(g:, "xdg", 0)')) 1303 end) 1304 end) 1305 1306 describe('user config init', function() 1307 local xhome = 'Xhome' 1308 local pathsep = n.get_pathsep() 1309 local xconfig = xhome .. pathsep .. 'Xconfig' 1310 local xdata = xhome .. pathsep .. 'Xdata' 1311 local init_lua_path = table.concat({ xconfig, 'nvim', 'init.lua' }, pathsep) 1312 local xenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata } 1313 1314 before_each(function() 1315 rmdir(xhome) 1316 1317 mkdir_p(xconfig .. pathsep .. 'nvim') 1318 mkdir_p(xdata) 1319 1320 write_file(init_lua_path, [[vim.g.lua_rc = 1]]) 1321 end) 1322 1323 after_each(function() 1324 rmdir(xhome) 1325 end) 1326 1327 it('loads init.lua from XDG config home by default', function() 1328 clear { args_rm = { '-u' }, env = xenv } 1329 1330 eq(1, eval('g:lua_rc')) 1331 eq(fn.fnamemodify(init_lua_path, ':p'), eval('$MYVIMRC')) 1332 end) 1333 1334 describe('loads existing', function() 1335 local exrc_path = '.exrc' 1336 local xstate = 'Xstate' 1337 local xstateenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata, XDG_STATE_HOME = xstate } 1338 1339 local function setup_exrc_file(filename) 1340 exrc_path = filename 1341 1342 if string.find(exrc_path, '%.lua$') then 1343 write_file( 1344 exrc_path, 1345 string.format( 1346 [[ 1347 vim.g.exrc_file = "%s" 1348 vim.g.exrc_path = debug.getinfo(1, 'S').source:gsub('^@', '') 1349 vim.g.exrc_count = (vim.g.exrc_count or 0) + 1 1350 ]], 1351 exrc_path 1352 ) 1353 ) 1354 else 1355 write_file( 1356 exrc_path, 1357 string.format( 1358 [[ 1359 let g:exrc_file = "%s" 1360 " let g:exrc_path = ?? 1361 let g:exrc_count = get(g:, 'exrc_count', 0) + 1 1362 ]], 1363 exrc_path 1364 ) 1365 ) 1366 end 1367 end 1368 1369 before_each(function() 1370 for _, file in ipairs({ '.exrc', '.nvimrc', '.nvim.lua' }) do 1371 os.remove('../' .. file) 1372 os.remove(file) 1373 end 1374 write_file( 1375 init_lua_path, 1376 [[ 1377 vim.o.exrc = true 1378 vim.g.exrc_file = '---' 1379 ]] 1380 ) 1381 mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) 1382 end) 1383 1384 after_each(function() 1385 for _, file in ipairs({ '.exrc', '.nvimrc', '.nvim.lua' }) do 1386 os.remove('../' .. file) 1387 os.remove(file) 1388 end 1389 rmdir(xstate) 1390 end) 1391 1392 for _, filename in ipairs({ '.exrc', '.nvimrc', '.nvim.lua', '../.nvim.lua', '../.nvimrc' }) do 1393 it(filename .. ' from cwd', function() 1394 setup_exrc_file(filename) 1395 1396 clear { args_rm = { '-u' }, env = xstateenv } 1397 -- The 'exrc' file is not trusted, and the prompt is skipped because there is no UI. 1398 eq('---', eval('g:exrc_file')) 1399 1400 local screen = Screen.new(50, 8) 1401 screen._default_attr_ids = nil 1402 fn.jobstart({ nvim_prog }, { 1403 term = true, 1404 env = { 1405 VIMRUNTIME = os.getenv('VIMRUNTIME'), 1406 }, 1407 }) 1408 screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny:') }) 1409 -- `i` to enter Terminal mode, `v` to view then `:trust` 1410 feed('iv') 1411 feed(':trust<CR>') 1412 feed(':q<CR>') 1413 screen:expect([[ 1414 ^ | 1415 ~ |*4 1416 [No Name] 0,0-1 All| 1417 | 1418 -- TERMINAL -- | 1419 ]]) 1420 feed(':echo g:exrc_file<CR>') 1421 screen:expect(string.format( 1422 [[ 1423 ^ | 1424 ~ |*4 1425 [No Name] 0,0-1 All| 1426 %s%s| 1427 -- TERMINAL -- | 1428 ]], 1429 '---', 1430 string.rep(' ', 50 - #'---') 1431 )) 1432 1433 clear { args_rm = { '-u' }, env = xstateenv } 1434 if string.find(exrc_path, '%.lua$') then 1435 eq( 1436 vim.fs.normalize(vim.fs.abspath(filename)), 1437 vim.fs.normalize(vim.fs.abspath(eval('g:exrc_path'))) 1438 ) 1439 end 1440 -- The 'exrc' file is now trusted. 1441 eq(filename, eval('g:exrc_file')) 1442 end) 1443 end 1444 1445 it('exrc from parent directories', function() 1446 setup_exrc_file('.nvim.lua') 1447 setup_exrc_file('../.exrc') 1448 clear { args_rm = { '-u' }, env = xstateenv } 1449 -- use a screen wide width to avoid wrapping the word `.exrc`, `.nvim.lua` below. 1450 local screen = Screen.new(500, 8) 1451 screen._default_attr_ids = nil 1452 fn.jobstart({ nvim_prog }, { 1453 term = true, 1454 env = { 1455 VIMRUNTIME = os.getenv('VIMRUNTIME'), 1456 }, 1457 }) 1458 -- current directory exrc is found first 1459 screen:expect({ any = '.nvim.lua' }) 1460 screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny:'), unchanged = true }) 1461 feed('iv') 1462 1463 -- after that the exrc in the parent directory 1464 screen:expect({ any = '.exrc', unchanged = true }) 1465 screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny:'), unchanged = true }) 1466 feed('v') 1467 1468 -- trust .exrc 1469 feed(':trust<CR>') 1470 screen:expect({ any = 'Allowed in trust database: ".*' .. pathsep .. '%.exrc"' }) 1471 feed(':q<CR>') 1472 -- trust .nvim.lua 1473 feed(':trust<CR>') 1474 screen:expect({ any = 'Allowed in trust database: ".*' .. pathsep .. '%.nvim%.lua"' }) 1475 feed(':q<CR>') 1476 -- no exrc file is executed 1477 feed(':echo g:exrc_count<CR>') 1478 screen:expect({ any = 'E121: Undefined variable: g:exrc_count' }) 1479 1480 -- restart nvim 1481 feed(':restart<CR>') 1482 screen:expect([[ 1483 ^{MATCH: +}| 1484 ~{MATCH: +}|*4 1485 [No Name]{MATCH: +}0,0-1{MATCH: +}All| 1486 {MATCH: +}| 1487 -- TERMINAL --{MATCH: +}| 1488 ]]) 1489 1490 -- a total of 2 exrc files are executed 1491 feed(':echo g:exrc_count<CR>') 1492 screen:expect({ any = '2' }) 1493 end) 1494 end) 1495 1496 describe('with explicitly provided config', function() 1497 local custom_lua_path = table.concat({ xhome, 'custom.lua' }, pathsep) 1498 before_each(function() 1499 write_file( 1500 custom_lua_path, 1501 [[ 1502 vim.g.custom_lua_rc = 1 1503 ]] 1504 ) 1505 end) 1506 1507 it('loads custom lua config and does not set $MYVIMRC', function() 1508 clear { args = { '-u', custom_lua_path }, env = xenv } 1509 eq(1, eval('g:custom_lua_rc')) 1510 eq('', eval('$MYVIMRC')) 1511 end) 1512 end) 1513 1514 describe('VIMRC also exists', function() 1515 before_each(function() 1516 write_file( 1517 table.concat({ xconfig, 'nvim', 'init.vim' }, pathsep), 1518 [[ 1519 let g:vim_rc = 1 1520 ]] 1521 ) 1522 end) 1523 1524 it('loads default lua config, but shows an error', function() 1525 clear { args_rm = { '-u' }, env = xenv } 1526 eq(1, eval('g:lua_rc')) 1527 t.matches( 1528 'E5422: Conflicting configs: "Xhome.Xconfig.nvim.init.lua" "Xhome.Xconfig.nvim.init.vim"', 1529 eval('v:errmsg') 1530 ) 1531 end) 1532 end) 1533 1534 describe('from XDG_CONFIG_DIRS', function() 1535 local xdgdir = 'Xxdgconfigdirs' 1536 1537 before_each(function() 1538 -- Remove init.lua from XDG_CONFIG_HOME so nvim falls back to XDG_CONFIG_DIRS 1539 os.remove(init_lua_path) 1540 rmdir(xdgdir) 1541 mkdir_p(xdgdir .. pathsep .. 'nvim') 1542 end) 1543 1544 after_each(function() 1545 rmdir(xdgdir) 1546 end) 1547 1548 it('loads init.lua from XDG_CONFIG_DIRS when no config in XDG_CONFIG_HOME', function() 1549 write_file( 1550 table.concat({ xdgdir, 'nvim', 'init.lua' }, pathsep), 1551 [[vim.g.xdg_config_dirs_lua = 1]] 1552 ) 1553 clear { 1554 args_rm = { '-u' }, 1555 env = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata, XDG_CONFIG_DIRS = xdgdir }, 1556 } 1557 eq(1, eval('g:xdg_config_dirs_lua')) 1558 eq( 1559 fn.fnamemodify(table.concat({ xdgdir, 'nvim', 'init.lua' }, pathsep), ':p'), 1560 eval('$MYVIMRC') 1561 ) 1562 end) 1563 1564 it('prefers init.lua over init.vim, shows E5422', function() 1565 write_file(table.concat({ xdgdir, 'nvim', 'init.lua' }, pathsep), [[vim.g.xdg_lua = 1]]) 1566 write_file(table.concat({ xdgdir, 'nvim', 'init.vim' }, pathsep), [[let g:xdg_vim = 1]]) 1567 clear { 1568 args_rm = { '-u' }, 1569 env = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata, XDG_CONFIG_DIRS = xdgdir }, 1570 } 1571 eq(1, eval('g:xdg_lua')) 1572 eq(0, eval('get(g:, "xdg_vim", 0)')) 1573 t.matches('E5422: Conflicting configs:', eval('v:errmsg')) 1574 end) 1575 1576 it('falls back to init.vim when no init.lua', function() 1577 write_file(table.concat({ xdgdir, 'nvim', 'init.vim' }, pathsep), [[let g:xdg_vim = 1]]) 1578 clear { 1579 args_rm = { '-u' }, 1580 env = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata, XDG_CONFIG_DIRS = xdgdir }, 1581 } 1582 eq(1, eval('g:xdg_vim')) 1583 end) 1584 1585 it('respects NVIM_APPNAME', function() 1586 local appname = 'mytestapp' 1587 mkdir_p(xdgdir .. pathsep .. appname) 1588 -- Also create nvim/ with a config that should NOT be loaded 1589 write_file(table.concat({ xdgdir, 'nvim', 'init.lua' }, pathsep), [[vim.g.wrong = 1]]) 1590 write_file(table.concat({ xdgdir, appname, 'init.lua' }, pathsep), [[vim.g.appname_lua = 1]]) 1591 clear { 1592 args_rm = { '-u' }, 1593 env = { 1594 XDG_CONFIG_HOME = xconfig, 1595 XDG_DATA_HOME = xdata, 1596 XDG_CONFIG_DIRS = xdgdir, 1597 NVIM_APPNAME = appname, 1598 }, 1599 } 1600 eq(1, eval('g:appname_lua')) 1601 eq(0, eval('get(g:, "wrong", 0)')) 1602 eq( 1603 fn.fnamemodify(table.concat({ xdgdir, appname, 'init.lua' }, pathsep), ':p'), 1604 eval('$MYVIMRC') 1605 ) 1606 end) 1607 end) 1608 end) 1609 1610 describe('runtime:', function() 1611 local xhome = 'Xhome' 1612 local pathsep = n.get_pathsep() 1613 local xconfig = xhome .. pathsep .. 'Xconfig' 1614 local xdata = xhome .. pathsep .. 'Xdata' 1615 local xenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata } 1616 1617 setup(function() 1618 rmdir(xhome) 1619 mkdir_p(xconfig .. pathsep .. 'nvim') 1620 mkdir_p(xdata) 1621 end) 1622 1623 teardown(function() 1624 rmdir(xhome) 1625 end) 1626 1627 it('loads plugin/*.lua from XDG config home', function() 1628 local plugin_folder_path = table.concat({ xconfig, 'nvim', 'plugin' }, pathsep) 1629 local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) 1630 mkdir_p(plugin_folder_path) 1631 finally(function() 1632 rmdir(plugin_folder_path) 1633 end) 1634 write_file(plugin_file_path, [[ vim.g.lua_plugin = 1 ]]) 1635 1636 clear { args_rm = { '-u' }, env = xenv } 1637 1638 eq(1, eval('g:lua_plugin')) 1639 end) 1640 1641 it('loads plugin/*.lua from start packages', function() 1642 local plugin_path = 1643 table.concat({ xconfig, 'nvim', 'pack', 'category', 'start', 'test_plugin' }, pathsep) 1644 local plugin_folder_path = table.concat({ plugin_path, 'plugin' }, pathsep) 1645 local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) 1646 local profiler_file = 'test_startuptime.log' 1647 mkdir_p(plugin_folder_path) 1648 finally(function() 1649 os.remove(profiler_file) 1650 rmdir(plugin_path) 1651 end) 1652 1653 write_file(plugin_file_path, [[vim.g.lua_plugin = 2]]) 1654 1655 clear { args_rm = { '-u' }, args = { '--startuptime', profiler_file }, env = xenv } 1656 1657 eq(2, eval('g:lua_plugin')) 1658 -- Check if plugin_file_path is listed in getscriptinfo() 1659 local scripts = tbl_map(function(s) 1660 return s.name 1661 end, fn.getscriptinfo()) 1662 ok(#tbl_filter(function(s) 1663 return endswith(s, plugin_file_path) 1664 end, scripts) > 0) 1665 1666 -- Check if plugin_file_path is listed in startup profile 1667 local profile_reader = io.open(profiler_file, 'r') 1668 local profile_log = profile_reader:read('*a') 1669 profile_reader:close() 1670 ok(profile_log:find(plugin_file_path) ~= nil) 1671 end) 1672 1673 it('loads plugin/*.lua from site packages', function() 1674 local nvimdata = is_os('win') and 'nvim-data' or 'nvim' 1675 local plugin_path = 1676 table.concat({ xdata, nvimdata, 'site', 'pack', 'xa', 'start', 'yb' }, pathsep) 1677 local plugin_folder_path = table.concat({ plugin_path, 'plugin' }, pathsep) 1678 local plugin_after_path = table.concat({ plugin_path, 'after', 'plugin' }, pathsep) 1679 local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) 1680 local plugin_after_file_path = table.concat({ plugin_after_path, 'helloo.lua' }, pathsep) 1681 mkdir_p(plugin_folder_path) 1682 mkdir_p(plugin_after_path) 1683 finally(function() 1684 rmdir(plugin_path) 1685 end) 1686 1687 write_file(plugin_file_path, [[table.insert(_G.lista, "unos")]]) 1688 write_file(plugin_after_file_path, [[table.insert(_G.lista, "dos")]]) 1689 1690 clear { args_rm = { '-u' }, args = { '--cmd', 'lua _G.lista = {}' }, env = xenv } 1691 1692 eq({ 'unos', 'dos' }, exec_lua 'return _G.lista') 1693 end) 1694 1695 it('no crash setting &rtp in plugins with :packloadall called before #18315', function() 1696 local plugin_folder_path = table.concat({ xconfig, 'nvim', 'plugin' }, pathsep) 1697 mkdir_p(plugin_folder_path) 1698 finally(function() 1699 rmdir(plugin_folder_path) 1700 end) 1701 1702 write_file( 1703 table.concat({ plugin_folder_path, 'plugin.vim' }, pathsep), 1704 [[ 1705 let &runtimepath = &runtimepath 1706 let g:vim_plugin = 1 1707 ]] 1708 ) 1709 write_file( 1710 table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep), 1711 [[ 1712 vim.o.runtimepath = vim.o.runtimepath 1713 vim.g.lua_plugin = 1 1714 ]] 1715 ) 1716 1717 clear { args_rm = { '-u' }, args = { '--cmd', 'packloadall' }, env = xenv } 1718 1719 eq(1, eval('g:vim_plugin')) 1720 eq(1, eval('g:lua_plugin')) 1721 end) 1722 1723 it("loads ftdetect/*.{vim,lua} respecting 'rtp' order", function() 1724 local rtp_folder = table.concat({ xconfig, 'nvim' }, pathsep) 1725 local after_rtp_folder = table.concat({ rtp_folder, 'after' }, pathsep) 1726 local ftdetect_folder = table.concat({ rtp_folder, 'ftdetect' }, pathsep) 1727 local after_ftdetect_folder = table.concat({ after_rtp_folder, 'ftdetect' }, pathsep) 1728 mkdir_p(ftdetect_folder) 1729 mkdir_p(after_ftdetect_folder) 1730 finally(function() 1731 rmdir(ftdetect_folder) 1732 rmdir(after_ftdetect_folder) 1733 end) 1734 write_file(table.concat({ rtp_folder, 'scripts.vim' }, pathsep), [[let g:aseq ..= 'S']]) 1735 write_file(table.concat({ after_rtp_folder, 'scripts.vim' }, pathsep), [[let g:aseq ..= 's']]) 1736 -- A .lua file is loaded after a .vim file if they only differ in extension. 1737 -- All files in after/ftdetect/ are loaded after all files in ftdetect/. 1738 write_file( 1739 table.concat({ ftdetect_folder, 'new-ft.vim' }, pathsep), 1740 [[ 1741 let g:seq ..= 'A' 1742 autocmd BufRead,BufNewFile FTDETECT let g:aseq ..= 'A' 1743 ]] 1744 ) 1745 write_file( 1746 table.concat({ ftdetect_folder, 'new-ft.lua' }, pathsep), 1747 [[ 1748 vim.g.seq = vim.g.seq .. 'B' 1749 vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, { 1750 pattern = 'FTDETECT', 1751 command = "let g:aseq ..= 'B'", 1752 }) 1753 ]] 1754 ) 1755 write_file( 1756 table.concat({ after_ftdetect_folder, 'new-ft.vim' }, pathsep), 1757 [[ 1758 let g:seq ..= 'a' 1759 autocmd BufRead,BufNewFile FTDETECT let g:aseq ..= 'a' 1760 ]] 1761 ) 1762 write_file( 1763 table.concat({ after_ftdetect_folder, 'new-ft.lua' }, pathsep), 1764 [[ 1765 vim.g.seq = vim.g.seq .. 'b' 1766 vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile' }, { 1767 pattern = 'FTDETECT', 1768 command = "let g:aseq ..= 'b'", 1769 }) 1770 ]] 1771 ) 1772 clear { args_rm = { '-u' }, args = { '--cmd', 'let g:seq = ""' }, env = xenv } 1773 eq('ABab', eval('g:seq')) 1774 command('let g:aseq = ""') 1775 command('edit FTDETECT') 1776 eq('SsABab', eval('g:aseq')) 1777 end) 1778 end) 1779 1780 describe('user session', function() 1781 local xhome = 'Xhome' 1782 local pathsep = n.get_pathsep() 1783 local session_file = table.concat({ xhome, 'session.lua' }, pathsep) 1784 1785 before_each(function() 1786 rmdir(xhome) 1787 1788 mkdir(xhome) 1789 write_file( 1790 session_file, 1791 [[ 1792 vim.g.lua_session = 1 1793 ]] 1794 ) 1795 end) 1796 1797 after_each(function() 1798 rmdir(xhome) 1799 end) 1800 1801 it('loads session from the provided lua file', function() 1802 clear { args = { '-S', session_file }, env = { HOME = xhome } } 1803 eq(1, eval('g:lua_session')) 1804 end) 1805 end) 1806 1807 describe('inccommand on ex mode', function() 1808 it('should not preview', function() 1809 clear() 1810 local screen 1811 screen = Screen.new(60, 10) 1812 local id = fn.jobstart({ 1813 nvim_prog, 1814 '-u', 1815 'NONE', 1816 '-i', 1817 'NONE', 1818 '-c', 1819 'set termguicolors background=dark', 1820 '-E', 1821 'README.md', 1822 }, { 1823 term = true, 1824 env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, 1825 }) 1826 fn.chansend(id, '%s/N') 1827 screen:add_extra_attr_ids({ 1828 [101] = { 1829 background = Screen.colors.NvimDarkGrey4, 1830 foreground = Screen.colors.NvimLightGray2, 1831 }, 1832 [102] = { 1833 background = Screen.colors.NvimDarkGray2, 1834 foreground = Screen.colors.NvimLightGray2, 1835 }, 1836 }) 1837 screen:expect([[ 1838 {102:^ }| 1839 {102: }|*5 1840 {101: }| 1841 {102:Entering Ex mode. Type "visual" to go to Normal mode. }| 1842 {102::%s/N }| 1843 | 1844 ]]) 1845 end) 1846 end)