# HG changeset patch # User Bram Moolenaar # Date 1669662005 -3600 # Node ID d8e7d725a666af973182ff4ae6ce9470d82612e5 # Parent 7f766b7e17d58148c7f23dfff140434419ecd741 patch 9.0.0965: using one window for executing autocommands is insufficient Commit: https://github.com/vim/vim/commit/e76062c078debed0df818f70e4db14ad7a7cb53a Author: Bram Moolenaar Date: Mon Nov 28 18:51:43 2022 +0000 patch 9.0.0965: using one window for executing autocommands is insufficient Problem: Using one window for executing autocommands is insufficient. Solution: Use up to five windows for executing autocommands. diff --git a/src/autocmd.c b/src/autocmd.c --- a/src/autocmd.c +++ b/src/autocmd.c @@ -629,28 +629,52 @@ do_augroup(char_u *arg, int del_group) } } + void +autocmd_init(void) +{ + CLEAR_FIELD(aucmd_win); +} + #if defined(EXITFREE) || defined(PROTO) void free_all_autocmds(void) { - int i; char_u *s; for (current_augroup = -1; current_augroup < augroups.ga_len; ++current_augroup) do_autocmd(NULL, (char_u *)"", TRUE); - for (i = 0; i < augroups.ga_len; ++i) + for (int i = 0; i < augroups.ga_len; ++i) { s = ((char_u **)(augroups.ga_data))[i]; if (s != get_deleted_augroup()) vim_free(s); } ga_clear(&augroups); + + for (int i = 0; i < AUCMD_WIN_COUNT; ++i) + if (aucmd_win[i].auc_win_used) + { + aucmd_win[i].auc_win_used = FALSE; + win_remove(aucmd_win[i].auc_win, NULL); + } } #endif /* + * Return TRUE if "win" is an active entry in aucmd_win[]. + */ + int +is_aucmd_win(win_T *win) +{ + for (int i = 0; i < AUCMD_WIN_COUNT; ++i) + if (aucmd_win[i].auc_win_used && aucmd_win[i].auc_win == win) + return TRUE; + return FALSE; +} + +/* * Return the event number for event name "start". * Return NUM_EVENTS if the event name was not found. * Return a pointer to the next event name in "end". @@ -1438,8 +1462,16 @@ ex_doautoall(exarg_T *eap) if (buf->b_ml.ml_mfp == NULL || buf == curbuf) continue; - // find a window for this buffer and save some values + // Find a window for this buffer and save some values. aucmd_prepbuf(&aco, buf); + if (curbuf != buf) + { + // Failed to find a window for this buffer. Better not execute + // autocommands then. + retval = FAIL; + break; + } + set_bufref(&bufref, buf); // execute the autocommands for this buffer @@ -1449,7 +1481,7 @@ ex_doautoall(exarg_T *eap) // Execute the modeline settings, but don't set window-local // options if we are using the current window for another // buffer. - do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0); + do_modelines(is_aucmd_win(curwin) ? OPT_NOWIN : 0); // restore the current window aucmd_restbuf(&aco); @@ -1490,8 +1522,9 @@ check_nomodeline(char_u **argp) /* * Prepare for executing autocommands for (hidden) buffer "buf". * Search for a visible window containing the current buffer. If there isn't - * one then use "aucmd_win". + * one then use an entry in "aucmd_win[]". * Set "curbuf" and "curwin" to match "buf". + * When this fails "curbuf" is not equal "buf". */ void aucmd_prepbuf( @@ -1512,18 +1545,29 @@ aucmd_prepbuf( if (win->w_buffer == buf) break; - // Allocate "aucmd_win" when needed. If this fails (out of memory) fall - // back to using the current window. - if (win == NULL && aucmd_win == NULL) + // Allocate a window when needed. + win_T *auc_win = NULL; + int auc_idx = AUCMD_WIN_COUNT; + if (win == NULL) { - aucmd_win = win_alloc_popup_win(); - if (aucmd_win == NULL) - win = curwin; + for (auc_idx = 0; auc_idx < AUCMD_WIN_COUNT; ++auc_idx) + if (!aucmd_win[auc_idx].auc_win_used) + { + auc_win = win_alloc_popup_win(); + if (auc_win != NULL) + { + aucmd_win[auc_idx].auc_win = auc_win; + aucmd_win[auc_idx].auc_win_used = TRUE; + } + break; + } + + // If this fails (out of memory or using all AUCMD_WIN_COUNT + // entries) then we can't reliable execute the autocmd, return with + // "curbuf" unequal "buf". + if (auc_win == NULL) + return; } - if (win == NULL && aucmd_win_used) - // Strange recursive autocommand, fall back to using the current - // window. Expect a few side effects... - win = curwin; aco->save_curwin_id = curwin->w_id; aco->save_curbuf = curbuf; @@ -1533,24 +1577,23 @@ aucmd_prepbuf( // There is a window for "buf" in the current tab page, make it the // curwin. This is preferred, it has the least side effects (esp. if // "buf" is curbuf). - aco->use_aucmd_win = FALSE; + aco->use_aucmd_win_idx = -1; curwin = win; } else { - // There is no window for "buf", use "aucmd_win". To minimize the side + // There is no window for "buf", use "auc_win". To minimize the side // effects, insert it in the current tab page. // Anything related to a window (e.g., setting folds) may have // unexpected results. - aco->use_aucmd_win = TRUE; - aucmd_win_used = TRUE; - - win_init_popup_win(aucmd_win, buf); + aco->use_aucmd_win_idx = auc_idx; + + win_init_popup_win(auc_win, buf); aco->globaldir = globaldir; globaldir = NULL; - // Split the current window, put the aucmd_win in the upper half. + // Split the current window, put the auc_win in the upper half. // We don't want the BufEnter or WinEnter autocommands. block_autocmds(); make_snapshot(SNAP_AUCMD_IDX); @@ -1565,7 +1608,7 @@ aucmd_prepbuf( // no redrawing and don't set the window title ++RedrawingDisabled; - (void)win_split_ins(0, WSP_TOP, aucmd_win, 0); + (void)win_split_ins(0, WSP_TOP, auc_win, 0); --RedrawingDisabled; (void)win_comp_pos(); // recompute window positions p_ea = save_ea; @@ -1573,7 +1616,7 @@ aucmd_prepbuf( p_acd = save_acd; #endif unblock_autocmds(); - curwin = aucmd_win; + curwin = auc_win; } curbuf = buf; aco->new_curwin_id = curwin->w_id; @@ -1595,24 +1638,26 @@ aucmd_restbuf( int dummy; win_T *save_curwin; - if (aco->use_aucmd_win) + if (aco->use_aucmd_win_idx >= 0) { + win_T *awp = aucmd_win[aco->use_aucmd_win_idx].auc_win; + --curbuf->b_nwindows; - // Find "aucmd_win", it can't be closed, but it may be in another tab + // Find "awp", it can't be closed, but it may be in another tab // page. Do not trigger autocommands here. block_autocmds(); - if (curwin != aucmd_win) + if (curwin != awp) { tabpage_T *tp; win_T *wp; FOR_ALL_TAB_WINDOWS(tp, wp) { - if (wp == aucmd_win) + if (wp == awp) { if (tp != curtab) goto_tabpage_tp(tp, TRUE, TRUE); - win_goto(aucmd_win); + win_goto(awp); goto win_found; } } @@ -1622,7 +1667,7 @@ win_found: // Remove the window and frame from the tree of frames. (void)winframe_remove(curwin, &dummy, NULL); win_remove(curwin, NULL); - aucmd_win_used = FALSE; + aucmd_win[aco->use_aucmd_win_idx].auc_win_used = FALSE; last_status(FALSE); // may need to remove last status line if (!valid_tabpage_win(curtab)) @@ -1646,8 +1691,8 @@ win_found: #endif prevwin = win_find_by_id(aco->save_prevwin_id); #ifdef FEAT_EVAL - vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables - hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab + vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables + hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab #endif vim_free(globaldir); globaldir = aco->globaldir; @@ -1664,11 +1709,9 @@ win_found: #if defined(FEAT_GUI) if (gui.in_use) { - // Hide the scrollbars from the aucmd_win and update. - gui_mch_enable_scrollbar( - &aucmd_win->w_scrollbars[SBAR_LEFT], FALSE); - gui_mch_enable_scrollbar( - &aucmd_win->w_scrollbars[SBAR_RIGHT], FALSE); + // Hide the scrollbars from the "awp" and update. + gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_LEFT], FALSE); + gui_mch_enable_scrollbar(&awp->w_scrollbars[SBAR_RIGHT], FALSE); gui_may_update_scrollbars(); } #endif diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -150,11 +150,15 @@ buffer_ensure_loaded(buf_T *buf) { aco_save_T aco; + // Make sure the buffer is in a window. If not then skip it. aucmd_prepbuf(&aco, buf); - if (swap_exists_action != SEA_READONLY) - swap_exists_action = SEA_NONE; - open_buffer(FALSE, NULL, 0); - aucmd_restbuf(&aco); + if (curbuf == buf) + { + if (swap_exists_action != SEA_READONLY) + swap_exists_action = SEA_NONE; + open_buffer(FALSE, NULL, 0); + aucmd_restbuf(&aco); + } } } #endif @@ -361,21 +365,26 @@ open_buffer( { aco_save_T aco; - // Go to the buffer that was opened. + // Go to the buffer that was opened, make sure it is in a window. + // If not then skip it. aucmd_prepbuf(&aco, old_curbuf.br_buf); - do_modelines(0); - curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED); - - if ((flags & READ_NOWINENTER) == 0) + if (curbuf == old_curbuf.br_buf) + { + do_modelines(0); + curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED); + + if ((flags & READ_NOWINENTER) == 0) #ifdef FEAT_EVAL - apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, - curbuf, &retval); + apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, + FALSE, curbuf, &retval); #else - apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); -#endif - - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); + apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, + FALSE, curbuf); +#endif + + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + } } } @@ -5942,8 +5951,14 @@ buf_contents_changed(buf_T *buf) return TRUE; } - // set curwin/curbuf to buf and save a few things + // Set curwin/curbuf to buf and save a few things. aucmd_prepbuf(&aco, newbuf); + if (curbuf != newbuf) + { + // Failed to find a window for "newbuf". + wipe_buffer(newbuf, FALSE); + return TRUE; + } if (ml_open(curbuf) == OK && readfile(buf->b_ffname, buf->b_fname, diff --git a/src/bufwrite.c b/src/bufwrite.c --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -802,8 +802,15 @@ buf_write( if (fname == buf->b_sfname) buf_fname_s = TRUE; - // set curwin/curbuf to buf and save a few things + // Set curwin/curbuf to buf and save a few things. aucmd_prepbuf(&aco, buf); + if (curbuf != buf) + { + // Could not find a window for "buf". Doing more might cause + // problems, better bail out. + return FAIL; + } + set_bufref(&bufref, buf); if (append) @@ -2592,23 +2599,26 @@ nofail: // Apply POST autocommands. // Careful: The autocommands may call buf_write() recursively! + // Only do this when a window was found for "buf". aucmd_prepbuf(&aco, buf); + if (curbuf == buf) + { + if (append) + apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname, + FALSE, curbuf, eap); + else if (filtering) + apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname, + FALSE, curbuf, eap); + else if (reset_changed && whole) + apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname, + FALSE, curbuf, eap); + else + apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname, + FALSE, curbuf, eap); - if (append) - apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname, - FALSE, curbuf, eap); - else if (filtering) - apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname, - FALSE, curbuf, eap); - else if (reset_changed && whole) - apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname, - FALSE, curbuf, eap); - else - apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname, - FALSE, curbuf, eap); - - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + } #ifdef FEAT_EVAL if (aborting()) // autocmds may abort script processing diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -2872,8 +2872,14 @@ append_to_buffer(buf_T *buffer, char_u * buffer->b_p_ma = TRUE; - // set curbuf to be our buf, temporarily + // Set curbuf to "buffer", temporarily. aucmd_prepbuf(&aco, buffer); + if (curbuf != buffer) + { + // Could not find a window for this buffer, the following might cause + // trouble, better bail out. + return; + } u_sync(TRUE); // ignore undo failure, undo is not very useful here diff --git a/src/diff.c b/src/diff.c --- a/src/diff.c +++ b/src/diff.c @@ -2786,8 +2786,12 @@ ex_diffgetput(exarg_T *eap) idx_to = idx_other; // Need to make the other buffer the current buffer to be able to make // changes in it. - // set curwin/curbuf to buf and save a few things + // Set curwin/curbuf to buf and save a few things. aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]); + if (curbuf != curtab->tp_diffbuf[idx_other]) + // Could not find a window for this buffer, the rest is likely to + // fail. + goto theend; } // May give the warning for a changed buffer here, which can trigger the diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -5084,9 +5084,10 @@ garbage_collect(int testing) FOR_ALL_TAB_WINDOWS(tp, wp) abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID, NULL, NULL); - if (aucmd_win != NULL) - abort = abort || set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID, - NULL, NULL); + for (int i = 0; i < AUCMD_WIN_COUNT; ++i) + if (aucmd_win[i].auc_win_used) + abort = abort || set_ref_in_item( + &aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL); #ifdef FEAT_PROP_POPUP FOR_ALL_POPUPWINS(wp) abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID, diff --git a/src/evalbuffer.c b/src/evalbuffer.c --- a/src/evalbuffer.c +++ b/src/evalbuffer.c @@ -136,6 +136,8 @@ typedef struct { * * Information is saved in "cob" and MUST be restored by calling * change_other_buffer_restore(). + * + * If this fails then "curbuf" will not be equal to "buf". */ static void change_other_buffer_prepare(cob_T *cob, buf_T *buf) @@ -156,7 +158,8 @@ change_other_buffer_prepare(cob_T *cob, // curwin->w_buffer differ from "curbuf", use the autocmd window. curbuf = curwin->w_buffer; aucmd_prepbuf(&cob->cob_aco, buf); - cob->cob_using_aco = TRUE; + if (curbuf == buf) + cob->cob_using_aco = TRUE; } } diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -4761,13 +4761,16 @@ f_setbufvar(typval_T *argvars, typval_T { aco_save_T aco; - // set curbuf to be our buf, temporarily + // Set curbuf to be our buf, temporarily. aucmd_prepbuf(&aco, buf); - - set_option_from_tv(varname + 1, varp); - - // reset notion of buffer - aucmd_restbuf(&aco); + if (curbuf == buf) + { + // Only when it worked to set "curbuf". + set_option_from_tv(varname + 1, varp); + + // reset notion of buffer + aucmd_restbuf(&aco); + } } else { diff --git a/src/evalwindow.c b/src/evalwindow.c --- a/src/evalwindow.c +++ b/src/evalwindow.c @@ -1064,7 +1064,7 @@ f_win_gettype(typval_T *argvars, typval_ return; } } - if (wp == aucmd_win) + if (is_aucmd_win(wp)) rettv->vval.v_string = vim_strsave((char_u *)"autocmd"); #if defined(FEAT_QUICKFIX) else if (wp->w_p_pvw) diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -705,9 +705,12 @@ ex_listdo(exarg_T *eap) else { aucmd_prepbuf(&aco, buf); - apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, + if (curbuf == buf) + { + apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, buf->b_fname, TRUE, buf); - aucmd_restbuf(&aco); + aucmd_restbuf(&aco); + } } // start over, in case autocommands messed things up. diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -6050,7 +6050,7 @@ ex_win_close( buf_T *buf = win->w_buffer; // Never close the autocommand window. - if (win == aucmd_win) + if (is_aucmd_win(win)) { emsg(_(e_cannot_close_autocmd_or_popup_window)); return; diff --git a/src/fileio.c b/src/fileio.c --- a/src/fileio.c +++ b/src/fileio.c @@ -4369,8 +4369,14 @@ buf_reload(buf_T *buf, int orig_mode, in int flags = READ_NEW; int prepped = OK; - // set curwin/curbuf for "buf" and save some things + // Set curwin/curbuf for "buf" and save some things. aucmd_prepbuf(&aco, buf); + if (curbuf != buf) + { + // Failed to find a window for "buf", it is dangerous to continue, + // better bail out. + return; + } // Unless reload_options is set, we only want to read the text from the // file, not reset the syntax highlighting, clear marks, diff status, etc. diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -977,8 +977,18 @@ EXTERN win_T *prevwin INIT(= NULL); // p EXTERN win_T *curwin; // currently active window -EXTERN win_T *aucmd_win; // window used in aucmd_prepbuf() -EXTERN int aucmd_win_used INIT(= FALSE); // aucmd_win is being used +// When executing autocommands for a buffer that is not in any window, a +// special window is created to handle the side effects. When autocommands +// nest we may need more than one. Allow for up to five, if more are needed +// something crazy is happening. +#define AUCMD_WIN_COUNT 5 + +typedef struct { + win_T *auc_win; // window used in aucmd_prepbuf() + int auc_win_used; // this auc_win is being used +} aucmdwin_T; + +EXTERN aucmdwin_T aucmd_win[AUCMD_WIN_COUNT]; #ifdef FEAT_PROP_POPUP EXTERN win_T *first_popupwin; // first global popup window diff --git a/src/if_perl.xs b/src/if_perl.xs --- a/src/if_perl.xs +++ b/src/if_perl.xs @@ -1869,18 +1869,21 @@ Set(vimbuf, ...) { aco_save_T aco; - /* set curwin/curbuf for "vimbuf" and save some things */ + /* Set curwin/curbuf for "vimbuf" and save some things. */ aucmd_prepbuf(&aco, vimbuf); - - if (u_savesub(lnum) == OK) + if (curbuf == vimbuf) { - ml_replace(lnum, (char_u *)line, TRUE); - changed_bytes(lnum, 0); - } + /* Only when a window was found. */ + if (u_savesub(lnum) == OK) + { + ml_replace(lnum, (char_u *)line, TRUE); + changed_bytes(lnum, 0); + } - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - /* Careful: autocommands may have made "vimbuf" invalid! */ + /* restore curwin/curbuf and a few other things */ + aucmd_restbuf(&aco); + /* Careful: autocommands may have made "vimbuf" invalid! */ + } } } } @@ -1921,18 +1924,22 @@ Delete(vimbuf, ...) /* set curwin/curbuf for "vimbuf" and save some things */ aucmd_prepbuf(&aco, vimbuf); - - if (u_savedel(lnum, 1) == OK) + if (curbuf == vimbuf) { - ml_delete(lnum); - check_cursor(); - deleted_lines_mark(lnum, 1L); + /* Only when a window was found. */ + if (u_savedel(lnum, 1) == OK) + { + ml_delete(lnum); + check_cursor(); + deleted_lines_mark(lnum, 1L); + } + + /* restore curwin/curbuf and a few other things */ + aucmd_restbuf(&aco); + /* Careful: autocommands may have made "vimbuf" + * invalid! */ } - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - /* Careful: autocommands may have made "vimbuf" invalid! */ - update_curbuf(UPD_VALID); } } @@ -1963,16 +1970,19 @@ Append(vimbuf, ...) /* set curwin/curbuf for "vimbuf" and save some things */ aucmd_prepbuf(&aco, vimbuf); - - if (u_inssub(lnum + 1) == OK) + if (curbuf == vimbuf) { - ml_append(lnum, (char_u *)line, (colnr_T)0, FALSE); - appended_lines_mark(lnum, 1L); - } + /* Only when a window for "vimbuf" was found. */ + if (u_inssub(lnum + 1) == OK) + { + ml_append(lnum, (char_u *)line, (colnr_T)0, FALSE); + appended_lines_mark(lnum, 1L); + } - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - /* Careful: autocommands may have made "vimbuf" invalid! */ + /* restore curwin/curbuf and a few other things */ + aucmd_restbuf(&aco); + /* Careful: autocommands may have made "vimbuf" invalid! */ + } update_curbuf(UPD_VALID); } diff --git a/src/if_py_both.h b/src/if_py_both.h --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -5283,8 +5283,11 @@ BufferSetattr(BufferObject *self, char * VimTryStart(); // Using aucmd_*: autocommands will be executed by rename_buffer aucmd_prepbuf(&aco, self->buf); - ren_ret = rename_buffer(val); - aucmd_restbuf(&aco); + if (curbuf == self->buf) + { + ren_ret = rename_buffer(val); + aucmd_restbuf(&aco); + } Py_XDECREF(todecref); if (VimTryEnd()) return -1; diff --git a/src/if_ruby.c b/src/if_ruby.c --- a/src/if_ruby.c +++ b/src/if_ruby.c @@ -1371,21 +1371,24 @@ set_buffer_line(buf_T *buf, linenr_T n, if (n > 0 && n <= buf->b_ml.ml_line_count && line != NULL) { - // set curwin/curbuf for "buf" and save some things + // Set curwin/curbuf for "buf" and save some things. aucmd_prepbuf(&aco, buf); - - if (u_savesub(n) == OK) + if (curbuf == buf) { - ml_replace(n, (char_u *)line, TRUE); - changed(); + // Only when it worked to set "curbuf". + if (u_savesub(n) == OK) + { + ml_replace(n, (char_u *)line, TRUE); + changed(); #ifdef SYNTAX_HL - syn_changed(n); // recompute syntax hl. for this line + syn_changed(n); // recompute syntax hl. for this line #endif - } + } - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); - // Careful: autocommands may have made "buf" invalid! + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + // Careful: autocommands may have made "buf" invalid! + } update_curbuf(UPD_NOT_VALID); } @@ -1415,23 +1418,26 @@ buffer_delete(VALUE self, VALUE num) if (n > 0 && n <= buf->b_ml.ml_line_count) { - // set curwin/curbuf for "buf" and save some things + // Set curwin/curbuf for "buf" and save some things. aucmd_prepbuf(&aco, buf); - - if (u_savedel(n, 1) == OK) + if (curbuf == buf) { - ml_delete(n); + // Only when it worked to set "curbuf". + if (u_savedel(n, 1) == OK) + { + ml_delete(n); - // Changes to non-active buffers should properly refresh - // SegPhault - 01/09/05 - deleted_lines_mark(n, 1L); + // Changes to non-active buffers should properly refresh + // SegPhault - 01/09/05 + deleted_lines_mark(n, 1L); - changed(); - } + changed(); + } - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); - // Careful: autocommands may have made "buf" invalid! + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + // Careful: autocommands may have made "buf" invalid! + } update_curbuf(UPD_NOT_VALID); } @@ -1458,21 +1464,24 @@ buffer_append(VALUE self, VALUE num, VAL { // set curwin/curbuf for "buf" and save some things aucmd_prepbuf(&aco, buf); - - if (u_inssub(n + 1) == OK) + if (curbuf == buf) { - ml_append(n, (char_u *) line, (colnr_T) 0, FALSE); + // Only when it worked to set "curbuf". + if (u_inssub(n + 1) == OK) + { + ml_append(n, (char_u *) line, (colnr_T) 0, FALSE); - // Changes to non-active buffers should properly refresh screen - // SegPhault - 12/20/04 - appended_lines_mark(n, 1L); + // Changes to non-active buffers should properly refresh screen + // SegPhault - 12/20/04 + appended_lines_mark(n, 1L); - changed(); - } + changed(); + } - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); - // Careful: autocommands may have made "buf" invalid! + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + // Careful: autocommands may have made "buf" invalid! + } update_curbuf(UPD_NOT_VALID); } diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -123,6 +123,8 @@ main #endif params.window_count = -1; + autocmd_init(); + #ifdef FEAT_RUBY { int ruby_stack_start; diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4490,27 +4490,30 @@ mch_call_shell_terminal( // Find a window to make "buf" curbuf. aucmd_prepbuf(&aco, buf); - - clear_oparg(&oa); - while (term_use_loop()) - { - if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active) - { - // If terminal_loop() returns OK we got a key that is handled - // in Normal model. We don't do redrawing anyway. - if (terminal_loop(TRUE) == OK) + if (curbuf == buf) + { + // Only when managed to find a window for "buf", + clear_oparg(&oa); + while (term_use_loop()) + { + if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active) + { + // If terminal_loop() returns OK we got a key that is handled + // in Normal model. We don't do redrawing anyway. + if (terminal_loop(TRUE) == OK) + normal_cmd(&oa, TRUE); + } + else normal_cmd(&oa, TRUE); } - else - normal_cmd(&oa, TRUE); - } - retval = job->jv_exitval; - ch_log(NULL, "system command finished"); - - job_unref(job); - - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); + retval = job->jv_exitval; + ch_log(NULL, "system command finished"); + + job_unref(job); + + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + } // Only require pressing Enter when redrawing, to avoid that system() gets // the hit-enter prompt even though it didn't output anything. diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -4878,27 +4878,30 @@ mch_call_shell_terminal( // Find a window to make "buf" curbuf. aucmd_prepbuf(&aco, buf); - - clear_oparg(&oa); - while (term_use_loop()) - { - if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active) - { - // If terminal_loop() returns OK we got a key that is handled - // in Normal model. We don't do redrawing anyway. - if (terminal_loop(TRUE) == OK) + if (curbuf == buf) + { + // Only do this when a window was found for "buf". + clear_oparg(&oa); + while (term_use_loop()) + { + if (oa.op_type == OP_NOP && oa.regname == NUL && !VIsual_active) + { + // If terminal_loop() returns OK we got a key that is handled + // in Normal model. We don't do redrawing anyway. + if (terminal_loop(TRUE) == OK) + normal_cmd(&oa, TRUE); + } + else normal_cmd(&oa, TRUE); } - else - normal_cmd(&oa, TRUE); - } - retval = job->jv_exitval; - ch_log(NULL, "system command finished"); - - job_unref(job); - - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); + retval = job->jv_exitval; + ch_log(NULL, "system command finished"); + + job_unref(job); + + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + } wait_return(TRUE); do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, buf->b_fnum, TRUE); diff --git a/src/proto/autocmd.pro b/src/proto/autocmd.pro --- a/src/proto/autocmd.pro +++ b/src/proto/autocmd.pro @@ -2,7 +2,9 @@ void aubuflocal_remove(buf_T *buf); int au_has_group(char_u *name); void do_augroup(char_u *arg, int del_group); +void autocmd_init(void); void free_all_autocmds(void); +int is_aucmd_win(win_T *win); int check_ei(void); char_u *au_event_disable(char *what); void au_event_restore(char_u *old_ei); diff --git a/src/quickfix.c b/src/quickfix.c --- a/src/quickfix.c +++ b/src/quickfix.c @@ -4598,21 +4598,29 @@ qf_update_buffer(qf_info_T *qi, qfline_T // autocommands may cause trouble incr_quickfix_busy(); + int do_fill = TRUE; if (old_last == NULL) + { // set curwin/curbuf to buf and save a few things aucmd_prepbuf(&aco, buf); - - qf_update_win_titlevar(qi); - - qf_fill_buffer(qf_get_curlist(qi), buf, old_last, qf_winid); - ++CHANGEDTICK(buf); - - if (old_last == NULL) + if (curbuf != buf) + do_fill = FALSE; // failed to find a window for "buf" + } + + if (do_fill) { - (void)qf_win_pos_update(qi, 0); - - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); + qf_update_win_titlevar(qi); + + qf_fill_buffer(qf_get_curlist(qi), buf, old_last, qf_winid); + ++CHANGEDTICK(buf); + + if (old_last == NULL) + { + (void)qf_win_pos_update(qi, 0); + + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + } } // Only redraw when added lines are visible. This avoids flickering @@ -6395,12 +6403,15 @@ vgr_process_files( // need to be done (again). But not the window-local // options! aucmd_prepbuf(&aco, buf); + if (curbuf == buf) + { #if defined(FEAT_SYN_HL) - apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, + apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, TRUE, buf); #endif - do_modelines(OPT_NOWIN); - aucmd_restbuf(&aco); + do_modelines(OPT_NOWIN); + aucmd_restbuf(&aco); + } } } } @@ -6593,40 +6604,43 @@ load_dummy_buffer( // set curwin/curbuf to buf and save a few things aucmd_prepbuf(&aco, newbuf); - - // Need to set the filename for autocommands. - (void)setfname(curbuf, fname, NULL, FALSE); - - // Create swap file now to avoid the ATTENTION message. - check_need_swap(TRUE); - - // Remove the "dummy" flag, otherwise autocommands may not - // work. - curbuf->b_flags &= ~BF_DUMMY; - - newbuf_to_wipe.br_buf = NULL; - readfile_result = readfile(fname, NULL, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, - NULL, READ_NEW | READ_DUMMY); - --newbuf->b_locked; - if (readfile_result == OK - && !got_int - && !(curbuf->b_flags & BF_NEW)) + if (curbuf == newbuf) { - failed = FALSE; - if (curbuf != newbuf) + // Need to set the filename for autocommands. + (void)setfname(curbuf, fname, NULL, FALSE); + + // Create swap file now to avoid the ATTENTION message. + check_need_swap(TRUE); + + // Remove the "dummy" flag, otherwise autocommands may not + // work. + curbuf->b_flags &= ~BF_DUMMY; + + newbuf_to_wipe.br_buf = NULL; + readfile_result = readfile(fname, NULL, + (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, + NULL, READ_NEW | READ_DUMMY); + --newbuf->b_locked; + if (readfile_result == OK + && !got_int + && !(curbuf->b_flags & BF_NEW)) { - // Bloody autocommands changed the buffer! Can happen when - // using netrw and editing a remote file. Use the current - // buffer instead, delete the dummy one after restoring the - // window stuff. - set_bufref(&newbuf_to_wipe, newbuf); - newbuf = curbuf; + failed = FALSE; + if (curbuf != newbuf) + { + // Bloody autocommands changed the buffer! Can happen when + // using netrw and editing a remote file. Use the current + // buffer instead, delete the dummy one after restoring the + // window stuff. + set_bufref(&newbuf_to_wipe, newbuf); + newbuf = curbuf; + } } + + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); } - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe)) wipe_buffer(newbuf_to_wipe.br_buf, FALSE); diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -2369,7 +2369,6 @@ screenalloc(int doclear) u8char_T *new_ScreenLinesUC = NULL; u8char_T *new_ScreenLinesC[MAX_MCO]; schar_T *new_ScreenLines2 = NULL; - int i; sattr_T *new_ScreenAttrs; colnr_T *new_ScreenCols; unsigned *new_LineOffset; @@ -2438,8 +2437,9 @@ retry: */ FOR_ALL_TAB_WINDOWS(tp, wp) win_free_lsize(wp); - if (aucmd_win != NULL) - win_free_lsize(aucmd_win); + for (int i = 0; i < AUCMD_WIN_COUNT; ++i) + if (aucmd_win[i].auc_win_used) + win_free_lsize(aucmd_win[i].auc_win); #ifdef FEAT_PROP_POPUP // global popup windows FOR_ALL_POPUPWINS(wp) @@ -2455,7 +2455,7 @@ retry: if (enc_utf8) { new_ScreenLinesUC = LALLOC_MULT(u8char_T, (Rows + 1) * Columns); - for (i = 0; i < p_mco; ++i) + for (int i = 0; i < p_mco; ++i) new_ScreenLinesC[i] = LALLOC_CLEAR_MULT(u8char_T, (Rows + 1) * Columns); } @@ -2482,9 +2482,14 @@ retry: goto give_up; } } - if (aucmd_win != NULL && aucmd_win->w_lines == NULL - && win_alloc_lines(aucmd_win) == FAIL) - outofmem = TRUE; + for (int i = 0; i < AUCMD_WIN_COUNT; ++i) + if (aucmd_win[i].auc_win_used + && aucmd_win[i].auc_win->w_lines == NULL + && win_alloc_lines(aucmd_win[i].auc_win) == FAIL) + { + outofmem = TRUE; + break; + } #ifdef FEAT_PROP_POPUP // global popup windows FOR_ALL_POPUPWINS(wp) @@ -2505,11 +2510,15 @@ retry: give_up: - for (i = 0; i < p_mco; ++i) + int found_null = FALSE; + for (int i = 0; i < p_mco; ++i) if (new_ScreenLinesC[i] == NULL) + { + found_null = TRUE; break; + } if (new_ScreenLines == NULL - || (enc_utf8 && (new_ScreenLinesUC == NULL || i != p_mco)) + || (enc_utf8 && (new_ScreenLinesUC == NULL || found_null)) || (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL) || new_ScreenAttrs == NULL || new_ScreenCols == NULL @@ -2534,7 +2543,7 @@ give_up: } VIM_CLEAR(new_ScreenLines); VIM_CLEAR(new_ScreenLinesUC); - for (i = 0; i < p_mco; ++i) + for (int i = 0; i < p_mco; ++i) VIM_CLEAR(new_ScreenLinesC[i]); VIM_CLEAR(new_ScreenLines2); VIM_CLEAR(new_ScreenAttrs); @@ -2571,7 +2580,7 @@ give_up: { (void)vim_memset(new_ScreenLinesUC + new_row * Columns, 0, (size_t)Columns * sizeof(u8char_T)); - for (i = 0; i < p_mco; ++i) + for (int i = 0; i < p_mco; ++i) (void)vim_memset(new_ScreenLinesC[i] + new_row * Columns, 0, (size_t)Columns * sizeof(u8char_T)); @@ -2603,7 +2612,7 @@ give_up: mch_memmove(new_ScreenLinesUC + new_LineOffset[new_row], ScreenLinesUC + LineOffset[old_row], (size_t)len * sizeof(u8char_T)); - for (i = 0; i < p_mco; ++i) + for (int i = 0; i < p_mco; ++i) mch_memmove(new_ScreenLinesC[i] + new_LineOffset[new_row], ScreenLinesC[i] + LineOffset[old_row], @@ -2636,7 +2645,7 @@ give_up: // NOTE: this may result in all pointers to become NULL. ScreenLines = new_ScreenLines; ScreenLinesUC = new_ScreenLinesUC; - for (i = 0; i < p_mco; ++i) + for (int i = 0; i < p_mco; ++i) ScreenLinesC[i] = new_ScreenLinesC[i]; Screen_mco = p_mco; ScreenLines2 = new_ScreenLines2; diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -4160,7 +4160,7 @@ typedef int vimmenu_T; typedef struct { buf_T *save_curbuf; // saved curbuf - int use_aucmd_win; // using aucmd_win + int use_aucmd_win_idx; // index in aucmd_win[] if >= 0 int save_curwin_id; // ID of saved curwin int new_curwin_id; // ID of new curwin int save_prevwin_id; // ID of saved prevwin diff --git a/src/term.c b/src/term.c --- a/src/term.c +++ b/src/term.c @@ -2232,10 +2232,13 @@ set_termname(char_u *term) if (curbuf->b_ml.ml_mfp != NULL) { aucmd_prepbuf(&aco, buf); - apply_autocmds(EVENT_TERMCHANGED, NULL, NULL, FALSE, + if (curbuf == buf) + { + apply_autocmds(EVENT_TERMCHANGED, NULL, NULL, FALSE, curbuf); - // restore curwin/curbuf and a few other things - aucmd_restbuf(&aco); + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + } } } } diff --git a/src/terminal.c b/src/terminal.c --- a/src/terminal.c +++ b/src/terminal.c @@ -3560,15 +3560,18 @@ term_after_channel_closed(term_T *term) // ++close or term_finish == "close" ch_log(NULL, "terminal job finished, closing window"); aucmd_prepbuf(&aco, term->tl_buffer); - // Avoid closing the window if we temporarily use it. - if (curwin == aucmd_win) - do_set_w_closing = TRUE; - if (do_set_w_closing) - curwin->w_closing = TRUE; - do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE); - if (do_set_w_closing) - curwin->w_closing = FALSE; - aucmd_restbuf(&aco); + if (curbuf == term->tl_buffer) + { + // Avoid closing the window if we temporarily use it. + if (is_aucmd_win(curwin)) + do_set_w_closing = TRUE; + if (do_set_w_closing) + curwin->w_closing = TRUE; + do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE); + if (do_set_w_closing) + curwin->w_closing = FALSE; + aucmd_restbuf(&aco); + } #ifdef FEAT_PROP_POPUP if (pwin != NULL) popup_close_with_retval(pwin, 0); diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -4068,4 +4068,27 @@ func Test_autocmd_split_dummy() call delete('Xerr') endfunc +" This was crashing because there was only one window to execute autocommands +" in. +func Test_autocmd_nested_setbufvar() + CheckFeature python3 + + set hidden + edit Xaaa + edit Xbbb + call setline(1, 'bar') + enew + au BufWriteCmd Xbbb ++nested call setbufvar('Xaaa', '&ft', 'foo') | bw! Xaaa + au FileType foo call py3eval('vim.current.buffer.options["cindent"]') + wall + + au! BufWriteCmd + au! FileType foo + set nohidden + call delete('Xaaa') + call delete('Xbbb') + %bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 965, +/**/ 964, /**/ 963, diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -1362,7 +1362,7 @@ win_split_ins( win_equal(wp, TRUE, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : dir == 'h' ? 'b' : 'v'); - else if (*p_spk != 'c' && wp != aucmd_win) + else if (*p_spk != 'c' && !is_aucmd_win(wp)) win_fix_scroll(FALSE); // Don't change the window height/width to 'winheight' / 'winwidth' if a @@ -1962,7 +1962,7 @@ win_equal( win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current, topframe, dir, 0, tabline_height(), (int)Columns, topframe->fr_height); - if (*p_spk != 'c' && next_curwin != aucmd_win) + if (*p_spk != 'c' && !is_aucmd_win(next_curwin)) win_fix_scroll(TRUE); } @@ -2426,7 +2426,7 @@ close_windows( /* * Return TRUE if the current window is the only window that exists (ignoring - * "aucmd_win"). + * "aucmd_win[]"). * Returns FALSE if there is a window, possibly in another tab page. */ static int @@ -2436,7 +2436,7 @@ last_window(void) } /* - * Return TRUE if there is only one window other than "aucmd_win" in the + * Return TRUE if there is only one window other than "aucmd_win[]" in the * current tab page. */ int @@ -2447,7 +2447,7 @@ one_window(void) FOR_ALL_WINDOWS(wp) { - if (wp != aucmd_win) + if (!is_aucmd_win(wp)) { if (seen_one) return FALSE; @@ -2588,7 +2588,7 @@ win_close(win_T *win, int free_buf) emsg(_(e_cannot_close_autocmd_or_popup_window)); return FAIL; } - if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) + if ((is_aucmd_win(firstwin) || is_aucmd_win(lastwin)) && one_window()) { emsg(_(e_cannot_close_window_only_autocmd_window_would_remain)); return FAIL; @@ -3292,11 +3292,12 @@ win_free_all(void) while (first_tabpage->tp_next != NULL) tabpage_close(TRUE); - if (aucmd_win != NULL) - { - (void)win_free_mem(aucmd_win, &dummy, NULL); - aucmd_win = NULL; - } + for (int i = 0; i < AUCMD_WIN_COUNT; ++i) + if (aucmd_win[i].auc_win_used) + { + (void)win_free_mem(aucmd_win[i].auc_win, &dummy, NULL); + aucmd_win[i].auc_win_used = FALSE; + } while (firstwin != NULL) (void)win_free_mem(firstwin, &dummy, NULL); @@ -5663,7 +5664,7 @@ win_free( int win_unlisted(win_T *wp) { - return wp == aucmd_win || WIN_IS_POPUP(wp); + return is_aucmd_win(wp) || WIN_IS_POPUP(wp); } #if defined(FEAT_PROP_POPUP) || defined(PROTO) @@ -7257,7 +7258,7 @@ only_one_window(void) # ifdef FEAT_QUICKFIX || wp->w_p_pvw # endif - ) || wp == curwin) && wp != aucmd_win) + ) || wp == curwin) && !is_aucmd_win(wp)) ++count; return (count <= 1); }