patch 9.1.1202: Missing TabClosedPre autocommand

Problem:  Missing TabClosedPre autocommand
          (zoumi)
Solution: Add the TabClosedPre autcommand (Jim Zhou).

fixes: #16518
closes: #16855

Signed-off-by: Jim Zhou <jimzhouzzy@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Jim Zhou
2025-03-13 21:58:25 +01:00
committed by Christian Brabandt
parent 587601671c
commit 5606ca5349
10 changed files with 217 additions and 4 deletions

View File

@ -387,6 +387,7 @@ Name triggered by ~
|TabNew| after creating a new tab page
|WinClosed| after closing a window
|TabClosed| after closing a tab page
|TabClosedPre| before closing a tab page
|WinEnter| after entering another window
|WinLeave| before leaving a window
|TabEnter| after entering another tab page
@ -1232,6 +1233,10 @@ Syntax When the 'syntax' option has been set. The
See |:syn-on|.
*TabClosed*
TabClosed After closing a tab page.
*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 triggering the WinEnter and before

View File

@ -5759,6 +5759,7 @@ TSQL ft_sql.txt /*TSQL*
TTpro-telnet syntax.txt /*TTpro-telnet*
Tab intro.txt /*Tab*
TabClosed autocmd.txt /*TabClosed*
TabClosedPre autocmd.txt /*TabClosedPre*
TabEnter autocmd.txt /*TabEnter*
TabLeave autocmd.txt /*TabLeave*
TabNew autocmd.txt /*TabNew*

View File

@ -1,4 +1,4 @@
*version9.txt* For Vim version 9.1. Last change: 2025 Mar 06
*version9.txt* For Vim version 9.1. Last change: 2025 Mar 13
VIM REFERENCE MANUAL by Bram Moolenaar
@ -41687,6 +41687,7 @@ Autocommands: ~
|CursorMovedC| after the cursor was moved in the command-line
|KeyInputPre| before processing any key event in any mode
|SessionWritePost| after writing the session file |:mksession|
|TabClosedPre| before closing a |tabpage|.
|TermResponseAll| after the terminal response to |t_RV| and others is
received
|WinNewPre| before creating a new window

View File

@ -2,7 +2,7 @@
" Language: Vim script
" Maintainer: Hirohito Higashi <h.east.727 ATMARK gmail.com>
" Doug Kearns <dougkearns@gmail.com>
" Last Change: 2025 Mar 10
" Last Change: 2025 Mar 13
" Former Maintainer: Charles E. Campbell
" DO NOT CHANGE DIRECTLY.
@ -116,7 +116,7 @@ syn keyword vimErrSetting contained invakm invaltkeymap invanti invantialias inv
syn case ignore
" GEN_SYN_VIM: vimAutoEvent, START_STR='syn keyword vimAutoEvent contained', END_STR=''
syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter BufFilePost BufFilePre BufHidden BufLeave BufNew BufNewFile BufRead BufReadCmd BufReadPost BufReadPre BufUnload BufWinEnter BufWinLeave BufWipeout BufWrite BufWriteCmd BufWritePost BufWritePre CmdlineChanged CmdlineEnter CmdlineLeave CmdUndefined CmdwinEnter CmdwinLeave ColorScheme ColorSchemePre CompleteChanged CompleteDone CompleteDonePre CursorHold CursorHoldI CursorMoved CursorMovedC CursorMovedI DiffUpdated DirChanged DirChangedPre EncodingChanged ExitPre FileAppendCmd FileAppendPost FileAppendPre FileChangedRO FileChangedShell FileChangedShellPost FileEncoding FileReadCmd FileReadPost FileReadPre FileType FileWriteCmd FileWritePost FileWritePre FilterReadPost FilterReadPre FilterWritePost
syn keyword vimAutoEvent contained FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave InsertLeavePre KeyInputPre MenuPopup ModeChanged OptionSet QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SafeState SafeStateAgain SessionLoadPost SessionWritePost ShellCmdPost ShellFilterPost SigUSR1 SourceCmd SourcePost SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabClosed TabEnter TabLeave TabNew TermChanged TerminalOpen TerminalWinOpen TermResponse TermResponseAll TextChanged TextChangedI TextChangedP TextChangedT TextYankPost User VimEnter VimLeave VimLeavePre VimResized VimResume VimSuspend WinClosed WinEnter WinLeave WinNew WinNewPre WinResized WinScrolled
syn keyword vimAutoEvent contained FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave InsertLeavePre KeyInputPre MenuPopup ModeChanged OptionSet QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SafeState SafeStateAgain SessionLoadPost SessionWritePost ShellCmdPost ShellFilterPost SigUSR1 SourceCmd SourcePost SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabClosed TabClosedPre TabEnter TabLeave TabNew TermChanged TerminalOpen TerminalWinOpen TermResponse TermResponseAll TextChanged TextChangedI TextChangedP TextChangedT TextYankPost User VimEnter VimLeave VimLeavePre VimResized VimResume VimSuspend WinClosed WinEnter WinLeave WinNew WinNewPre WinResized WinScrolled
" Highlight commonly used Groupnames {{{2
syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo
@ -1153,7 +1153,7 @@ syn keyword vimSyncCcomment contained ccomment skipwhite nextgroup=vimGroupName
syn keyword vimSyncClear contained clear skipwhite nextgroup=vimSyncGroupName
syn keyword vimSyncFromstart contained fromstart
syn keyword vimSyncMatch contained match skipwhite nextgroup=vimSyncGroupName
syn keyword vimSyncRegion contained region skipwhite nextgroup=vimSynRegion
syn keyword vimSyncRegion contained region skipwhite nextgroup=vimSynReg
syn match vimSyncLinebreak contained "\<linebreaks=" nextgroup=vimNumber
syn keyword vimSyncLinecont contained linecont skipwhite nextgroup=vimSynRegPat
syn match vimSyncLines contained "\<lines=" nextgroup=vimNumber

View File

@ -180,6 +180,7 @@ static keyvalue_T event_tab[NUM_EVENTS] = {
KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
KEYVALUE_ENTRY(EVENT_TABCLOSEDPRE, "TabClosedPre"),
KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
@ -2900,6 +2901,14 @@ get_event_name_no_group(expand_T *xp UNUSED, int idx, int win)
return NULL;
}
/*
* Return TRUE when there is a TabClosedPre autocommand defined.
*/
int
has_tabclosedpre(void)
{
return (first_autopat[(int)EVENT_TABCLOSEDPRE] != NULL);
}
#if defined(FEAT_EVAL) || defined(PROTO)
/*

View File

@ -47,4 +47,5 @@ int au_exists(char_u *arg);
void f_autocmd_add(typval_T *argvars, typval_T *rettv);
void f_autocmd_delete(typval_T *argvars, typval_T *rettv);
void f_autocmd_get(typval_T *argvars, typval_T *rettv);
int has_tabclosedpre(void);
/* vim: set ft=c : */

View File

@ -4986,4 +4986,162 @@ 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)
func ClearAutomcdAndCreateTabs()
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')
return tabsout
endfunc
call CleanUpTestAuGroup()
" Close tab in TabClosedPre autocmd
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabclose
call assert_fails('tabclose', 'E1312')
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabclose
call assert_fails('tabclose 2', 'E1312')
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabclose 1
call assert_fails('tabclose', 'E1312')
" Close other (all) tabs in TabClosedPre autocmd
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabonly
call assert_fails('tabclose', 'E1312')
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabonly
call assert_fails('tabclose 2', 'E1312')
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabclose 4
call assert_fails('tabclose 2', 'E1312')
" Open new tabs in TabClosedPre autocmd
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabnew D
call assert_fails('tabclose', 'E1312')
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabnew D
call assert_fails('tabclose 1', 'E1312')
" Moving the tab page in TabClosedPre autocmd
call ClearAutomcdAndCreateTabs()
au TabClosedPre * tabmove 0
tabclose
call assert_equal('1Z2A3>B', GetTabs())
call ClearAutomcdAndCreateTabs()
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 ClearAutomcdAndCreateTabs()
au TabClosedPre * tabnext | e Y
tabclose
call assert_equal('1Y2A3>B', GetTabs())
call ClearAutomcdAndCreateTabs()
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 ClearAutomcdAndCreateTabs()
au TabClosedPre * split | e X| vsplit | e Y | split | e Z
call assert_fails('tabclose', 'E242')
call ClearAutomcdAndCreateTabs()
au TabClosedPre * new X | new Y | new Z
call assert_fails('tabclose 1', 'E242')
" Clean up
au!
only
tabonly
bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1202,
/**/
1201,
/**/

View File

@ -1435,6 +1435,7 @@ enum auto_event
EVENT_SWAPEXISTS, // found existing swap file
EVENT_SYNTAX, // syntax selected
EVENT_TABCLOSED, // after closing a tab page
EVENT_TABCLOSEDPRE, // before closing a tab page
EVENT_TABENTER, // after entering a tab page
EVENT_TABLEAVE, // before leaving a tab page
EVENT_TABNEW, // when entering a new tab page

View File

@ -2978,6 +2978,33 @@ trigger_winclosed(win_T *win)
recursive = FALSE;
}
static void
trigger_tabclosedpre(tabpage_T *tp)
{
static int recursive = FALSE;
tabpage_T *ptp = curtab;
// Quickly return when no TabClosedPre autocommands to be executed or
// already executing
if (!has_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 tappage first
goto_tabpage_tp(ptp, FALSE, FALSE);
else
// fall back to the first tappage
goto_tabpage_tp(first_tabpage, FALSE, FALSE);
}
/*
* Make a snapshot of all the window scroll positions and sizes of the current
* tab page.
@ -3353,6 +3380,14 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
return;
}
if (tp->tp_firstwin == tp->tp_lastwin)
{
trigger_tabclosedpre(tp);
// autocmd may have freed the window already.
if (!win_valid_any_tab(win))
return;
}
if (win->w_buffer != NULL)
// Close the link to the buffer.
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0,