vim-patch:9.1.0059: No event triggered before creating a window

Problem:  No event is triggered before creating a window.
          (Sergey Vlasov)
Solution: Add the WinNewPre event (Sergey Vlasov)

fixes: vim/vim#10635
closes: vim/vim#12761

1f47db75fd

Not sure if this should be triggered before creating a floating window,
as its use case is related to window layout.

Co-authored-by: Sergey Vlasov <sergey@vlasov.me>
This commit is contained in:
zeertzjq
2026-01-14 15:37:16 +08:00
parent 992eaac082
commit d6e5286d2c
7 changed files with 113 additions and 6 deletions

View File

@@ -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.

View File

@@ -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)

View File

@@ -218,6 +218,7 @@ error('Cannot require a meta file')
--- |'WinEnter'
--- |'WinLeave'
--- |'WinNew'
--- |'WinNewPre'
--- |'WinResized'
--- |'WinScrolled'

View File

@@ -2258,7 +2258,8 @@ vim.go.ei = vim.go.eventignore
--- `VimResized`,
--- `VimResume`,
--- `VimSuspend`,
--- `WinNew`
--- `WinNew`,
--- `WinNewPre`
---
--- @type string
vim.o.eventignorewin = ""

View File

@@ -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
},

View File

@@ -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.

View File

@@ -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