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: #10635
closes: #12761

Signed-off-by: Sergey Vlasov <sergey@vlasov.me>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/autocmd.c b/src/autocmd.c
index 80ce8ca..7e4a1b2 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -188,6 +188,7 @@
     {"VimEnter",	EVENT_VIMENTER},
     {"VimLeave",	EVENT_VIMLEAVE},
     {"VimLeavePre",	EVENT_VIMLEAVEPRE},
+    {"WinNewPre",	EVENT_WINNEWPRE},
     {"WinNew",		EVENT_WINNEW},
     {"WinClosed",	EVENT_WINCLOSED},
     {"WinEnter",	EVENT_WINENTER},
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index cecb55a..be73c18 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -270,6 +270,7 @@
   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')
@@ -286,8 +287,8 @@
   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)
@@ -298,17 +299,96 @@
   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
 
diff --git a/src/version.c b/src/version.c
index 10a70e3..841d018 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    59,
+/**/
     58,
 /**/
     57,
diff --git a/src/vim.h b/src/vim.h
index 0f52ba2..fe23958 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1435,7 +1435,8 @@
     EVENT_VIMRESIZED,		// after Vim window was resized
     EVENT_WINENTER,		// after entering a window
     EVENT_WINLEAVE,		// before leaving a window
-    EVENT_WINNEW,		// when entering a new window
+    EVENT_WINNEWPRE,		// before creating a new window
+    EVENT_WINNEW,		// after creating 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 fda42e9..5cb6c3c 100644
--- a/src/window.c
+++ b/src/window.c
@@ -19,6 +19,7 @@
 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_winnewpre(void);
 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);
@@ -955,6 +956,8 @@
     // Do not redraw here, curwin->w_buffer may be invalid.
     ++RedrawingDisabled;
 
+    trigger_winnewpre();
+
     if (flags & WSP_TOP)
 	oldwin = firstwin;
     else if (flags & WSP_BOT)
@@ -2887,6 +2890,14 @@
 }
 
     static void
+trigger_winnewpre(void)
+{
+    window_layout_lock();
+    apply_autocmds(EVENT_WINNEWPRE, NULL, NULL, FALSE, NULL);
+    window_layout_unlock();
+}
+
+    static void
 trigger_winclosed(win_T *win)
 {
     static int	recursive = FALSE;
@@ -4477,6 +4488,9 @@
 
     newtp->tp_localdir = (tp->tp_localdir == NULL)
 				    ? NULL : vim_strsave(tp->tp_localdir);
+
+    trigger_winnewpre();
+
     // Create a new empty window.
     if (win_alloc_firstwin(tp->tp_curwin) == OK)
     {