diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index cbcdd4036e..6957c2a9b6 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -818,27 +818,49 @@ 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 +" This used to trigger WinClosed/WinLeave/BufLeave twice for the same window, +" and the window's buffer was NULL in the second autocommand. +func Run_test_BufUnload_close_other(extra_cmd) + let oldtab = tabpagenr() + tabnew Xb1 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) + let g:w1 = win_getid() + new Xb2 + let g:w2 = win_getid() + let g:log = [] + exe a:extra_cmd + + augroup test-BufUnload-close-other + autocmd BufUnload * ++nested ++once bwipe! Xb1 + for event in ['WinClosed', 'BufLeave', 'WinLeave', 'TabLeave'] + exe $'autocmd {event} * call tabpagebuflist(g:tab)' + exe $'autocmd {event} * let g:log += ["{event}:" .. expand("")]' + endfor augroup END + close + " WinClosed is triggered once for each of the 2 closed windows. + " Others are only triggered once. + call assert_equal(['BufLeave:Xb2', 'WinLeave:Xb2', $'WinClosed:{g:w2}', + \ $'WinClosed:{g:w1}', 'TabLeave:Xb2'], g:log) + call assert_equal(oldtab, tabpagenr()) + call assert_equal([0, 0], win_id2tabwin(g:w1)) + call assert_equal([0, 0], win_id2tabwin(g:w2)) unlet g:tab - unlet g:buf - autocmd! test-WinClosed - augroup! test-WinClosed + unlet g:w1 + unlet g:w2 + unlet g:log + autocmd! test-BufUnload-close-other + augroup! test-BufUnload-close-other %bwipe! endfunc +func Test_BufUnload_close_other() + call Run_test_BufUnload_close_other('') + call Run_test_BufUnload_close_other('setlocal bufhidden=wipe') +endfunc + func s:AddAnAutocmd() augroup vimBarTest au BufReadCmd * echo 'hello' diff --git a/src/version.c b/src/version.c index 4864220d3a..2e0c771771 100644 --- a/src/version.c +++ b/src/version.c @@ -724,6 +724,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1773, /**/ 1772, /**/ diff --git a/src/window.c b/src/window.c index 723195a0f7..f071a0e7d7 100644 --- a/src/window.c +++ b/src/window.c @@ -2605,10 +2605,12 @@ close_last_window_tabpage( * page and then close the window and the tab page. This avoids that * curwin and curtab are invalid while we are freeing memory, they may * be used in GUI events. - * Don't trigger autocommands yet, they may use wrong values, so do + * Don't trigger *Enter autocommands yet, they may use wrong values, so do * that below. + * Do trigger *Leave autocommands, unless win->w_buffer is NULL, in which + * case they have already been triggered. */ - goto_tabpage_tp(alt_tabpage(), FALSE, TRUE); + goto_tabpage_tp(alt_tabpage(), FALSE, win->w_buffer != NULL); // Safety check: Autocommands may have closed the window when jumping // to the other tab page. @@ -2906,6 +2908,7 @@ win_close(win_T *win, int free_buf) win_comp_pos(); win_fix_scroll(FALSE); } + if (close_curwin) { // Pass WEE_ALLOW_PARSE_MESSAGES to decrement dont_parse_messages @@ -2923,6 +2926,13 @@ win_close(win_T *win, int free_buf) apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); } + if (ONE_WINDOW && curwin->w_locked && curbuf->b_locked_split + && first_tabpage->tp_next != NULL) + // The new curwin is the last window in the current tab page, and it is + // already being closed. Trigger TabLeave now, as after its buffer is + // removed it's no longer safe to do that. + apply_autocmds(EVENT_TABLEAVE, NULL, NULL, FALSE, curbuf); + --split_disallowed; #ifdef MESSAGE_QUEUE if (!did_decrement)