# HG changeset patch # User Bram Moolenaar # Date 1339002165 -7200 # Node ID e7ff3251dfa157284a48c5322783499d079e3eb0 # Parent 6707a6a1ff9c95f6241c69433b93d4c30545ac3c updated for version 7.3.545 Problem: When closing a window or buffer autocommands may close it too, causing problems for where the autocommand was invoked from. Solution: Add the w_closing and b_closing flags. When set disallow ":q" and ":close" to prevent recursive closing. diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -377,28 +377,35 @@ close_buffer(win, buf, action, abort_if_ /* When the buffer is no longer in a window, trigger BufWinLeave */ if (buf->b_nwindows == 1) { + buf->b_closing = TRUE; apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, FALSE, buf); - /* Return if autocommands deleted the buffer or made it the only one. */ - if (!buf_valid(buf) || (abort_if_last && one_window())) + if (!buf_valid(buf)) { + /* Autocommands deleted the buffer. */ +aucmd_abort: EMSG(_(e_auabort)); return; } + buf->b_closing = FALSE; + if (abort_if_last && one_window()) + /* Autocommands made this the only window. */ + goto aucmd_abort; /* When the buffer becomes hidden, but is not unloaded, trigger * BufHidden */ if (!unload_buf) { + buf->b_closing = TRUE; apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, FALSE, buf); - /* Return if autocommands deleted the buffer or made it the only - * one. */ - if (!buf_valid(buf) || (abort_if_last && one_window())) - { - EMSG(_(e_auabort)); - return; - } + if (!buf_valid(buf)) + /* Autocommands deleted the buffer. */ + goto aucmd_abort; + buf->b_closing = FALSE; + if (abort_if_last && one_window()) + /* Autocommands made this the only window. */ + goto aucmd_abort; } # ifdef FEAT_EVAL if (aborting()) /* autocmds may abort script processing */ @@ -552,6 +559,7 @@ buf_freeall(buf, flags) #ifdef FEAT_AUTOCMD int is_curbuf = (buf == curbuf); + buf->b_closing = TRUE; apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf); if (!buf_valid(buf)) /* autocommands may delete the buffer */ return; @@ -568,6 +576,7 @@ buf_freeall(buf, flags) if (!buf_valid(buf)) /* autocommands may delete the buffer */ return; } + buf->b_closing = FALSE; # ifdef FEAT_EVAL if (aborting()) /* autocmds may abort script processing */ return; @@ -1150,6 +1159,9 @@ do_buffer(action, start, dir, count, for * a window with this buffer. */ while (buf == curbuf +# ifdef FEAT_AUTOCMD + && !(curwin->w_closing || curwin->w_buffer->b_closing) +# endif && (firstwin != lastwin || first_tabpage->tp_next != NULL)) win_close(curwin, FALSE); #endif @@ -4750,7 +4762,11 @@ ex_buffer_all(eap) #ifdef FEAT_WINDOWS || (had_tab > 0 && wp != firstwin) #endif - ) && firstwin != lastwin) + ) && firstwin != lastwin +#ifdef FEAT_AUTOCMD + && !(wp->w_closing || wp->w_buffer->b_closing) +#endif + ) { win_close(wp, FALSE); #ifdef FEAT_AUTOCMD diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -6459,7 +6459,9 @@ ex_quit(eap) } #ifdef FEAT_AUTOCMD apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - if (curbuf_locked()) + /* Refuse to quick when locked or when the buffer in the last window is + * being closed (can only happen in autocommands). */ + if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) return; #endif diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1201,6 +1201,10 @@ struct dictvar_S typedef struct qf_info_S qf_info_T; #endif +/* + * These are items normally related to a buffer. But when using ":ownsyntax" + * a window may have its own instance. + */ typedef struct { #ifdef FEAT_SYN_HL hashtab_T b_keywtab; /* syntax keywords hash table */ @@ -1290,6 +1294,10 @@ struct file_buffer int b_nwindows; /* nr of windows open on this buffer */ int b_flags; /* various BF_ flags */ +#ifdef FEAT_AUTOCMD + int b_closing; /* buffer is being closed, don't let + autocommands close it too. */ +#endif /* * b_ffname has the full path of the file (NULL for no name). @@ -1853,6 +1861,10 @@ struct window_S win_T *w_prev; /* link to previous window */ win_T *w_next; /* link to next window */ #endif +#ifdef FEAT_AUTOCMD + int w_closing; /* window is being closed, don't let + autocommands close it too. */ +#endif frame_T *w_frame; /* frame containing this window */ diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -715,6 +715,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 545, +/**/ 544, /**/ 543, diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -2034,7 +2034,11 @@ close_windows(buf, keep_curwin) for (wp = firstwin; wp != NULL && lastwin != firstwin; ) { - if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)) + if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) +#ifdef FEAT_AUTOCMD + && !(wp->w_closing || wp->w_buffer->b_closing) +#endif + ) { win_close(wp, FALSE); @@ -2051,7 +2055,11 @@ close_windows(buf, keep_curwin) nexttp = tp->tp_next; if (tp != curtab) for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer == buf) + if (wp->w_buffer == buf +#ifdef FEAT_AUTOCMD + && !(wp->w_closing || wp->w_buffer->b_closing) +#endif + ) { win_close_othertab(wp, FALSE, tp); @@ -2168,6 +2176,8 @@ win_close(win, free_buf) } #ifdef FEAT_AUTOCMD + if (win->w_closing || win->w_buffer->b_closing) + return; /* window is already being closed */ if (win == aucmd_win) { EMSG(_("E813: Cannot close autocmd window")); @@ -2203,17 +2213,26 @@ win_close(win, free_buf) wp = frame2win(win_altframe(win, NULL)); /* - * Be careful: If autocommands delete the window, return now. + * Be careful: If autocommands delete the window or cause this window + * to be the last one left, return now. */ if (wp->w_buffer != curbuf) { other_buffer = TRUE; + win->w_closing = TRUE; apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (!win_valid(win) || last_window()) + if (!win_valid(win)) + return; + win->w_closing = FALSE; + if (last_window()) return; } + win->w_closing = TRUE; apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); - if (!win_valid(win) || last_window()) + if (!win_valid(win)) + return; + win->w_closing = FALSE; + if (last_window()) return; # ifdef FEAT_EVAL /* autocmds may abort script processing */ @@ -2240,7 +2259,16 @@ win_close(win, free_buf) * Close the link to the buffer. */ if (win->w_buffer != NULL) - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, TRUE); + { +#ifdef FEAT_AUTOCMD + win->w_closing = TRUE; +#endif + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, FALSE); +#ifdef FEAT_AUTOCMD + if (win_valid(win)) + win->w_closing = FALSE; +#endif + } /* Autocommands may have closed the window already, or closed the only * other window or moved to another tab page. */ @@ -2346,6 +2374,11 @@ win_close_othertab(win, free_buf, tp) tabpage_T *ptp = NULL; int free_tp = FALSE; +#ifdef FEAT_AUTOCMD + if (win->w_closing || win->w_buffer->b_closing) + return; /* window is already being closed */ +#endif + /* Close the link to the buffer. */ close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, FALSE);