autocmd_spec.lua (26086B)
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_visible = n.assert_visible 6 local assert_alive = n.assert_alive 7 local dedent = t.dedent 8 local eq = t.eq 9 local neq = t.neq 10 local eval = n.eval 11 local exec = n.exec 12 local feed = n.feed 13 local clear = n.clear 14 local matches = t.matches 15 local api = n.api 16 local pcall_err = t.pcall_err 17 local fn = n.fn 18 local expect = n.expect 19 local command = n.command 20 local exec_lua = n.exec_lua 21 local retry = t.retry 22 local source = n.source 23 24 describe('autocmd', function() 25 before_each(clear) 26 27 it(':tabnew, :split, :close events order, <afile>', function() 28 local expected = { 29 { 'WinLeave', '' }, 30 { 'TabLeave', '' }, 31 { 'WinEnter', '' }, 32 { 'TabNew', 'testfile1' }, -- :tabnew 33 { 'TabEnter', '' }, 34 { 'BufLeave', '' }, 35 { 'BufEnter', 'testfile1' }, -- :split 36 { 'WinLeave', 'testfile1' }, 37 { 'WinEnter', 'testfile1' }, 38 { 'WinLeave', 'testfile1' }, 39 { 'WinClosed', '1002' }, -- :close, WinClosed <afile> = window-id 40 { 'WinEnter', 'testfile1' }, 41 { 'WinLeave', 'testfile1' }, -- :bdelete 42 { 'WinEnter', 'testfile1' }, 43 { 'BufLeave', 'testfile1' }, 44 { 'BufEnter', 'testfile2' }, 45 { 'WinClosed', '1000' }, 46 } 47 command('let g:evs = []') 48 command('autocmd BufEnter * :call add(g:evs, ["BufEnter", expand("<afile>")])') 49 command('autocmd BufLeave * :call add(g:evs, ["BufLeave", expand("<afile>")])') 50 command('autocmd TabEnter * :call add(g:evs, ["TabEnter", expand("<afile>")])') 51 command('autocmd TabLeave * :call add(g:evs, ["TabLeave", expand("<afile>")])') 52 command('autocmd TabNew * :call add(g:evs, ["TabNew", expand("<afile>")])') 53 command('autocmd WinEnter * :call add(g:evs, ["WinEnter", expand("<afile>")])') 54 command('autocmd WinLeave * :call add(g:evs, ["WinLeave", expand("<afile>")])') 55 command('autocmd WinClosed * :call add(g:evs, ["WinClosed", expand("<afile>")])') 56 command('tabnew testfile1') 57 command('split') 58 command('close') 59 command('new testfile2') 60 command('bdelete 1') 61 eq(expected, eval('g:evs')) 62 end) 63 64 it('first edit causes BufUnload on NoName', function() 65 local expected = { 66 { 'BufUnload', '' }, 67 { 'BufDelete', '' }, 68 { 'BufWipeout', '' }, 69 { 'BufEnter', 'testfile1' }, 70 } 71 command('let g:evs = []') 72 command('autocmd BufEnter * :call add(g:evs, ["BufEnter", expand("<afile>")])') 73 command('autocmd BufDelete * :call add(g:evs, ["BufDelete", expand("<afile>")])') 74 command('autocmd BufLeave * :call add(g:evs, ["BufLeave", expand("<afile>")])') 75 command('autocmd BufUnload * :call add(g:evs, ["BufUnload", expand("<afile>")])') 76 command('autocmd BufWipeout * :call add(g:evs, ["BufWipeout", expand("<afile>")])') 77 command('edit testfile1') 78 eq(expected, eval('g:evs')) 79 end) 80 81 it('WinClosed is non-recursive', function() 82 command('let g:triggered = 0') 83 command('autocmd WinClosed * :let g:triggered+=1 | :bdelete 2') 84 command('new testfile2') 85 command('new testfile3') 86 87 -- All 3 buffers are visible. 88 assert_visible(1, true) 89 assert_visible(2, true) 90 assert_visible(3, true) 91 92 -- Trigger WinClosed, which also deletes buffer/window 2. 93 command('bdelete 1') 94 95 -- Buffers 1 and 2 were closed but WinClosed was triggered only once. 96 eq(1, eval('g:triggered')) 97 assert_visible(1, false) 98 assert_visible(2, false) 99 assert_visible(3, true) 100 end) 101 102 it('WinClosed from a different tabpage', function() 103 command('let g:evs = []') 104 command('edit tesfile1') 105 command('autocmd WinClosed <buffer> :call add(g:evs, ["WinClosed", expand("<abuf>")])') 106 local buf1 = eval("bufnr('%')") 107 command('new') 108 local buf2 = eval("bufnr('%')") 109 command( 110 'autocmd WinClosed <buffer> :call add(g:evs, ["WinClosed", expand("<abuf>")])' 111 -- Attempt recursion. 112 .. ' | bdelete ' 113 .. buf2 114 ) 115 command('tabedit testfile2') 116 command('tabedit testfile3') 117 command('bdelete ' .. buf2) 118 -- Non-recursive: only triggered once. 119 eq({ 120 { 'WinClosed', '2' }, 121 }, eval('g:evs')) 122 command('bdelete ' .. buf1) 123 eq({ 124 { 'WinClosed', '2' }, 125 { 'WinClosed', '1' }, 126 }, eval('g:evs')) 127 end) 128 129 it('WinClosed from root directory', function() 130 command('cd /') 131 command('let g:evs = []') 132 command('autocmd WinClosed * :call add(g:evs, ["WinClosed", expand("<afile>")])') 133 command('new') 134 command('close') 135 eq({ 136 { 'WinClosed', '1001' }, 137 }, eval('g:evs')) 138 end) 139 140 it('v:vim_did_enter is 1 after VimEnter', function() 141 eq(1, eval('v:vim_did_enter')) 142 end) 143 144 describe('BufLeave autocommand', function() 145 it('can wipe out the buffer created by :edit which triggered autocmd', function() 146 api.nvim_set_option_value('hidden', true, {}) 147 api.nvim_buf_set_lines(0, 0, 1, false, { 148 'start of test file xx', 149 'end of test file xx', 150 }) 151 152 command('autocmd BufLeave * bwipeout yy') 153 eq( 154 'Vim(edit):E143: Autocommands unexpectedly deleted new buffer yy', 155 pcall_err(command, 'edit yy') 156 ) 157 158 expect([[ 159 start of test file xx 160 end of test file xx]]) 161 end) 162 end) 163 164 it('++once', function() -- :help autocmd-once 165 -- 166 -- ":autocmd … ++once" executes its handler once, then removes the handler. 167 -- 168 local expected = { 169 'Many1', 170 'Once1', 171 'Once2', 172 'Many2', 173 'Once3', 174 'Many1', 175 'Many2', 176 'Many1', 177 'Many2', 178 } 179 command('let g:foo = []') 180 command('autocmd TabNew * :call add(g:foo, "Many1")') 181 command('autocmd TabNew * ++once :call add(g:foo, "Once1")') 182 command('autocmd TabNew * ++once :call add(g:foo, "Once2")') 183 command('autocmd TabNew * :call add(g:foo, "Many2")') 184 command('autocmd TabNew * ++once :call add(g:foo, "Once3")') 185 eq( 186 dedent([[ 187 188 --- Autocommands --- 189 TabNew 190 * :call add(g:foo, "Many1") 191 :call add(g:foo, "Once1") 192 :call add(g:foo, "Once2") 193 :call add(g:foo, "Many2") 194 :call add(g:foo, "Once3")]]), 195 fn.execute('autocmd Tabnew') 196 ) 197 command('tabnew') 198 command('tabnew') 199 command('tabnew') 200 eq(expected, eval('g:foo')) 201 eq( 202 dedent([[ 203 204 --- Autocommands --- 205 TabNew 206 * :call add(g:foo, "Many1") 207 :call add(g:foo, "Many2")]]), 208 fn.execute('autocmd Tabnew') 209 ) 210 211 -- 212 -- ":autocmd … ++once" handlers can be deleted. 213 -- 214 expected = {} 215 command('let g:foo = []') 216 command('autocmd TabNew * ++once :call add(g:foo, "Once1")') 217 command('autocmd! TabNew') 218 command('tabnew') 219 eq(expected, eval('g:foo')) 220 221 -- 222 -- ":autocmd … <buffer> ++once ++nested" 223 -- 224 expected = { 225 'OptionSet-Once', 226 'CursorMoved-Once', 227 } 228 command('let g:foo = []') 229 command('autocmd OptionSet binary ++nested ++once :call add(g:foo, "OptionSet-Once")') 230 command( 231 'autocmd CursorMoved <buffer> ++once ++nested setlocal binary|:call add(g:foo, "CursorMoved-Once")' 232 ) 233 command("put ='foo bar baz'") 234 feed('0llhlh') 235 eq(expected, eval('g:foo')) 236 237 -- 238 -- :autocmd should not show empty section after ++once handlers expire. 239 -- 240 expected = { 241 'Once1', 242 'Once2', 243 } 244 command('let g:foo = []') 245 command('autocmd! TabNew') -- Clear all TabNew handlers. 246 command('autocmd TabNew * ++once :call add(g:foo, "Once1")') 247 command('autocmd TabNew * ++once :call add(g:foo, "Once2")') 248 command('tabnew') 249 eq(expected, eval('g:foo')) 250 eq( 251 dedent([[ 252 253 --- Autocommands ---]]), 254 fn.execute('autocmd Tabnew') 255 ) 256 257 -- 258 -- :autocmd does not recursively call ++once Lua handlers. 259 -- 260 exec_lua [[vim.g.count = 0]] 261 eq(0, eval('g:count')) 262 exec_lua [[ 263 vim.api.nvim_create_autocmd('User', { 264 once = true, 265 pattern = nil, 266 callback = function() 267 vim.g.count = vim.g.count + 1 268 vim.api.nvim_exec_autocmds('User', { pattern = nil }) 269 end, 270 }) 271 vim.api.nvim_exec_autocmds('User', { pattern = nil }) 272 ]] 273 eq(1, eval('g:count')) 274 end) 275 276 it('internal `aucmd_win` window', function() 277 -- Nvim uses a special internal window `aucmd_win` to execute certain 278 -- actions for an invisible buffer (:help E813). 279 -- Check redrawing and API accesses to this window. 280 281 local screen = Screen.new(50, 10) 282 283 source([[ 284 function! Doit() 285 let g:winid = nvim_get_current_win() 286 redraw! 287 echo getchar() 288 " API functions work when aucmd_win is in scope 289 let g:had_value = has_key(w:, "testvar") 290 call nvim_win_set_var(g:winid, "testvar", 7) 291 let g:test = w:testvar 292 endfunction 293 set hidden 294 " add dummy text to not discard the buffer 295 call setline(1,"bb") 296 autocmd User <buffer> call Doit() 297 ]]) 298 screen:expect([[ 299 ^bb | 300 {1:~ }|*8 301 | 302 ]]) 303 304 feed(':enew | doautoall User<cr>') 305 screen:expect([[ 306 {4:bb }| 307 {11:~ }|*4 308 {1:~ }|*4 309 ^:enew | doautoall User | 310 ]]) 311 312 feed('<cr>') 313 screen:expect([[ 314 ^ | 315 {1:~ }|*8 316 13 | 317 ]]) 318 eq(7, eval('g:test')) 319 320 -- API calls are blocked when aucmd_win is not in scope 321 eq( 322 'Vim(call):E5555: API call: Invalid window id: 1001', 323 pcall_err(command, 'call nvim_set_current_win(g:winid)') 324 ) 325 326 -- second time aucmd_win is needed, a different code path is invoked 327 -- to reuse the same window, so check again 328 command('let g:test = v:null') 329 command('let g:had_value = v:null') 330 feed(':doautoall User<cr>') 331 screen:expect([[ 332 {4:bb }| 333 {11:~ }|*4 334 {1:~ }|*4 335 ^:doautoall User | 336 ]]) 337 338 feed('<cr>') 339 screen:expect([[ 340 ^ | 341 {1:~ }|*8 342 13 | 343 ]]) 344 -- win vars in aucmd_win should have been reset 345 eq(0, eval('g:had_value')) 346 eq(7, eval('g:test')) 347 348 eq( 349 'Vim(call):E5555: API call: Invalid window id: 1001', 350 pcall_err(command, 'call nvim_set_current_win(g:winid)') 351 ) 352 end) 353 354 it('`aucmd_win` cannot be changed into a normal window #13699', function() 355 local screen = Screen.new(50, 10) 356 357 -- Create specific layout and ensure it's left unchanged. 358 -- Use vim._with on a hidden buffer so aucmd_win is used. 359 exec_lua [[ 360 vim.cmd "wincmd s | wincmd _" 361 _G.buf = vim.api.nvim_create_buf(true, true) 362 vim._with({buf = _G.buf}, function() vim.cmd "wincmd J" end) 363 ]] 364 screen:expect [[ 365 ^ | 366 {1:~ }|*5 367 {3:[No Name] }| 368 | 369 {2:[No Name] }| 370 | 371 ]] 372 -- This used to crash after making aucmd_win a normal window via the above. 373 exec_lua [[ 374 vim.cmd "tabnew | tabclose # | wincmd s | wincmd _" 375 vim._with({buf = _G.buf}, function() vim.cmd "wincmd K" end) 376 ]] 377 assert_alive() 378 screen:expect_unchanged() 379 380 -- Also check with win_splitmove(). 381 exec_lua [[ 382 vim._with({buf = _G.buf}, function() 383 vim.fn.win_splitmove(vim.fn.win_getid(), vim.fn.win_getid(1)) 384 end) 385 ]] 386 screen:expect_unchanged() 387 388 -- Also check with nvim_win_set_config(). 389 matches( 390 '^Failed to move window %d+ into split$', 391 pcall_err( 392 exec_lua, 393 [[ 394 vim._with({buf = _G.buf}, function() 395 vim.api.nvim_win_set_config(0, { 396 vertical = true, 397 win = vim.fn.win_getid(1) 398 }) 399 end) 400 ]] 401 ) 402 ) 403 screen:expect_unchanged() 404 405 -- Ensure splitting still works from inside the aucmd_win. 406 exec_lua [[vim._with({buf = _G.buf}, function() vim.cmd "split" end)]] 407 screen:expect [[ 408 ^ | 409 {1:~ }| 410 {3:[No Name] }| 411 | 412 {1:~ }| 413 {2:[Scratch] }| 414 | 415 {1:~ }| 416 {2:[No Name] }| 417 | 418 ]] 419 420 -- After all of our messing around, aucmd_win should still be floating. 421 -- Use :only to ensure _G.buf is hidden again (so the aucmd_win is used). 422 eq( 423 'editor', 424 exec_lua [[ 425 vim.cmd "only" 426 vim._with({buf = _G.buf}, function() 427 _G.config = vim.api.nvim_win_get_config(0) 428 end) 429 return _G.config.relative 430 ]] 431 ) 432 end) 433 434 it('cannot close `aucmd_win` in non-current tabpage', function() 435 exec([[ 436 file Xa 437 tabnew Xb 438 call setline(1, 'foo') 439 tabfirst 440 autocmd BufWriteCmd Xb tablast | bwipe! Xa 441 ]]) 442 eq( 443 'BufWriteCmd Autocommands for "Xb": Vim(bwipeout):E813: Cannot close autocmd window', 444 pcall_err(command, 'wall') 445 ) 446 -- Sanity check: :bwipe failing to close all windows into Xa should keep it loaded. 447 -- (So there's no risk of it being left unloaded inside a window) 448 eq(1, eval('bufloaded("Xa")')) 449 end) 450 451 describe('closing last non-floating window in tab from `aucmd_win`', function() 452 before_each(function() 453 command('edit Xa.txt') 454 command('tabnew Xb.txt') 455 command('autocmd BufAdd Xa.txt 1close') 456 end) 457 458 it('gives E814 when there are no other floating windows', function() 459 eq( 460 'BufAdd Autocommands for "Xa.txt": Vim(close):E814: Cannot close window, only autocmd window would remain', 461 pcall_err(command, 'doautoall BufAdd') 462 ) 463 end) 464 465 it('gives E814 when there are other floating windows', function() 466 api.nvim_open_win( 467 0, 468 true, 469 { width = 10, height = 10, relative = 'editor', row = 10, col = 10 } 470 ) 471 eq( 472 'BufAdd Autocommands for "Xa.txt": Vim(close):E814: Cannot close window, only autocmd window would remain', 473 pcall_err(command, 'doautoall BufAdd') 474 ) 475 end) 476 end) 477 478 it('closing `aucmd_win` using API gives E813', function() 479 exec_lua([[ 480 vim.cmd('tabnew') 481 _G.buf = vim.api.nvim_create_buf(true, true) 482 ]]) 483 matches( 484 'Vim:E813: Cannot close autocmd window$', 485 pcall_err( 486 exec_lua, 487 [[ 488 vim._with({buf = _G.buf}, function() 489 local win = vim.api.nvim_get_current_win() 490 vim.api.nvim_win_close(win, true) 491 end) 492 ]] 493 ) 494 ) 495 matches( 496 'Vim:E813: Cannot close autocmd window$', 497 pcall_err( 498 exec_lua, 499 [[ 500 vim._with({buf = _G.buf}, function() 501 local win = vim.api.nvim_get_current_win() 502 vim.cmd('tabnext') 503 vim.api.nvim_win_close(win, true) 504 end) 505 ]] 506 ) 507 ) 508 matches( 509 'Vim:E813: Cannot close autocmd window$', 510 pcall_err( 511 exec_lua, 512 [[ 513 vim._with({buf = _G.buf}, function() 514 local win = vim.api.nvim_get_current_win() 515 vim.api.nvim_win_hide(win) 516 end) 517 ]] 518 ) 519 ) 520 matches( 521 'Vim:E813: Cannot close autocmd window$', 522 pcall_err( 523 exec_lua, 524 [[ 525 vim._with({buf = _G.buf}, function() 526 local win = vim.api.nvim_get_current_win() 527 vim.cmd('tabnext') 528 vim.api.nvim_win_hide(win) 529 end) 530 ]] 531 ) 532 ) 533 end) 534 535 it(':doautocmd does not warn "No matching autocommands" #10689', function() 536 local screen = Screen.new(32, 3) 537 538 feed(':doautocmd User Foo<cr>') 539 screen:expect { 540 grid = [[ 541 ^ | 542 {1:~ }| 543 :doautocmd User Foo | 544 ]], 545 } 546 feed(':autocmd! SessionLoadPost<cr>') 547 feed(':doautocmd SessionLoadPost<cr>') 548 screen:expect { 549 grid = [[ 550 ^ | 551 {1:~ }| 552 :doautocmd SessionLoadPost | 553 ]], 554 } 555 end) 556 557 describe('v:event is readonly #18063', function() 558 it('during ChanOpen event', function() 559 command('autocmd ChanOpen * let v:event.info.id = 0') 560 fn.jobstart({ 'cat' }) 561 retry(nil, nil, function() 562 eq('E46: Cannot change read-only variable "v:event.info"', api.nvim_get_vvar('errmsg')) 563 end) 564 end) 565 566 it('during ChanOpen event', function() 567 command('autocmd ChanInfo * let v:event.info.id = 0') 568 api.nvim_set_client_info('foo', {}, 'remote', {}, {}) 569 retry(nil, nil, function() 570 eq('E46: Cannot change read-only variable "v:event.info"', api.nvim_get_vvar('errmsg')) 571 end) 572 end) 573 574 it('during RecordingLeave event', function() 575 command([[autocmd RecordingLeave * let v:event.regname = '']]) 576 eq( 577 'RecordingLeave Autocommands for "*": Vim(let):E46: Cannot change read-only variable "v:event.regname"', 578 pcall_err(command, 'normal! qqq') 579 ) 580 end) 581 582 it('during TermClose event', function() 583 command('autocmd TermClose * let v:event.status = 0') 584 command('terminal') 585 eq( 586 'TermClose Autocommands for "*": Vim(let):E46: Cannot change read-only variable "v:event.status"', 587 pcall_err(command, 'bdelete!') 588 ) 589 end) 590 end) 591 592 describe('old_tests', function() 593 it('vimscript: WinNew ++once', function() 594 source [[ 595 " Without ++once WinNew triggers twice 596 let g:did_split = 0 597 augroup Testing 598 au! 599 au WinNew * let g:did_split += 1 600 augroup END 601 split 602 split 603 call assert_equal(2, g:did_split) 604 call assert_true(exists('#WinNew')) 605 close 606 close 607 608 " With ++once WinNew triggers once 609 let g:did_split = 0 610 augroup Testing 611 au! 612 au WinNew * ++once let g:did_split += 1 613 augroup END 614 split 615 split 616 call assert_equal(1, g:did_split) 617 call assert_false(exists('#WinNew')) 618 close 619 close 620 621 call assert_fails('au WinNew * ++once ++once echo bad', 'E983:') 622 ]] 623 624 api.nvim_set_var('did_split', 0) 625 626 source [[ 627 augroup Testing 628 au! 629 au WinNew * let g:did_split += 1 630 augroup END 631 632 split 633 split 634 ]] 635 636 eq(2, api.nvim_get_var('did_split')) 637 eq(1, fn.exists('#WinNew')) 638 639 -- Now with once 640 api.nvim_set_var('did_split', 0) 641 642 source [[ 643 augroup Testing 644 au! 645 au WinNew * ++once let g:did_split += 1 646 augroup END 647 648 split 649 split 650 ]] 651 652 eq(1, api.nvim_get_var('did_split')) 653 eq(0, fn.exists('#WinNew')) 654 655 -- call assert_fails('au WinNew * ++once ++once echo bad', 'E983:') 656 local ok, msg = pcall( 657 source, 658 [[ 659 au WinNew * ++once ++once echo bad 660 ]] 661 ) 662 663 eq(false, ok) 664 eq(true, not not string.find(msg, 'E983:')) 665 end) 666 667 it('should have autocmds in filetypedetect group', function() 668 source [[filetype on]] 669 neq({}, api.nvim_get_autocmds { group = 'filetypedetect' }) 670 end) 671 672 it('should allow comma-separated patterns', function() 673 source [[ 674 augroup TestingPatterns 675 au! 676 autocmd BufReadCmd *.shada,*.shada.tmp.[a-z] echo 'hello' 677 autocmd BufReadCmd *.shada,*.shada.tmp.[a-z] echo 'hello' 678 augroup END 679 ]] 680 681 eq(4, #api.nvim_get_autocmds { event = 'BufReadCmd', group = 'TestingPatterns' }) 682 end) 683 end) 684 685 it('no use-after-free when adding autocommands from a callback', function() 686 exec_lua [[ 687 vim.cmd "autocmd! TabNew" 688 vim.g.count = 0 689 vim.api.nvim_create_autocmd('TabNew', { 690 callback = function() 691 vim.g.count = vim.g.count + 1 692 for _ = 1, 100 do 693 vim.cmd "autocmd TabNew * let g:count += 1" 694 end 695 return true 696 end, 697 }) 698 vim.cmd "tabnew" 699 ]] 700 eq(1, eval('g:count')) -- Added autocommands should not be executed 701 end) 702 703 it('no crash when clearing a group inside a callback #23355', function() 704 exec_lua [[ 705 vim.cmd "autocmd! TabNew" 706 local group = vim.api.nvim_create_augroup('Test', {}) 707 local id 708 id = vim.api.nvim_create_autocmd('TabNew', { 709 group = group, 710 callback = function() 711 vim.api.nvim_del_autocmd(id) 712 vim.api.nvim_create_augroup('Test', { clear = true }) 713 end, 714 }) 715 vim.cmd "tabnew" 716 ]] 717 end) 718 719 it('no use-after-free when wiping buffer in Syntax autocommand', function() 720 exec([[ 721 new 722 autocmd Syntax * ++once bwipe! 723 setlocal syntax=vim 724 ]]) 725 assert_alive() 726 end) 727 728 it('no use-after-free from win_enter autocommands in win_move_after', function() 729 exec [[ 730 split foo 731 split bar 732 lcd .. 733 wincmd b 734 ]] 735 eq(fn.winnr('$'), fn.winnr()) 736 -- Using DirChanged as Enter/Leave autocmds are blocked by :ball here. 737 exec [[ 738 autocmd DirChanged * ++once split flarb | only! 739 ball 740 ]] 741 eq('flarb', fn.bufname()) 742 end) 743 744 it('does not ignore comma-separated patterns after a buffer-local pattern', function() 745 exec [[ 746 edit baz " reuses buffer 1 747 edit bazinga 748 edit bar 749 edit boop 750 edit foo 751 edit floob 752 753 let g:events1 = [] 754 autocmd BufEnter <buffer>,<buffer=1>,boop,bar let g:events1 += [expand('<afile>')] 755 let g:events2 = [] 756 augroup flobby 757 autocmd BufEnter <buffer=2>,foo let g:events2 += [expand('<afile>')] 758 autocmd BufEnter foo,<buffer=3> let g:events2 += ['flobby ' .. expand('<afile>')] 759 augroup END 760 ]] 761 eq( 762 dedent([=[ 763 764 --- Autocommands --- 765 BufEnter 766 <buffer=6> 767 let g:events1 += [expand('<afile>')] 768 <buffer=1> 769 let g:events1 += [expand('<afile>')] 770 boop let g:events1 += [expand('<afile>')] 771 bar let g:events1 += [expand('<afile>')] 772 flobby BufEnter 773 <buffer=2> 774 let g:events2 += [expand('<afile>')] 775 foo let g:events2 += [expand('<afile>')] 776 let g:events2 += ['flobby ' .. expand('<afile>')] 777 <buffer=3> 778 let g:events2 += ['flobby ' .. expand('<afile>')]]=]), 779 fn.execute('autocmd BufEnter') 780 ) 781 command('bufdo "') 782 eq({ 'baz', 'bar', 'boop', 'floob' }, eval('g:events1')) 783 eq({ 'bazinga', 'flobby bar', 'foo', 'flobby foo' }, eval('g:events2')) 784 785 -- Also make sure it doesn't repeat the group/event name or pattern for each printed event. 786 -- Do however repeat the pattern if the user specified it multiple times to be printed. 787 -- These conditions aim to make the output consistent with Vim. 788 eq( 789 dedent([=[ 790 791 --- Autocommands --- 792 BufEnter 793 <buffer=1> 794 let g:events1 += [expand('<afile>')] 795 bar let g:events1 += [expand('<afile>')] 796 bar let g:events1 += [expand('<afile>')] 797 flobby BufEnter 798 foo let g:events2 += [expand('<afile>')] 799 let g:events2 += ['flobby ' .. expand('<afile>')] 800 foo let g:events2 += [expand('<afile>')] 801 let g:events2 += ['flobby ' .. expand('<afile>')] 802 BufEnter 803 <buffer=6> 804 let g:events1 += [expand('<afile>')] 805 <buffer=6> 806 let g:events1 += [expand('<afile>')] 807 <buffer=6> 808 let g:events1 += [expand('<afile>')]]=]), 809 fn.execute('autocmd BufEnter <buffer=1>,bar,bar,foo,foo,<buffer>,<buffer=6>,<buffer>') 810 ) 811 end) 812 813 it('parses empty comma-delimited patterns correctly', function() 814 exec [[ 815 autocmd User , " 816 autocmd User ,, " 817 autocmd User ,,according,to,,all,known,,,laws,, " 818 ]] 819 api.nvim_create_autocmd('User', { pattern = ',,of,,,aviation,,,,there,,', command = '' }) 820 api.nvim_create_autocmd('User', { 821 pattern = { ',,,,is,,no', ',,way,,,', 'a,,bee{,should be, able to,},fly' }, 822 command = '', 823 }) 824 eq( 825 { 826 'according', 827 'to', 828 'all', 829 'known', 830 'laws', 831 'of', 832 'aviation', 833 'there', 834 'is', 835 'no', 836 'way', 837 'a', 838 'bee{,should be, able to,}', 839 'fly', 840 }, 841 exec_lua(function() 842 return vim.tbl_map(function(v) 843 return v.pattern 844 end, vim.api.nvim_get_autocmds({ event = 'User' })) 845 end) 846 ) 847 eq( 848 dedent([[ 849 850 --- Autocommands --- 851 User 852 there 853 is 854 a 855 fly]]), 856 fn.execute('autocmd User ,,,there,is,,a,fly,,') 857 ) 858 end) 859 end)