aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/buffer.c')
-rw-r--r--src/nvim/buffer.c1828
1 files changed, 1156 insertions, 672 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 438a85dd5d..c15a6f1330 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1,3 +1,6 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
/*
* buffer.c: functions for dealing with the buffer structure
*/
@@ -19,8 +22,11 @@
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
+#include <assert.h>
#include "nvim/api/private/handle.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/api/vim.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/vim.h"
@@ -36,9 +42,11 @@
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
+#include "nvim/file_search.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/hashtab.h"
+#include "nvim/highlight.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
#include "nvim/main.h"
@@ -48,7 +56,6 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
-#include "nvim/misc2.h"
#include "nvim/garray.h"
#include "nvim/move.h"
#include "nvim/option.h"
@@ -60,7 +67,6 @@
#include "nvim/spell.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
-#include "nvim/terminal.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/version.h"
@@ -69,6 +75,13 @@
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/os/input.h"
+#include "nvim/buffer_updates.h"
+
+typedef enum {
+ kBLSUnchanged = 0,
+ kBLSChanged = 1,
+ kBLSDeleted = 2,
+} BufhlLineStatus;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "buffer.c.generated.h"
@@ -78,6 +91,60 @@ 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;
+
+// Read data from buffer for retrying.
+static int
+read_buffer(
+ int read_stdin, // read file from stdin, otherwise fifo
+ exarg_T *eap, // for forced 'ff' and 'fenc' or NULL
+ int flags) // extra flags for readfile()
+{
+ int retval = OK;
+ linenr_T line_count;
+
+ //
+ // Read from the buffer which the text is already filled in and append at
+ // the end. This makes it possible to retry when 'fileformat' or
+ // 'fileencoding' was guessed wrong.
+ //
+ line_count = curbuf->b_ml.ml_line_count;
+ retval = readfile(
+ read_stdin ? NULL : curbuf->b_ffname,
+ read_stdin ? NULL : curbuf->b_fname,
+ (linenr_T)line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap,
+ flags | READ_BUFFER);
+ if (retval == OK) {
+ // Delete the binary lines.
+ while (--line_count >= 0) {
+ ml_delete((linenr_T)1, false);
+ }
+ } else {
+ // Delete the converted lines.
+ while (curbuf->b_ml.ml_line_count > line_count) {
+ ml_delete(line_count, false);
+ }
+ }
+ // Put the cursor on the first line.
+ curwin->w_cursor.lnum = 1;
+ curwin->w_cursor.col = 0;
+
+ if (read_stdin) {
+ // Set or reset 'modified' before executing autocommands, so that
+ // it can be changed there.
+ if (!readonlymode && !BUFEMPTY()) {
+ changed();
+ } else if (retval != FAIL) {
+ unchanged(curbuf, false);
+ }
+
+ apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false,
+ curbuf, &retval);
+ }
+ return retval;
+}
+
/*
* Open current buffer, that is: open the memfile and read the file into
* memory.
@@ -91,8 +158,9 @@ open_buffer (
)
{
int retval = OK;
- buf_T *old_curbuf;
+ bufref_T old_curbuf;
long old_tw = curbuf->b_p_tw;
+ int read_fifo = false;
/*
* The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
@@ -133,32 +201,63 @@ 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;
if (curbuf->b_ffname != NULL) {
int old_msg_silent = msg_silent;
+#ifdef UNIX
+ int save_bin = curbuf->b_p_bin;
+ int perm;
+
+ perm = os_getperm((const char *)curbuf->b_ffname);
+ if (perm >= 0 && (0
+# ifdef S_ISFIFO
+ || S_ISFIFO(perm)
+# endif
+# ifdef S_ISSOCK
+ || S_ISSOCK(perm)
+# endif
+# ifdef OPEN_CHR_FILES
+ || (S_ISCHR(perm)
+ && is_dev_fd_file(curbuf->b_ffname))
+# endif
+ )
+ ) {
+ read_fifo = true;
+ }
+ if (read_fifo) {
+ curbuf->b_p_bin = true;
+ }
+#endif
if (shortmess(SHM_FILEINFO)) {
msg_silent = 1;
}
retval = readfile(curbuf->b_ffname, curbuf->b_fname,
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_NEW);
+ flags | READ_NEW | (read_fifo ? READ_FIFO : 0));
+#ifdef UNIX
+ if (read_fifo) {
+ curbuf->b_p_bin = save_bin;
+ if (retval == OK) {
+ retval = read_buffer(false, eap, flags);
+ }
+ }
+#endif
msg_silent = old_msg_silent;
// Help buffer is filtered.
- if (curbuf->b_help) {
+ if (bt_help(curbuf)) {
fix_help_buffer();
}
} else if (read_stdin) {
int save_bin = curbuf->b_p_bin;
- linenr_T line_count;
/*
* First read the text in binary mode into the buffer.
@@ -172,56 +271,36 @@ open_buffer (
flags | (READ_NEW + READ_STDIN));
curbuf->b_p_bin = save_bin;
if (retval == OK) {
- line_count = curbuf->b_ml.ml_line_count;
- retval = readfile(NULL, NULL, (linenr_T)line_count,
- (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_BUFFER);
- if (retval == OK) {
- /* Delete the binary lines. */
- while (--line_count >= 0)
- ml_delete((linenr_T)1, FALSE);
- } else {
- /* Delete the converted lines. */
- while (curbuf->b_ml.ml_line_count > line_count)
- ml_delete(line_count, FALSE);
- }
- /* Put the cursor on the first line. */
- curwin->w_cursor.lnum = 1;
- curwin->w_cursor.col = 0;
-
- /* Set or reset 'modified' before executing autocommands, so that
- * it can be changed there. */
- if (!readonlymode && !bufempty())
- changed();
- else if (retval != FAIL)
- unchanged(curbuf, FALSE);
- apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE,
- curbuf, &retval);
+ retval = read_buffer(true, eap, flags);
}
}
/* if first time loading this buffer, init b_chartab[] */
if (curbuf->b_flags & BF_NEVERLOADED) {
- (void)buf_init_chartab(curbuf, FALSE);
+ (void)buf_init_chartab(curbuf, false);
parse_cino(curbuf);
}
- /*
- * Set/reset the Changed flag first, autocmds may change the buffer.
- * Apply the automatic commands, before processing the modelines.
- * So the modelines have priority over auto commands.
- */
- /* When reading stdin, the buffer contents always needs writing, so set
- * the changed flag. Unless in readonly mode: "ls | nvim -R -".
- * When interrupted and 'cpoptions' contains 'i' set changed flag. */
+ // Set/reset the Changed flag first, autocmds may change the buffer.
+ // Apply the automatic commands, before processing the modelines.
+ // So the modelines have priority over auto commands.
+
+ // When reading stdin, the buffer contents always needs writing, so set
+ // the changed flag. Unless in readonly mode: "ls | nvim -R -".
+ // When interrupted and 'cpoptions' contains 'i' set changed flag.
if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
- || modified_was_set /* ":set modified" used in autocmd */
- || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
- )
+ || modified_was_set // ":set modified" used in autocmd
+ || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) {
changed();
- else if (retval != FAIL && !read_stdin)
- unchanged(curbuf, FALSE);
- save_file_ff(curbuf); /* keep this fileformat */
+ } else if (retval != FAIL && !read_stdin && !read_fifo) {
+ unchanged(curbuf, false);
+ }
+ save_file_ff(curbuf); // keep this fileformat
+
+ // Set last_changedtick to avoid triggering a TextChanged autocommand right
+ // after it was added.
+ curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
+ curbuf->b_last_changedtick_pum = buf_get_changedtick(curbuf);
/* require "!" to overwrite the file, because it wasn't read completely */
if (aborting())
@@ -246,11 +325,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);
@@ -264,11 +343,45 @@ 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_fnum = buf == NULL ? 0 : buf->b_fnum;
+ bufref->br_buf_free_count = buf_free_count;
+}
+
+/// Return true if "bufref->br_buf" points to the same buffer as when
+/// set_bufref() was called and it is a valid buffer.
+/// Only goes through the buffer list if buf_free_count changed.
+/// Also checks if b_fnum is still the same, a :bwipe followed by :new might get
+/// the same allocated memory, but it's a different buffer.
+///
+/// @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) && bufref->br_fnum == bufref->br_buf->b_fnum;
+}
+
+/// 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
{
- FOR_ALL_BUFFERS(bp) {
+ if (buf == NULL) {
+ return false;
+ }
+ // 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;
}
@@ -276,35 +389,34 @@ bool buf_valid(buf_T *buf)
return false;
}
-/*
- * Close the link to a buffer.
- * "action" is used when there is no longer a window for the buffer.
- * It can be:
- * 0 buffer becomes hidden
- * DOBUF_UNLOAD buffer is unloaded
- * DOBUF_DELETE buffer is unloaded and removed from buffer list
- * DOBUF_WIPE buffer is unloaded and really deleted
- * When doing all but the first one on the current buffer, the caller should
- * get a new buffer very soon!
- *
- * The 'bufhidden' option can force freeing and deleting.
- *
- * When "abort_if_last" is TRUE then do not close the buffer if autocommands
- * cause there to be only one window with this buffer. e.g. when ":quit" is
- * supposed to close the window but autocommands close all other windows.
- */
-void
-close_buffer (
- win_T *win, /* if not NULL, set b_last_cursor */
- buf_T *buf,
- int action,
- int abort_if_last
-)
+/// Close the link to a buffer.
+///
+/// @param win If not NULL, set b_last_cursor.
+/// @param buf
+/// @param action Used when there is no longer a window for the buffer.
+/// Possible values:
+/// 0 buffer becomes hidden
+/// DOBUF_UNLOAD buffer is unloaded
+/// DOBUF_DELETE buffer is unloaded and removed from buffer list
+/// DOBUF_WIPE buffer is unloaded and really deleted
+/// When doing all but the first one on the current buffer, the
+/// caller should get a new buffer very soon!
+/// The 'bufhidden' option can force freeing and deleting.
+/// @param abort_if_last
+/// If TRUE, do not close the buffer if autocommands cause
+/// there to be only one window with this buffer. e.g. when
+/// ":quit" is supposed to close the window but autocommands
+/// close all other windows.
+void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
{
bool unload_buf = (action != 0);
bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
bool wipe_buf = (action == DOBUF_WIPE);
+ bool is_curwin = (curwin != NULL && curwin->w_buffer == buf);
+ win_T *the_curwin = curwin;
+ tabpage_T *the_curtab = curtab;
+
// Force unloading or deleting when 'bufhidden' says so, but not for terminal
// buffers.
// The caller must take care of NOT deleting/freeing when 'bufhidden' is
@@ -328,47 +440,57 @@ close_buffer (
wipe_buf = true;
}
- if (win_valid(win)) {
- /* Set b_last_cursor when closing the last window for the buffer.
- * Remember the last cursor position and window options of the buffer.
- * This used to be only for the current window, but then options like
- * 'foldmethod' may be lost with a ":only" command. */
- if (buf->b_nwindows == 1)
+ // Disallow deleting the buffer when it is locked (already being closed or
+ // halfway a command that relies on it). Unloading is allowed.
+ if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
+ EMSG(_("E937: Attempt to delete a buffer that is in use"));
+ return;
+ }
+
+ if (win != NULL // Avoid bogus clang warning.
+ && win_valid_any_tab(win)) {
+ // Set b_last_cursor when closing the last window for the buffer.
+ // Remember the last cursor position and window options of the buffer.
+ // This used to be only for the current window, but then options like
+ // 'foldmethod' may be lost with a ":only" command.
+ if (buf->b_nwindows == 1) {
set_last_cursor(win);
+ }
buflist_setfpos(buf, win,
win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
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. */
+ buf->b_locked++;
+ if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false,
+ buf) && !bufref_valid(&bufref)) {
+ // Autocommands deleted the buffer.
EMSG(_(e_auabort));
return;
}
- buf->b_closing = false;
+ buf->b_locked--;
if (abort_if_last && one_window()) {
/* Autocommands made this the only window. */
EMSG(_(e_auabort));
return;
}
- /* When the buffer becomes hidden, but is not unloaded, trigger
- * BufHidden */
+ // When the buffer becomes hidden, but is not unloaded, trigger
+ // BufHidden
if (!unload_buf) {
- buf->b_closing = true;
- apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
- FALSE, buf);
- if (!buf_valid(buf)) {
- /* Autocommands deleted the buffer. */
+ buf->b_locked++;
+ if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false,
+ buf) && !bufref_valid(&bufref)) {
+ // Autocommands deleted the buffer.
EMSG(_(e_auabort));
return;
}
- buf->b_closing = false;
+ buf->b_locked--;
if (abort_if_last && one_window()) {
/* Autocommands made this the only window. */
EMSG(_(e_auabort));
@@ -378,20 +500,36 @@ close_buffer (
if (aborting()) /* autocmds may abort script processing */
return;
}
+
+ // If the buffer was in curwin and the window has changed, go back to that
+ // window, if it still exists. This avoids that ":edit x" triggering a
+ // "tabnext" BufUnload autocmd leaves a window behind without a buffer.
+ if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) {
+ block_autocmds();
+ goto_tabpage_win(the_curtab, the_curwin);
+ unblock_autocmds();
+ }
+
int nwindows = buf->b_nwindows;
- /* decrease the link count from windows (unless not in any window) */
- if (buf->b_nwindows > 0)
- --buf->b_nwindows;
+ // decrease the link count from windows (unless not in any window)
+ if (buf->b_nwindows > 0) {
+ buf->b_nwindows--;
+ }
+
+ if (diffopt_hiddenoff() && !unload_buf && buf->b_nwindows == 0) {
+ diff_buf_delete(buf); // Clear 'diff' for hidden buffer.
+ }
/* Return when a window is displaying the buffer or when it's not
* unloaded. */
- if (buf->b_nwindows > 0 || !unload_buf)
+ if (buf->b_nwindows > 0 || !unload_buf) {
return;
+ }
if (buf->terminal) {
terminal_close(buf->terminal, NULL);
- }
+ }
/* Always remove the buffer when there is no file name. */
if (buf->b_ffname == NULL)
@@ -404,23 +542,29 @@ close_buffer (
/* Remember if we are closing the current buffer. Restore the number of
* windows, so that autocommands in buf_freeall() don't get confused. */
bool is_curbuf = (buf == curbuf);
+
+ // When closing the current buffer stop Visual mode before freeing
+ // anything.
+ if (is_curbuf && VIsual_active
+#if defined(EXITFREE)
+ && !entered_free_all_mem
+#endif
+ ) {
+ end_visual_mode();
+ }
+
buf->b_nwindows = nwindows;
buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
- if (win_valid(win) && win->w_buffer == buf) {
- win->w_buffer = NULL; // make sure we don't use the buffer now
- }
- /* 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;
-
- /* Autocommands may have opened or closed windows for this buffer.
- * Decrement the count for the close we do here. */
- if (buf->b_nwindows > 0)
- --buf->b_nwindows;
+ }
/*
* It's possible that autocommands change curbuf to the one being deleted.
@@ -432,9 +576,24 @@ close_buffer (
if (buf == curbuf && !is_curbuf)
return;
+ if (win != NULL // Avoid bogus clang warning.
+ && win_valid_any_tab(win)
+ && win->w_buffer == buf) {
+ win->w_buffer = NULL; // make sure we don't use the buffer now
+ }
+
+ // Autocommands may have opened or closed windows for this buffer.
+ // Decrement the count for the close we do here.
+ if (buf->b_nwindows > 0) {
+ buf->b_nwindows--;
+ }
+
/* Change directories when the 'acd' option is set. */
do_autochdir();
+ // disable buffer updates for the current buffer
+ buf_updates_unregister_all(buf);
+
/*
* Remove the buffer from the list.
*/
@@ -483,35 +642,70 @@ void buf_clear_file(buf_T *buf)
buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */
}
-/*
- * buf_freeall() - free all things allocated for a buffer that are related to
- * the file. flags:
- * BFA_DEL buffer is going to be deleted
- * BFA_WIPE buffer is going to be wiped out
- * BFA_KEEP_UNDO do not free undo information
- */
+/// Clears the current buffer contents.
+void buf_clear(void)
+{
+ linenr_T line_count = curbuf->b_ml.ml_line_count;
+ while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) {
+ ml_delete((linenr_T)1, false);
+ }
+ deleted_lines_mark(1, line_count); // prepare for display
+ ml_close(curbuf, true); // free memline_T
+ buf_clear_file(curbuf);
+}
+
+/// buf_freeall() - free all things allocated for a buffer that are related to
+/// the file. Careful: get here with "curwin" NULL when exiting.
+///
+/// @param flags BFA_DEL buffer is going to be deleted
+/// BFA_WIPE buffer is going to be wiped out
+/// BFA_KEEP_UNDO do not free undo information
void buf_freeall(buf_T *buf, int flags)
{
bool is_curbuf = (buf == curbuf);
+ int is_curwin = (curwin != NULL && curwin->w_buffer == buf);
+ win_T *the_curwin = curwin;
+ tabpage_T *the_curtab = curtab;
- buf->b_closing = true;
- apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf);
- if (!buf_valid(buf)) /* autocommands may delete the buffer */
+ // Make sure the buffer isn't closed by autocommands.
+ buf->b_locked++;
+
+ 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_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_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;
}
- buf->b_closing = false;
- if (aborting()) /* autocmds may abort script processing */
+ 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_locked--;
+
+ // If the buffer was in curwin and the window has changed, go back to that
+ // window, if it still exists. This avoids that ":edit x" triggering a
+ // "tabnext" BufUnload autocmd leaves a window behind without a buffer.
+ if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) {
+ block_autocmds();
+ goto_tabpage_win(the_curtab, the_curwin);
+ unblock_autocmds();
+ }
+ if (aborting()) { // autocmds may abort script processing
+ return;
+ }
/*
* It's possible that autocommands change curbuf to the one being deleted.
@@ -521,10 +715,11 @@ void buf_freeall(buf_T *buf, int flags)
*/
if (buf == curbuf && !is_curbuf)
return;
- diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */
- /* Remove any ownsyntax, unless exiting. */
- if (firstwin != NULL && curwin->w_buffer == buf)
+ diff_buf_delete(buf); // Can't use 'diff' for unloaded buffer.
+ // Remove any ownsyntax, unless exiting.
+ if (curwin != NULL && curwin->w_buffer == buf) {
reset_synblock(curwin);
+ }
/* No folds in an empty buffer. */
FOR_ALL_TAB_WINDOWS(tp, win) {
@@ -550,10 +745,11 @@ 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);
- dict_unref(buf->additional_data);
+ tv_dict_unref(buf->additional_data);
clear_fmark(&buf->b_last_cursor);
clear_fmark(&buf->b_last_insert);
clear_fmark(&buf->b_last_change);
@@ -589,8 +785,18 @@ free_buffer_stuff (
free_buf_options(buf, true);
ga_clear(&buf->b_s.b_langp);
}
+ {
+ // Avoid loosing b:changedtick when deleting buffer: clearing variables
+ // implies using clear_tv() on b:changedtick and that sets changedtick to
+ // zero.
+ hashitem_T *const changedtick_hi = hash_find(
+ &buf->b_vars->dv_hashtab, (const char_u *)"changedtick");
+ assert(changedtick_hi != NULL);
+ hash_remove(&buf->b_vars->dv_hashtab, changedtick_hi);
+ }
vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables
hash_init(&buf->b_vars->dv_hashtab);
+ buf_init_changedtick(buf);
uc_clear(&buf->b_ucmds); // clear local user commands
buf_delete_signs(buf); // delete any signs
bufhl_clear_all(buf); // delete any highligts
@@ -598,6 +804,8 @@ free_buffer_stuff (
map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
xfree(buf->b_start_fenc);
buf->b_start_fenc = NULL;
+
+ buf_updates_unregister_all(buf);
}
/*
@@ -623,11 +831,13 @@ 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;
+ bufref_T old_curbuf;
+ set_bufref(&old_curbuf, curbuf);
swap_exists_action = SEA_DIALOG;
+ (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
+ start, dir, count, eap->forceit);
+
if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') {
cleanup_T cs;
@@ -635,8 +845,8 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count)
* aborting() returns FALSE when closing a window. */
enter_cleanup(&cs);
- /* Quitting means closing the split window, nothing else. */
- win_close(curwin, TRUE);
+ // Quitting means closing the split window, nothing else.
+ win_close(curwin, true);
swap_exists_action = SEA_NONE;
swap_exists_did_quit = TRUE;
@@ -644,36 +854,52 @@ 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
* aborting() returns FALSE when closing a buffer. */
enter_cleanup(&cs);
- /* User selected Quit at ATTENTION prompt. Go back to previous
- * buffer. If that buffer is gone or the same as the current one,
- * open a new, empty buffer. */
- 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) {
- enter_buffer(old_curbuf);
- if (old_tw != curbuf->b_p_tw)
+ // User selected Quit at ATTENTION prompt. Go back to previous
+ // buffer. If that buffer is gone or the same as the current one,
+ // open a new, empty buffer.
+ swap_exists_action = SEA_NONE; // don't want it again
+ swap_exists_did_quit = true;
+ close_buffer(curwin, curbuf, DOBUF_UNLOAD, false);
+ 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 (buf != NULL) {
+ int old_msg_silent = msg_silent;
+
+ if (shortmess(SHM_FILEINFO)) {
+ msg_silent = 1; // prevent fileinfo message
+ }
+ enter_buffer(buf);
+ // restore msg_silent, so that the command line will be shown
+ msg_silent = old_msg_silent;
+
+ if (old_tw != curbuf->b_p_tw) {
check_colorcolumn(curwin);
+ }
}
/* If "old_curbuf" is NULL we are in big trouble here... */
@@ -696,7 +922,7 @@ void handle_swap_exists(buf_T *old_curbuf)
* new aborting error, interrupt, or uncaught exception. */
leave_cleanup(&cs);
}
- swap_exists_action = SEA_NONE;
+ swap_exists_action = SEA_NONE; // -V519
}
/*
@@ -824,6 +1050,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);
@@ -833,15 +1062,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;
}
/*
@@ -943,6 +1174,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. */
@@ -951,28 +1184,41 @@ 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)"),
- (char *)buf->b_fname);
+ if (p_confirm || cmdmod.confirm) {
+ if (!dialog_close_terminal(buf)) {
+ return FAIL;
+ }
+ } else {
+ EMSG2(_("E89: %s will be killed(add ! to override)"),
+ (char *)buf->b_fname);
+ return FAIL;
+ }
} else {
EMSGN(_("E89: No write since last change for buffer %" PRId64
" (add ! to override)"),
buf->b_fnum);
+ return FAIL;
}
- return FAIL;
}
}
+ // When closing the current buffer stop Visual mode.
+ if (buf == curbuf && VIsual_active) {
+ end_visual_mode();
+ }
+
/*
* If deleting the last (listed) buffer, make it empty.
* The last (listed) buffer cannot be unloaded.
@@ -993,9 +1239,9 @@ do_buffer (
* a window with this buffer.
*/
while (buf == curbuf
- && !(curwin->w_closing || curwin->w_buffer->b_closing)
- && (firstwin != lastwin || first_tabpage->tp_next != NULL)) {
- if (win_close(curwin, FALSE) == FAIL)
+ && !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
+ && (!ONE_WINDOW || first_tabpage->tp_next != NULL)) {
+ if (win_close(curwin, false) == FAIL)
break;
}
@@ -1003,27 +1249,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;
@@ -1128,10 +1373,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));
@@ -1177,27 +1425,40 @@ void set_curbuf(buf_T *buf, int action)
/* Don't restart Select mode after switching to another buffer. */
VIsual_reselect = FALSE;
- /* close_windows() or apply_autocmds() may change curbuf */
+ // close_windows() or apply_autocmds() may change curbuf and wipe out "buf"
prevbuf = curbuf;
-
- apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
- if (buf_valid(prevbuf) && !aborting()) {
- if (prevbuf == curwin->w_buffer)
+ bufref_T newbufref;
+ bufref_T prevbufref;
+ set_bufref(&prevbufref, prevbuf);
+ set_bufref(&newbufref, buf);
+
+ // Autocommands may delete the curren buffer and/or the buffer we wan to go
+ // to. In those cases don't close the buffer.
+ if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf)
+ || (bufref_valid(&prevbufref) && bufref_valid(&newbufref)
+ && !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(&prevbufref) && !aborting()) {
win_T *previouswin = curwin;
- if (prevbuf == curbuf)
- u_sync(FALSE);
- close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf,
- unload ? action : (action == DOBUF_GOTO
- && !P_HID(prevbuf)
- && !bufIsChanged(
- prevbuf)) ? DOBUF_UNLOAD : 0, FALSE);
- if (curwin != previouswin && win_valid(previouswin))
- /* autocommands changed curwin, Grr! */
+ if (prevbuf == curbuf) {
+ u_sync(false);
+ }
+ close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL,
+ prevbuf,
+ unload
+ ? action
+ : (action == DOBUF_GOTO && !buf_hide(prevbuf)
+ && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0,
+ false);
+ if (curwin != previouswin && win_valid(previouswin)) {
+ // autocommands changed curwin, Grr!
curwin = previouswin;
+ }
}
}
/* An autocommand may have deleted "buf", already entered it (e.g., when
@@ -1249,10 +1510,6 @@ void enter_buffer(buf_T *buf)
/* mark cursor position as being invalid */
curwin->w_valid = 0;
- if (buf->terminal) {
- terminal_resize(buf->terminal, curwin->w_width, curwin->w_height);
- }
-
/* Make sure the buffer is loaded. */
if (curbuf->b_ml.ml_mfp == NULL) { /* need to load the file */
/* If there is no filetype, allow for detecting one. Esp. useful for
@@ -1310,32 +1567,55 @@ void do_autochdir(void)
}
}
-/*
- * functions for dealing with the buffer list
- */
+//
+// functions for dealing with the buffer list
+//
-/*
- * Add a file name to the buffer list. Return a pointer to the buffer.
- * If the same file name already exists return a pointer to that buffer.
- * If it does not exist, or if fname == NULL, a new entry is created.
- * If (flags & BLN_CURBUF) is TRUE, may use current buffer.
- * 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.
- * This is the ONLY way to create a new buffer.
- */
-static int top_file_num = 1; /* highest file number */
-
-buf_T *
-buflist_new (
- char_u *ffname, /* full path of fname or relative */
- char_u *sfname, /* short fname or NULL */
- linenr_T lnum, /* preferred cursor line */
- int flags /* BLN_ defines */
-)
+static int top_file_num = 1; ///< highest file number
+
+/// Initialize b:changedtick and changedtick_val attribute
+///
+/// @param[out] buf Buffer to intialize for.
+static inline void buf_init_changedtick(buf_T *const buf)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
+{
+ STATIC_ASSERT(sizeof("changedtick") <= sizeof(buf->changedtick_di.di_key),
+ "buf->changedtick_di cannot hold large enough keys");
+ buf->changedtick_di = (ChangedtickDictItem) {
+ .di_flags = DI_FLAGS_RO|DI_FLAGS_FIX, // Must not include DI_FLAGS_ALLOC.
+ .di_tv = (typval_T) {
+ .v_type = VAR_NUMBER,
+ .v_lock = VAR_FIXED,
+ .vval.v_number = buf_get_changedtick(buf),
+ },
+ .di_key = "changedtick",
+ };
+ tv_dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di);
+}
+
+/// Add a file name to the buffer list.
+/// If the same file name already exists return a pointer to that buffer.
+/// If it does not exist, or if fname == NULL, a new entry is created.
+/// If (flags & BLN_CURBUF) is TRUE, may use current buffer.
+/// 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
+/// @param sfname short fname or NULL
+/// @param lnum preferred cursor line
+/// @param flags BLN_ defines
+/// @param bufnr
+///
+/// @return pointer to the buffer
+buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
{
buf_T *buf;
- fname_expand(curbuf, &ffname, &sfname); /* will allocate ffname */
+ fname_expand(curbuf, &ffname, &sfname); // will allocate ffname
/*
* If file name already exists in the list, update the entry.
@@ -1349,16 +1629,20 @@ buflist_new (
&& (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;
}
}
@@ -1379,7 +1663,7 @@ buflist_new (
&& curbuf != NULL
&& curbuf->b_ffname == NULL
&& curbuf->b_nwindows <= 1
- && (curbuf->b_ml.ml_mfp == NULL || bufempty())) {
+ && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())) {
buf = curbuf;
/* It's like this buffer is deleted. Watch out for autocommands that
* change curbuf! If that happens, allocate a new buffer anyway. */
@@ -1397,10 +1681,10 @@ buflist_new (
}
if (buf != curbuf || curbuf == NULL) {
buf = xcalloc(1, sizeof(buf_T));
- handle_register_buffer(buf);
- /* init b: variables */
- buf->b_vars = dict_alloc();
+ // init b: variables
+ buf->b_vars = tv_dict_alloc();
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
+ buf_init_changedtick(buf);
}
if (ffname != NULL) {
@@ -1451,11 +1735,12 @@ buflist_new (
lastbuf = buf;
buf->b_fnum = top_file_num++;
- if (top_file_num < 0) { /* wrap around (may cause duplicates) */
+ handle_register_buffer(buf);
+ if (top_file_num < 0) { // wrap around (may cause duplicates)
EMSG(_("W14: Warning: List of file names overflow"));
if (emsg_silent == 0) {
ui_flush();
- os_delay(3000L, true); /* make sure it is noticed */
+ os_delay(3000L, true); // make sure it is noticed
}
top_file_num = 1;
}
@@ -1484,25 +1769,30 @@ buflist_new (
if (flags & BLN_DUMMY)
buf->b_flags |= BF_DUMMY;
buf_clear_file(buf);
- clrallmarks(buf); /* clear marks */
- fmarks_check_names(buf); /* check file marks for this file */
- buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */
+ clrallmarks(buf); // clear marks
+ fmarks_check_names(buf); // check file marks for this file
+ buf->b_p_bl = (flags & BLN_LISTED) ? true : false; // init 'buflisted'
+ kv_destroy(buf->update_channels);
+ kv_init(buf->update_channels);
if (!(flags & BLN_DUMMY)) {
// 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;
@@ -1526,6 +1816,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_inex);
clear_string_option(&buf->b_p_inde);
clear_string_option(&buf->b_p_indk);
+ clear_string_option(&buf->b_p_fp);
clear_string_option(&buf->b_p_fex);
clear_string_option(&buf->b_p_kp);
clear_string_option(&buf->b_p_mps);
@@ -1533,6 +1824,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_flp);
clear_string_option(&buf->b_p_isk);
clear_string_option(&buf->b_p_keymap);
+ keymap_ga_clear(&buf->b_kmap_ga);
ga_clear(&buf->b_kmap_ga);
clear_string_option(&buf->b_p_com);
clear_string_option(&buf->b_p_cms);
@@ -1566,18 +1858,18 @@ void free_buf_options(buf_T *buf, int free_p_ff)
buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
clear_string_option(&buf->b_p_lw);
clear_string_option(&buf->b_p_bkc);
+ clear_string_option(&buf->b_p_menc);
}
-/*
- * get alternate file n
- * set linenr to lnum or altfpos.lnum if lnum == 0
- * also set cursor column to altfpos.col if 'startofline' is not set.
- * if (options & GETF_SETMARK) call setpcmark()
- * if (options & GETF_ALT) we are jumping to an alternate file.
- * if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping
- *
- * return FAIL for failure, OK for success
- */
+
+/// Get alternate file "n".
+/// Set linenr to "lnum" or altfpos.lnum if "lnum" == 0.
+/// Also set cursor column to altfpos.col if 'startofline' is not set.
+/// if (options & GETF_SETMARK) call setpcmark()
+/// if (options & GETF_ALT) we are jumping to an alternate file.
+/// if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping
+///
+/// Return FAIL for failure, OK for success.
int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
{
buf_T *buf;
@@ -1629,7 +1921,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
// If 'switchbuf' contains "split", "vsplit" or "newtab" and the
// current buffer isn't empty: open new tab or window
if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB))
- && !bufempty()) {
+ && !BUFEMPTY()) {
if (swb_flags & SWB_NEWTAB) {
tabpage_new();
} else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
@@ -1640,10 +1932,10 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
}
}
- ++RedrawingDisabled;
- if (getfile(buf->b_fnum, NULL, NULL, (options & GETF_SETMARK),
- lnum, forceit) <= 0) {
- --RedrawingDisabled;
+ RedrawingDisabled++;
+ if (GETFILE_SUCCESS(getfile(buf->b_fnum, NULL, NULL,
+ (options & GETF_SETMARK), lnum, forceit))) {
+ RedrawingDisabled--;
/* cursor is at to BOL and w_cursor.lnum is checked due to getfile() */
if (!p_sol && col != 0) {
@@ -1654,13 +1946,11 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
}
return OK;
}
- --RedrawingDisabled;
+ RedrawingDisabled--;
return FAIL;
}
-/*
- * go to the last know line number for the current buffer
- */
+// Go to the last known line number for the current buffer.
void buflist_getfpos(void)
{
pos_T *fpos;
@@ -1725,7 +2015,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;
@@ -1799,7 +2090,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(&regmatch, buf, false) != NULL) {
@@ -1913,7 +2204,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options)
if (count == 0) /* no match found, break here */
break;
if (round == 1) {
- *file = xmalloc(count * sizeof(**file));
+ *file = xmalloc((size_t)count * sizeof(**file));
}
}
vim_regfree(regmatch.regprog);
@@ -1968,21 +2259,14 @@ static char_u *fname_match(regmatch_T *rmp, char_u *name, bool ignore_case)
return match;
}
-/*
- * find file in buffer list by number
- */
+/// Find a file in the buffer list by buffer number.
buf_T *buflist_findnr(int nr)
{
if (nr == 0) {
nr = curwin->w_alt_fnum;
}
- FOR_ALL_BUFFERS(buf) {
- if (buf->b_fnum == nr) {
- return buf;
- }
- }
- return NULL;
+ return handle_get_buffer((handle_T)nr);
}
/*
@@ -2128,8 +2412,15 @@ void get_winopts(buf_T *buf)
clear_winopt(&curwin->w_onebuf_opt);
clearFolding(curwin);
- wip = find_wininfo(buf, TRUE);
- if (wip != NULL && wip->wi_optset) {
+ wip = find_wininfo(buf, true);
+ if (wip != NULL && wip->wi_win != curwin && wip->wi_win != NULL
+ && wip->wi_win->w_buffer == buf) {
+ win_T *wp = wip->wi_win;
+ copy_winopt(&wp->w_onebuf_opt, &curwin->w_onebuf_opt);
+ curwin->w_fold_manual = wp->w_fold_manual;
+ curwin->w_foldinvalid = true;
+ cloneFoldGrowArray(&wp->w_folds, &curwin->w_folds);
+ } else if (wip != NULL && wip->wi_optset) {
copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt);
curwin->w_fold_manual = wip->wi_fold_manual;
curwin->w_foldinvalid = true;
@@ -2140,7 +2431,7 @@ void get_winopts(buf_T *buf)
/* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */
if (p_fdls >= 0)
curwin->w_p_fdl = p_fdls;
- check_colorcolumn(curwin);
+ didset_window_options(curwin);
}
/*
@@ -2164,9 +2455,7 @@ linenr_T buflist_findlnum(buf_T *buf)
return buflist_findfpos(buf)->lnum;
}
-/*
- * List all know file names (for :files and :buffers command).
- */
+// List all known file names (for :files and :buffers command).
void buflist_list(exarg_T *eap)
{
buf_T *buf;
@@ -2191,12 +2480,17 @@ void buflist_list(exarg_T *eap)
&& (buf == curbuf || curwin->w_alt_fnum != buf->b_fnum))) {
continue;
}
- msg_putchar('\n');
- if (buf_spname(buf) != NULL)
+ if (buf_spname(buf) != NULL) {
STRLCPY(NameBuff, buf_spname(buf), MAXPATHL);
- else
- home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE);
+ } else {
+ home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, true);
+ }
+ if (message_filtered(NameBuff)) {
+ continue;
+ }
+
+ msg_putchar('\n');
len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
buf->b_fnum,
buf->b_p_bl ? ' ' : 'u',
@@ -2365,10 +2659,11 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum)
{
buf_T *buf;
- /* Create a buffer. 'buflisted' is not set if it's a new buffer */
+ // Create a buffer. 'buflisted' is not set if it's a new buffer
buf = buflist_new(ffname, sfname, lnum, 0);
- if (buf != NULL && !cmdmod.keepalt)
+ if (buf != NULL && !cmdmod.keepalt) {
curwin->w_alt_fnum = buf->b_fnum;
+ }
return buf;
}
@@ -2376,9 +2671,8 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum)
* Get alternate file name for current window.
* Return NULL if there isn't any, and give error message if requested.
*/
-char_u *
-getaltfname (
- int errmsg /* give error message */
+char_u * getaltfname(
+ bool errmsg // give error message
)
{
char_u *fname;
@@ -2403,8 +2697,9 @@ int buflist_add(char_u *fname, int flags)
buf_T *buf;
buf = buflist_new(fname, NULL, (linenr_T)0, flags);
- if (buf != NULL)
+ if (buf != NULL) {
return buf->b_fnum;
+ }
return 0;
}
@@ -2434,7 +2729,7 @@ void buflist_altfpos(win_T *win)
}
/// Check that "ffname" is not the same file as current file.
-/// Fname must have a full path (expanded by path_get_absolute_path()).
+/// Fname must have a full path (expanded by path_to_absolute()).
///
/// @param ffname full path name to check
bool otherfile(char_u *ffname)
@@ -2444,7 +2739,7 @@ bool otherfile(char_u *ffname)
}
/// Check that "ffname" is not the same file as the file loaded in "buf".
-/// Fname must have a full path (expanded by path_get_absolute_path()).
+/// Fname must have a full path (expanded by path_to_absolute()).
///
/// @param buf buffer to check
/// @param ffname full path name to check
@@ -2548,7 +2843,7 @@ fileinfo (
else
name = curbuf->b_ffname;
home_replace(shorthelp ? curbuf : NULL, name, p,
- (int)(IOSIZE - (p - buffer)), TRUE);
+ (size_t)(IOSIZE - (p - buffer)), true);
}
vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s",
@@ -2636,15 +2931,13 @@ static char_u *lasticon = NULL;
void maketitle(void)
{
- char_u *p;
char_u *t_str = NULL;
char_u *i_name;
char_u *i_str = NULL;
int maxlen = 0;
int len;
int mustset;
- char_u buf[IOSIZE];
- int off;
+ char buf[IOSIZE];
if (!redrawing()) {
/* Postpone updating the title when 'lazyredraw' is set. */
@@ -2658,102 +2951,123 @@ void maketitle(void)
if (p_title) {
if (p_titlelen > 0) {
- maxlen = p_titlelen * Columns / 100;
- if (maxlen < 10)
+ maxlen = (int)(p_titlelen * Columns / 100);
+ if (maxlen < 10) {
maxlen = 10;
+ }
}
- t_str = buf;
if (*p_titlestring != NUL) {
if (stl_syntax & STL_IN_TITLE) {
int use_sandbox = FALSE;
int save_called_emsg = called_emsg;
use_sandbox = was_set_insecurely((char_u *)"titlestring", 0);
- called_emsg = FALSE;
- build_stl_str_hl(curwin, t_str, sizeof(buf),
- p_titlestring, use_sandbox,
- 0, maxlen, NULL, NULL);
- if (called_emsg)
- set_string_option_direct((char_u *)"titlestring", -1,
- (char_u *)"", OPT_FREE, SID_ERROR);
+ called_emsg = false;
+ build_stl_str_hl(curwin, (char_u *)buf, sizeof(buf),
+ p_titlestring, use_sandbox,
+ 0, maxlen, NULL, NULL);
+ t_str = (char_u *)buf;
+ if (called_emsg) {
+ set_string_option_direct((char_u *)"titlestring", -1, (char_u *)"",
+ OPT_FREE, SID_ERROR);
+ }
called_emsg |= save_called_emsg;
- } else
+ } else {
t_str = p_titlestring;
+ }
} else {
- /* format: "fname + (path) (1 of 2) - VIM" */
-
-#define SPACE_FOR_FNAME (IOSIZE - 100)
-#define SPACE_FOR_DIR (IOSIZE - 20)
-#define SPACE_FOR_ARGNR (IOSIZE - 10) /* at least room for " - VIM" */
- if (curbuf->b_fname == NULL)
- STRLCPY(buf, _("[No Name]"), SPACE_FOR_FNAME + 1);
- else {
- p = transstr(path_tail(curbuf->b_fname));
- STRLCPY(buf, p, SPACE_FOR_FNAME + 1);
- xfree(p);
+ // Format: "fname + (path) (1 of 2) - VIM".
+
+#define SPACE_FOR_FNAME (sizeof(buf) - 100)
+#define SPACE_FOR_DIR (sizeof(buf) - 20)
+#define SPACE_FOR_ARGNR (sizeof(buf) - 10) // At least room for " - NVIM".
+ char *buf_p = buf;
+ if (curbuf->b_fname == NULL) {
+ const size_t size = xstrlcpy(buf_p, _("[No Name]"),
+ SPACE_FOR_FNAME + 1);
+ buf_p += MIN(size, SPACE_FOR_FNAME);
+ } else {
+ buf_p += transstr_buf((const char *)path_tail(curbuf->b_fname),
+ buf_p, SPACE_FOR_FNAME + 1);
}
switch (bufIsChanged(curbuf)
- + (curbuf->b_p_ro * 2)
- + (!MODIFIABLE(curbuf) * 4)) {
- case 1: STRCAT(buf, " +"); break;
- case 2: STRCAT(buf, " ="); break;
- case 3: STRCAT(buf, " =+"); break;
- case 4:
- case 6: STRCAT(buf, " -"); break;
- case 5:
- case 7: STRCAT(buf, " -+"); break;
+ | (curbuf->b_p_ro << 1)
+ | (!MODIFIABLE(curbuf) << 2)) {
+ case 0: break;
+ case 1: buf_p = strappend(buf_p, " +"); break;
+ case 2: buf_p = strappend(buf_p, " ="); break;
+ case 3: buf_p = strappend(buf_p, " =+"); break;
+ case 4:
+ case 6: buf_p = strappend(buf_p, " -"); break;
+ case 5:
+ case 7: buf_p = strappend(buf_p, " -+"); break;
+ default: assert(false);
}
if (curbuf->b_fname != NULL) {
- /* Get path of file, replace home dir with ~ */
- off = (int)STRLEN(buf);
- buf[off++] = ' ';
- buf[off++] = '(';
- home_replace(curbuf, curbuf->b_ffname,
- buf + off, SPACE_FOR_DIR - off, TRUE);
+ // Get path of file, replace home dir with ~.
+ *buf_p++ = ' ';
+ *buf_p++ = '(';
+ home_replace(curbuf, curbuf->b_ffname, (char_u *)buf_p,
+ (SPACE_FOR_DIR - (size_t)(buf_p - buf)), true);
#ifdef BACKSLASH_IN_FILENAME
- /* avoid "c:/name" to be reduced to "c" */
- if (isalpha(buf[off]) && buf[off + 1] == ':')
- off += 2;
+ // Avoid "c:/name" to be reduced to "c".
+ if (isalpha((uint8_t)buf_p) && *(buf_p + 1) == ':') {
+ buf_p += 2;
+ }
#endif
- /* remove the file name */
- p = path_tail_with_sep(buf + off);
- if (p == buf + off)
- /* must be a help buffer */
- STRLCPY(buf + off, _("help"), SPACE_FOR_DIR - off);
- else
+ // Remove the file name.
+ char *p = (char *)path_tail_with_sep((char_u *)buf_p);
+ if (p == buf_p) {
+ // Must be a help buffer.
+ xstrlcpy(buf_p, _("help"), SPACE_FOR_DIR - (size_t)(buf_p - buf));
+ } else {
*p = NUL;
+ }
- /* Translate unprintable chars and concatenate. Keep some
- * room for the server name. When there is no room (very long
- * file name) use (...). */
- if (off < SPACE_FOR_DIR) {
- p = transstr(buf + off);
- STRLCPY(buf + off, p, SPACE_FOR_DIR - off + 1);
- xfree(p);
+ // Translate unprintable chars and concatenate. Keep some
+ // room for the server name. When there is no room (very long
+ // file name) use (...).
+ if ((size_t)(buf_p - buf) < SPACE_FOR_DIR) {
+ char *const tbuf = transstr(buf_p);
+ const size_t free_space = SPACE_FOR_DIR - (size_t)(buf_p - buf) + 1;
+ const size_t dir_len = xstrlcpy(buf_p, tbuf, free_space);
+ buf_p += MIN(dir_len, free_space - 1);
+ xfree(tbuf);
} else {
- STRLCPY(buf + off, "...", SPACE_FOR_ARGNR - off + 1);
+ const size_t free_space = SPACE_FOR_ARGNR - (size_t)(buf_p - buf) + 1;
+ const size_t dots_len = xstrlcpy(buf_p, "...", free_space);
+ buf_p += MIN(dots_len, free_space - 1);
}
- STRCAT(buf, ")");
+ *buf_p++ = ')';
+ *buf_p = NUL;
+ } else {
+ *buf_p = NUL;
}
- append_arg_number(curwin, buf, SPACE_FOR_ARGNR, FALSE);
+ append_arg_number(curwin, (char_u *)buf_p,
+ (int)(SPACE_FOR_ARGNR - (size_t)(buf_p - buf)), false);
- STRCAT(buf, " - NVIM");
+ xstrlcat(buf_p, " - NVIM", (sizeof(buf) - (size_t)(buf_p - buf)));
if (maxlen > 0) {
- /* make it shorter by removing a bit in the middle */
- if (vim_strsize(buf) > maxlen)
- trunc_string(buf, buf, maxlen, IOSIZE);
+ // Make it shorter by removing a bit in the middle.
+ if (vim_strsize((char_u *)buf) > maxlen) {
+ trunc_string((char_u *)buf, (char_u *)buf, maxlen, sizeof(buf));
+ }
}
+ t_str = (char_u *)buf;
+#undef SPACE_FOR_FNAME
+#undef SPACE_FOR_DIR
+#undef SPACE_FOR_ARGNR
}
}
mustset = ti_change(t_str, &lasttitle);
if (p_icon) {
- i_str = buf;
+ i_str = (char_u *)buf;
if (*p_iconstring != NUL) {
if (stl_syntax & STL_IN_ICON) {
int use_sandbox = FALSE;
@@ -2819,13 +3133,13 @@ static bool ti_change(char_u *str, char_u **last)
return false;
}
-/*
- * Put current window title back (used after calling a shell)
- */
+
+/// Set current window title
void resettitle(void)
{
- ui_set_title((char *)lasttitle);
- ui_set_icon((char *)lasticon);
+ ui_call_set_icon(cstr_as_string((char *)lasticon));
+ ui_call_set_title(cstr_as_string((char *)lasttitle));
+ ui_flush();
}
# if defined(EXITFREE)
@@ -2841,7 +3155,6 @@ void free_titles(void)
/// be used when printing numbers in the status line.
typedef enum {
kNumBaseDecimal = 10,
- kNumBaseOctal = 8,
kNumBaseHexadecimal = 16
} NumberBase;
@@ -2877,13 +3190,13 @@ int build_stl_str_hl(
size_t outlen,
char_u *fmt,
int use_sandbox,
- int fillchar,
+ char_u fillchar,
int maxwidth,
struct stl_hlrec *hltab,
StlClickRecord *tabtab
)
{
- int groupitem[STL_MAX_ITEM];
+ int groupitems[STL_MAX_ITEM];
struct stl_item {
// Where the item starts in the status line output buffer
char_u *start;
@@ -2903,7 +3216,7 @@ int build_stl_str_hl(
ClickFunc,
Trunc
} type;
- } item[STL_MAX_ITEM];
+ } items[STL_MAX_ITEM];
#define TMPLEN 70
char_u tmp[TMPLEN];
char_u *usefmt = fmt;
@@ -2929,10 +3242,11 @@ int build_stl_str_hl(
// Get the byte value now, in case we need it below. This is more
// efficient than making a copy of the line.
int byteval;
- if (wp->w_cursor.col > (colnr_T)STRLEN(line_ptr))
+ if (wp->w_cursor.col > (colnr_T)STRLEN(line_ptr)) {
byteval = 0;
- else
- byteval = (*mb_ptr2char)(line_ptr + wp->w_cursor.col);
+ } else {
+ byteval = utf_ptr2char(line_ptr + wp->w_cursor.col);
+ }
int groupdepth = 0;
@@ -2990,9 +3304,6 @@ int build_stl_str_hl(
// Two `%` in a row is the escape sequence to print a
// single `%` in the output buffer.
if (*fmt_p == '%') {
- // Ignore the character if we're out of room in the output buffer.
- if (out_p >= out_end_p)
- break;
*out_p++ = *fmt_p++;
prevchar_isflag = prevchar_isitem = false;
continue;
@@ -3005,16 +3316,16 @@ int build_stl_str_hl(
if (groupdepth > 0) {
continue;
}
- item[curitem].type = Separate;
- item[curitem++].start = out_p;
+ items[curitem].type = Separate;
+ items[curitem++].start = out_p;
continue;
}
// STL_TRUNCMARK: Where to begin truncating if the statusline is too long.
if (*fmt_p == STL_TRUNCMARK) {
fmt_p++;
- item[curitem].type = Trunc;
- item[curitem++].start = out_p;
+ items[curitem].type = Trunc;
+ items[curitem++].start = out_p;
continue;
}
@@ -3030,7 +3341,7 @@ int build_stl_str_hl(
// Determine how long the group is.
// Note: We set the current output position to null
// so `vim_strsize` will work.
- char_u *t = item[groupitem[groupdepth]].start;
+ char_u *t = items[groupitems[groupdepth]].start;
*out_p = NUL;
long group_len = vim_strsize(t);
@@ -3040,11 +3351,11 @@ int build_stl_str_hl(
// move the output pointer back to where the group started.
// Note: This erases any non-item characters that were in the group.
// Otherwise there would be no reason to do this step.
- if (curitem > groupitem[groupdepth] + 1
- && item[groupitem[groupdepth]].minwid == 0) {
+ if (curitem > groupitems[groupdepth] + 1
+ && items[groupitems[groupdepth]].minwid == 0) {
bool has_normal_items = false;
- for (long n = groupitem[groupdepth] + 1; n < curitem; n++) {
- if (item[n].type == Normal || item[n].type == Highlight) {
+ for (long n = groupitems[groupdepth] + 1; n < curitem; n++) {
+ if (items[n].type == Normal || items[n].type == Highlight) {
has_normal_items = true;
break;
}
@@ -3058,18 +3369,18 @@ int build_stl_str_hl(
// If the group is longer than it is allowed to be
// truncate by removing bytes from the start of the group text.
- if (group_len > item[groupitem[groupdepth]].maxwid) {
+ if (group_len > items[groupitems[groupdepth]].maxwid) {
// { Determine the number of bytes to remove
long n;
if (has_mbyte) {
/* Find the first character that should be included. */
n = 0;
- while (group_len >= item[groupitem[groupdepth]].maxwid) {
+ while (group_len >= items[groupitems[groupdepth]].maxwid) {
group_len -= ptr2cells(t + n);
n += (*mb_ptr2len)(t + n);
}
} else {
- n = (long)(out_p - t) - item[groupitem[groupdepth]].maxwid + 1;
+ n = (long)(out_p - t) - items[groupitems[groupdepth]].maxwid + 1;
}
// }
@@ -3079,25 +3390,26 @@ int build_stl_str_hl(
// { Move the truncated output
memmove(t + 1, t + n, (size_t)(out_p - (t + n)));
out_p = out_p - n + 1;
- /* Fill up space left over by half a double-wide char. */
- while (++group_len < item[groupitem[groupdepth]].minwid)
+ // Fill up space left over by half a double-wide char.
+ while (++group_len < items[groupitems[groupdepth]].minwid) {
*out_p++ = fillchar;
+ }
// }
- /* correct the start of the items for the truncation */
- for (int idx = groupitem[groupdepth] + 1; idx < curitem; idx++) {
+ // correct the start of the items for the truncation
+ for (int idx = groupitems[groupdepth] + 1; idx < curitem; idx++) {
// Shift everything back by the number of removed bytes
- item[idx].start -= n;
+ items[idx].start -= n;
// If the item was partially or completely truncated, set its
// start to the start of the group
- if (item[idx].start < t) {
- item[idx].start = t;
+ if (items[idx].start < t) {
+ items[idx].start = t;
}
}
// If the group is shorter than the minimum width, add padding characters.
- } else if (abs(item[groupitem[groupdepth]].minwid) > group_len) {
- long min_group_width = item[groupitem[groupdepth]].minwid;
+ } else if (abs(items[groupitems[groupdepth]].minwid) > group_len) {
+ long min_group_width = items[groupitems[groupdepth]].minwid;
// If the group is left-aligned, add characters to the right.
if (min_group_width < 0) {
min_group_width = 0 - min_group_width;
@@ -3116,8 +3428,8 @@ int build_stl_str_hl(
// }
// Adjust item start positions
- for (int n = groupitem[groupdepth] + 1; n < curitem; n++) {
- item[n].start += group_len;
+ for (int n = groupitems[groupdepth] + 1; n < curitem; n++) {
+ items[n].start += group_len;
}
// Prepend the fill characters
@@ -3155,9 +3467,9 @@ int build_stl_str_hl(
// User highlight groups override the min width field
// to denote the styling to use.
if (*fmt_p == STL_USER_HL) {
- item[curitem].type = Highlight;
- item[curitem].start = out_p;
- item[curitem].minwid = minwid > 9 ? 1 : minwid;
+ items[curitem].type = Highlight;
+ items[curitem].start = out_p;
+ items[curitem].minwid = minwid > 9 ? 1 : minwid;
fmt_p++;
curitem++;
continue;
@@ -3189,20 +3501,21 @@ int build_stl_str_hl(
if (*fmt_p == STL_TABPAGENR || *fmt_p == STL_TABCLOSENR) {
if (*fmt_p == STL_TABCLOSENR) {
if (minwid == 0) {
- /* %X ends the close label, go back to the previously
- * define tab label nr. */
- for (long n = curitem - 1; n >= 0; --n)
- if (item[n].type == TabPage && item[n].minwid >= 0) {
- minwid = item[n].minwid;
+ // %X ends the close label, go back to the previous tab label nr.
+ for (long n = curitem - 1; n >= 0; n--) {
+ if (items[n].type == TabPage && items[n].minwid >= 0) {
+ minwid = items[n].minwid;
break;
}
- } else
- /* close nrs are stored as negative values */
+ }
+ } else {
+ // close nrs are stored as negative values
minwid = -minwid;
+ }
}
- item[curitem].type = TabPage;
- item[curitem].start = out_p;
- item[curitem].minwid = minwid;
+ items[curitem].type = TabPage;
+ items[curitem].start = out_p;
+ items[curitem].minwid = minwid;
fmt_p++;
curitem++;
continue;
@@ -3217,10 +3530,10 @@ int build_stl_str_hl(
if (*fmt_p != STL_CLICK_FUNC) {
break;
}
- item[curitem].type = ClickFunc;
- item[curitem].start = out_p;
- item[curitem].cmd = xmemdupz(t, (size_t) (((char *) fmt_p - t)));
- item[curitem].minwid = minwid;
+ items[curitem].type = ClickFunc;
+ items[curitem].start = out_p;
+ items[curitem].cmd = xmemdupz(t, (size_t)(((char *)fmt_p - t)));
+ items[curitem].minwid = minwid;
fmt_p++;
curitem++;
continue;
@@ -3243,11 +3556,11 @@ int build_stl_str_hl(
// Denotes the start of a new group
if (*fmt_p == '(') {
- groupitem[groupdepth++] = curitem;
- item[curitem].type = Group;
- item[curitem].start = out_p;
- item[curitem].minwid = minwid;
- item[curitem].maxwid = maxwid;
+ groupitems[groupdepth++] = curitem;
+ items[curitem].type = Group;
+ items[curitem].start = out_p;
+ items[curitem].minwid = minwid;
+ items[curitem].maxwid = maxwid;
fmt_p++;
curitem++;
continue;
@@ -3274,7 +3587,7 @@ int build_stl_str_hl(
case STL_FULLPATH:
case STL_FILENAME:
{
- // Set fillable to false to that ' ' in the filename will not
+ // Set fillable to false so that ' ' in the filename will not
// get replaced with the fillchar
fillable = false;
if (buf_spname(wp->w_buffer) != NULL) {
@@ -3313,7 +3626,7 @@ int build_stl_str_hl(
// Store the current buffer number as a string variable
vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum);
- set_internal_string_var((char_u *)"actual_curbuf", tmp);
+ set_internal_string_var((char_u *)"g:actual_curbuf", tmp);
buf_T *o_curbuf = curbuf;
win_T *o_curwin = curwin;
@@ -3327,7 +3640,7 @@ int build_stl_str_hl(
curbuf = o_curbuf;
// Remove the variable we just stored
- do_unlet((char_u *)"g:actual_curbuf", true);
+ do_unlet(S_LEN("g:actual_curbuf"), true);
// }
@@ -3409,7 +3722,7 @@ int build_stl_str_hl(
case STL_KEYMAP:
fillable = false;
- if (get_keymap_str(wp, tmp, TMPLEN))
+ if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN))
str = tmp;
break;
case STL_PAGENUM:
@@ -3422,9 +3735,11 @@ int build_stl_str_hl(
case STL_OFFSET_X:
base = kNumBaseHexadecimal;
+ FALLTHROUGH;
case STL_OFFSET:
{
- long l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL);
+ long l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL,
+ false);
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ?
0L : l + 1 + (!(State & INSERT) && empty_line ?
0 : (int)wp->w_cursor.col);
@@ -3432,6 +3747,7 @@ int build_stl_str_hl(
}
case STL_BYTEVAL_X:
base = kNumBaseHexadecimal;
+ FALLTHROUGH;
case STL_BYTEVAL:
num = byteval;
if (num == NL)
@@ -3479,7 +3795,7 @@ int build_stl_str_hl(
wp->w_buffer->b_p_ft);
// Uppercase the file extension
for (char_u *t = tmp; *t != 0; t++) {
- *t = TOUPPER_LOC(*t);
+ *t = (char_u)TOUPPER_LOC(*t);
}
str = tmp;
}
@@ -3526,9 +3842,9 @@ int build_stl_str_hl(
// Create a highlight item based on the name
if (*fmt_p == '#') {
- item[curitem].type = Highlight;
- item[curitem].start = out_p;
- item[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t));
+ items[curitem].type = Highlight;
+ items[curitem].start = out_p;
+ items[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t));
curitem++;
fmt_p++;
}
@@ -3539,8 +3855,8 @@ int build_stl_str_hl(
// If we made it this far, the item is normal and starts at
// our current position in the output buffer.
// Non-normal items would have `continued`.
- item[curitem].start = out_p;
- item[curitem].type = Normal;
+ items[curitem].start = out_p;
+ items[curitem].type = Normal;
// Copy the item string into the output buffer
if (str != NULL && *str) {
@@ -3635,9 +3951,7 @@ int build_stl_str_hl(
// Note: The `*` means we take the width as one of the arguments
*t++ = '*';
- *t++ = (char_u) (base == kNumBaseHexadecimal ? 'X'
- : (base == kNumBaseOctal ? 'o'
- : 'd'));
+ *t++ = (char_u)(base == kNumBaseHexadecimal ? 'X' : 'd');
*t = 0;
// }
@@ -3656,7 +3970,8 @@ int build_stl_str_hl(
}
// }
- size_t remaining_buf_len = (out_end_p - out_p) + 1;
+ assert(out_end_p >= out_p);
+ size_t remaining_buf_len = (size_t)(out_end_p - out_p) + 1;
// If the number is going to take up too much room
// Figure out the approximate number in "scientific" type notation.
@@ -3671,7 +3986,7 @@ int build_stl_str_hl(
// { Reduce the number by base^n
while (num_chars-- > maxwid) {
- num /= base;
+ num /= (long)base;
}
// }
@@ -3696,7 +4011,7 @@ int build_stl_str_hl(
// Otherwise, there was nothing to print so mark the item as empty
} else {
- item[curitem].type = Empty;
+ items[curitem].type = Empty;
}
// Only free the string buffer if we allocated it.
@@ -3721,8 +4036,7 @@ int build_stl_str_hl(
}
// We have now processed the entire statusline format string.
- // What follows is post-processing to handle alignment and
- // highlighting factors.
+ // What follows is post-processing to handle alignment and highlighting.
int width = vim_strsize(out);
if (maxwidth > 0 && width > maxwidth) {
@@ -3737,16 +4051,17 @@ int build_stl_str_hl(
// Otherwise, look for the truncation item
} else {
// Default to truncating at the first item
- trunc_p = item[0].start;
+ trunc_p = items[0].start;
item_idx = 0;
- for (int i = 0; i < itemcnt; i++)
- if (item[i].type == Trunc) {
- // Truncate at %< item.
- trunc_p = item[i].start;
+ for (int i = 0; i < itemcnt; i++) {
+ if (items[i].type == Trunc) {
+ // Truncate at %< items.
+ trunc_p = items[i].start;
item_idx = i;
break;
}
+ }
}
// If the truncation point we found is beyond the maximum
@@ -3776,7 +4091,7 @@ int build_stl_str_hl(
// Ignore any items in the statusline that occur after
// the truncation point
for (int i = 0; i < itemcnt; i++) {
- if (item[i].start > trunc_p) {
+ if (items[i].start > trunc_p) {
itemcnt = i;
break;
}
@@ -3831,12 +4146,12 @@ int build_stl_str_hl(
for (int i = item_idx; i < itemcnt; i++) {
// Items starting at or after the end of the truncated section need
// to be moved backwards.
- if (item[i].start >= trunc_end_p) {
- item[i].start -= item_offset;
+ if (items[i].start >= trunc_end_p) {
+ items[i].start -= item_offset;
// Anything inside the truncated area is set to start
// at the `<` truncation character.
} else {
- item[i].start = trunc_p;
+ items[i].start = trunc_p;
}
}
// }
@@ -3847,12 +4162,12 @@ int build_stl_str_hl(
// add characters at the separate marker (if there is one) to
// fill up the available space.
} else if (width < maxwidth
- && STRLEN(out) + maxwidth - width + 1 < outlen) {
+ && STRLEN(out) + (size_t)(maxwidth - width) + 1 < outlen) {
// Find how many separators there are, which we will use when
// figuring out how many groups there are.
int num_separators = 0;
for (int i = 0; i < itemcnt; i++) {
- if (item[i].type == Separate) {
+ if (items[i].type == Separate) {
num_separators++;
}
}
@@ -3864,7 +4179,7 @@ int build_stl_str_hl(
int separator_locations[STL_MAX_ITEM];
int index = 0;
for (int i = 0; i < itemcnt; i++) {
- if (item[i].type == Separate) {
+ if (items[i].type == Separate) {
separator_locations[index] = i;
index++;
}
@@ -3875,18 +4190,18 @@ int build_stl_str_hl(
standard_spaces * (num_separators - 1);
for (int i = 0; i < num_separators; i++) {
- int dislocation = (i == (num_separators - 1)) ?
- final_spaces : standard_spaces;
- char_u *sep_loc = item[separator_locations[i]].start + dislocation;
- STRMOVE(sep_loc, item[separator_locations[i]].start);
- for (char_u *s = item[separator_locations[i]].start; s < sep_loc; s++) {
+ int dislocation = (i == (num_separators - 1))
+ ? final_spaces : standard_spaces;
+ char_u *seploc = items[separator_locations[i]].start + dislocation;
+ STRMOVE(seploc, items[separator_locations[i]].start);
+ for (char_u *s = items[separator_locations[i]].start; s < seploc; s++) {
*s = fillchar;
}
for (int item_idx = separator_locations[i] + 1;
item_idx < itemcnt;
item_idx++) {
- item[item_idx].start += dislocation;
+ items[item_idx].start += dislocation;
}
}
@@ -3898,9 +4213,9 @@ int build_stl_str_hl(
if (hltab != NULL) {
struct stl_hlrec *sp = hltab;
for (long l = 0; l < itemcnt; l++) {
- if (item[l].type == Highlight) {
- sp->start = item[l].start;
- sp->userhl = item[l].minwid;
+ if (items[l].type == Highlight) {
+ sp->start = items[l].start;
+ sp->userhl = items[l].minwid;
sp++;
}
}
@@ -3912,14 +4227,14 @@ int build_stl_str_hl(
if (tabtab != NULL) {
StlClickRecord *cur_tab_rec = tabtab;
for (long l = 0; l < itemcnt; l++) {
- if (item[l].type == TabPage) {
- cur_tab_rec->start = (char *) item[l].start;
- if (item[l].minwid == 0) {
+ if (items[l].type == TabPage) {
+ cur_tab_rec->start = (char *)items[l].start;
+ if (items[l].minwid == 0) {
cur_tab_rec->def.type = kStlClickDisabled;
cur_tab_rec->def.tabnr = 0;
} else {
- int tabnr = item[l].minwid;
- if (item[l].minwid > 0) {
+ int tabnr = items[l].minwid;
+ if (items[l].minwid > 0) {
cur_tab_rec->def.type = kStlClickTabSwitch;
} else {
cur_tab_rec->def.type = kStlClickTabClose;
@@ -3929,11 +4244,11 @@ int build_stl_str_hl(
}
cur_tab_rec->def.func = NULL;
cur_tab_rec++;
- } else if (item[l].type == ClickFunc) {
- cur_tab_rec->start = (char *) item[l].start;
+ } else if (items[l].type == ClickFunc) {
+ cur_tab_rec->start = (char *)items[l].start;
cur_tab_rec->def.type = kStlClickFuncRun;
- cur_tab_rec->def.tabnr = item[l].minwid;
- cur_tab_rec->def.func = item[l].cmd;
+ cur_tab_rec->def.tabnr = items[l].minwid;
+ cur_tab_rec->def.func = items[l].cmd;
cur_tab_rec++;
}
}
@@ -4028,14 +4343,12 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname)
#ifdef WIN32
if (!buf->b_p_bin) {
- char_u *rfname;
-
- /* If the file name is a shortcut file, use the file it links to. */
- rfname = mch_resolve_shortcut(*ffname);
+ // If the file name is a shortcut file, use the file it links to.
+ char *rfname = os_resolve_shortcut((const char *)(*ffname));
if (rfname != NULL) {
xfree(*ffname);
- *ffname = rfname;
- *sfname = rfname;
+ *ffname = (char_u *)rfname;
+ *sfname = (char_u *)rfname;
}
}
#endif
@@ -4085,6 +4398,8 @@ do_arg_all (
win_T *new_curwin = NULL;
tabpage_T *new_curtab = NULL;
+ assert(firstwin != NULL); // satisfy coverity
+
if (ARGCOUNT <= 0) {
/* Don't give an error message. We don't want it when the ":all"
* command is in the .vimrc. */
@@ -4093,7 +4408,7 @@ do_arg_all (
setpcmark();
opened_len = ARGCOUNT;
- opened = xcalloc(opened_len, 1);
+ opened = xcalloc((size_t)opened_len, 1);
/* Autocommands may do anything to the argument list. Make sure it's not
* freed while we are working here by "locking" it. We still have to
@@ -4121,17 +4436,15 @@ do_arg_all (
wpnext = wp->w_next;
buf = wp->w_buffer;
if (buf->b_ffname == NULL
- || (!keep_tabs && buf->b_nwindows > 1)
- || wp->w_width != Columns
- )
+ || (!keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
i = opened_len;
- else {
- /* check if the buffer in this window is in the arglist */
+ } else {
+ // check if the buffer in this window is in the arglist
for (i = 0; i < opened_len; ++i) {
if (i < alist->al_ga.ga_len
&& (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
|| path_full_compare(alist_name(&AARGLIST(alist)[i]),
- buf->b_ffname, TRUE) & kEqualFiles)) {
+ buf->b_ffname, true) & kEqualFiles)) {
int weight = 1;
if (old_curtab == curtab) {
@@ -4164,29 +4477,32 @@ do_arg_all (
}
wp->w_arg_idx = i;
- if (i == opened_len && !keep_tabs) { /* close this window */
- if (P_HID(buf) || forceit || buf->b_nwindows > 1
+ if (i == opened_len && !keep_tabs) { // close this window
+ if (buf_hide(buf) || forceit || buf->b_nwindows > 1
|| !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 (!buf_hide(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;
}
}
- /* don't close last window */
- if (firstwin == lastwin
- && (first_tabpage->tp_next == NULL || !had_tab))
- use_firstwin = TRUE;
- else {
- win_close(wp, !P_HID(buf) && !bufIsChanged(buf));
- /* check if autocommands removed the next window */
- if (!win_valid(wpnext))
- wpnext = firstwin; /* start all over... */
+ // don't close last window
+ if (ONE_WINDOW
+ && (first_tabpage->tp_next == NULL || !had_tab)) {
+ use_firstwin = true;
+ } else {
+ win_close(wp, !buf_hide(buf) && !bufIsChanged(buf));
+ // check if autocommands removed the next window
+ if (!win_valid(wpnext)) {
+ // start all over...
+ wpnext = firstwin;
+ }
}
}
}
@@ -4215,15 +4531,17 @@ do_arg_all (
last_curwin = curwin;
last_curtab = curtab;
win_enter(lastwin, false);
- /* ":drop all" should re-use an empty window to avoid "--remote-tab"
- * leaving an empty tab page when executed locally. */
- if (keep_tabs && bufempty() && curbuf->b_nwindows == 1
- && curbuf->b_ffname == NULL && !curbuf->b_changed)
- use_firstwin = TRUE;
-
- for (i = 0; i < count && i < opened_len && !got_int; ++i) {
- if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1)
- arg_had_last = TRUE;
+ // ":drop all" should re-use an empty window to avoid "--remote-tab"
+ // leaving an empty tab page when executed locally.
+ if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1
+ && curbuf->b_ffname == NULL && !curbuf->b_changed) {
+ use_firstwin = true;
+ }
+
+ for (i = 0; i < count && i < opened_len && !got_int; i++) {
+ if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
+ arg_had_last = true;
+ }
if (opened[i] > 0) {
/* Move the already present window to below the current window */
if (curwin->w_arg_idx != i) {
@@ -4258,14 +4576,15 @@ do_arg_all (
new_curwin = curwin;
new_curtab = curtab;
}
- (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL,
- ECMD_ONE,
- ((P_HID(curwin->w_buffer)
- || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0)
- + ECMD_OLDBUF, curwin);
- if (use_firstwin)
- ++autocmd_no_leave;
- use_firstwin = FALSE;
+ (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
+ ((buf_hide(curwin->w_buffer)
+ || bufIsChanged(curwin->w_buffer))
+ ? ECMD_HIDE : 0) + ECMD_OLDBUF,
+ curwin);
+ if (use_firstwin) {
+ autocmd_no_leave++;
+ }
+ use_firstwin = false;
}
os_breakcheck();
@@ -4277,19 +4596,23 @@ do_arg_all (
/* Remove the "lock" on the argument list. */
alist_unlink(alist);
- --autocmd_no_enter;
- /* restore last referenced tabpage's curwin */
+ autocmd_no_enter--;
+ // restore last referenced tabpage's curwin
if (last_curtab != new_curtab) {
- if (valid_tabpage(last_curtab))
- goto_tabpage_tp(last_curtab, TRUE, TRUE);
- if (win_valid(last_curwin))
+ if (valid_tabpage(last_curtab)) {
+ goto_tabpage_tp(last_curtab, true, true);
+ }
+ if (win_valid(last_curwin)) {
win_enter(last_curwin, false);
+ }
+ }
+ // to window with first arg
+ if (valid_tabpage(new_curtab)) {
+ goto_tabpage_tp(new_curtab, true, true);
}
- /* to window with first arg */
- if (valid_tabpage(new_curtab))
- goto_tabpage_tp(new_curtab, TRUE, TRUE);
- if (win_valid(new_curwin))
+ if (win_valid(new_curwin)) {
win_enter(new_curwin, false);
+ }
--autocmd_no_leave;
xfree(opened);
@@ -4306,8 +4629,8 @@ void ex_buffer_all(exarg_T *eap)
bool p_ea_save;
int open_wins = 0;
int r;
- int count; /* Maximum number of windows to open. */
- int all; /* When TRUE also load inactive buffers. */
+ long count; // Maximum number of windows to open.
+ int all; // When TRUE also load inactive buffers.
int had_tab = cmdmod.tab;
tabpage_T *tpnext;
@@ -4338,14 +4661,14 @@ void ex_buffer_all(exarg_T *eap)
? wp->w_height + wp->w_status_height < Rows - p_ch
- tabline_height()
: wp->w_width != Columns)
- || (had_tab > 0 && wp != firstwin)
- ) && firstwin != lastwin
- && !(wp->w_closing || wp->w_buffer->b_closing)
+ || (had_tab > 0 && wp != firstwin))
+ && !ONE_WINDOW
+ && !(wp->w_closing || wp->w_buffer->b_locked > 0)
) {
- win_close(wp, FALSE);
- wpnext = firstwin; /* just in case an autocommand does
- something strange with windows */
- tpnext = first_tabpage; /* start all over...*/
+ win_close(wp, false);
+ wpnext = firstwin; // just in case an autocommand does
+ // something strange with windows
+ tpnext = first_tabpage; // start all over...
open_wins = 0;
} else
++open_wins;
@@ -4388,7 +4711,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);
@@ -4400,7 +4725,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;
}
@@ -4411,9 +4737,9 @@ void ex_buffer_all(exarg_T *eap)
* aborting() returns FALSE when closing a window. */
enter_cleanup(&cs);
- /* User selected Quit at ATTENTION prompt; close this window. */
- win_close(curwin, TRUE);
- --open_wins;
+ // User selected Quit at ATTENTION prompt; close this window.
+ win_close(curwin, true);
+ open_wins--;
swap_exists_action = SEA_NONE;
swap_exists_did_quit = TRUE;
@@ -4445,14 +4771,14 @@ void ex_buffer_all(exarg_T *eap)
* Close superfluous windows.
*/
for (wp = lastwin; open_wins > count; ) {
- r = (P_HID(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
- || autowrite(wp->w_buffer, FALSE) == OK);
+ r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
+ || autowrite(wp->w_buffer, false) == OK);
if (!win_valid(wp)) {
/* BufWrite Autocommands made the window invalid, start over */
wp = lastwin;
} else if (r) {
- win_close(wp, !P_HID(wp->w_buffer));
- --open_wins;
+ win_close(wp, !buf_hide(wp->w_buffer));
+ open_wins--;
wp = lastwin;
} else {
wp = wp->w_prev;
@@ -4514,7 +4840,7 @@ chk_modeline (
char_u *e;
char_u *linecopy; /* local copy of any modeline found */
int prev;
- int vers;
+ intmax_t vers;
int end;
int retval = OK;
char_u *save_sourcing_name;
@@ -4533,7 +4859,10 @@ chk_modeline (
e = s + 4;
else
e = s + 3;
- vers = getdigits_int(&e);
+ if (getdigits_safe(&e, &vers) != OK) {
+ continue;
+ }
+
if (*e == ':'
&& (s[0] != 'V'
|| STRNCMP(skipwhite(e + 1), "set", 3) == 0)
@@ -4541,8 +4870,9 @@ chk_modeline (
|| (VIM_VERSION_100 >= vers && isdigit(s[3]))
|| (VIM_VERSION_100 < vers && s[3] == '<')
|| (VIM_VERSION_100 > vers && s[3] == '>')
- || (VIM_VERSION_100 == vers && s[3] == '=')))
+ || (VIM_VERSION_100 == vers && s[3] == '='))) {
break;
+ }
}
}
prev = *s;
@@ -4614,6 +4944,12 @@ chk_modeline (
return retval;
}
+// Return true if "buf" is a help buffer.
+bool bt_help(const buf_T *const buf)
+{
+ return buf != NULL && buf->b_help;
+}
+
/*
* Return special buffer name.
* Returns NULL when the buffer has a normal file name.
@@ -4753,13 +5089,16 @@ linenr_T buf_change_sign_type(
return (linenr_T)0;
}
-int buf_getsigntype(
- buf_T *buf,
- linenr_T lnum,
- int type /* SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL */
- )
+/// Gets a sign from a given line.
+/// In case of multiple signs, returns the most recently placed one.
+///
+/// @param buf Buffer in which to search
+/// @param lnum Line in which to search
+/// @param type Type of sign to look for
+/// @return Identifier of the first matching sign, or 0
+int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type)
{
- signlist_T *sign; /* a sign in a b_signlist */
+ signlist_T *sign; // a sign in a b_signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->lnum == lnum
@@ -4767,7 +5106,9 @@ int buf_getsigntype(
|| (type == SIGN_TEXT
&& sign_get_text(sign->typenr) != NULL)
|| (type == SIGN_LINEHL
- && sign_get_attr(sign->typenr, TRUE) != 0))) {
+ && sign_get_attr(sign->typenr, SIGN_LINEHL) != 0)
+ || (type == SIGN_NUMHL
+ && sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) {
return sign->typenr;
}
}
@@ -4824,7 +5165,7 @@ int buf_findsign(
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->id == id) {
- return sign->lnum;
+ return (int)sign->lnum;
}
}
@@ -4900,7 +5241,7 @@ void sign_list_placed(buf_T *rbuf)
while (buf != NULL && !got_int) {
if (buf->b_signlist != NULL) {
vim_snprintf(lbuf, BUFSIZ, _("Signs for %s:"), buf->b_fname);
- MSG_PUTS_ATTR(lbuf, hl_attr(HLF_D));
+ MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D));
msg_putchar('\n');
}
for (p = buf->b_signlist; p != NULL && !got_int; p = p->next) {
@@ -4938,6 +5279,30 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a
// bufhl: plugin highlights associated with a buffer
+/// Get reference to line in kbtree_t
+///
+/// @param b the three
+/// @param line the linenumber to lookup
+/// @param put if true, put a new line when not found
+/// if false, return NULL when not found
+BufhlLine *bufhl_tree_ref(BufhlInfo *b, linenr_T line, bool put)
+{
+ BufhlLine t = BUFHLLINE_INIT(line);
+
+ // kp_put() only works if key is absent, try get first
+ BufhlLine **pp = kb_get(bufhl, b, &t);
+ if (pp) {
+ return *pp;
+ } else if (!put) {
+ return NULL;
+ }
+
+ BufhlLine *p = xmalloc(sizeof(*p));
+ *p = (BufhlLine)BUFHLLINE_INIT(line);
+ kb_put(bufhl, b, p);
+ return p;
+}
+
/// Adds a highlight to buffer.
///
/// Unlike matchaddpos() highlights follow changes to line numbering (as lines
@@ -4966,34 +5331,105 @@ int bufhl_add_hl(buf_T *buf,
int hl_id,
linenr_T lnum,
colnr_T col_start,
- colnr_T col_end) {
- static int next_src_id = 1;
+ colnr_T col_end)
+{
if (src_id == 0) {
- src_id = next_src_id++;
+ src_id = (int)nvim_create_namespace((String)STRING_INIT);
}
if (hl_id <= 0) {
// no highlight group or invalid line, just return src_id
return src_id;
}
- if (!buf->b_bufhl_info) {
- buf->b_bufhl_info = map_new(linenr_T, bufhl_vec_T)();
- }
- bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info,
- lnum, true);
- bufhl_hl_item_T *hlentry = kv_pushp(*lineinfo);
+ BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, true);
+
+ BufhlItem *hlentry = kv_pushp(lineinfo->items);
hlentry->src_id = src_id;
hlentry->hl_id = hl_id;
hlentry->start = col_start;
hlentry->stop = col_end;
if (0 < lnum && lnum <= buf->b_ml.ml_line_count) {
- changed_lines_buf(buf, lnum, lnum+1, 0);
- redraw_buf_later(buf, VALID);
+ redraw_buf_line_later(buf, lnum);
+ }
+ return src_id;
+}
+
+/// Add highlighting to a buffer, bounded by two cursor positions,
+/// with an offset.
+///
+/// @param buf Buffer to add highlights to
+/// @param src_id src_id to use or 0 to use a new src_id group,
+/// or -1 for ungrouped highlight.
+/// @param hl_id Highlight group id
+/// @param pos_start Cursor position to start the hightlighting at
+/// @param pos_end Cursor position to end the highlighting at
+/// @param offset Move the whole highlighting this many columns to the right
+void bufhl_add_hl_pos_offset(buf_T *buf,
+ int src_id,
+ int hl_id,
+ lpos_T pos_start,
+ lpos_T pos_end,
+ colnr_T offset)
+{
+ colnr_T hl_start = 0;
+ colnr_T hl_end = 0;
+
+ for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) {
+ if (pos_start.lnum < lnum && lnum < pos_end.lnum) {
+ hl_start = offset;
+ hl_end = MAXCOL;
+ } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) {
+ hl_start = pos_start.col + offset + 1;
+ hl_end = MAXCOL;
+ } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) {
+ hl_start = offset;
+ hl_end = pos_end.col + offset;
+ } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) {
+ hl_start = pos_start.col + offset + 1;
+ hl_end = pos_end.col + offset;
+ }
+ (void)bufhl_add_hl(buf, src_id, hl_id, lnum, hl_start, hl_end);
+ }
+}
+
+int bufhl_add_virt_text(buf_T *buf,
+ int src_id,
+ linenr_T lnum,
+ VirtText virt_text)
+{
+ if (src_id == 0) {
+ src_id = (int)nvim_create_namespace((String)STRING_INIT);
+ }
+
+ BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, true);
+
+ bufhl_clear_virttext(&lineinfo->virt_text);
+ if (kv_size(virt_text) > 0) {
+ lineinfo->virt_text_src = src_id;
+ lineinfo->virt_text = virt_text;
+ } else {
+ lineinfo->virt_text_src = 0;
+ // currently not needed, but allow a future caller with
+ // 0 size and non-zero capacity
+ kv_destroy(virt_text);
+ }
+
+ if (0 < lnum && lnum <= buf->b_ml.ml_line_count) {
+ redraw_buf_line_later(buf, lnum);
}
return src_id;
}
+static void bufhl_clear_virttext(VirtText *text)
+{
+ for (size_t i = 0; i < kv_size(*text); i++) {
+ xfree(kv_A(*text, i).text);
+ }
+ kv_destroy(*text);
+ *text = (VirtText)KV_INITIAL_VALUE;
+}
+
/// Clear bufhl highlights from a given source group and range of lines.
///
/// @param buf The buffer to remove highlights from
@@ -5003,33 +5439,29 @@ int bufhl_add_hl(buf_T *buf,
void bufhl_clear_line_range(buf_T *buf,
int src_id,
linenr_T line_start,
- linenr_T line_end) {
- if (!buf->b_bufhl_info) {
- return;
- }
- linenr_T line;
- linenr_T first_changed = MAXLNUM, last_changed = -1;
- // In the case line_start - line_end << bufhl_info->size
- // it might be better to reverse this, i e loop over the lines
- // to clear on.
- bufhl_vec_T unused;
- map_foreach(buf->b_bufhl_info, line, unused, {
- (void)unused;
- if (line_start <= line && line <= line_end) {
- if (bufhl_clear_line(buf->b_bufhl_info, src_id, line)) {
- if (line > last_changed) {
- last_changed = line;
- }
- if (line < first_changed) {
- first_changed = line;
- }
+ linenr_T line_end)
+{
+ kbitr_t(bufhl) itr;
+ BufhlLine *l, t = BUFHLLINE_INIT(line_start);
+ if (!kb_itr_get(bufhl, &buf->b_bufhl_info, &t, &itr)) {
+ kb_itr_next(bufhl, &buf->b_bufhl_info, &itr);
+ }
+ for (; kb_itr_valid(&itr); kb_itr_next(bufhl, &buf->b_bufhl_info, &itr)) {
+ l = kb_itr_key(&itr);
+ linenr_T line = l->line;
+ if (line > line_end) {
+ break;
+ }
+ if (line_start <= line) {
+ BufhlLineStatus status = bufhl_clear_line(l, src_id, line);
+ if (status != kBLSUnchanged) {
+ redraw_buf_line_later(buf, line);
+ }
+ if (status == kBLSDeleted) {
+ kb_del_itr(bufhl, &buf->b_bufhl_info, &itr);
+ xfree(l);
}
}
- })
-
- if (last_changed != -1) {
- changed_lines_buf(buf, first_changed, last_changed+1, 0);
- redraw_buf_later(buf, VALID);
}
}
@@ -5038,40 +5470,52 @@ void bufhl_clear_line_range(buf_T *buf,
/// @param bufhl_info The highlight info for the buffer
/// @param src_id Highlight source group to clear, or -1 to clear all groups.
/// @param lnum Linenr where the highlight should be cleared
-static bool bufhl_clear_line(bufhl_info_T *bufhl_info, int src_id, int lnum) {
- bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(bufhl_info,
- lnum, false);
- size_t oldsize = kv_size(*lineinfo);
+static BufhlLineStatus bufhl_clear_line(BufhlLine *lineinfo, int src_id,
+ linenr_T lnum)
+{
+ BufhlLineStatus changed = kBLSUnchanged;
+ size_t oldsize = kv_size(lineinfo->items);
if (src_id < 0) {
- kv_size(*lineinfo) = 0;
+ kv_size(lineinfo->items) = 0;
} else {
- size_t newind = 0;
- for (size_t i = 0; i < kv_size(*lineinfo); i++) {
- if (kv_A(*lineinfo, i).src_id != src_id) {
- if (i != newind) {
- kv_A(*lineinfo, newind) = kv_A(*lineinfo, i);
+ size_t newidx = 0;
+ for (size_t i = 0; i < kv_size(lineinfo->items); i++) {
+ if (kv_A(lineinfo->items, i).src_id != src_id) {
+ if (i != newidx) {
+ kv_A(lineinfo->items, newidx) = kv_A(lineinfo->items, i);
}
- newind++;
+ newidx++;
}
}
- kv_size(*lineinfo) = newind;
+ kv_size(lineinfo->items) = newidx;
+ }
+ if (kv_size(lineinfo->items) != oldsize) {
+ changed = kBLSChanged;
+ }
+
+ if (kv_size(lineinfo->virt_text) != 0
+ && (src_id < 0 || src_id == lineinfo->virt_text_src)) {
+ bufhl_clear_virttext(&lineinfo->virt_text);
+ lineinfo->virt_text_src = 0;
+ changed = kBLSChanged;
}
- if (kv_size(*lineinfo) == 0) {
- kv_destroy(*lineinfo);
- map_del(linenr_T, bufhl_vec_T)(bufhl_info, lnum);
+ if (kv_size(lineinfo->items) == 0 && kv_size(lineinfo->virt_text) == 0) {
+ kv_destroy(lineinfo->items);
+ return kBLSDeleted;
}
- return kv_size(*lineinfo) != oldsize;
+ return changed;
}
+
/// Remove all highlights and free the highlight data
-void bufhl_clear_all(buf_T* buf) {
- if (!buf->b_bufhl_info) {
- return;
- }
+void bufhl_clear_all(buf_T *buf)
+{
bufhl_clear_line_range(buf, -1, 1, MAXLNUM);
- map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info);
- buf->b_bufhl_info = NULL;
+ kb_destroy(bufhl, (&buf->b_bufhl_info));
+ kb_init(&buf->b_bufhl_info);
+ kv_destroy(buf->b_bufhl_move_space);
+ kv_init(buf->b_bufhl_move_space);
}
/// Adjust a placed highlight for inserted/deleted lines.
@@ -5079,29 +5523,49 @@ void bufhl_mark_adjust(buf_T* buf,
linenr_T line1,
linenr_T line2,
long amount,
- long amount_after) {
- if (!buf->b_bufhl_info) {
+ long amount_after,
+ bool end_temp)
+{
+ kbitr_t(bufhl) itr;
+ BufhlLine *l, t = BUFHLLINE_INIT(line1);
+ if (end_temp && amount < 0) {
+ // Move all items from b_bufhl_move_space to the btree.
+ for (size_t i = 0; i < kv_size(buf->b_bufhl_move_space); i++) {
+ l = kv_A(buf->b_bufhl_move_space, i);
+ l->line += amount;
+ kb_put(bufhl, &buf->b_bufhl_info, l);
+ }
+ kv_size(buf->b_bufhl_move_space) = 0;
return;
}
- bufhl_info_T *newmap = map_new(linenr_T, bufhl_vec_T)();
- linenr_T line;
- bufhl_vec_T lineinfo;
- map_foreach(buf->b_bufhl_info, line, lineinfo, {
- if (line >= line1 && line <= line2) {
+ if (!kb_itr_get(bufhl, &buf->b_bufhl_info, &t, &itr)) {
+ kb_itr_next(bufhl, &buf->b_bufhl_info, &itr);
+ }
+ for (; kb_itr_valid(&itr); kb_itr_next(bufhl, &buf->b_bufhl_info, &itr)) {
+ l = kb_itr_key(&itr);
+ if (l->line >= line1 && l->line <= line2) {
+ if (end_temp && amount > 0) {
+ kb_del_itr(bufhl, &buf->b_bufhl_info, &itr);
+ kv_push(buf->b_bufhl_move_space, l);
+ }
if (amount == MAXLNUM) {
- bufhl_clear_line(buf->b_bufhl_info, -1, line);
- continue;
+ if (bufhl_clear_line(l, -1, l->line) == kBLSDeleted) {
+ kb_del_itr(bufhl, &buf->b_bufhl_info, &itr);
+ xfree(l);
+ } else {
+ assert(false);
+ }
} else {
- line += amount;
+ l->line += amount;
}
- } else if (line > line2) {
- line += amount_after;
+ } else if (l->line > line2) {
+ if (amount_after == 0) {
+ break;
+ }
+ l->line += amount_after;
}
- map_put(linenr_T, bufhl_vec_T)(newmap, line, lineinfo);
- });
- map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info);
- buf->b_bufhl_info = newmap;
+ }
}
@@ -5111,14 +5575,15 @@ void bufhl_mark_adjust(buf_T* buf,
/// @param lnum The line number
/// @param[out] info The highligts for the line
/// @return true if there was highlights to display
-bool bufhl_start_line(buf_T *buf, linenr_T lnum, bufhl_lineinfo_T *info) {
- if (!buf->b_bufhl_info) {
+bool bufhl_start_line(buf_T *buf, linenr_T lnum, BufhlLineInfo *info)
+{
+ BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, false);
+ if (!lineinfo) {
return false;
}
-
info->valid_to = -1;
- info->entries = map_get(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, lnum);
- return kv_size(info->entries) > 0;
+ info->line = lineinfo;
+ return true;
}
/// get highlighting at column col
@@ -5131,14 +5596,15 @@ bool bufhl_start_line(buf_T *buf, linenr_T lnum, bufhl_lineinfo_T *info) {
/// @param info The info returned by bufhl_start_line
/// @param col The column to get the attr for
/// @return The highilight attr to display at the column
-int bufhl_get_attr(bufhl_lineinfo_T *info, colnr_T col) {
+int bufhl_get_attr(BufhlLineInfo *info, colnr_T col)
+{
if (col <= info->valid_to) {
return info->current;
}
int attr = 0;
info->valid_to = MAXCOL;
- for (size_t i = 0; i < kv_size(info->entries); i++) {
- bufhl_hl_item_T entry = kv_A(info->entries, i);
+ for (size_t i = 0; i < kv_size(info->line->items); i++) {
+ BufhlItem entry = kv_A(info->line->items, i);
if (entry.start <= col && col <= entry.stop) {
int entry_attr = syn_id2attr(entry.hl_id);
attr = hl_combine_attr(attr, entry_attr);
@@ -5231,12 +5697,30 @@ wipe_buffer (
int aucmd /* When TRUE trigger autocommands. */
)
{
- if (buf->b_fnum == top_file_num - 1)
- --top_file_num;
-
- if (!aucmd) /* Don't trigger BufDelete autocommands here. */
+ if (!aucmd) {
+ // Don't trigger BufDelete autocommands here.
block_autocmds();
- close_buffer(NULL, buf, DOBUF_WIPE, FALSE);
- if (!aucmd)
+ }
+ close_buffer(NULL, buf, DOBUF_WIPE, false);
+ if (!aucmd) {
unblock_autocmds();
+ }
+}
+
+/// Creates or switches to a scratch buffer. :h special-buffers
+/// Scratch buffer is:
+/// - buftype=nofile bufhidden=hide noswapfile
+/// - Always considered 'nomodified'
+///
+/// @param bufnr Buffer to switch to, or 0 to create a new buffer.
+///
+/// @see curbufIsChanged()
+void buf_open_scratch(handle_T bufnr, char *bufname)
+{
+ (void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL);
+ (void)setfname(curbuf, (char_u *)bufname, NULL, true);
+ set_option_value("bh", 0L, "hide", OPT_LOCAL);
+ set_option_value("bt", 0L, "nofile", OPT_LOCAL);
+ set_option_value("swf", 0L, NULL, OPT_LOCAL);
+ RESET_BINDING(curwin);
}