diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 5d732e6948..66d3c2826d 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1232,6 +1232,18 @@ WinLeave Before leaving a window. If the window to be WinLeave autocommands (but not for ":new"). Not used for ":qa" or ":q" when exiting Vim. Before WinClosed. + *WinNewPre* +WinNewPre Before creating a new window. Triggered + before commands that modify window layout by + creating a split or new tab page. Not done for + the first window, when Vim has just started. + It is not allowed to modify window layout + while executing commands for the WinNewPre + event. + Most useful to store current window layout + and compare it with the new layout after the + Window has been created. + *WinNew* WinNew When a new window was created. Not done for the first window, when Vim has just started. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 5c2d00f564..49aca166e9 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2580,7 +2580,8 @@ A jump table for the options with a short description can be found at |Q_op|. |VimResized|, |VimResume|, |VimSuspend|, - |WinNew| + |WinNew|, + |WinNewPre| *'expandtab'* *'et'* *'noexpandtab'* *'noet'* 'expandtab' 'et' boolean (default off) diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 2cd6514f78..91914538e6 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -218,6 +218,7 @@ error('Cannot require a meta file') --- |'WinEnter' --- |'WinLeave' --- |'WinNew' +--- |'WinNewPre' --- |'WinResized' --- |'WinScrolled' diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 319b80303c..81c894af77 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -2258,7 +2258,8 @@ vim.go.ei = vim.go.eventignore --- `VimResized`, --- `VimResume`, --- `VimSuspend`, ---- `WinNew` +--- `WinNew`, +--- `WinNewPre` --- --- @type string vim.o.eventignorewin = "" diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 4b0167f86b..adeb453263 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -138,7 +138,8 @@ return { WinClosed = true, -- after closing a window WinEnter = true, -- after entering a window WinLeave = true, -- before leaving a window - WinNew = false, -- when entering a new window + WinNewPre = false, -- before creating a new window + WinNew = false, -- after creating a new window WinResized = true, -- after a window was resized WinScrolled = true, -- after a window was scrolled or resized }, diff --git a/src/nvim/window.c b/src/nvim/window.c index 4142cd0053..68f258b793 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1127,6 +1127,8 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_fl return NULL; } + trigger_winnewpre(); + win_T *oldwin; if (flags & WSP_TOP) { oldwin = firstwin; @@ -3069,6 +3071,13 @@ int win_close(win_T *win, bool free_buf, bool force) return OK; } +static void trigger_winnewpre(void) +{ + window_layout_lock(); + apply_autocmds(EVENT_WINNEWPRE, NULL, NULL, false, NULL); + window_layout_unlock(); +} + static void do_autocmd_winclosed(win_T *win) FUNC_ATTR_NONNULL_ALL { @@ -4380,6 +4389,8 @@ int win_new_tabpage(int after, char *filename) curtab = newtp; + trigger_winnewpre(); + // Create a new empty window. if (win_alloc_firstwin(old_curtab->tp_curwin) == OK) { // Make the new Tab page the new topframe. diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim index b2df2c3bf8..b69db59b86 100644 --- a/test/old/testdir/test_autocmd.vim +++ b/test/old/testdir/test_autocmd.vim @@ -277,6 +277,7 @@ func Test_win_tab_autocmd() let g:record = [] augroup testing + au WinNewPre * call add(g:record, 'WinNewPre') au WinNew * call add(g:record, 'WinNew') au WinClosed * call add(g:record, 'WinClosed') au WinEnter * call add(g:record, 'WinEnter') @@ -293,8 +294,8 @@ func Test_win_tab_autocmd() close call assert_equal([ - \ 'WinLeave', 'WinNew', 'WinEnter', - \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter', + \ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter', + \ 'WinLeave', 'TabLeave', 'WinNewPre', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter', \ 'WinLeave', 'TabLeave', 'WinClosed', 'TabClosed', 'WinEnter', 'TabEnter', \ 'WinLeave', 'WinClosed', 'WinEnter' \ ], g:record) @@ -305,17 +306,96 @@ func Test_win_tab_autocmd() bwipe somefile call assert_equal([ - \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter', + \ 'WinLeave', 'TabLeave', 'WinNewPre', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter', \ 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', \ 'WinClosed', 'TabClosed' \ ], g:record) + let g:record = [] + copen + help + tabnext + vnew + + call assert_equal([ + \ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter', + \ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter', + \ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter' + \ ], g:record) + augroup testing au! augroup END unlet g:record endfunc +func Test_WinNewPre() + " Test that the old window layout can be accessed before a new window is created. + let g:layouts_pre = [] + let g:layouts_post = [] + augroup testing + au WinNewPre * call add(g:layouts_pre, winlayout()) + au WinNew * call add(g:layouts_post, winlayout()) + augroup END + split + call assert_notequal(g:layouts_pre[0], g:layouts_post[0]) + split + call assert_equal(g:layouts_pre[1], g:layouts_post[0]) + call assert_notequal(g:layouts_pre[1], g:layouts_post[1]) + tabnew + call assert_notequal(g:layouts_pre[2], g:layouts_post[1]) + call assert_notequal(g:layouts_pre[2], g:layouts_post[2]) + augroup testing + au! + augroup END + unlet g:layouts_pre + unlet g:layouts_post + + " Test modifying window layout during WinNewPre throws. + let g:caught = 0 + augroup testing + au! + au WinNewPre * split + augroup END + try + vnew + catch + let g:caught += 1 + endtry + augroup testing + au! + au WinNewPre * tabnew + augroup END + try + vnew + catch + let g:caught += 1 + endtry + augroup testing + au! + au WinNewPre * close + augroup END + try + vnew + catch + let g:caught += 1 + endtry + augroup testing + au! + au WinNewPre * tabclose + augroup END + try + vnew + catch + let g:caught += 1 + endtry + call assert_equal(4, g:caught) + augroup testing + au! + augroup END + unlet g:caught +endfunc + func Test_WinResized() CheckRunVimInTerminal