diff options
author | James McCoy <jamessan@jamessan.com> | 2017-02-06 10:20:49 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-06 10:20:49 -0500 |
commit | 11efbc80e328d6e92bdb1cdcf6aeac98050c2077 (patch) | |
tree | ffd45f3dab501339999687416c8a3a1e577d899f /src | |
parent | a767fee8cd0e6874e528d88f5a8dcf70259dabdd (diff) | |
parent | b0cf071d437699b9b89e3735b3df41914f302eb1 (diff) | |
download | rneovim-11efbc80e328d6e92bdb1cdcf6aeac98050c2077.tar.gz rneovim-11efbc80e328d6e92bdb1cdcf6aeac98050c2077.tar.bz2 rneovim-11efbc80e328d6e92bdb1cdcf6aeac98050c2077.zip |
Merge pull request #5913 from mhinz/buf-lookup-patches
vim-patch:7.4.2017,7.4.2018,7.4.2021,7.4.2022,7.4.2023,7.4.2024
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/buffer.c | 12 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 4 | ||||
-rw-r--r-- | src/nvim/buffer.c | 315 | ||||
-rw-r--r-- | src/nvim/buffer.h | 23 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 7 | ||||
-rw-r--r-- | src/nvim/diff.c | 7 | ||||
-rw-r--r-- | src/nvim/eval.c | 8 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 93 | ||||
-rw-r--r-- | src/nvim/ex_cmds2.c | 32 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 9 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 19 | ||||
-rw-r--r-- | src/nvim/fileio.c | 83 | ||||
-rw-r--r-- | src/nvim/fileio.h | 12 | ||||
-rw-r--r-- | src/nvim/globals.h | 11 | ||||
-rw-r--r-- | src/nvim/main.c | 9 | ||||
-rw-r--r-- | src/nvim/memory.c | 8 | ||||
-rw-r--r-- | src/nvim/option.c | 6 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 56 | ||||
-rw-r--r-- | src/nvim/spell.c | 8 | ||||
-rw-r--r-- | src/nvim/version.c | 12 | ||||
-rw-r--r-- | src/nvim/window.c | 38 |
21 files changed, 471 insertions, 301 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index eaaae943d2..738a5ae091 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -290,7 +290,6 @@ void nvim_buf_set_lines(uint64_t channel_id, return; } - buf_T *save_curbuf = NULL; win_T *save_curwin = NULL; tabpage_T *save_curtab = NULL; size_t new_len = replacement.size; @@ -322,6 +321,7 @@ void nvim_buf_set_lines(uint64_t channel_id, } try_start(); + bufref_T save_curbuf = { NULL, 0 }; switch_to_win_for_buf(buf, &save_curwin, &save_curtab, &save_curbuf); if (u_save((linenr_T)(start - 1), (linenr_T)end) == FAIL) { @@ -389,7 +389,7 @@ void nvim_buf_set_lines(uint64_t channel_id, // changed range, and move any in the remainder of the buffer. // Only adjust marks if we managed to switch to a window that holds // the buffer, otherwise line numbers will be invalid. - if (save_curbuf == NULL) { + if (save_curbuf.br_buf == NULL) { mark_adjust((linenr_T)start, (linenr_T)(end - 1), MAXLNUM, extra); } @@ -405,7 +405,7 @@ end: } xfree(lines); - restore_win_for_buf(save_curwin, save_curtab, save_curbuf); + restore_win_for_buf(save_curwin, save_curtab, &save_curbuf); try_end(err); } @@ -651,13 +651,13 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) } pos_T *posp; - buf_T *savebuf; char mark = *name.data; try_start(); - switch_buffer(&savebuf, buf); + bufref_T save_buf; + switch_buffer(&save_buf, buf); posp = getmark(mark, false); - restore_buffer(savebuf); + restore_buffer(&save_buf); if (try_end(err)) { return rv; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7daa4d7207..6f3f654bdc 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -899,7 +899,7 @@ static void set_option_value_for(char *key, { win_T *save_curwin = NULL; tabpage_T *save_curtab = NULL; - buf_T *save_curbuf = NULL; + bufref_T save_curbuf = { NULL, 0 }; try_start(); switch (opt_type) @@ -922,7 +922,7 @@ static void set_option_value_for(char *key, case SREQ_BUF: switch_buffer(&save_curbuf, (buf_T *)from); set_option_value_err(key, numval, stringval, opt_flags, err); - restore_buffer(save_curbuf); + restore_buffer(&save_curbuf); break; case SREQ_GLOBAL: set_option_value_err(key, numval, stringval, opt_flags, err); 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; } diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index 36cbec7e60..193c9f73d8 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -15,9 +15,11 @@ enum getf_values { // Values for buflist_new() flags enum bln_values { - BLN_CURBUF = 1, // May re-use curbuf for new buffer - BLN_LISTED = 2, // Put new buffer in buffer list - BLN_DUMMY = 4, // Allocating dummy buffer + BLN_CURBUF = 1, // May re-use curbuf for new buffer + BLN_LISTED = 2, // Put new buffer in buffer list + BLN_DUMMY = 4, // Allocating dummy buffer + // TODO(mhinz): merge patch that introduces BLN_NEW + BLN_NOOPT = 16, // Don't copy options to existing buffer }; // Values for action argument for do_buffer() @@ -55,21 +57,22 @@ enum bfa_values { static inline void switch_to_win_for_buf(buf_T *buf, win_T **save_curwinp, tabpage_T **save_curtabp, - buf_T **save_curbufp) + bufref_T *save_curbuf) { win_T *wp; tabpage_T *tp; if (!find_win_for_buf(buf, &wp, &tp) - || switch_win(save_curwinp, save_curtabp, wp, tp, true) == FAIL) - switch_buffer(save_curbufp, buf); + || switch_win(save_curwinp, save_curtabp, wp, tp, true) == FAIL) { + switch_buffer(save_curbuf, buf); + } } static inline void restore_win_for_buf(win_T *save_curwin, tabpage_T *save_curtab, - buf_T *save_curbuf) + bufref_T *save_curbuf) { - if (save_curbuf == NULL) { + if (save_curbuf->br_buf == NULL) { restore_win(save_curwin, save_curtab, true); } else { restore_buffer(save_curbuf); @@ -78,12 +81,12 @@ static inline void restore_win_for_buf(win_T *save_curwin, #define WITH_BUFFER(b, code) \ do { \ - buf_T *save_curbuf = NULL; \ win_T *save_curwin = NULL; \ tabpage_T *save_curtab = NULL; \ + bufref_T save_curbuf = { NULL, 0 }; \ switch_to_win_for_buf(b, &save_curwin, &save_curtab, &save_curbuf); \ code; \ - restore_win_for_buf(save_curwin, save_curtab, save_curbuf); \ + restore_win_for_buf(save_curwin, save_curtab, &save_curbuf); \ } while (0) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 29f4dfe4fb..73d06de964 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -8,6 +8,13 @@ typedef struct file_buffer buf_T; // Forward declaration +// Reference to a buffer that stores the value of buf_free_count. +// bufref_valid() only needs to check "buf" when the count differs. +typedef struct { + buf_T *br_buf; + int br_buf_free_count; +} bufref_T; + // for garray_T #include "nvim/garray.h" // for pos_T, lpos_T and linenr_T diff --git a/src/nvim/diff.c b/src/nvim/diff.c index f7b96ba3e1..aafd50687e 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1004,7 +1004,8 @@ theend: void ex_diffsplit(exarg_T *eap) { win_T *old_curwin = curwin; - buf_T *old_curbuf = curbuf; + bufref_T old_curbuf; + set_bufref(&old_curbuf, curbuf); // don't use a new tab page, each tab page has its own diffs cmdmod.tab = 0; @@ -1022,10 +1023,10 @@ void ex_diffsplit(exarg_T *eap) if (win_valid(old_curwin)) { diff_win_options(old_curwin, true); - if (buf_valid(old_curbuf)) { + if (bufref_valid(&old_curbuf)) { // Move the cursor position to that of the old window. curwin->w_cursor.lnum = diff_get_corresponding_line( - old_curbuf, + old_curbuf.br_buf, old_curwin->w_cursor.lnum, curbuf, curwin->w_cursor.lnum); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2ec04eee0b..1cc84f8c16 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10306,7 +10306,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) ExpandInit(&xpc); xpc.xp_pattern = get_tv_string(&argvars[0]); - xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); if (xpc.xp_context == EXPAND_NOTHING) { if (argvars[1].v_type == VAR_STRING) { @@ -10319,17 +10319,17 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (xpc.xp_context == EXPAND_MENUS) { set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, false); - xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } if (xpc.xp_context == EXPAND_CSCOPE) { set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope); - xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } if (xpc.xp_context == EXPAND_SIGN) { set_context_in_sign_cmd(&xpc, xpc.xp_pattern); - xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); + xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index e6e85a3f6c..3b7f3b7b97 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1904,11 +1904,15 @@ void do_wqall(exarg_T *eap) FALSE) == FAIL) { ++error; } else { - if (buf_write_all(buf, eap->forceit) == FAIL) - ++error; - /* an autocommand may have deleted the buffer */ - if (!buf_valid(buf)) + bufref_T bufref; + set_bufref(&bufref, buf); + if (buf_write_all(buf, eap->forceit) == FAIL) { + error++; + } + // An autocommand may have deleted the buffer. + if (!bufref_valid(&bufref)) { buf = firstbuf; + } } eap->forceit = save_forceit; /* check_overwrite() may set it */ } @@ -2081,7 +2085,8 @@ int do_ecmd( char_u *new_name = NULL; int did_set_swapcommand = FALSE; buf_T *buf; - buf_T *old_curbuf = curbuf; + bufref_T bufref; + bufref_T old_curbuf; char_u *free_fname = NULL; int retval = FAIL; long n; @@ -2098,6 +2103,8 @@ int do_ecmd( if (eap != NULL) command = eap->do_ecmd_cmd; + set_bufref(&old_curbuf, curbuf); + if (fnum != 0) { if (fnum == curbuf->b_fnum) /* file is already being edited */ return OK; /* nothing to do */ @@ -2209,23 +2216,27 @@ int do_ecmd( if (oldwin != NULL) { oldwin = curwin; } - old_curbuf = curbuf; + set_bufref(&old_curbuf, curbuf); } if (buf == NULL) goto theend; - if (buf->b_ml.ml_mfp == NULL) { /* no memfile yet */ - oldbuf = FALSE; - } else { /* existing memfile */ - oldbuf = TRUE; - (void)buf_check_timestamp(buf, FALSE); - /* Check if autocommands made buffer invalid or changed the current - * buffer. */ - if (!buf_valid(buf) - || curbuf != old_curbuf - ) + if (buf->b_ml.ml_mfp == NULL) { + // No memfile yet. + oldbuf = false; + } else { + // Existing memfile. + oldbuf = true; + set_bufref(&bufref, buf); + (void)buf_check_timestamp(buf, false); + // Check if autocommands made buffer invalid or changed the current + // buffer. + if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf) { goto theend; - if (aborting()) /* autocmds may abort script processing */ + } + if (aborting()) { + // Autocmds may abort script processing. goto theend; + } } /* May jump to last used line number for a loaded buffer or when asked @@ -2253,12 +2264,14 @@ int do_ecmd( * - If we ended up in the new buffer already, need to skip a few * things, set auto_buf. */ - if (buf->b_fname != NULL) + if (buf->b_fname != NULL) { new_name = vim_strsave(buf->b_fname); - au_new_curbuf = buf; - apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (!buf_valid(buf)) { /* new buffer has been deleted */ - delbuf_msg(new_name); /* frees new_name */ + } + set_bufref(&au_new_curbuf, buf); + apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf); + if (!bufref_valid(&au_new_curbuf)) { + // New buffer has been deleted. + delbuf_msg(new_name); // Frees new_name. goto theend; } if (aborting()) { /* autocmds may abort script processing */ @@ -2272,7 +2285,7 @@ int do_ecmd( // Set the w_closing flag to avoid that autocommands close the window. the_curwin->w_closing = true; - if (curbuf == old_curbuf) { + if (curbuf == old_curbuf.br_buf) { buf_copy_options(buf, BCO_ENTER); } @@ -2290,9 +2303,10 @@ int do_ecmd( xfree(new_name); goto theend; } - /* Be careful again, like above. */ - if (!buf_valid(buf)) { /* new buffer has been deleted */ - delbuf_msg(new_name); /* frees new_name */ + // Be careful again, like above. + if (!bufref_valid(&au_new_curbuf)) { + // New buffer has been deleted. + delbuf_msg(new_name); // Frees new_name. goto theend; } if (buf == curbuf) { // already in new buffer @@ -2325,7 +2339,7 @@ int do_ecmd( } xfree(new_name); - au_new_curbuf = NULL; + au_new_curbuf.br_buf = NULL; } curwin->w_pcmark.lnum = 1; @@ -2378,10 +2392,12 @@ int do_ecmd( solcol = curwin->w_cursor.col; } buf = curbuf; - if (buf->b_fname != NULL) + if (buf->b_fname != NULL) { new_name = vim_strsave(buf->b_fname); - else + } else { new_name = NULL; + } + set_bufref(&bufref, buf); if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) { /* Save all the text, so that the reload can be undone. * Sync first so that this is a separate undo-able action. */ @@ -2394,14 +2410,15 @@ int do_ecmd( u_unchanged(curbuf); buf_freeall(curbuf, BFA_KEEP_UNDO); - /* tell readfile() not to clear or reload undo info */ + // Tell readfile() not to clear or reload undo info. readfile_flags = READ_KEEP_UNDO; - } else - buf_freeall(curbuf, 0); /* free all things for buffer */ - /* If autocommands deleted the buffer we were going to re-edit, give - * up and jump to the end. */ - if (!buf_valid(buf)) { - delbuf_msg(new_name); /* frees new_name */ + } else { + buf_freeall(curbuf, 0); // Free all things for buffer. + } + // If autocommands deleted the buffer we were going to re-edit, give + // up and jump to the end. + if (!bufref_valid(&bufref)) { + delbuf_msg(new_name); // Frees new_name. goto theend; } xfree(new_name); @@ -2472,7 +2489,7 @@ int do_ecmd( if (swap_exists_action == SEA_QUIT) retval = FAIL; - handle_swap_exists(old_curbuf); + handle_swap_exists(&old_curbuf); } else { /* Read the modelines, but only to set window-local options. Any * buffer-local options have already been set and may have been @@ -2607,7 +2624,7 @@ static void delbuf_msg(char_u *name) EMSG2(_("E143: Autocommands unexpectedly deleted new buffer %s"), name == NULL ? (char_u *)"" : name); xfree(name); - au_new_curbuf = NULL; + au_new_curbuf.br_buf = NULL; } static int append_indent = 0; /* autoindent for first line */ diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 3b92b3734a..b4e57bc916 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1181,6 +1181,7 @@ bool prof_def_func(void) int autowrite(buf_T *buf, int forceit) { int r; + bufref_T bufref; if (!(p_aw || p_awa) || !p_write // never autowrite a "nofile" or "nowrite" buffer @@ -1188,11 +1189,12 @@ int autowrite(buf_T *buf, int forceit) || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL) { return FAIL; } + set_bufref(&bufref, buf); r = buf_write_all(buf, forceit); // Writing may succeed but the buffer still changed, e.g., when there is a // conversion error. We do want to return FAIL then. - if (buf_valid(buf) && bufIsChanged(buf)) { + if (bufref_valid(&bufref) && bufIsChanged(buf)) { r = FAIL; } return r; @@ -1207,9 +1209,11 @@ void autowrite_all(void) FOR_ALL_BUFFERS(buf) { if (bufIsChanged(buf) && !buf->b_p_ro) { + bufref_T bufref; + set_bufref(&bufref, buf); (void)buf_write_all(buf, false); // an autocommand may have deleted the buffer - if (!buf_valid(buf)) { + if (!bufref_valid(&bufref)) { buf = firstbuf; } } @@ -1221,6 +1225,8 @@ void autowrite_all(void) bool check_changed(buf_T *buf, int flags) { int forceit = (flags & CCGD_FORCEIT); + bufref_T bufref; + set_bufref(&bufref, buf); if (!forceit && bufIsChanged(buf) @@ -1236,12 +1242,12 @@ bool check_changed(buf_T *buf, int flags) } } } - if (!buf_valid(buf)) { + if (!bufref_valid(&bufref)) { // Autocommand deleted buffer, oops! It's not changed now. return false; } dialog_changed(buf, count > 1); - if (!buf_valid(buf)) { + if (!bufref_valid(&bufref)) { // Autocommand deleted buffer, oops! It's not changed now. return false; } @@ -1300,9 +1306,10 @@ void dialog_changed(buf_T *buf, int checkall) // Skip readonly buffers, these need to be confirmed // individually. FOR_ALL_BUFFERS(buf2) { - if (bufIsChanged(buf2) - && (buf2->b_ffname != NULL) - && !buf2->b_p_ro) { + if (bufIsChanged(buf2) && (buf2->b_ffname != NULL) && !buf2->b_p_ro) { + bufref_T bufref; + set_bufref(&bufref, buf2); + if (buf2->b_fname != NULL && check_overwrite(&ea, buf2, buf2->b_fname, buf2->b_ffname, false) == OK) { @@ -1310,7 +1317,7 @@ void dialog_changed(buf_T *buf, int checkall) (void)buf_write_all(buf2, false); } // an autocommand may have deleted the buffer - if (!buf_valid(buf2)) { + if (!bufref_valid(&bufref)) { buf2 = firstbuf; } } @@ -1407,11 +1414,14 @@ bool check_changed_any(bool hidden, bool unload) continue; } if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf)) { + bufref_T bufref; + set_bufref(&bufref, buf); + // Try auto-writing the buffer. If this fails but the buffer no // longer exists it's not changed, that's OK. if (check_changed(buf, (p_awa ? CCGD_AW : 0) | CCGD_MULTWIN - | CCGD_ALLBUF) && buf_valid(buf)) { + | CCGD_ALLBUF) && bufref_valid(&bufref)) { break; // didn't save - still changes } } @@ -1447,9 +1457,11 @@ bool check_changed_any(bool hidden, bool unload) if (buf != curbuf) { FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { + bufref_T bufref; + set_bufref(&bufref, buf); goto_tabpage_win(tp, wp); // Paranoia: did autocmds wipe out the buffer with changes? - if (!buf_valid(buf)) { + if (!bufref_valid(&bufref)) { goto theend; } goto buf_found; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4070e9fe5b..c5625d7882 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5914,10 +5914,13 @@ ex_win_close ( need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1); if (need_hide && !P_HID(buf) && !forceit) { if ((p_confirm || cmdmod.confirm) && p_write) { - dialog_changed(buf, FALSE); - if (buf_valid(buf) && bufIsChanged(buf)) + bufref_T bufref; + set_bufref(&bufref, buf); + dialog_changed(buf, false); + if (bufref_valid(&bufref) && bufIsChanged(buf)) { return; - need_hide = FALSE; + } + need_hide = false; } else { EMSG(_(e_nowrtmsg)); return; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index dba7a73814..2555b64dfd 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5134,9 +5134,9 @@ int cmd_gchar(int offset) static int ex_window(void) { struct cmdline_info save_ccline; - buf_T *old_curbuf = curbuf; + bufref_T old_curbuf; + bufref_T bufref; win_T *old_curwin = curwin; - buf_T *bp; win_T *wp; int i; linenr_T lnum; @@ -5155,6 +5155,8 @@ static int ex_window(void) return K_IGNORE; } + set_bufref(&old_curbuf, curbuf); + /* Save current window sizes. */ win_size_save(&winsizes); @@ -5269,7 +5271,7 @@ static int ex_window(void) /* Safety check: The old window or buffer was deleted: It's a bug when * this happens! */ - if (!win_valid(old_curwin) || !buf_valid(old_curbuf)) { + if (!win_valid(old_curwin) || !bufref_valid(&old_curbuf)) { cmdwin_result = Ctrl_C; EMSG(_("E199: Active window or buffer deleted")); } else { @@ -5320,14 +5322,15 @@ static int ex_window(void) // Avoid command-line window first character being concealed curwin->w_p_cole = 0; wp = curwin; - bp = curbuf; + set_bufref(&bufref, curbuf); win_goto(old_curwin); win_close(wp, TRUE); - /* win_close() may have already wiped the buffer when 'bh' is - * set to 'wipe' */ - if (buf_valid(bp)) - close_buffer(NULL, bp, DOBUF_WIPE, FALSE); + // win_close() may have already wiped the buffer when 'bh' is + // set to 'wipe'. + if (bufref_valid(&bufref)) { + close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false); + } /* Restore window sizes. */ win_size_restore(&winsizes); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d433afab3e..cdb912ca94 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2402,6 +2402,7 @@ buf_write ( int did_cmd = FALSE; int nofile_err = FALSE; int empty_memline = (buf->b_ml.ml_mfp == NULL); + bufref_T bufref; /* * Apply PRE autocommands. @@ -2417,8 +2418,9 @@ 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); + set_bufref(&bufref, buf); if (append) { if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD, @@ -2466,14 +2468,13 @@ buf_write ( /* restore curwin/curbuf and a few other things */ aucmd_restbuf(&aco); - /* - * In three situations we return here and don't write the file: - * 1. the autocommands deleted or unloaded the buffer. - * 2. The autocommands abort script processing. - * 3. If one of the "Cmd" autocommands was executed. - */ - if (!buf_valid(buf)) + // In three situations we return here and don't write the file: + // 1. the autocommands deleted or unloaded the buffer. + // 2. The autocommands abort script processing. + // 3. If one of the "Cmd" autocommands was executed. + if (!bufref_valid(&bufref)) { buf = NULL; + } if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline) || did_cmd || nofile_err || aborting() @@ -4760,12 +4761,14 @@ check_timestamps ( for (buf = firstbuf; buf != NULL; ) { /* Only check buffers in a window. */ if (buf->b_nwindows > 0) { + bufref_T bufref; + set_bufref(&bufref, buf); n = buf_check_timestamp(buf, focus); - if (didit < n) + if (didit < n) { didit = n; - if (n > 0 && !buf_valid(buf)) { - /* Autocommands have removed the buffer, start at the - * first one again. */ + } + if (n > 0 && !bufref_valid(&bufref)) { + // Autocommands have removed the buffer, start at the first one again. buf = firstbuf; continue; } @@ -4850,6 +4853,9 @@ buf_check_timestamp ( char_u *s; char *reason; + bufref_T bufref; + set_bufref(&bufref, buf); + // If its a terminal, there is no file name, the buffer is not loaded, // 'buftype' is set, we are in the middle of a save or being called // recursively: ignore this buffer. @@ -4919,8 +4925,9 @@ buf_check_timestamp ( allbuf_lock--; busy = false; if (n) { - if (!buf_valid(buf)) + if (!bufref_valid(&bufref)) { EMSG(_("E246: FileChangedShell autocommand deleted buffer")); + } s = get_vim_var_str(VV_FCS_CHOICE); if (STRCMP(s, "reload") == 0 && *reason != 'd') reload = TRUE; @@ -5037,11 +5044,11 @@ buf_check_timestamp ( } } - /* Trigger FileChangedShell when the file was changed in any way. */ - if (buf_valid(buf) && retval != 0) - (void)apply_autocmds(EVENT_FILECHANGEDSHELLPOST, - buf->b_fname, buf->b_fname, FALSE, buf); - + // Trigger FileChangedShell when the file was changed in any way. + if (bufref_valid(&bufref) && retval != 0) { + (void)apply_autocmds(EVENT_FILECHANGEDSHELLPOST, buf->b_fname, buf->b_fname, + false, buf); + } return retval; } @@ -5058,6 +5065,7 @@ void buf_reload(buf_T *buf, int orig_mode) linenr_T old_topline; int old_ro = buf->b_p_ro; buf_T *savebuf; + bufref_T bufref; int saved = OK; aco_save_T aco; int flags = READ_NEW; @@ -5093,6 +5101,7 @@ void buf_reload(buf_T *buf, int orig_mode) } else { // Allocate a buffer without putting it in the buffer list. savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + set_bufref(&bufref, savebuf); if (savebuf != NULL && buf == curbuf) { /* Open the memline. */ curbuf = savebuf; @@ -5117,12 +5126,14 @@ void buf_reload(buf_T *buf, int orig_mode) if (!aborting()) { EMSG2(_("E321: Could not reload \"%s\""), buf->b_fname); } - if (savebuf != NULL && buf_valid(savebuf) && buf == curbuf) { - /* Put the text back from the save buffer. First - * delete any lines that readfile() added. */ - while (!bufempty()) - if (ml_delete(buf->b_ml.ml_line_count, FALSE) == FAIL) + if (savebuf != NULL && bufref_valid(&bufref) && buf == curbuf) { + // Put the text back from the save buffer. First + // delete any lines that readfile() added. + while (!bufempty()) { + if (ml_delete(buf->b_ml.ml_line_count, false) == FAIL) { break; + } + } (void)move_lines(savebuf, buf); } } else if (buf == curbuf) { /* "buf" still valid */ @@ -5139,8 +5150,9 @@ void buf_reload(buf_T *buf, int orig_mode) } xfree(ea.cmd); - if (savebuf != NULL && buf_valid(savebuf)) - wipe_buffer(savebuf, FALSE); + if (savebuf != NULL && bufref_valid(&bufref)) { + wipe_buffer(savebuf, false); + } /* Invalidate diff info if necessary. */ diff_invalidate(curbuf); @@ -6288,6 +6300,7 @@ void ex_doautoall(exarg_T *eap) aco_save_T aco; char_u *arg = eap->arg; int call_do_modelines = check_nomodeline(&arg); + bufref_T bufref; /* * This is a bit tricky: For some commands curwin->w_buffer needs to be @@ -6300,8 +6313,9 @@ void ex_doautoall(exarg_T *eap) if (buf->b_ml.ml_mfp == NULL) { 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); + set_bufref(&bufref, buf); bool did_aucmd; // execute the autocommands for this buffer @@ -6317,9 +6331,10 @@ void ex_doautoall(exarg_T *eap) /* restore the current window */ aucmd_restbuf(&aco); - /* stop if there is some error or buffer was deleted */ - if (retval == FAIL || !buf_valid(buf)) + // Stop if there is some error or buffer was deleted. + if (retval == FAIL || !bufref_valid(&bufref)) { break; + } } check_cursor(); /* just in case lines got deleted */ @@ -6426,7 +6441,7 @@ aucmd_prepbuf ( } curbuf = buf; aco->new_curwin = curwin; - aco->new_curbuf = curbuf; + set_bufref(&aco->new_curbuf, curbuf); } /// Cleanup after executing autocommands for a (hidden) buffer. @@ -6489,14 +6504,14 @@ win_found: // Restore the buffer which was previously edited by curwin, if it was // changed, we are still the same window and the buffer is valid. if (curwin == aco->new_curwin - && curbuf != aco->new_curbuf - && buf_valid(aco->new_curbuf) - && aco->new_curbuf->b_ml.ml_mfp != NULL) { + && curbuf != aco->new_curbuf.br_buf + && bufref_valid(&aco->new_curbuf) + && aco->new_curbuf.br_buf->b_ml.ml_mfp != NULL) { if (curwin->w_s == &curbuf->b_s) { - curwin->w_s = &aco->new_curbuf->b_s; + curwin->w_s = &aco->new_curbuf.br_buf->b_s; } curbuf->b_nwindows--; - curbuf = aco->new_curbuf; + curbuf = aco->new_curbuf.br_buf; curwin->w_buffer = curbuf; curbuf->b_nwindows++; } diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h index ceb101167d..426dc0fcb3 100644 --- a/src/nvim/fileio.h +++ b/src/nvim/fileio.h @@ -19,12 +19,12 @@ * not the current buffer. */ typedef struct { - buf_T *save_curbuf; /* saved curbuf */ - int use_aucmd_win; /* using aucmd_win */ - win_T *save_curwin; /* saved curwin */ - win_T *new_curwin; /* new curwin */ - buf_T *new_curbuf; /* new curbuf */ - char_u *globaldir; /* saved value of globaldir */ + buf_T *save_curbuf; ///< saved curbuf + int use_aucmd_win; ///< using aucmd_win + win_T *save_curwin; ///< saved curwin + win_T *new_curwin; ///< new curwin + bufref_T new_curbuf; ///< new curbuf + char_u *globaldir; ///< saved value of globaldir } aco_save_T; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/globals.h b/src/nvim/globals.h index baa85c01f8..9da6062ffb 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -512,9 +512,9 @@ EXTERN int keep_filetype INIT(= FALSE); /* value for did_filetype when starting to execute autocommands */ -/* When deleting the current buffer, another one must be loaded. If we know - * which one is preferred, au_new_curbuf is set to it */ -EXTERN buf_T *au_new_curbuf INIT(= NULL); +// When deleting the current buffer, another one must be loaded. +// If we know which one is preferred, au_new_curbuf is set to it. +EXTERN bufref_T au_new_curbuf INIT(= { NULL, 0 }); // When deleting a buffer/window and autocmd_busy is TRUE, do not free the // buffer/window. but link it in the list starting with @@ -602,7 +602,10 @@ EXTERN buf_T *lastbuf INIT(= NULL); // last buffer EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer // Iterates over all buffers in the buffer list. -# define FOR_ALL_BUFFERS(buf) for (buf_T *buf = firstbuf; buf != NULL; buf = buf->b_next) +#define FOR_ALL_BUFFERS(buf) \ + for (buf_T *buf = firstbuf; buf != NULL; buf = buf->b_next) +#define FOR_ALL_BUFFERS_BACKWARDS(buf) \ + for (buf_T *buf = lastbuf; buf != NULL; buf = buf->b_prev) /* Flag that is set when switching off 'swapfile'. It means that all blocks * are to be loaded into memory. Shouldn't be global... */ diff --git a/src/nvim/main.c b/src/nvim/main.c index 6194e5f948..88c1990786 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -588,10 +588,13 @@ void getout(int exitval) /* Trigger BufUnload for buffers that are loaded */ FOR_ALL_BUFFERS(buf) { if (buf->b_ml.ml_mfp != NULL) { - apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) /* autocmd may delete the buffer */ + bufref_T bufref; + set_bufref(&bufref, buf); + apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf); + if (!bufref_valid(&bufref)) { + // Autocmd deleted the buffer. break; + } } } apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, FALSE, curbuf); diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 25fa2f150e..51e0dd926e 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -629,12 +629,12 @@ void free_all_mem(void) * were freed already. */ p_acd = false; for (buf = firstbuf; buf != NULL; ) { + bufref_T bufref; + set_bufref(&bufref, buf); nextbuf = buf->b_next; close_buffer(NULL, buf, DOBUF_WIPE, false); - if (buf_valid(buf)) - buf = nextbuf; /* didn't work, try next one */ - else - buf = firstbuf; + // Didn't work, try next one. + buf = bufref_valid(&bufref) ? nextbuf : firstbuf; } free_cmdline_buf(); diff --git a/src/nvim/option.c b/src/nvim/option.c index a4e7da770e..a218323dac 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5588,12 +5588,6 @@ void buf_copy_options(buf_T *buf, int flags) int did_isk = FALSE; /* - * Don't do anything if the buffer is invalid. - */ - if (buf == NULL || !buf_valid(buf)) - return; - - /* * Skip this when the option defaults have not been set yet. Happens when * main() allocates the first buffer. */ diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index cebff5e8b7..c40403e3f6 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1283,11 +1283,17 @@ void copy_loclist(win_T *from, win_T *to) to->w_llist->qf_curlist = qi->qf_curlist; /* current list */ } +// Looking up a buffer can be slow if there are many. Remember the last one to +// make this a lot faster if there are multiple matches in the same file. +static char_u *qf_last_bufname = NULL; +static bufref_T qf_last_bufref = { NULL, 0 }; + // Get buffer number for file "directory.fname". // Also sets the b_has_qf_entry flag. static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname) { - char_u *ptr; + char_u *ptr = NULL; + char_u *bufname; buf_T *buf; if (fname == NULL || *fname == NUL) { // no file name return 0; @@ -1314,11 +1320,22 @@ static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname) ptr = vim_strsave(fname); } } - // Use concatenated directory name and file name - buf = buflist_new(ptr, NULL, (linenr_T)0, 0); + // Use concatenated directory name and file name. + bufname = ptr; + } else { + bufname = fname; + } + + if (qf_last_bufname != NULL + && STRCMP(bufname, qf_last_bufname) == 0 + && bufref_valid(&qf_last_bufref)) { + buf = qf_last_bufref.br_buf; xfree(ptr); } else { - buf = buflist_new(fname, NULL, (linenr_T)0, 0); + xfree(qf_last_bufname); + buf = buflist_new(bufname, NULL, (linenr_T)0, BLN_NOOPT); + qf_last_bufname = (bufname == ptr) ? bufname : vim_strsave(bufname); + set_bufref(&qf_last_bufref, buf); } if (buf == NULL) { return 0; @@ -3717,8 +3734,9 @@ load_dummy_buffer ( ) { buf_T *newbuf; - buf_T *newbuf_to_wipe = NULL; - int failed = TRUE; + bufref_T newbufref; + bufref_T newbuf_to_wipe; + int failed = true; aco_save_T aco; // Allocate a buffer without putting it in the buffer list. @@ -3726,6 +3744,7 @@ load_dummy_buffer ( if (newbuf == NULL) { return NULL; } + set_bufref(&newbufref, newbuf); /* Init the options. */ buf_copy_options(newbuf, BCO_ENTER | BCO_NOHELP); @@ -3745,6 +3764,7 @@ load_dummy_buffer ( * work. */ curbuf->b_flags &= ~BF_DUMMY; + newbuf_to_wipe.br_buf = NULL; if (readfile(fname, NULL, (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW | READ_DUMMY) == OK @@ -3752,19 +3772,24 @@ load_dummy_buffer ( && !(curbuf->b_flags & BF_NEW)) { 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. */ - newbuf_to_wipe = 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 */ + // Restore curwin/curbuf and a few other things. aucmd_restbuf(&aco); - if (newbuf_to_wipe != NULL && buf_valid(newbuf_to_wipe)) - wipe_buffer(newbuf_to_wipe, FALSE); + if (newbuf_to_wipe.br_buf != NULL && bufref_valid(&newbuf_to_wipe)) { + wipe_buffer(newbuf_to_wipe.br_buf, false); + } + + // Add back the "dummy" flag, otherwise buflist_findname_file_id() + // won't skip it. + newbuf->b_flags |= BF_DUMMY; } /* @@ -3775,8 +3800,9 @@ load_dummy_buffer ( os_dirname(resulting_dir, MAXPATHL); restore_start_dir(dirname_start); - if (!buf_valid(newbuf)) + if (!bufref_valid(&newbufref)) { return NULL; + } if (failed) { wipe_dummy_buffer(newbuf, dirname_start); return NULL; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 7e0bbd6ad1..1ace39344d 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3746,6 +3746,9 @@ char_u *did_set_spelllang(win_T *wp) char_u *ret_msg = NULL; char_u *spl_copy; + bufref_T bufref; + set_bufref(&bufref, wp->w_buffer); + // We don't want to do this recursively. May happen when a language is // not available and the SpellFileMissing autocommand opens a new buffer // in which 'spell' is set. @@ -3824,7 +3827,7 @@ char_u *did_set_spelllang(win_T *wp) spell_load_lang(lang); // SpellFileMissing autocommands may do anything, including // destroying the buffer we are using... - if (!buf_valid(wp->w_buffer)) { + if (!bufref_valid(&bufref)) { ret_msg = (char_u *)"E797: SpellFileMissing autocommand deleted buffer"; goto theend; @@ -13037,8 +13040,9 @@ void ex_spelldump(exarg_T *eap) set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); xfree(spl); - if (!bufempty() || !buf_valid(curbuf)) + if (!bufempty()) { return; + } spell_dump_compl(NULL, 0, NULL, eap->forceit ? DUMPFLAG_COUNT : 0); diff --git a/src/nvim/version.c b/src/nvim/version.c index a12621d06f..e73b68b6e7 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -416,14 +416,14 @@ static int included_patches[] = { // 2027 NA // 2026 NA // 2025 NA - // 2024, - // 2023, - // 2022, - // 2021, + 2024, + 2023, + 2022, + 2021, // 2020 NA 2019, - // 2018, - // 2017, + 2018, + 2017, // 2016 NA 2015, 2014, diff --git a/src/nvim/window.c b/src/nvim/window.c index 801969a594..510f182353 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1945,6 +1945,8 @@ int win_close(win_T *win, int free_buf) * Close the link to the buffer. */ if (win->w_buffer != NULL) { + bufref_T bufref; + set_bufref(&bufref, curbuf); win->w_closing = true; close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true); if (win_valid_any_tab(win)) { @@ -1953,7 +1955,7 @@ int win_close(win_T *win, int free_buf) // Make sure curbuf is valid. It can become invalid if 'bufhidden' is // "wipe". - if (!buf_valid(curbuf)) { + if (!bufref_valid(&bufref)) { curbuf = firstbuf; } } @@ -5401,32 +5403,30 @@ void restore_win(win_T *save_curwin, tabpage_T *save_curtab, int no_display) unblock_autocmds(); } -/* - * Make "buf" the current buffer. restore_buffer() MUST be called to undo. - * No autocommands will be executed. Use aucmd_prepbuf() if there are any. - */ -void switch_buffer(buf_T **save_curbuf, buf_T *buf) +/// Make "buf" the current buffer. +/// +/// restore_buffer() MUST be called to undo. +/// No autocommands will be executed. Use aucmd_prepbuf() if there are any. +void switch_buffer(bufref_T *save_curbuf, buf_T *buf) { block_autocmds(); - *save_curbuf = curbuf; - --curbuf->b_nwindows; + set_bufref(save_curbuf, curbuf); + curbuf->b_nwindows--; curbuf = buf; curwin->w_buffer = buf; - ++curbuf->b_nwindows; + curbuf->b_nwindows++; } -/* - * Restore the current buffer after using switch_buffer(). - */ -void restore_buffer(buf_T *save_curbuf) +/// Restore the current buffer after using switch_buffer(). +void restore_buffer(bufref_T *save_curbuf) { unblock_autocmds(); - /* Check for valid buffer, just in case. */ - if (buf_valid(save_curbuf)) { - --curbuf->b_nwindows; - curwin->w_buffer = save_curbuf; - curbuf = save_curbuf; - ++curbuf->b_nwindows; + // Check for valid buffer, just in case. + if (bufref_valid(save_curbuf)) { + curbuf->b_nwindows--; + curwin->w_buffer = save_curbuf->br_buf; + curbuf = save_curbuf->br_buf; + curbuf->b_nwindows++; } } |