Skip to content

Commit 5606ca5

Browse files
JimZhouZZYchrisbra
authored andcommitted
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>
1 parent 5876016 commit 5606ca5

File tree

10 files changed

+217
-4
lines changed

10 files changed

+217
-4
lines changed

runtime/doc/autocmd.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ Name triggered by ~
387387
|TabNew| after creating a new tab page
388388
|WinClosed| after closing a window
389389
|TabClosed| after closing a tab page
390+
|TabClosedPre| before closing a tab page
390391
|WinEnter| after entering another window
391392
|WinLeave| before leaving a window
392393
|TabEnter| after entering another tab page
@@ -1232,6 +1233,10 @@ Syntax When the 'syntax' option has been set. The
12321233
See |:syn-on|.
12331234
*TabClosed*
12341235
TabClosed After closing a tab page.
1236+
*TabClosedPre*
1237+
TabClosedPre Before closing a tab page. The window layout
1238+
is locked, thus opening and closing of windows
1239+
is prohibited.
12351240
*TabEnter*
12361241
TabEnter Just after entering a tab page. |tab-page|
12371242
After triggering the WinEnter and before

runtime/doc/tags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5759,6 +5759,7 @@ TSQL ft_sql.txt /*TSQL*
57595759
TTpro-telnet syntax.txt /*TTpro-telnet*
57605760
Tab intro.txt /*Tab*
57615761
TabClosed autocmd.txt /*TabClosed*
5762+
TabClosedPre autocmd.txt /*TabClosedPre*
57625763
TabEnter autocmd.txt /*TabEnter*
57635764
TabLeave autocmd.txt /*TabLeave*
57645765
TabNew autocmd.txt /*TabNew*

runtime/doc/version9.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*version9.txt* For Vim version 9.1. Last change: 2025 Mar 06
1+
*version9.txt* For Vim version 9.1. Last change: 2025 Mar 13
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -41687,6 +41687,7 @@ Autocommands: ~
4168741687
|CursorMovedC| after the cursor was moved in the command-line
4168841688
|KeyInputPre| before processing any key event in any mode
4168941689
|SessionWritePost| after writing the session file |:mksession|
41690+
|TabClosedPre| before closing a |tabpage|.
4169041691
|TermResponseAll| after the terminal response to |t_RV| and others is
4169141692
received
4169241693
|WinNewPre| before creating a new window

runtime/syntax/vim.vim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
" Language: Vim script
33
" Maintainer: Hirohito Higashi <h.east.727 ATMARK gmail.com>
44
" Doug Kearns <dougkearns@gmail.com>
5-
" Last Change: 2025 Mar 10
5+
" Last Change: 2025 Mar 13
66
" Former Maintainer: Charles E. Campbell
77

88
" DO NOT CHANGE DIRECTLY.
@@ -116,7 +116,7 @@ syn keyword vimErrSetting contained invakm invaltkeymap invanti invantialias inv
116116
syn case ignore
117117
" GEN_SYN_VIM: vimAutoEvent, START_STR='syn keyword vimAutoEvent contained', END_STR=''
118118
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
119-
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
119+
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
120120

121121
" Highlight commonly used Groupnames {{{2
122122
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
11531153
syn keyword vimSyncClear contained clear skipwhite nextgroup=vimSyncGroupName
11541154
syn keyword vimSyncFromstart contained fromstart
11551155
syn keyword vimSyncMatch contained match skipwhite nextgroup=vimSyncGroupName
1156-
syn keyword vimSyncRegion contained region skipwhite nextgroup=vimSynRegion
1156+
syn keyword vimSyncRegion contained region skipwhite nextgroup=vimSynReg
11571157
syn match vimSyncLinebreak contained "\<linebreaks=" nextgroup=vimNumber
11581158
syn keyword vimSyncLinecont contained linecont skipwhite nextgroup=vimSynRegPat
11591159
syn match vimSyncLines contained "\<lines=" nextgroup=vimNumber

src/autocmd.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ static keyvalue_T event_tab[NUM_EVENTS] = {
180180
KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"),
181181
KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"),
182182
KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"),
183+
KEYVALUE_ENTRY(EVENT_TABCLOSEDPRE, "TabClosedPre"),
183184
KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"),
184185
KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"),
185186
KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"),
@@ -2900,6 +2901,14 @@ get_event_name_no_group(expand_T *xp UNUSED, int idx, int win)
29002901
return NULL;
29012902
}
29022903

2904+
/*
2905+
* Return TRUE when there is a TabClosedPre autocommand defined.
2906+
*/
2907+
int
2908+
has_tabclosedpre(void)
2909+
{
2910+
return (first_autopat[(int)EVENT_TABCLOSEDPRE] != NULL);
2911+
}
29032912

29042913
#if defined(FEAT_EVAL) || defined(PROTO)
29052914
/*

src/proto/autocmd.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ int au_exists(char_u *arg);
4747
void f_autocmd_add(typval_T *argvars, typval_T *rettv);
4848
void f_autocmd_delete(typval_T *argvars, typval_T *rettv);
4949
void f_autocmd_get(typval_T *argvars, typval_T *rettv);
50+
int has_tabclosedpre(void);
5051
/* vim: set ft=c : */

src/testdir/test_autocmd.vim

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4986,4 +4986,162 @@ func Test_WinScrolled_Resized_eiw()
49864986
call StopVimInTerminal(buf)
49874987
endfunc
49884988

4989+
" Test that TabClosedPre and TabClosed are triggered when closing a tab.
4990+
func Test_autocmd_tabclosedpre()
4991+
augroup testing
4992+
au TabClosedPre * call add(g:tabpagenr_pre, t:testvar)
4993+
au TabClosed * call add(g:tabpagenr_post, t:testvar)
4994+
augroup END
4995+
4996+
" Test 'tabclose' triggering
4997+
let g:tabpagenr_pre = []
4998+
let g:tabpagenr_post = []
4999+
let t:testvar = 1
5000+
tabnew
5001+
let t:testvar = 2
5002+
tabnew
5003+
let t:testvar = 3
5004+
tabnew
5005+
let t:testvar = 4
5006+
tabnext
5007+
tabclose
5008+
tabclose
5009+
tabclose
5010+
call assert_equal([1, 2, 3], g:tabpagenr_pre)
5011+
call assert_equal([2, 3, 4], g:tabpagenr_post)
5012+
5013+
" Test 'tabclose {count}' triggering
5014+
let g:tabpagenr_pre = []
5015+
let g:tabpagenr_post = []
5016+
let t:testvar = 1
5017+
tabnew
5018+
let t:testvar = 2
5019+
tabnew
5020+
let t:testvar = 3
5021+
tabclose 2
5022+
tabclose 2
5023+
call assert_equal([2, 3], g:tabpagenr_pre)
5024+
call assert_equal([3, 1], g:tabpagenr_post)
5025+
5026+
" Test 'tabonly' triggering
5027+
let g:tabpagenr_pre = []
5028+
let g:tabpagenr_post = []
5029+
let t:testvar = 1
5030+
tabnew
5031+
let t:testvar = 2
5032+
tabonly
5033+
call assert_equal([1], g:tabpagenr_pre)
5034+
call assert_equal([2], g:tabpagenr_post)
5035+
5036+
" Test 'q' and 'close' triggering (closing the last window in a tab)
5037+
let g:tabpagenr_pre = []
5038+
let g:tabpagenr_post = []
5039+
split
5040+
let t:testvar = 1
5041+
tabnew
5042+
let t:testvar = 2
5043+
split
5044+
vsplit
5045+
tabnew
5046+
let t:testvar = 3
5047+
tabnext
5048+
only
5049+
quit
5050+
quit
5051+
close
5052+
close
5053+
call assert_equal([1, 2], g:tabpagenr_pre)
5054+
call assert_equal([2, 3], g:tabpagenr_post)
5055+
5056+
func ClearAutomcdAndCreateTabs()
5057+
au! TabClosedPre
5058+
bw!
5059+
e Z
5060+
tabonly
5061+
tabnew A
5062+
tabnew B
5063+
tabnew C
5064+
endfunc
5065+
5066+
func GetTabs()
5067+
redir => tabsout
5068+
tabs
5069+
redir END
5070+
let tabsout = substitute(tabsout, '\n', '', 'g')
5071+
let tabsout = substitute(tabsout, 'Tab page ', '', 'g')
5072+
let tabsout = substitute(tabsout, ' ', '', 'g')
5073+
return tabsout
5074+
endfunc
5075+
5076+
call CleanUpTestAuGroup()
5077+
5078+
" Close tab in TabClosedPre autocmd
5079+
call ClearAutomcdAndCreateTabs()
5080+
au TabClosedPre * tabclose
5081+
call assert_fails('tabclose', 'E1312')
5082+
call ClearAutomcdAndCreateTabs()
5083+
au TabClosedPre * tabclose
5084+
call assert_fails('tabclose 2', 'E1312')
5085+
call ClearAutomcdAndCreateTabs()
5086+
au TabClosedPre * tabclose 1
5087+
call assert_fails('tabclose', 'E1312')
5088+
5089+
" Close other (all) tabs in TabClosedPre autocmd
5090+
call ClearAutomcdAndCreateTabs()
5091+
au TabClosedPre * tabonly
5092+
call assert_fails('tabclose', 'E1312')
5093+
call ClearAutomcdAndCreateTabs()
5094+
au TabClosedPre * tabonly
5095+
call assert_fails('tabclose 2', 'E1312')
5096+
call ClearAutomcdAndCreateTabs()
5097+
au TabClosedPre * tabclose 4
5098+
call assert_fails('tabclose 2', 'E1312')
5099+
5100+
" Open new tabs in TabClosedPre autocmd
5101+
call ClearAutomcdAndCreateTabs()
5102+
au TabClosedPre * tabnew D
5103+
call assert_fails('tabclose', 'E1312')
5104+
call ClearAutomcdAndCreateTabs()
5105+
au TabClosedPre * tabnew D
5106+
call assert_fails('tabclose 1', 'E1312')
5107+
5108+
" Moving the tab page in TabClosedPre autocmd
5109+
call ClearAutomcdAndCreateTabs()
5110+
au TabClosedPre * tabmove 0
5111+
tabclose
5112+
call assert_equal('1Z2A3>B', GetTabs())
5113+
call ClearAutomcdAndCreateTabs()
5114+
au TabClosedPre * tabmove 0
5115+
tabclose 1
5116+
call assert_equal('1A2B3>C', GetTabs())
5117+
tabonly
5118+
call assert_equal('1>C', GetTabs())
5119+
5120+
" Switching tab page in TabClosedPre autocmd
5121+
call ClearAutomcdAndCreateTabs()
5122+
au TabClosedPre * tabnext | e Y
5123+
tabclose
5124+
call assert_equal('1Y2A3>B', GetTabs())
5125+
call ClearAutomcdAndCreateTabs()
5126+
au TabClosedPre * tabnext | e Y
5127+
tabclose 1
5128+
call assert_equal('1Y2B3>C', GetTabs())
5129+
tabonly
5130+
call assert_equal('1>Y', GetTabs())
5131+
5132+
" Create new windows in TabClosedPre autocmd
5133+
call ClearAutomcdAndCreateTabs()
5134+
au TabClosedPre * split | e X| vsplit | e Y | split | e Z
5135+
call assert_fails('tabclose', 'E242')
5136+
call ClearAutomcdAndCreateTabs()
5137+
au TabClosedPre * new X | new Y | new Z
5138+
call assert_fails('tabclose 1', 'E242')
5139+
5140+
" Clean up
5141+
au!
5142+
only
5143+
tabonly
5144+
bw!
5145+
endfunc
5146+
49895147
" vim: shiftwidth=2 sts=2 expandtab

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,8 @@ static char *(features[]) =
704704

705705
static int included_patches[] =
706706
{ /* Add new patch number below this line */
707+
/**/
708+
1202,
707709
/**/
708710
1201,
709711
/**/

src/vim.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,7 @@ enum auto_event
14351435
EVENT_SWAPEXISTS, // found existing swap file
14361436
EVENT_SYNTAX, // syntax selected
14371437
EVENT_TABCLOSED, // after closing a tab page
1438+
EVENT_TABCLOSEDPRE, // before closing a tab page
14381439
EVENT_TABENTER, // after entering a tab page
14391440
EVENT_TABLEAVE, // before leaving a tab page
14401441
EVENT_TABNEW, // when entering a new tab page

src/window.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2978,6 +2978,33 @@ trigger_winclosed(win_T *win)
29782978
recursive = FALSE;
29792979
}
29802980

2981+
static void
2982+
trigger_tabclosedpre(tabpage_T *tp)
2983+
{
2984+
static int recursive = FALSE;
2985+
tabpage_T *ptp = curtab;
2986+
2987+
// Quickly return when no TabClosedPre autocommands to be executed or
2988+
// already executing
2989+
if (!has_tabclosedpre() || recursive)
2990+
return;
2991+
2992+
if (valid_tabpage(tp))
2993+
goto_tabpage_tp(tp, FALSE, FALSE);
2994+
recursive = TRUE;
2995+
window_layout_lock();
2996+
apply_autocmds(EVENT_TABCLOSEDPRE, NULL, NULL, FALSE, NULL);
2997+
window_layout_unlock();
2998+
recursive = FALSE;
2999+
// tabpage may have been modified or deleted by autocmds
3000+
if (valid_tabpage(ptp))
3001+
// try to recover the tappage first
3002+
goto_tabpage_tp(ptp, FALSE, FALSE);
3003+
else
3004+
// fall back to the first tappage
3005+
goto_tabpage_tp(first_tabpage, FALSE, FALSE);
3006+
}
3007+
29813008
/*
29823009
* Make a snapshot of all the window scroll positions and sizes of the current
29833010
* tab page.
@@ -3353,6 +3380,14 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
33533380
return;
33543381
}
33553382

3383+
if (tp->tp_firstwin == tp->tp_lastwin)
3384+
{
3385+
trigger_tabclosedpre(tp);
3386+
// autocmd may have freed the window already.
3387+
if (!win_valid_any_tab(win))
3388+
return;
3389+
}
3390+
33563391
if (win->w_buffer != NULL)
33573392
// Close the link to the buffer.
33583393
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0,

0 commit comments

Comments
 (0)