patch 9.1.1323: b:undo_ftplugin not executed when re-using buffer

Problem:  b:undo_ftplugin not executed when re-using buffer
          (archy3)
Solution: explicitly execute b:undo_ftplugin in buflist_new() when
          re-using the current buffer

fixes: #17113
closes: #17133

Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/buffer.c b/src/buffer.c
index eed3e8d..0624f9d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -72,6 +72,20 @@
 static int	top_file_num = 1;	// highest file number
 static garray_T buf_reuse = GA_EMPTY;	// file numbers to recycle
 
+    static void
+trigger_undo_ftplugin(buf_T *buf, win_T *win)
+{
+    window_layout_lock();
+    buf->b_locked++;
+    win->w_locked = TRUE;
+    // b:undo_ftplugin may be set, undo it
+    do_cmdline_cmd((char_u*)"if exists('b:undo_ftplugin') | :legacy :exe \
+	    b:undo_ftplugin | endif");
+    buf->b_locked--;
+    win->w_locked = FALSE;
+    window_layout_unlock();
+}
+
 /*
  * Calculate the percentage that `part` is of the `whole`.
  */
@@ -2206,6 +2220,7 @@
     if ((flags & BLN_CURBUF) && curbuf_reusable())
     {
 	buf = curbuf;
+	trigger_undo_ftplugin(buf, curwin);
 	// It's like this buffer is deleted.  Watch out for autocommands that
 	// change curbuf!  If that happens, allocate a new buffer anyway.
 	buf_freeall(buf, BFA_WIPE | BFA_DEL);
diff --git a/src/proto/window.pro b/src/proto/window.pro
index ccb69ef..d92a5af 100644
--- a/src/proto/window.pro
+++ b/src/proto/window.pro
@@ -1,4 +1,6 @@
 /* window.c */
+void window_layout_lock(void);
+void window_layout_unlock(void);
 int window_layout_locked(enum CMD_index cmd);
 int check_can_set_curbuf_disabled(void);
 int check_can_set_curbuf_forceit(int forceit);
diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim
index 6ec55ae..1411559 100644
--- a/src/testdir/test_filetype.vim
+++ b/src/testdir/test_filetype.vim
@@ -1131,6 +1131,34 @@
   close
 endfunc
 
+func Test_undo_ftplugin_on_buffer_reuse()
+  filetype on
+
+  new
+  let b:undo_ftplugin = ":let g:var='exists'"
+  let g:bufnr = bufnr('%')
+  " no changes done to the buffer, so the buffer will be re-used
+  e $VIMRUNTIME/defaults.vim
+  call assert_equal(g:bufnr, bufnr('%'))
+  call assert_equal('exists', get(g:, 'var', 'fail'))
+  unlet! g:bufnr g:var
+
+  " try to wipe the buffer
+  enew
+  bw defaults.vim
+  let b:undo_ftplugin = ':bw'
+  call assert_fails(':e $VIMRUNTIME/defaults.vim', 'E937')
+
+  " try to split the window
+  enew
+  bw defaults.vim
+  let b:undo_ftplugin = ':sp $VIMRUNTIME/defaults.vim'
+  call assert_fails(':e $VIMRUNTIME/defaults.vim', 'E242')
+
+  bwipe!
+  filetype off
+endfunc
+
 """""""""""""""""""""""""""""""""""""""""""""""""
 " Tests for specific extensions and filetypes.
 " Keep sorted.
diff --git a/src/version.c b/src/version.c
index 4990e44..db77e45 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1323,
+/**/
     1322,
 /**/
     1321,
diff --git a/src/window.c b/src/window.c
index 21f81e5..9ea6810 100644
--- a/src/window.c
+++ b/src/window.c
@@ -97,14 +97,14 @@
  * make sure the previously selected window is still there.
  * Must be matched with exactly one call to window_layout_unlock()!
  */
-    static void
+    void
 window_layout_lock(void)
 {
     ++split_disallowed;
     ++close_disallowed;
 }
 
-    static void
+    void
 window_layout_unlock(void)
 {
     --split_disallowed;