From 92596a37e721c5d4cf4327e9c5348b0355ec29d0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 14 Jan 2026 09:21:35 +0800 Subject: [PATCH] vim-patch:partial:9.0.0907: restoring window after WinScrolled may fail Problem: Restoring window after WinScrolled may fail. Solution: Lock the window layout when triggering WinScrolled. https://github.com/vim/vim/commit/d63a85592cef0ee4f0fec5efe2f8d66b31f01f05 Only check close_disallowed in window_layout_locked() for now. Also don't check window_layout_locked() when closing a floating window, as it's not checked when creating a floating window. Co-authored-by: Bram Moolenaar --- src/nvim/errors.h | 2 ++ src/nvim/ex_docmd.c | 35 ++++++++++++++++++++++++++--------- src/nvim/window.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/nvim/errors.h b/src/nvim/errors.h index 4ebf6d40c7..2982ee62e9 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -195,6 +195,8 @@ EXTERN const char e_stray_closing_curly_str[] INIT(= N_("E1278: Stray '}' without a matching '{': %s")); EXTERN const char e_missing_close_curly_str[] INIT(= N_("E1279: Missing '}': %s")); +EXTERN const char e_not_allowed_to_change_window_layout_in_this_autocmd[] +INIT(= N_("E1312: Not allowed to change the window layout in this autocmd")); EXTERN const char e_val_too_large[] INIT(= N_("E1510: Value too large: %s")); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index e15c1d8b45..125646e348 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5033,6 +5033,9 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp) emsg(_(e_autocmd_close)); return; } + if (!win->w_floating && window_layout_locked()) { + return; + } buf_T *buf = win->w_buffer; @@ -5074,6 +5077,10 @@ static void ex_tabclose(exarg_T *eap) return; } + if (window_layout_locked()) { + return; + } + int tab_number = get_tabpage_arg(eap); if (eap->errmsg != NULL) { return; @@ -5105,6 +5112,10 @@ static void ex_tabonly(exarg_T *eap) return; } + if (window_layout_locked()) { + return; + } + int tab_number = get_tabpage_arg(eap); if (eap->errmsg != NULL) { return; @@ -5175,9 +5186,12 @@ void tabpage_close_other(tabpage_T *tp, int forceit) /// ":only". static void ex_only(exarg_T *eap) { - win_T *wp; + if (window_layout_locked()) { + return; + } if (eap->addr_count > 0) { + win_T *wp; linenr_T wnr = eap->line2; for (wp = firstwin; --wnr > 0;) { if (wp->w_next == NULL) { @@ -5185,11 +5199,9 @@ static void ex_only(exarg_T *eap) } wp = wp->w_next; } - } else { - wp = curwin; - } - if (wp != curwin) { - win_goto(wp); + if (wp != curwin) { + win_goto(wp); + } } close_others(true, eap->forceit); } @@ -5201,11 +5213,11 @@ static void ex_hide(exarg_T *eap) return; } + win_T *win = NULL; if (eap->addr_count == 0) { - win_close(curwin, false, eap->forceit); // don't free buffer + win = curwin; } else { int winnr = 0; - win_T *win = NULL; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { winnr++; @@ -5217,8 +5229,13 @@ static void ex_hide(exarg_T *eap) if (win == NULL) { win = lastwin; } - win_close(win, false, eap->forceit); } + + if (!win->w_floating && window_layout_locked()) { + return; + } + + win_close(win, false, eap->forceit); // don't free buffer } /// ":stop" and ":suspend": Suspend Vim. diff --git a/src/nvim/window.c b/src/nvim/window.c index 2e1abf9a00..1514c26558 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -108,6 +108,38 @@ static char *m_onlyone = N_("Already only one window"); /// autocommands mess up the window structure. static int split_disallowed = 0; +/// When non-zero closing a window is forbidden. Used to avoid that nasty +/// autocommands mess up the window structure. +static int close_disallowed = 0; + +/// Disallow changing the window layout (split window, close window, move +/// window). Resizing is still allowed. +/// Used for autocommands that temporarily use another window and need to +/// make sure the previously selected window is still there. +/// Must be matched with exactly one call to window_layout_unlock()! +static void window_layout_lock(void) +{ + split_disallowed++; + close_disallowed++; +} + +static void window_layout_unlock(void) +{ + split_disallowed--; + close_disallowed--; +} + +/// When the window layout cannot be changed give an error and return true. +bool window_layout_locked(void) +{ + // if (split_disallowed > 0 || close_disallowed > 0) { + if (close_disallowed > 0) { + emsg(_(e_not_allowed_to_change_window_layout_in_this_autocmd)); + return true; + } + return false; +} + // #define WIN_DEBUG #ifdef WIN_DEBUG /// Call this method to log the current window layout. @@ -2739,6 +2771,9 @@ int win_close(win_T *win, bool free_buf, bool force) emsg(_(e_cannot_close_last_window)); return FAIL; } + if (!win->w_floating && window_layout_locked()) { + return FAIL; + } if (win_locked(win) || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { @@ -4315,6 +4350,9 @@ int win_new_tabpage(int after, char *filename) emsg(_(e_cmdwin)); return FAIL; } + if (window_layout_locked()) { + return FAIL; + } tabpage_T *newtp = alloc_tabpage();