diff options
Diffstat (limited to 'src/nvim/buffer.c')
-rw-r--r-- | src/nvim/buffer.c | 315 |
1 files changed, 197 insertions, 118 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 868e842ecd..1089a2dc3b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -78,6 +78,9 @@ static char *msg_loclist = N_("[Location List]"); static char *msg_qflist = N_("[Quickfix List]"); static char *e_auabort = N_("E855: Autocommands caused command to abort"); +// Number of times free_buffer() was called. +static int buf_free_count = 0; + /* * Open current buffer, that is: open the memfile and read the file into * memory. @@ -91,7 +94,7 @@ open_buffer ( ) { int retval = OK; - buf_T *old_curbuf; + bufref_T old_curbuf; long old_tw = curbuf->b_p_tw; /* @@ -133,10 +136,10 @@ open_buffer ( return FAIL; } - /* The autocommands in readfile() may change the buffer, but only AFTER - * reading the file. */ - old_curbuf = curbuf; - modified_was_set = FALSE; + // The autocommands in readfile() may change the buffer, but only AFTER + // reading the file. + set_bufref(&old_curbuf, curbuf); + modified_was_set = false; /* mark cursor position as being invalid */ curwin->w_valid = 0; @@ -249,11 +252,11 @@ open_buffer ( * The autocommands may have changed the current buffer. Apply the * modelines to the correct buffer, if it still exists and is loaded. */ - if (buf_valid(old_curbuf) && old_curbuf->b_ml.ml_mfp != NULL) { + if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->b_ml.ml_mfp != NULL) { aco_save_T aco; - /* Go to the buffer that was opened. */ - aucmd_prepbuf(&aco, old_curbuf); + // Go to the buffer that was opened. + aucmd_prepbuf(&aco, old_curbuf.br_buf); do_modelines(0); curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED); @@ -267,14 +270,42 @@ open_buffer ( return retval; } -/// Check that "buf" points to a valid buffer (in the buffer list). +/// Store "buf" in "bufref" and set the free count. +/// +/// @param bufref Reference to be used for the buffer. +/// @param buf The buffer to reference. +void set_bufref(bufref_T *bufref, buf_T *buf) +{ + bufref->br_buf = buf; + bufref->br_buf_free_count = buf_free_count; +} + +/// Check if "bufref" points to a valid buffer. +/// +/// Only goes through the buffer list if buf_free_count changed. +/// +/// @param bufref Buffer reference to check for. +bool bufref_valid(bufref_T *bufref) +{ + return bufref->br_buf_free_count == buf_free_count + ? true + : buf_valid(bufref->br_buf); +} + +/// Check that "buf" points to a valid buffer in the buffer list. +/// +/// Can be slow if there are many buffers, prefer using bufref_valid(). +/// +/// @param buf The buffer to check for. bool buf_valid(buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (buf == NULL) { return false; } - FOR_ALL_BUFFERS(bp) { + // Assume that we more often have a recent buffer, + // start with the last one. + for (buf_T *bp = lastbuf; bp != NULL; bp = bp->b_prev) { if (bp == buf) { return true; } @@ -363,13 +394,15 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) win->w_cursor.col, TRUE); } + bufref_T bufref; + set_bufref(&bufref, buf); + /* 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); - if (!buf_valid(buf)) { - /* Autocommands deleted the buffer. */ + if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false, + buf) && !bufref_valid(&bufref)) { + // Autocommands deleted the buffer. EMSG(_(e_auabort)); return; } @@ -384,10 +417,9 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) * BufHidden */ if (!unload_buf) { buf->b_closing = true; - apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) { - /* Autocommands deleted the buffer. */ + if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false, + buf) && !bufref_valid(&bufref)) { + // Autocommands deleted the buffer. EMSG(_(e_auabort)); return; } @@ -431,11 +463,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); - /* Autocommands may have deleted the buffer. */ - if (!buf_valid(buf)) + if (!bufref_valid(&bufref)) { + // Autocommands may have deleted the buffer. return; - if (aborting()) /* autocmds may abort script processing */ + } + if (aborting()) { + // Autocmds may abort script processing. return; + } /* * It's possible that autocommands change curbuf to the one being deleted. @@ -535,22 +570,29 @@ void buf_freeall(buf_T *buf, int flags) // Make sure the buffer isn't closed by autocommands. buf->b_closing = true; - if (buf->b_ml.ml_mfp != NULL) { - apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf); - if (!buf_valid(buf)) { // autocommands may delete the buffer - return; - } + + bufref_T bufref; + set_bufref(&bufref, buf); + + if ((buf->b_ml.ml_mfp != NULL) + && apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf) + && !bufref_valid(&bufref)) { + // Autocommands deleted the buffer. + return; } - if ((flags & BFA_DEL) && buf->b_p_bl) { - apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ - return; + if ((flags & BFA_DEL) + && buf->b_p_bl + && apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, false, buf) + && !bufref_valid(&bufref)) { + // Autocommands may delete the buffer. + return; } - if (flags & BFA_WIPE) { - apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ - return; + if ((flags & BFA_WIPE) + && apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, false, + buf) + && !bufref_valid(&bufref)) { + // Autocommands may delete the buffer. + return; } buf->b_closing = false; @@ -603,7 +645,8 @@ void buf_freeall(buf_T *buf, int flags) static void free_buffer(buf_T *buf) { handle_unregister_buffer(buf); - free_buffer_stuff(buf, TRUE); + buf_free_count++; + free_buffer_stuff(buf, true); unref_var_dict(buf->b_vars); aubuflocal_remove(buf); buf_hashtab_remove(buf); @@ -678,8 +721,9 @@ static void clear_wininfo(buf_T *buf) void goto_buffer(exarg_T *eap, int start, int dir, int count) { (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, - start, dir, count, eap->forceit); - buf_T *old_curbuf = curbuf; + start, dir, count, eap->forceit); + bufref_T old_curbuf; + set_bufref(&old_curbuf, curbuf); swap_exists_action = SEA_DIALOG; if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') { @@ -698,18 +742,20 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count) * new aborting error, interrupt, or uncaught exception. */ leave_cleanup(&cs); } else { - handle_swap_exists(old_curbuf); + handle_swap_exists(&old_curbuf); } } -/* - * Handle the situation of swap_exists_action being set. - * It is allowed for "old_curbuf" to be NULL or invalid. - */ -void handle_swap_exists(buf_T *old_curbuf) +/// Handle the situation of swap_exists_action being set. +/// +/// It is allowed for "old_curbuf" to be NULL or invalid. +/// +/// @param old_curbuf The buffer to check for. +void handle_swap_exists(bufref_T *old_curbuf) { cleanup_T cs; long old_tw = curbuf->b_p_tw; + buf_T *buf; if (swap_exists_action == SEA_QUIT) { /* Reset the error/interrupt/exception state here so that @@ -722,13 +768,18 @@ void handle_swap_exists(buf_T *old_curbuf) swap_exists_action = SEA_NONE; // don't want it again swap_exists_did_quit = true; close_buffer(curwin, curbuf, DOBUF_UNLOAD, false); - if (!buf_valid(old_curbuf) || old_curbuf == curbuf) { - old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); + if (old_curbuf == NULL + || !bufref_valid(old_curbuf) + || old_curbuf->br_buf == curbuf) { + buf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); + } else { + buf = old_curbuf->br_buf; } - if (old_curbuf != NULL) { - enter_buffer(old_curbuf); - if (old_tw != curbuf->b_p_tw) + if (buf != NULL) { + enter_buffer(buf); + if (old_tw != curbuf->b_p_tw) { check_colorcolumn(curwin); + } } /* If "old_curbuf" is NULL we are in big trouble here... */ @@ -879,6 +930,9 @@ static int empty_curbuf(int close_others, int forceit, int action) return FAIL; } + bufref_T bufref; + set_bufref(&bufref, buf); + if (close_others) { /* Close any other windows on this buffer, then make it empty. */ close_windows(buf, TRUE); @@ -888,15 +942,17 @@ static int empty_curbuf(int close_others, int forceit, int action) retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, forceit ? ECMD_FORCEIT : 0, curwin); - /* - * do_ecmd() may create a new buffer, then we have to delete - * the old one. But do_ecmd() may have done that already, check - * if the buffer still exists. - */ - if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0) - close_buffer(NULL, buf, action, FALSE); - if (!close_others) - need_fileinfo = FALSE; + // do_ecmd() may create a new buffer, then we have to delete + // the old one. But do_ecmd() may have done that already, check + // if the buffer still exists. + if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0) { + close_buffer(NULL, buf, action, false); + } + + if (!close_others) { + need_fileinfo = false; + } + return retval; } /* @@ -998,6 +1054,8 @@ do_buffer ( */ if (unload) { int forward; + bufref_T bufref; + set_bufref(&bufref, buf); /* When unloading or deleting a buffer that's already unloaded and * unlisted: fail silently. */ @@ -1006,15 +1064,16 @@ do_buffer ( if (!forceit && (buf->terminal || bufIsChanged(buf))) { if ((p_confirm || cmdmod.confirm) && p_write && !buf->terminal) { - dialog_changed(buf, FALSE); - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! It's not changed - * now. */ + dialog_changed(buf, false); + if (!bufref_valid(&bufref)) { + // Autocommand deleted buffer, oops! It's not changed now. return FAIL; - /* If it's still changed fail silently, the dialog already - * mentioned why it fails. */ - if (bufIsChanged(buf)) + } + // If it's still changed fail silently, the dialog already + // mentioned why it fails. + if (bufIsChanged(buf)) { return FAIL; + } } else { if (buf->terminal) { EMSG2(_("E89: %s will be killed(add ! to override)"), @@ -1058,27 +1117,26 @@ do_buffer ( * If the buffer to be deleted is not the current one, delete it here. */ if (buf != curbuf) { - close_windows(buf, FALSE); - if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0) - close_buffer(NULL, buf, action, FALSE); + close_windows(buf, false); + if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0) { + close_buffer(NULL, buf, action, false); + } return OK; } - /* - * Deleting the current buffer: Need to find another buffer to go to. - * There should be another, otherwise it would have been handled - * above. However, autocommands may have deleted all buffers. - * First use au_new_curbuf, if it is valid. - * Then prefer the buffer we most recently visited. - * Else try to find one that is loaded, after the current buffer, - * then before the current buffer. - * Finally use any buffer. - */ - buf = NULL; /* selected buffer */ - bp = NULL; /* used when no loaded buffer found */ - if (au_new_curbuf != NULL && buf_valid(au_new_curbuf)) - buf = au_new_curbuf; - else if (curwin->w_jumplistlen > 0) { + // Deleting the current buffer: Need to find another buffer to go to. + // There should be another, otherwise it would have been handled + // above. However, autocommands may have deleted all buffers. + // First use au_new_curbuf.br_buf, if it is valid. + // Then prefer the buffer we most recently visited. + // Else try to find one that is loaded, after the current buffer, + // then before the current buffer. + // Finally use any buffer. + buf = NULL; // Selected buffer. + bp = NULL; // Used when no loaded buffer found. + if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)) { + buf = au_new_curbuf.br_buf; + } else if (curwin->w_jumplistlen > 0) { int jumpidx; jumpidx = curwin->w_jumplistidx - 1; @@ -1183,10 +1241,13 @@ do_buffer ( */ if (action == DOBUF_GOTO && !can_abandon(curbuf, forceit)) { if ((p_confirm || cmdmod.confirm) && p_write) { - dialog_changed(curbuf, FALSE); - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! */ + bufref_T bufref; + set_bufref(&bufref, buf); + dialog_changed(curbuf, false); + if (!bufref_valid(&bufref)) { + // Autocommand deleted buffer, oops! return FAIL; + } } if (bufIsChanged(curbuf)) { EMSG(_(e_nowrtmsg)); @@ -1234,14 +1295,18 @@ void set_curbuf(buf_T *buf, int action) /* close_windows() or apply_autocmds() may change curbuf */ prevbuf = curbuf; + bufref_T bufref; + set_bufref(&bufref, prevbuf); - apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (buf_valid(prevbuf) && !aborting()) { - if (prevbuf == curwin->w_buffer) + if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf) + || (bufref_valid(&bufref) && !aborting())) { + if (prevbuf == curwin->w_buffer) { reset_synblock(curwin); - if (unload) - close_windows(prevbuf, FALSE); - if (buf_valid(prevbuf) && !aborting()) { + } + if (unload) { + close_windows(prevbuf, false); + } + if (bufref_valid(&bufref) && !aborting()) { win_T *previouswin = curwin; if (prevbuf == curbuf) u_sync(FALSE); @@ -1378,6 +1443,8 @@ static int top_file_num = 1; ///< highest file number /// If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list. /// If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer. /// If (flags & BLN_NEW) is TRUE, don't use an existing buffer. +/// If (flags & BLN_NOOPT) is TRUE, don't copy options from the current buffer +/// if the buffer already exists. /// This is the ONLY way to create a new buffer. /// /// @param ffname full path of fname or relative @@ -1408,16 +1475,20 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) && (buf = buflist_findname_file_id(ffname, &file_id, file_id_valid)) != NULL) { xfree(ffname); - if (lnum != 0) - buflist_setfpos(buf, curwin, lnum, (colnr_T)0, FALSE); - /* copy the options now, if 'cpo' doesn't have 's' and not done - * already */ - buf_copy_options(buf, 0); + if (lnum != 0) { + buflist_setfpos(buf, curwin, lnum, (colnr_T)0, false); + } + if ((flags & BLN_NOOPT) == 0) { + // Copy the options now, if 'cpo' doesn't have 's' and not done already. + buf_copy_options(buf, 0); + } if ((flags & BLN_LISTED) && !buf->b_p_bl) { - buf->b_p_bl = TRUE; + buf->b_p_bl = true; + bufref_T bufref; + set_bufref(&bufref, buf); if (!(flags & BLN_DUMMY)) { - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { + if (apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf) + && !bufref_valid(&bufref)) { return NULL; } } @@ -1551,18 +1622,21 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) // Tricky: these autocommands may change the buffer list. They could also // split the window with re-using the one empty buffer. This may result in // unexpectedly losing the empty buffer. - apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { + bufref_T bufref; + set_bufref(&bufref, buf); + if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf) + && !bufref_valid(&bufref)) { return NULL; } - if (flags & BLN_LISTED) { - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { - return NULL; - } + if ((flags & BLN_LISTED) + && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf) + && !bufref_valid(&bufref)) { + return NULL; } - if (aborting()) /* autocmds may abort script processing */ + if (aborting()) { + // Autocmds may abort script processing. return NULL; + } } return buf; @@ -1785,7 +1859,8 @@ buf_T *buflist_findname(char_u *ffname) static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, bool file_id_valid) { - FOR_ALL_BUFFERS(buf) { + // Start at the last buffer, expect to find a match sooner. + FOR_ALL_BUFFERS_BACKWARDS(buf) { if ((buf->b_flags & BF_DUMMY) == 0 && !otherfile_buf(buf, ffname, file_id, file_id_valid)) { return buf; @@ -1859,7 +1934,7 @@ int buflist_findpat( return -1; } - FOR_ALL_BUFFERS(buf) { + FOR_ALL_BUFFERS_BACKWARDS(buf) { if (buf->b_p_bl == find_listed && (!diffmode || diff_mode_buf(buf)) && buflist_match(®match, buf, false) != NULL) { @@ -4223,12 +4298,13 @@ do_arg_all ( || !bufIsChanged(buf)) { /* If the buffer was changed, and we would like to hide it, * try autowriting. */ - if (!P_HID(buf) && buf->b_nwindows <= 1 - && bufIsChanged(buf)) { - (void)autowrite(buf, FALSE); - /* check if autocommands removed the window */ - if (!win_valid(wp) || !buf_valid(buf)) { - wpnext = firstwin; /* start all over... */ + if (!P_HID(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) { + bufref_T bufref; + set_bufref(&bufref, buf); + (void)autowrite(buf, false); + // Check if autocommands removed the window. + if (!win_valid(wp) || !bufref_valid(&bufref)) { + wpnext = firstwin; // Start all over... continue; } } @@ -4442,7 +4518,9 @@ void ex_buffer_all(exarg_T *eap) } if (wp == NULL && split_ret == OK) { - /* Split the window and put the buffer in it */ + bufref_T bufref; + set_bufref(&bufref, buf); + // Split the window and put the buffer in it. p_ea_save = p_ea; p_ea = true; /* use space from all windows */ split_ret = win_split(0, WSP_ROOM | WSP_BELOW); @@ -4454,7 +4532,8 @@ void ex_buffer_all(exarg_T *eap) /* Open the buffer in this window. */ swap_exists_action = SEA_DIALOG; set_curbuf(buf, DOBUF_GOTO); - if (!buf_valid(buf)) { /* autocommands deleted the buffer!!! */ + if (!bufref_valid(&bufref)) { + // Autocommands deleted the buffer. swap_exists_action = SEA_NONE; break; } |