aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2024-03-10 07:03:36 +0800
committerGitHub <noreply@github.com>2024-03-10 07:03:36 +0800
commit448cf10c47e0678cee080baaf75f395511e13269 (patch)
treeaa95dbd765da98a32d9638404a40addfa7cf292f
parent241c16129919e169b71ef1e788420224b358fbb3 (diff)
downloadrneovim-448cf10c47e0678cee080baaf75f395511e13269.tar.gz
rneovim-448cf10c47e0678cee080baaf75f395511e13269.tar.bz2
rneovim-448cf10c47e0678cee080baaf75f395511e13269.zip
vim-patch:9.1.0159: Crash in WinClosed after BufUnload closes other windows (#27792)
Problem: Crash in WinClosed after BufUnload closes other windows Solution: Don't trigger WinClosed if the buffer is NULL (zeertzjq) Now win_close_othertab() doesn't trigger any autocommands if the buffer is NULL, so remove the autocmd blocking above (which was added not long ago in patch v9.0.0550) for consistency. Also remove an unreachable close_last_window_tabpage() above: - It is only reached if only_one_window() returns TRUE and last_window() returns FALSE. - If only_one_window() returns TRUE, there is only one tabpage. - If there is only one tabpage and last_window() returns FALSE, the one_window() in last_window() must return FALSE, and the ONE_WINDOW in close_last_window_tabpage() must also be FALSE. - So close_last_window_tabpage() doesn't do anything and returns FALSE. Then the curtab != prev_curtab check also doesn't make much sense, and the only_one_window() can be replaced with a check for popup and a call to last_window() since this is a stricter check than only_one_window(). closes: vim/vim#14166 https://github.com/vim/vim/commit/b2ec0da080fb24f12a8d6f54bd7318a078ca4e6c
-rw-r--r--src/nvim/window.c23
-rw-r--r--test/old/testdir/test_autocmd.vim21
2 files changed, 32 insertions, 12 deletions
diff --git a/src/nvim/window.c b/src/nvim/window.c
index ecd2e83500..6c7ca86636 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2546,7 +2546,7 @@ bool can_close_in_cmdwin(win_T *win, Error *err)
/// @param prev_curtab previous tabpage that will be closed if "win" is the
/// last window in the tabpage
///
-/// @return true when the window was closed already.
+/// @return false if there are other windows and nothing is done, true otherwise.
static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev_curtab)
FUNC_ATTR_NONNULL_ARG(1)
{
@@ -2751,10 +2751,8 @@ int win_close(win_T *win, bool free_buf, bool force)
win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, true);
- if (only_one_window() && win_valid(win) && win->w_buffer == NULL
- && (last_window(win) || curtab != prev_curtab
- || close_last_window_tabpage(win, free_buf, prev_curtab))
- && !win->w_floating) {
+ if (win_valid(win) && win->w_buffer == NULL
+ && !win->w_floating && last_window(win)) {
// Autocommands have closed all windows, quit now. Restore
// curwin->w_buffer, otherwise writing ShaDa file may fail.
if (curwin->w_buffer == NULL) {
@@ -2766,10 +2764,7 @@ int win_close(win_T *win, bool free_buf, bool force)
if (curtab != prev_curtab && win_valid_any_tab(win)
&& win->w_buffer == NULL) {
// Need to close the window anyway, since the buffer is NULL.
- // Don't trigger autocmds with a NULL buffer.
- block_autocmds();
win_close_othertab(win, false, prev_curtab);
- unblock_autocmds();
return FAIL;
}
@@ -2940,10 +2935,14 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
}
// Fire WinClosed just before starting to free window-related resources.
- do_autocmd_winclosed(win);
- // autocmd may have freed the window already.
- if (!win_valid_any_tab(win)) {
- return;
+ // If the buffer is NULL, it isn't safe to trigger autocommands,
+ // and win_close() should have already triggered WinClosed.
+ if (win->w_buffer != NULL) {
+ do_autocmd_winclosed(win);
+ // autocmd may have freed the window already.
+ if (!win_valid_any_tab(win)) {
+ return;
+ }
}
if (win->w_buffer != NULL) {
diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim
index 4d88573a1f..2b37ccf4a6 100644
--- a/test/old/testdir/test_autocmd.vim
+++ b/test/old/testdir/test_autocmd.vim
@@ -740,6 +740,27 @@ func Test_WinClosed_switch_tab()
%bwipe!
endfunc
+" This used to trigger WinClosed twice for the same window, and the window's
+" buffer was NULL in the second autocommand.
+func Test_WinClosed_BufUnload_close_other()
+ tabnew
+ let g:tab = tabpagenr()
+ let g:buf = bufnr()
+ new
+ setlocal bufhidden=wipe
+ augroup test-WinClosed
+ autocmd BufUnload * ++once exe g:buf .. 'bwipe!'
+ autocmd WinClosed * call tabpagebuflist(g:tab)
+ augroup END
+ close
+
+ unlet g:tab
+ unlet g:buf
+ autocmd! test-WinClosed
+ augroup! test-WinClosed
+ %bwipe!
+endfunc
+
func s:AddAnAutocmd()
augroup vimBarTest
au BufReadCmd * echo 'hello'