]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1202: Missing TabClosedPre autocommand v9.1.1202
authorJim Zhou <jimzhouzzy@gmail.com>
Thu, 13 Mar 2025 20:58:25 +0000 (21:58 +0100)
committerChristian Brabandt <cb@256bit.org>
Thu, 13 Mar 2025 20:58:25 +0000 (21:58 +0100)
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>
runtime/doc/autocmd.txt
runtime/doc/tags
runtime/doc/version9.txt
runtime/syntax/vim.vim
src/autocmd.c
src/proto/autocmd.pro
src/testdir/test_autocmd.vim
src/version.c
src/vim.h
src/window.c

index 8532fc95624ad685b55ad59c5ba36bb9374593e8..372d1acbc34f1dd0ffdb4323a9f7b9561122e52c 100644 (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
index a91b4af0b1d5118e0050bc6ac07736cbd33b5bd4..3bd49d90d65369048a379191fbf683bb17d3245e 100644 (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*
index f12bcd4c354e05679d72b1c90fd75e7711e62f33..6d41e342fb60a26c8108ad9feb1f823d171b804e 100644 (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
index f537e039797a2c890f0f19aa39ce437ea5fa7a16..d469134db14e05a88b1d4aba1397bdcea36db049 100644 (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
index f0b573bf5d837a0251bd4c7bb41229454f90506a..392168269a71a5612b1b2ea26cd285c1989a5d6f 100644 (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)
 /*
index ed85603e3cd18d637a54cb48326292ef5db7ec8f..ff42b600227a44df9c91e1f219667787fba7b7d5 100644 (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 : */
index 54558b043a83a552c1ab04af94d1033f4623d85f..138b852f095e8d882cfc77c2ab4e1b8f0bf66e59 100644 (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
index b9fcedc01d67c4c14b581aef1919df090281fd8e..c129404aa3353e8eb5eebe244c10a3fcf22c1255 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1202,
 /**/
     1201,
 /**/
index 212b7e774fbd5cb01b82a6b53d08674bfff51b10..85fad6ce41228f7102ce5f530f129f1cb4b850b9 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -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
index cc76f79516904ba3f8d0dcbc5257d2dc3a8d53af..cce7f4c797a85d13f9f933c402ddb2d2432888ec 100644 (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,