From 23beefed73aadb243fb67cf944e3d60fe8c038bb Mon Sep 17 00:00:00 2001 From: naohiro ono Date: Sat, 13 Nov 2021 12:38:49 +0000 Subject: [PATCH] patch 8.2.3591: no event is triggered when closing a window Problem: No event is triggered when closing a window. Solution: Add the WinClosed event. (Naohiro Ono, closes #9110) --- runtime/doc/autocmd.txt | 7 ++++++ src/autocmd.c | 4 +++- src/testdir/test_autocmd.vim | 46 +++++++++++++++++++++++++++++++++--- src/version.c | 2 ++ src/vim.h | 1 + src/window.c | 28 ++++++++++++++++++++++ 6 files changed, 84 insertions(+), 4 deletions(-) diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 6a4edf903..fdac7daad 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -348,6 +348,7 @@ Name triggered by ~ |WinNew| after creating a new window |TabNew| after creating a new tab page +|WinClosed| after closing a window |TabClosed| after closing a tab page |WinEnter| after entering another window |WinLeave| before leaving a window @@ -1280,6 +1281,12 @@ VimResume When the Vim instance is resumed after being VimSuspend When the Vim instance is suspended. Only when CTRL-Z was typed inside Vim, not when the SIGSTOP or SIGTSTP signal was sent to Vim. + *WinClosed* +WinClosed After closing a window. The pattern is + matched against the |window-ID|. Both + and are set to the + |window-ID|. Non-recursive (event cannot + trigger itself). *WinEnter* WinEnter After entering another window. Not done for the first window, when Vim has just started. diff --git a/src/autocmd.c b/src/autocmd.c index 1704cd4e3..14cd4af59 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -186,6 +186,7 @@ static struct event_name {"VimLeave", EVENT_VIMLEAVE}, {"VimLeavePre", EVENT_VIMLEAVEPRE}, {"WinNew", EVENT_WINNEW}, + {"WinClosed", EVENT_WINCLOSED}, {"WinEnter", EVENT_WINENTER}, {"WinLeave", EVENT_WINLEAVE}, {"VimResized", EVENT_VIMRESIZED}, @@ -2042,7 +2043,8 @@ apply_autocmds_group( || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST || event == EVENT_DIRCHANGED - || event == EVENT_MODECHANGED) + || event == EVENT_MODECHANGED + || event == EVENT_WINCLOSED) { fname = vim_strsave(fname); autocmd_fname_full = TRUE; // don't expand it later diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 8161183dc..9eb718f9d 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -270,6 +270,7 @@ func Test_win_tab_autocmd() augroup testing au WinNew * call add(g:record, 'WinNew') + au WinClosed * call add(g:record, 'WinClosed') au WinEnter * call add(g:record, 'WinEnter') au WinLeave * call add(g:record, 'WinLeave') au TabNew * call add(g:record, 'TabNew') @@ -286,8 +287,8 @@ func Test_win_tab_autocmd() call assert_equal([ \ 'WinLeave', 'WinNew', 'WinEnter', \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter', - \ 'WinLeave', 'TabLeave', 'TabClosed', 'WinEnter', 'TabEnter', - \ 'WinLeave', 'WinEnter' + \ 'WinLeave', 'TabLeave', 'WinClosed', 'TabClosed', 'WinEnter', 'TabEnter', + \ 'WinLeave', 'WinClosed', 'WinEnter' \ ], g:record) let g:record = [] @@ -298,7 +299,7 @@ func Test_win_tab_autocmd() call assert_equal([ \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter', \ 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', - \ 'TabClosed' + \ 'WinClosed', 'TabClosed' \ ], g:record) augroup testing @@ -307,6 +308,45 @@ func Test_win_tab_autocmd() unlet g:record endfunc +func Test_WinClosed() + " Test that the pattern is matched against the closed window's ID, and both + " and are set to it. + new + let winid = win_getid() + let g:matched = v:false + augroup test-WinClosed + autocmd! + execute 'autocmd WinClosed' winid 'let g:matched = v:true' + autocmd WinClosed * let g:amatch = str2nr(expand('')) + autocmd WinClosed * let g:afile = str2nr(expand('')) + augroup END + close + call assert_true(g:matched) + call assert_equal(winid, g:amatch) + call assert_equal(winid, g:afile) + + " Test that WinClosed is non-recursive. + new + new + call assert_equal(3, winnr('$')) + let g:triggered = 0 + augroup test-WinClosed + autocmd! + autocmd WinClosed * let g:triggered += 1 + autocmd WinClosed * 2 wincmd c + augroup END + close + call assert_equal(1, winnr('$')) + call assert_equal(1, g:triggered) + + autocmd! test-WinClosed + augroup! test-WinClosed + unlet g:matched + unlet g:amatch + unlet g:afile + unlet g:triggered +endfunc + func s:AddAnAutocmd() augroup vimBarTest au BufReadCmd * echo 'hello' diff --git a/src/version.c b/src/version.c index e294d8eb5..ca022bf8b 100644 --- a/src/version.c +++ b/src/version.c @@ -757,6 +757,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3591, /**/ 3590, /**/ diff --git a/src/vim.h b/src/vim.h index 22451c7e4..f86d68d04 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1379,6 +1379,7 @@ enum auto_event EVENT_WINENTER, // after entering a window EVENT_WINLEAVE, // before leaving a window EVENT_WINNEW, // when entering a new window + EVENT_WINCLOSED, // after closing a window EVENT_VIMSUSPEND, // before Vim is suspended EVENT_VIMRESUME, // after Vim is resumed diff --git a/src/window.c b/src/window.c index 5de6ed7f5..226d6c1a4 100644 --- a/src/window.c +++ b/src/window.c @@ -19,6 +19,7 @@ static void win_exchange(long); static void win_rotate(int, int); static void win_totop(int size, int flags); static void win_equal_rec(win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height); +static void trigger_winclosed(win_T *win); static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp); static frame_T *win_altframe(win_T *win, tabpage_T *tp); static tabpage_T *alt_tabpage(void); @@ -2566,6 +2567,13 @@ win_close(win_T *win, int free_buf) if (popup_win_closed(win) && !win_valid(win)) return FAIL; #endif + + // Trigger WinClosed just before starting to free window-related resources. + trigger_winclosed(win); + // autocmd may have freed the window already. + if (!win_valid_any_tab(win)) + return OK; + win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, TRUE); if (only_one_window() && win_valid(win) && win->w_buffer == NULL @@ -2710,6 +2718,20 @@ win_close(win_T *win, int free_buf) return OK; } + static void +trigger_winclosed(win_T *win) +{ + static int recursive = FALSE; + char_u winid[NUMBUFLEN]; + + if (recursive) + return; + recursive = TRUE; + vim_snprintf((char *)winid, sizeof(winid), "%i", win->w_id); + apply_autocmds(EVENT_WINCLOSED, winid, winid, FALSE, win->w_buffer); + recursive = 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, @@ -2731,6 +2753,12 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) && win->w_buffer->b_locked > 0)) return; // window is already being closed + // Trigger WinClosed just before starting to free window-related resources. + trigger_winclosed(win); + // 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, -- 2.50.1