neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 933b99a2b1c95c877b67eb4e0d35fd8dd6b0d532
parent 8bdfd286e5846325bf51e12f116ec56d38eaa212
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Tue, 20 Jan 2026 07:01:21 +0800

Merge pull request #37464 from zeertzjq/vim-9.1.1202

vim-patch:9.1.{1202,1211,1325,2093,2097}: TabClosedPre
Diffstat:
Mruntime/doc/autocmd.txt | 10+++++++---
Mruntime/doc/news.txt | 1+
Mruntime/doc/options.txt | 1+
Mruntime/lua/vim/_meta/api_keysets.lua | 1+
Mruntime/lua/vim/_meta/options.lua | 1+
Msrc/nvim/auevents.lua | 3++-
Msrc/nvim/buffer_defs.h | 5+++--
Msrc/nvim/ex_docmd.c | 32+++++++++++++++++++++++++++++---
Msrc/nvim/window.c | 52+++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtest/old/testdir/test_arglist.vim | 2+-
Mtest/old/testdir/test_autocmd.vim | 338++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtest/old/testdir/test_blob.vim | 4++--
Mtest/old/testdir/test_edit.vim | 2+-
Mtest/old/testdir/test_expr.vim | 6+++---
Mtest/old/testdir/test_listdict.vim | 2+-
Mtest/old/testdir/test_startup.vim | 4++--
Mtest/old/testdir/test_trycatch.vim | 12++++++------
Mtest/old/testdir/test_usercommands.vim | 18++++++++++++++++--
18 files changed, 463 insertions(+), 31 deletions(-)

diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt @@ -1041,6 +1041,13 @@ Syntax When the 'syntax' option has been set. The this option was set. <amatch> expands to the new value of 'syntax'. See |:syn-on|. + *TabClosed* +TabClosed After closing a tab page. <afile> expands to + the tab page number. + *TabClosedPre* +TabClosedPre Before closing a tab page. The window layout + is locked, thus opening and closing of windows + is prohibited. *TabEnter* TabEnter Just after entering a tab page. |tab-page| After WinEnter. @@ -1055,9 +1062,6 @@ TabNew When creating a new tab page. |tab-page| *TabNewEntered* TabNewEntered After entering a new tab page. |tab-page| After BufEnter. - *TabClosed* -TabClosed After closing a tab page. <afile> expands to - the tab page number. *TermOpen* TermOpen When a |terminal| job is starting. Can be used to configure the terminal buffer. To get diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt @@ -233,6 +233,7 @@ EVENTS • Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event. • |MarkSet| is triggered after a |mark| is set by the user (currently doesn't support implicit marks like |'[| or |'<|, …). +• |TabClosedPre| is triggered before closing a |tabpage|. • New `terminator` parameter for |TermRequest| event. HIGHLIGHTS diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt @@ -2561,6 +2561,7 @@ A jump table for the options with a short description can be found at |Q_op|. |SwapExists|, |Syntax|, |TabClosed|, + |TabClosedPre|, |TabEnter|, |TabLeave|, |TabNew|, diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua @@ -189,6 +189,7 @@ error('Cannot require a meta file') --- |'SwapExists' --- |'Syntax' --- |'TabClosed' +--- |'TabClosedPre' --- |'TabEnter' --- |'TabLeave' --- |'TabNew' diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua @@ -2239,6 +2239,7 @@ vim.go.ei = vim.go.eventignore --- `SwapExists`, --- `Syntax`, --- `TabClosed`, +--- `TabClosedPre`, --- `TabEnter`, --- `TabLeave`, --- `TabNew`, diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua @@ -109,7 +109,8 @@ return { StdinReadPre = false, -- before reading from stdin SwapExists = false, -- found existing swap file Syntax = false, -- syntax selected - TabClosed = false, -- a tab has closed + TabClosed = false, -- after closing a tab page + TabClosedPre = false, -- before closing a tab page TabEnter = false, -- after entering a tab page TabLeave = false, -- before leaving a tab page TabNew = false, -- when creating a new tab diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h @@ -841,9 +841,10 @@ struct tabpage_S { win_T *tp_firstwin; ///< first window in this Tab page win_T *tp_lastwin; ///< last window in this Tab page int64_t tp_old_Rows_avail; ///< ROWS_AVAIL when Tab page was left - int64_t tp_old_Columns; ///< Columns when Tab page was left, -1 when - ///< calling win_new_screen_cols() postponed + int64_t tp_old_Columns; ///< Columns when Tab page was left, -1 when + ///< calling win_new_screen_cols() postponed OptInt tp_ch_used; ///< value of 'cmdheight' when frame size was set + bool tp_did_tabclosedpre; ///< whether TabClosedPre was triggered diff_T *tp_first_diff; buf_T *(tp_diffbuf[DB_COUNT]); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c @@ -5150,6 +5150,10 @@ void tabpage_close(int forceit) return; } + trigger_tabclosedpre(curtab); + curtab->tp_did_tabclosedpre = true; + tabpage_T *const save_curtab = curtab; + // First close all the windows but the current one. If that worked then // close the last window in this tab, that will close it. while (curwin->w_floating) { @@ -5161,6 +5165,11 @@ void tabpage_close(int forceit) if (ONE_WINDOW) { ex_win_close(forceit, curwin, NULL); } + if (curtab == save_curtab) { + // When closing the tab page failed, reset tp_did_tabclosedpre so that + // TabClosedPre behaves consistently on next :close vs :tabclose. + curtab->tp_did_tabclosedpre = false; + } } /// Close tab page "tp", which is not the current tab page. @@ -5172,6 +5181,13 @@ void tabpage_close_other(tabpage_T *tp, int forceit) int done = 0; char prev_idx[NUMBUFLEN]; + if (window_layout_locked(CMD_SIZE)) { + return; + } + + trigger_tabclosedpre(tp); + tp->tp_did_tabclosedpre = true; + // Limit to 1000 windows, autocommands may add a window while we close // one. OK, so I'm paranoid... while (++done < 1000) { @@ -5179,12 +5195,22 @@ void tabpage_close_other(tabpage_T *tp, int forceit) win_T *wp = tp->tp_lastwin; ex_win_close(forceit, wp, tp); - // Autocommands may delete the tab page under our fingers and we may - // fail to close a window with a modified buffer. - if (!valid_tabpage(tp) || tp->tp_lastwin == wp) { + // Autocommands may delete the tab page under our fingers. + if (!valid_tabpage(tp)) { + break; + } + // We may fail to close a window with a modified buffer. + if (tp->tp_lastwin == wp) { + done = 1000; break; } } + if (done >= 1000) { + // When closing the tab page failed, reset tp_did_tabclosedpre so that + // TabClosedPre behaves consistently on next :close vs :tabclose. + tp->tp_did_tabclosedpre = false; + return; + } } /// ":only". diff --git a/src/nvim/window.c b/src/nvim/window.c @@ -140,7 +140,8 @@ bool frames_locked(void) /// When the window layout cannot be changed give an error and return true. /// "cmd" indicates the action being performed and is used to pick the relevant -/// error message. +/// error message. When closing window(s) and the command isn't easy to know, +/// passing CMD_SIZE will also work. bool window_layout_locked(cmdidx_T cmd) { if (split_disallowed > 0 || close_disallowed > 0) { @@ -2569,6 +2570,9 @@ void close_windows(buf_T *buf, bool keep_curwin) for (win_T *wp = lastwin; wp != NULL && (is_aucmd_win(lastwin) || !one_window(wp, NULL));) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) && !(win_locked(wp) || wp->w_buffer->b_locked > 0)) { + if (window_layout_locked(CMD_SIZE)) { + goto theend; // Only give one error message. + } if (win_close(wp, false, false) == FAIL) { // If closing the window fails give up, to avoid looping forever. break; @@ -2591,6 +2595,9 @@ void close_windows(buf_T *buf, bool keep_curwin) for (win_T *wp = tp->tp_lastwin; wp != NULL; wp = wp->w_prev) { if (wp->w_buffer == buf && !(win_locked(wp) || wp->w_buffer->b_locked > 0)) { + if (window_layout_locked(CMD_SIZE)) { + goto theend; // Only give one error message. + } if (!win_close_othertab(wp, false, tp, false)) { // If closing the window fails give up, to avoid looping forever. break; @@ -2605,6 +2612,7 @@ void close_windows(buf_T *buf, bool keep_curwin) } } +theend: RedrawingDisabled--; } @@ -3102,6 +3110,35 @@ static void do_autocmd_winclosed(win_T *win) recursive = false; } +void trigger_tabclosedpre(tabpage_T *tp) +{ + static bool recursive = false; + tabpage_T *ptp = curtab; + + // Quickly return when no TabClosedPre autocommands to be executed or + // already executing + if (!has_event(EVENT_TABCLOSEDPRE) || recursive) { + return; + } + + if (valid_tabpage(tp)) { + goto_tabpage_tp(tp, false, false); + } + recursive = true; + window_layout_lock(); + apply_autocmds(EVENT_TABCLOSEDPRE, NULL, NULL, false, NULL); + window_layout_unlock(); + recursive = false; + // tabpage may have been modified or deleted by autocmds + if (valid_tabpage(ptp)) { + // try to recover the tabpage first + goto_tabpage_tp(ptp, false, false); + } else { + // fall back to the first tabpage + goto_tabpage_tp(first_tabpage, false, false); + } +} + // Close window "win" in tab page "tp", which is not the current tab page. // This may be the last window in that tab page and result in closing the tab, // thus "tp" may become invalid! @@ -3115,6 +3152,11 @@ bool win_close_othertab(win_T *win, int free_buf, tabpage_T *tp, bool force) assert(tp != curtab); bool did_decrement = false; + // Commands that may call win_close_othertab() already check this, but + // check here again just in case. + if (window_layout_locked(CMD_SIZE)) { + return false; + } // Get here with win->w_buffer == NULL when win_close() detects the tab page // changed. if (win_locked(win) @@ -3157,6 +3199,14 @@ bool win_close_othertab(win_T *win, int free_buf, tabpage_T *tp, bool force) } } + if (tp->tp_firstwin == tp->tp_lastwin && !tp->tp_did_tabclosedpre) { + trigger_tabclosedpre(tp); + // autocmd may have freed the window already. + if (!win_valid_any_tab(win)) { + return false; + } + } + bufref_T bufref; set_bufref(&bufref, win->w_buffer); diff --git a/test/old/testdir/test_arglist.vim b/test/old/testdir/test_arglist.vim @@ -640,7 +640,7 @@ endfunc func Test_clear_arglist_in_all() n 0 00 000 0000 00000 000000 au WinNew 0 n 0 - call assert_fails("all", "E1156") + call assert_fails("all", "E1156:") au! * endfunc diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim @@ -3817,10 +3817,10 @@ func Test_autocmd_normal_mess() au BufLeave,BufWinLeave,BufHidden,BufUnload,BufDelete,BufWipeout * norm 7q/qc augroup END " Nvim has removed :open - " call assert_fails('o4', 'E1159') - call assert_fails('e4', 'E1159') + " call assert_fails('o4', 'E1159:') + call assert_fails('e4', 'E1159:') silent! H - call assert_fails('e xx', 'E1159') + call assert_fails('e xx', 'E1159:') normal G augroup aucmd_normal_test @@ -4566,6 +4566,7 @@ func Test_OptionSet_cmdheight() call Ntest_setmouse(&lines - 2, 1) call feedkeys("\<LeftDrag>", 'xt') call assert_equal(2, &l:ch) + call feedkeys("\<LeftRelease>", 'xt') tabnew | resize +1 call assert_equal(1, &l:ch) @@ -4636,6 +4637,337 @@ func Test_WinScrolled_Resized_eiw() call StopVimInTerminal(buf) endfunc +" Test that TabClosedPre and TabClosed are triggered when closing a tab. +func Test_autocmd_TabClosedPre() + augroup testing + au TabClosedPre * call add(g:tabpagenr_pre, t:testvar) + au TabClosed * call add(g:tabpagenr_post, t:testvar) + augroup END + + " Test 'tabclose' triggering + let g:tabpagenr_pre = [] + let g:tabpagenr_post = [] + let t:testvar = 1 + tabnew + let t:testvar = 2 + tabnew + let t:testvar = 3 + tabnew + let t:testvar = 4 + tabnext + tabclose + tabclose + tabclose + call assert_equal([1, 2, 3], g:tabpagenr_pre) + call assert_equal([2, 3, 4], g:tabpagenr_post) + + " Test 'tabclose {count}' triggering + let g:tabpagenr_pre = [] + let g:tabpagenr_post = [] + let t:testvar = 1 + tabnew + let t:testvar = 2 + tabnew + let t:testvar = 3 + tabclose 2 + tabclose 2 + call assert_equal([2, 3], g:tabpagenr_pre) + call assert_equal([3, 1], g:tabpagenr_post) + + " Test 'tabonly' triggering + let g:tabpagenr_pre = [] + let g:tabpagenr_post = [] + let t:testvar = 1 + tabnew + let t:testvar = 2 + tabonly + call assert_equal([1], g:tabpagenr_pre) + call assert_equal([2], g:tabpagenr_post) + + " Test 'q' and 'close' triggering (closing the last window in a tab) + let g:tabpagenr_pre = [] + let g:tabpagenr_post = [] + split + let t:testvar = 1 + tabnew + let t:testvar = 2 + split + vsplit + tabnew + let t:testvar = 3 + tabnext + only + quit + quit + close + close + call assert_equal([1, 2], g:tabpagenr_pre) + call assert_equal([2, 3], g:tabpagenr_post) + + " Test failing to close tab page + let g:tabpagenr_pre = [] + let g:tabpagenr_post = [] + let t:testvar = 1 + call setline(1, 'foo') + setlocal bufhidden=wipe + tabnew + let t:testvar = 2 + tabnew + let t:testvar = 3 + call setline(1, 'bar') + setlocal bufhidden=wipe + tabnew + let t:testvar = 4 + call setline(1, 'baz') + setlocal bufhidden=wipe + new + call assert_fails('tabclose', 'E445:') + call assert_equal([4], g:tabpagenr_pre) + call assert_equal([], g:tabpagenr_post) + " :tabclose! after failed :tabclose should trigger TabClosedPre again. + tabclose! + call assert_equal([4, 4], g:tabpagenr_pre) + call assert_equal([3], g:tabpagenr_post) + call assert_fails('tabclose', 'E37:') + call assert_equal([4, 4, 3], g:tabpagenr_pre) + call assert_equal([3], g:tabpagenr_post) + " The same for :close! if the tab page only has one window. + close! + call assert_equal([4, 4, 3, 3], g:tabpagenr_pre) + call assert_equal([3, 2], g:tabpagenr_post) + " Also test with :close! after failed :tabonly. + call assert_fails('tabonly', 'E37:') + call assert_equal([4, 4, 3, 3, 1], g:tabpagenr_pre) + call assert_equal([3, 2], g:tabpagenr_post) + tabprevious | close! + call assert_equal([4, 4, 3, 3, 1, 1], g:tabpagenr_pre) + call assert_equal([3, 2, 2], g:tabpagenr_post) + %bwipe! + + " Test closing another tab page in BufWinLeave + let g:tabpagenr_pre = [] + let g:tabpagenr_post = [] + split + let t:testvar = 1 + tabnew + let t:testvar = 2 + tabnew Xsomebuf + let t:testvar = 3 + new + autocmd BufWinLeave Xsomebuf ++once ++nested tabclose 1 + tabclose + " TabClosedPre should not be triggered for tab page 3 twice. + call assert_equal([3, 1], g:tabpagenr_pre) + " When tab page 1 was closed, tab page 3 was still the current tab page. + call assert_equal([3, 2], g:tabpagenr_post) + %bwipe! + + func ClearAutocmdAndCreateTabs() + au! TabClosedPre + bw! + e Z + tabonly + tabnew A + tabnew B + tabnew C + endfunc + + func GetTabs() + redir => tabsout + tabs + redir END + let tabsout = substitute(tabsout, '\n', '', 'g') + let tabsout = substitute(tabsout, 'Tab page ', '', 'g') + let tabsout = substitute(tabsout, '#', '', 'g') " Nvim: remove '#' + let tabsout = substitute(tabsout, ' ', '', 'g') + return tabsout + endfunc + + call CleanUpTestAuGroup() + + " Close tab in TabClosedPre autocmd + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabclose + call assert_fails('tabclose', 'E1312:') + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabclose + call assert_fails('tabclose 2', 'E1312:') + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabclose 1 + call assert_fails('tabclose', 'E1312:') + + " Close other (all) tabs in TabClosedPre autocmd + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabonly + call assert_fails('tabclose', 'E1312:') + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabonly + call assert_fails('tabclose 2', 'E1312:') + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabclose 4 + call assert_fails('tabclose 2', 'E1312:') + + " Open new tabs in TabClosedPre autocmd + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabnew D + call assert_fails('tabclose', 'E1312:') + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabnew D + call assert_fails('tabclose 1', 'E1312:') + + " Moving the tab page in TabClosedPre autocmd + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabmove 0 + tabclose + call assert_equal('1>Z2A3B', GetTabs()) + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabmove 0 + tabclose 1 + call assert_equal('1A2B3>C', GetTabs()) + tabonly + call assert_equal('1>C', GetTabs()) + + " Switching tab page in TabClosedPre autocmd + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabnext | e Y + tabclose + call assert_equal('1Y2A3>B', GetTabs()) + call ClearAutocmdAndCreateTabs() + au TabClosedPre * tabnext | e Y + tabclose 1 + call assert_equal('1Y2B3>C', GetTabs()) + tabonly + call assert_equal('1>Y', GetTabs()) + + " Create new windows in TabClosedPre autocmd + call ClearAutocmdAndCreateTabs() + au TabClosedPre * split | e X| vsplit | e Y | split | e Z + call assert_fails('tabclose', 'E242:') + call ClearAutocmdAndCreateTabs() + au TabClosedPre * new X | new Y | new Z + call assert_fails('tabclose 1', 'E242:') + + " Test directly closing the tab page with ':tabclose' + au! + tabonly + bw! + e Z + au TabClosedPre * mksession! + tabnew A + sp + tabclose + source Session.vim + call assert_equal('1Z2>AA', GetTabs()) + + " Test directly closing the tab page with ':tabonly' + " Z is closed before A. Hence A overwrites the session. + au! + tabonly + bw! + e Z + au TabClosedPre * mksession! + tabnew A + tabnew B + tabonly + source Session.vim + call assert_equal('1>A2B', GetTabs()) + + " Clean up + call delete('Session.vim') + au! + only + tabonly + bw! + delfunc ClearAutocmdAndCreateTabs + delfunc GetTabs +endfunc + +" This used to cause heap-use-after-free. +func Run_test_TabClosedPre_wipe_buffer(split_cmds) + file Xa + exe a:split_cmds + autocmd TabClosedPre * ++once tabnext | bwipe! Xa + " Closing window inside TabClosedPre is not allowed. + call assert_fails('tabonly', 'E1312:') + + %bwipe! +endfunc + +func Test_TabClosedPre_wipe_buffer() + " Test with Xa only in other tab pages. + call Run_test_TabClosedPre_wipe_buffer('split | tab split | tabnew Xb') + " Test with Xa in both current and other tab pages. + call Run_test_TabClosedPre_wipe_buffer('split | tab split | new Xb') +endfunc + +func Test_TabClosedPre_mouse() + func MyTabline() + let cnt = tabpagenr('$') + return range(1, cnt)->mapnew({_, n -> $'%{n}X|Close{n}|%X'})->join('') + endfunc + + let save_mouse = &mouse + if has('gui') + set guioptions-=e + endif + set mouse=a tabline=%!MyTabline() + + func OpenTwoTabPages() + %bwipe! + file Xa | split | split + let g:Xa_bufnr = bufnr() + tabnew Xb | split + let g:Xb_bufnr = bufnr() + redraw! + call assert_match('^|Close1||Close2| *$', Screenline(1)) + call assert_equal(2, tabpagenr('$')) + endfunc + + autocmd! TabClosedPre + call OpenTwoTabPages() + let g:autocmd_bufnrs = [] + autocmd TabClosedPre * let g:autocmd_bufnrs += [tabpagebuflist()] + call Ntest_setmouse(1, 2) + call feedkeys("\<LeftMouse>\<LeftRelease>", 'tx') + call assert_equal(1, tabpagenr('$')) + call assert_equal([[g:Xa_bufnr]->repeat(3)], g:autocmd_bufnrs) + call assert_equal([g:Xb_bufnr]->repeat(2), tabpagebuflist()) + + call OpenTwoTabPages() + let g:autocmd_bufnrs = [] + autocmd TabClosedPre * call feedkeys("\<LeftRelease>\<LeftMouse>", 'tx') + call Ntest_setmouse(1, 2) + " Closing tab page inside TabClosedPre is not allowed. + call assert_fails('call feedkeys("\<LeftMouse>", "tx")', 'E1312:') + call feedkeys("\<LeftRelease>", 'tx') + + autocmd! TabClosedPre + call OpenTwoTabPages() + let g:autocmd_bufnrs = [] + autocmd TabClosedPre * let g:autocmd_bufnrs += [tabpagebuflist()] + call Ntest_setmouse(1, 10) + call feedkeys("\<LeftMouse>\<LeftRelease>", 'tx') + call assert_equal(1, tabpagenr('$')) + call assert_equal([[g:Xb_bufnr]->repeat(2)], g:autocmd_bufnrs) + call assert_equal([g:Xa_bufnr]->repeat(3), tabpagebuflist()) + + call OpenTwoTabPages() + let g:autocmd_bufnrs = [] + autocmd TabClosedPre * call feedkeys("\<LeftRelease>\<LeftMouse>", 'tx') + call Ntest_setmouse(1, 10) + " Closing tab page inside TabClosedPre is not allowed. + call assert_fails('call feedkeys("\<LeftMouse>", "tx")', 'E1312:') + call feedkeys("\<LeftRelease>", 'tx') + + autocmd! TabClosedPre + %bwipe! + unlet g:Xa_bufnr g:Xb_bufnr g:autocmd_bufnrs + let &mouse = save_mouse + set tabline& guioptions& + delfunc MyTabline + delfunc OpenTwoTabPages +endfunc + func Test_eventignorewin_non_current() defer CleanUpTestAuGroup() let s:triggered = '' diff --git a/test/old/testdir/test_blob.vim b/test/old/testdir/test_blob.vim @@ -229,13 +229,13 @@ func Test_blob_compare() VAR b1 = 0z0011 echo b1 == 9 END - call CheckLegacyAndVim9Failure(lines, ['E977:', 'E1072', 'E1072']) + call CheckLegacyAndVim9Failure(lines, ['E977:', 'E1072:', 'E1072:']) let lines =<< trim END VAR b1 = 0z0011 echo b1 != 9 END - call CheckLegacyAndVim9Failure(lines, ['E977:', 'E1072', 'E1072']) + call CheckLegacyAndVim9Failure(lines, ['E977:', 'E1072:', 'E1072:']) let lines =<< trim END VAR b1 = 0z0011 diff --git a/test/old/testdir/test_edit.vim b/test/old/testdir/test_edit.vim @@ -2123,7 +2123,7 @@ endfunc func Test_read_invalid() " set encoding=latin1 " This was not properly checking for going past the end. - call assert_fails('r`=', 'E484') + call assert_fails('r`=', 'E484:') set encoding=utf-8 endfunc diff --git a/test/old/testdir/test_expr.vim b/test/old/testdir/test_expr.vim @@ -213,10 +213,10 @@ func Test_method_with_prefix() call CheckLegacyAndVim9Success(lines) call assert_equal([0, 1, 2], --3->range()) - call CheckDefAndScriptFailure(['eval --3->range()'], 'E15') + call CheckDefAndScriptFailure(['eval --3->range()'], 'E15:') call assert_equal(1, !+-+0) - call CheckDefAndScriptFailure(['eval !+-+0'], 'E15') + call CheckDefAndScriptFailure(['eval !+-+0'], 'E15:') endfunc func Test_option_value() @@ -836,7 +836,7 @@ endfunc " Test for errors in expression evaluation func Test_expr_eval_error() call CheckLegacyAndVim9Failure(["VAR i = 'abc' .. []"], ['E730:', 'E1105:', 'E730:']) - call CheckLegacyAndVim9Failure(["VAR l = [] + 10"], ['E745:', 'E1051:', 'E745']) + call CheckLegacyAndVim9Failure(["VAR l = [] + 10"], ['E745:', 'E1051:', 'E745:']) call CheckLegacyAndVim9Failure(["VAR v = 10 + []"], ['E745:', 'E1051:', 'E745:']) call CheckLegacyAndVim9Failure(["VAR v = 10 / []"], ['E745:', 'E1036:', 'E745:']) call CheckLegacyAndVim9Failure(["VAR v = -{}"], ['E728:', 'E1012:', 'E728:']) diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim @@ -1366,7 +1366,7 @@ func Test_listdict_index() call CheckLegacyAndVim9Failure(['VAR d = {"k": 10}', 'echo d[1 : 2]'], 'E719:') call assert_fails("let v = [4, 6][{-> 1}]", 'E729:') - call CheckDefAndScriptFailure(['var v = [4, 6][() => 1]'], ['E1012', 'E703:']) + call CheckDefAndScriptFailure(['var v = [4, 6][() => 1]'], ['E1012:', 'E703:']) call CheckLegacyAndVim9Failure(['VAR v = range(5)[2 : []]'], ['E730:', 'E1012:', 'E730:']) diff --git a/test/old/testdir/test_startup.vim b/test/old/testdir/test_startup.vim @@ -1356,7 +1356,7 @@ func Test_cq_zero_exmode() let logfile = 'Xcq_log.txt' let out = system(GetVimCommand() .. ' --clean --log ' .. logfile .. ' -es -X -c "argdelete foobar" -c"7cq"') call assert_equal(8, v:shell_error) - let log = filter(readfile(logfile), {idx, val -> val =~ "E480"}) + let log = filter(readfile(logfile), {idx, val -> val =~ "E480:"}) call assert_match('E480: No match: foobar', log[0]) call delete(logfile) @@ -1367,7 +1367,7 @@ func Test_cq_zero_exmode() else call assert_equal(256, v:shell_error) endif - let log = filter(readfile(logfile), {idx, val -> val =~ "E480"}) + let log = filter(readfile(logfile), {idx, val -> val =~ "E480:"}) call assert_match('E480: No match: foobar', log[0]) call delete('Xcq_log.txt') endfunc diff --git a/test/old/testdir/test_trycatch.vim b/test/old/testdir/test_trycatch.vim @@ -1850,7 +1850,7 @@ func T75_R() Xpath 'f' finally Xpath 'g' - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21:' Xpath 'h' endif break " discard error for $VIMNOERRTHROW @@ -1877,7 +1877,7 @@ func Test_builtin_func_error() Xpath 'k' finally Xpath 'l' - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21:' Xpath 'm' endif break " discard error for $VIMNOERRTHROW @@ -1896,7 +1896,7 @@ func Test_builtin_func_error() Xpath 'o' finally Xpath 'p' - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21:' Xpath 'q' endif break " discard error for $VIMNOERRTHROW @@ -1915,7 +1915,7 @@ func Test_builtin_func_error() Xpath 's' finally Xpath 't' - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21:' Xpath 'u' endif break " discard error for $VIMNOERRTHROW @@ -1938,7 +1938,7 @@ func Test_builtin_func_error() Xpath 'x' finally Xpath 'y' - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21:' Xpath 'z' endif break " discard error for $VIMNOERRTHROW @@ -1958,7 +1958,7 @@ func Test_builtin_func_error() Xpath 'B' finally Xpath 'C' - if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21' + if caught || $VIMNOERRTHROW && v:errmsg =~ 'E21:' Xpath 'D' endif call assert_equal('a', x) diff --git a/test/old/testdir/test_usercommands.vim b/test/old/testdir/test_usercommands.vim @@ -2,6 +2,7 @@ source check.vim source screendump.vim +source vim9.vim " Test for <mods> in user defined commands function Test_cmdmods() @@ -307,13 +308,26 @@ func Test_CmdErrors() call assert_fails('com! -complete=custom DoCmd :', 'E467:') call assert_fails('com! -complete=customlist DoCmd :', 'E467:') " call assert_fails('com! -complete=behave,CustomComplete DoCmd :', 'E468:') - call assert_fails('com! -complete=file DoCmd :', 'E1208:') - call assert_fails('com! -nargs=0 -complete=file DoCmd :', 'E1208:') call assert_fails('com! -nargs=x DoCmd :', 'E176:') call assert_fails('com! -count=1 -count=2 DoCmd :', 'E177:') call assert_fails('com! -count=x DoCmd :', 'E178:') call assert_fails('com! -range=x DoCmd :', 'E178:') + call assert_fails('com! -complete=file DoCmd :', 'E1208:') + call assert_fails('com! -nargs=0 -complete=file DoCmd :', 'E1208:') + + let lines =<< trim END + vim9script + com! -complete=file DoCmd : + END + call CheckScriptFailure(lines, 'E1208:', 2) + + let lines =<< trim END + vim9script + com! -nargs=0 -complete=file DoCmd : + END + call CheckScriptFailure(lines, 'E1208:', 2) + com! -nargs=0 DoCmd : call assert_fails('DoCmd x', 'E488:')