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.c1669
1 files changed, 1148 insertions, 521 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index b3eba4f5f6..72716daf0e 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1,12 +1,4 @@
/*
- * VIM - Vi IMproved by Bram Moolenaar
- *
- * Do ":help uganda" in Vim to read copying and usage conditions.
- * Do ":help credits" in Vim to see a list of people who contributed.
- * See README.txt for an overview of the Vim source code.
- */
-
-/*
* buffer.c: functions for dealing with the buffer structure
*/
@@ -25,12 +17,12 @@
*/
#include <stdbool.h>
-#include <errno.h>
#include <string.h>
#include <inttypes.h>
#include "nvim/api/private/handle.h"
#include "nvim/ascii.h"
+#include "nvim/assert.h"
#include "nvim/vim.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
@@ -149,14 +141,21 @@ open_buffer (
/* mark cursor position as being invalid */
curwin->w_valid = 0;
- if (curbuf->b_ffname != NULL
- ) {
+ if (curbuf->b_ffname != NULL) {
+ int old_msg_silent = msg_silent;
+ 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);
- /* Help buffer is filtered. */
- if (curbuf->b_help)
+ (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
+ flags | READ_NEW);
+ msg_silent = old_msg_silent;
+
+ // Help buffer is filtered.
+ if (curbuf->b_help) {
fix_help_buffer();
+ }
} else if (read_stdin) {
int save_bin = curbuf->b_p_bin;
linenr_T line_count;
@@ -265,17 +264,16 @@ open_buffer (
return retval;
}
-/*
- * Return TRUE if "buf" points to a valid buffer (in the buffer list).
- */
-int buf_valid(buf_T *buf)
+/// Check that "buf" points to a valid buffer (in the buffer list).
+bool buf_valid(buf_T *buf)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
FOR_ALL_BUFFERS(bp) {
if (bp == buf) {
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
/*
@@ -409,10 +407,9 @@ close_buffer (
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 */
+ 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))
@@ -588,16 +585,17 @@ free_buffer_stuff (
)
{
if (free_options) {
- clear_wininfo(buf); /* including window-local options */
- free_buf_options(buf, TRUE);
+ clear_wininfo(buf); // including window-local options
+ free_buf_options(buf, true);
ga_clear(&buf->b_s.b_langp);
}
- vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */
+ vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables
hash_init(&buf->b_vars->dv_hashtab);
- uc_clear(&buf->b_ucmds); /* clear local user commands */
- buf_delete_signs(buf); /* delete any signs */
- map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */
- map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */
+ uc_clear(&buf->b_ucmds); // clear local user commands
+ buf_delete_signs(buf); // delete any signs
+ bufhl_clear_all(buf); // delete any highligts
+ map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings
+ map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
xfree(buf->b_start_fenc);
buf->b_start_fenc = NULL;
}
@@ -927,13 +925,15 @@ do_buffer (
if (buf == NULL) { /* could not find it */
if (start == DOBUF_FIRST) {
- /* don't warn when deleting */
- if (!unload)
- EMSGN(_("E86: Buffer %" PRId64 " does not exist"), count);
- } else if (dir == FORWARD)
+ // don't warn when deleting
+ if (!unload) {
+ EMSGN(_(e_nobufnr), count);
+ }
+ } else if (dir == FORWARD) {
EMSG(_("E87: Cannot go beyond last buffer"));
- else
+ } else {
EMSG(_("E88: Cannot go before first buffer"));
+ }
return FAIL;
}
@@ -1293,14 +1293,15 @@ void enter_buffer(buf_T *buf)
redraw_later(NOT_VALID);
}
-/*
- * Change to the directory of the current buffer.
- */
+// Change to the directory of the current buffer.
+// Don't do this while still starting up.
void do_autochdir(void)
{
if (p_acd) {
- if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK) {
- shorten_fnames(TRUE);
+ if (starting == 0
+ && curbuf->b_ffname != NULL
+ && vim_chdirfile(curbuf->b_ffname) == OK) {
+ shorten_fnames(true);
}
}
}
@@ -1338,8 +1339,8 @@ buflist_new (
/* We can use inode numbers when the file exists. Works better
* for hard links. */
FileID file_id;
- bool file_id_valid = (sfname != NULL &&
- os_fileid((char *)sfname, &file_id));
+ bool file_id_valid = (sfname != NULL
+ && os_fileid((char *)sfname, &file_id));
if (ffname != NULL && !(flags & BLN_DUMMY)
&& (buf = buflist_findname_file_id(ffname, &file_id,
file_id_valid)) != NULL) {
@@ -1423,7 +1424,6 @@ buflist_new (
return NULL;
if (aborting()) /* autocmds may abort script processing */
return NULL;
- /* buf->b_nwindows = 0; why was this here? */
free_buffer_stuff(buf, FALSE); /* delete local variables et al. */
/* Init the options. */
@@ -1484,6 +1484,9 @@ buflist_new (
fmarks_check_names(buf); /* check file marks for this file */
buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */
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)) {
return NULL;
@@ -1550,6 +1553,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_ep);
clear_string_option(&buf->b_p_path);
clear_string_option(&buf->b_p_tags);
+ clear_string_option(&buf->b_p_tc);
clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr);
clear_string_option(&buf->b_p_qe);
@@ -1605,21 +1609,28 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
col = 0;
if (options & GETF_SWITCH) {
- /* If 'switchbuf' contains "useopen": jump to first window containing
- * "buf" if one exists */
- if (swb_flags & SWB_USEOPEN)
+ // If 'switchbuf' contains "useopen": jump to first window containing
+ // "buf" if one exists
+ if (swb_flags & SWB_USEOPEN) {
wp = buf_jump_open_win(buf);
- /* If 'switchbuf' contains "usetab": jump to first window in any tab
- * page containing "buf" if one exists */
- if (wp == NULL && (swb_flags & SWB_USETAB))
+ }
+
+ // If 'switchbuf' contains "usetab": jump to first window in any tab
+ // page containing "buf" if one exists
+ if (wp == NULL && (swb_flags & SWB_USETAB)) {
wp = buf_jump_open_tab(buf);
- /* If 'switchbuf' contains "split" or "newtab" and the current buffer
- * isn't empty: open new window */
- if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty()) {
- if (swb_flags & SWB_NEWTAB) /* Open in a new tab */
+ }
+
+ // 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()) {
+ if (swb_flags & SWB_NEWTAB) {
tabpage_new();
- else if (win_split(0, 0) == FAIL) /* Open in a new window */
+ } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
+ == FAIL) {
return FAIL;
+ }
RESET_BINDING(curwin);
}
}
@@ -1718,18 +1729,15 @@ static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id,
return NULL;
}
-/*
- * Find file in buffer list by a regexp pattern.
- * Return fnum of the found buffer.
- * Return < 0 for error.
- */
-int
-buflist_findpat (
- char_u *pattern,
- char_u *pattern_end, /* pointer to first char after pattern */
- int unlisted, /* find unlisted buffers */
- int diffmode, /* find diff-mode buffers only */
- int curtab_only /* find buffers in current tab only */
+/// Find file in buffer list by a regexp pattern.
+/// Return fnum of the found buffer.
+/// Return < 0 for error.
+int buflist_findpat(
+ const char_u *pattern,
+ const char_u *pattern_end, // pointer to first char after pattern
+ int unlisted, // find unlisted buffers
+ int diffmode, // find diff-mode buffers only
+ int curtab_only // find buffers in current tab only
)
{
int match = -1;
@@ -1741,12 +1749,15 @@ buflist_findpat (
int toggledollar;
if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) {
- if (*pattern == '%')
+ if (*pattern == '%') {
match = curbuf->b_fnum;
- else
+ } else {
match = curwin->w_alt_fnum;
- if (diffmode && !diff_mode_buf(buflist_findnr(match)))
+ }
+ buf_T *found_buf = buflist_findnr(match);
+ if (diffmode && !(found_buf && diff_mode_buf(found_buf))) {
match = -1;
+ }
}
/*
* Try four ways of matching a listed buffer:
@@ -2051,16 +2062,15 @@ void buflist_setfpos(buf_T *const buf, win_T *const win,
}
-/*
- * Return true when "wip" has 'diff' set and the diff is only for another tab
- * page. That's because a diff is local to a tab page.
- */
+/// Check that "wip" has 'diff' set and the diff is only for another tab page.
+/// That's because a diff is local to a tab page.
static bool wininfo_other_tab_diff(wininfo_T *wip)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (wip->wi_opt.wo_diff) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- /* return false when it's a window in the current tab page, thus
- * the buffer was in diff mode here */
+ // return false when it's a window in the current tab page, thus
+ // the buffer was in diff mode here
if (wip->wi_win == wp) {
return false;
}
@@ -2159,9 +2169,23 @@ void buflist_list(exarg_T *eap)
int i;
for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next) {
- /* skip unlisted buffers, unless ! was used */
- if (!buf->b_p_bl && !eap->forceit)
+ // skip unspecified buffers
+ if ((!buf->b_p_bl && !eap->forceit && !strchr((char *)eap->arg, 'u'))
+ || (strchr((char *)eap->arg, 'u') && buf->b_p_bl)
+ || (strchr((char *)eap->arg, '+')
+ && ((buf->b_flags & BF_READERR) || !bufIsChanged(buf)))
+ || (strchr((char *)eap->arg, 'a')
+ && (buf->b_ml.ml_mfp == NULL || buf->b_nwindows == 0))
+ || (strchr((char *)eap->arg, 'h')
+ && (buf->b_ml.ml_mfp == NULL || buf->b_nwindows != 0))
+ || (strchr((char *)eap->arg, '-') && buf->b_p_ma)
+ || (strchr((char *)eap->arg, '=') && !buf->b_p_ro)
+ || (strchr((char *)eap->arg, 'x') && !(buf->b_flags & BF_READERR))
+ || (strchr((char *)eap->arg, '%') && buf != curbuf)
+ || (strchr((char *)eap->arg, '#')
+ && (buf == curbuf || curwin->w_alt_fnum != buf->b_fnum))) {
continue;
+ }
msg_putchar('\n');
if (buf_spname(buf) != NULL)
STRLCPY(NameBuff, buf_spname(buf), MAXPATHL);
@@ -2171,15 +2195,16 @@ void buflist_list(exarg_T *eap)
len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
buf->b_fnum,
buf->b_p_bl ? ' ' : 'u',
- buf == curbuf ? '%' :
- (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
- buf->b_ml.ml_mfp == NULL ? ' ' :
- (buf->b_nwindows == 0 ? 'h' : 'a'),
+ buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
+ buf->b_ml.ml_mfp == NULL ? ' ' : (buf->b_nwindows == 0 ? 'h' : 'a'),
!MODIFIABLE(buf) ? '-' : (buf->b_p_ro ? '=' : ' '),
- (buf->b_flags & BF_READERR) ? 'x'
- : (bufIsChanged(buf) ? '+' : ' '),
+ (buf->b_flags & BF_READERR) ? 'x' : (bufIsChanged(buf) ? '+' : ' '),
NameBuff);
+ if (len > IOSIZE - 20) {
+ len = IOSIZE - 20;
+ }
+
/* put "line 999" in column 40 or after the file name */
i = 40 - vim_strsize(IObuff);
do {
@@ -2403,52 +2428,62 @@ void buflist_altfpos(win_T *win)
buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE);
}
-/*
- * Return TRUE if 'ffname' is not the same file as current file.
- * Fname must have a full path (expanded by path_get_absolute_path()).
- */
-int otherfile(char_u *ffname)
+/// Check that "ffname" is not the same file as current file.
+/// Fname must have a full path (expanded by path_get_absolute_path()).
+///
+/// @param ffname full path name to check
+bool otherfile(char_u *ffname)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
return otherfile_buf(curbuf, ffname, NULL, false);
}
-static int otherfile_buf(buf_T *buf, char_u *ffname,
- FileID *file_id_p, bool file_id_valid)
+/// 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()).
+///
+/// @param buf buffer to check
+/// @param ffname full path name to check
+/// @param file_id_p information about the file at "ffname".
+/// @param file_id_valid whether a valid "file_id_p" was passed in.
+static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p,
+ bool file_id_valid)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- /* no name is different */
+ // no name is different
if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) {
- return TRUE;
+ return true;
}
if (fnamecmp(ffname, buf->b_ffname) == 0) {
- return FALSE;
+ return false;
}
{
FileID file_id;
- /* If no struct stat given, get it now */
+ // If no struct stat given, get it now
if (file_id_p == NULL) {
file_id_p = &file_id;
file_id_valid = os_fileid((char *)ffname, file_id_p);
}
if (!file_id_valid) {
// file_id not valid, assume files are different.
- return TRUE;
+ return true;
}
- /* Use dev/ino to check if the files are the same, even when the names
- * are different (possible with links). Still need to compare the
- * name above, for when the file doesn't exist yet.
- * Problem: The dev/ino changes when a file is deleted (and created
- * again) and remains the same when renamed/moved. We don't want to
- * stat() each buffer each time, that would be too slow. Get the
- * dev/ino again when they appear to match, but not when they appear
- * to be different: Could skip a buffer when it's actually the same
- * file. */
+ // Use dev/ino to check if the files are the same, even when the names
+ // are different (possible with links). Still need to compare the
+ // name above, for when the file doesn't exist yet.
+ // Problem: The dev/ino changes when a file is deleted (and created
+ // again) and remains the same when renamed/moved. We don't want to
+ // stat() each buffer each time, that would be too slow. Get the
+ // dev/ino again when they appear to match, but not when they appear
+ // to be different: Could skip a buffer when it's actually the same
+ // file.
if (buf_same_file_id(buf, file_id_p)) {
buf_set_file_id(buf);
- if (buf_same_file_id(buf, file_id_p))
- return FALSE;
+ if (buf_same_file_id(buf, file_id_p)) {
+ return false;
+ }
}
}
- return TRUE;
+ return true;
}
// Set file_id for a buffer.
@@ -2465,11 +2500,14 @@ void buf_set_file_id(buf_T *buf)
}
}
-// return TRUE if file_id in buffer "buf" matches with "file_id".
+/// Check that file_id in buffer "buf" matches with "file_id".
+///
+/// @param buf buffer
+/// @param file_id file id
static bool buf_same_file_id(buf_T *buf, FileID *file_id)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- return buf->file_id_valid
- && os_fileid_equal(&(buf->file_id), file_id);
+ return buf->file_id_valid && os_fileid_equal(&(buf->file_id), file_id);
}
/*
@@ -2752,23 +2790,28 @@ void maketitle(void)
resettitle();
}
-/*
- * Used for title and icon: Check if "str" differs from "*last". Set "*last"
- * from "str" if it does.
- * Return TRUE when "*last" changed.
- */
-static int ti_change(char_u *str, char_u **last)
+/// Used for title and icon: Check if "str" differs from "*last". Set "*last"
+/// from "str" if it does by freeing the old value of "*last" and duplicating
+/// "str".
+///
+/// @param str desired title string
+/// @param[in,out] last current title string
+//
+/// @return true when "*last" changed.
+static bool ti_change(char_u *str, char_u **last)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
if ((str == NULL) != (*last == NULL)
|| (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) {
xfree(*last);
- if (str == NULL)
+ if (str == NULL) {
*last = NULL;
- else
+ } else {
*last = vim_strsave(str);
- return TRUE;
+ }
+ return true;
}
- return FALSE;
+ return false;
}
/*
@@ -2789,58 +2832,61 @@ void free_titles(void)
# endif
+/// Enumeration specifying the valid numeric bases that can
+/// be used when printing numbers in the status line.
+typedef enum {
+ kNumBaseDecimal = 10,
+ kNumBaseOctal = 8,
+ kNumBaseHexadecimal = 16
+} NumberBase;
-/*
- * Build a string from the status line items in "fmt".
- * Return length of string in screen cells.
- *
- * Normally works for window "wp", except when working for 'tabline' then it
- * is "curwin".
- *
- * Items are drawn interspersed with the text that surrounds it
- * Specials: %-<wid>(xxx%) => group, %= => middle marker, %< => truncation
- * Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional
- *
- * If maxwidth is not zero, the string will be filled at any middle marker
- * or truncated if too long, fillchar is used for all whitespace.
- */
-int
-build_stl_str_hl (
+
+/// Build a string from the status line items in "fmt".
+/// Return length of string in screen cells.
+///
+/// Normally works for window "wp", except when working for 'tabline' then it
+/// is "curwin".
+///
+/// Items are drawn interspersed with the text that surrounds it
+/// Specials: %-<wid>(xxx%) => group, %= => middle marker, %< => truncation
+/// Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional
+///
+/// If maxwidth is not zero, the string will be filled at any middle marker
+/// or truncated if too long, fillchar is used for all whitespace.
+///
+/// @param wp The window to build a statusline for
+/// @param out The output buffer to write the statusline to
+/// Note: This should not be NameBuff
+/// @param outlen The length of the output buffer
+/// @param fmt The statusline format string
+/// @param use_sandbox Use a sandboxed environment when evaluating fmt
+/// @param fillchar Character to use when filling empty space in the statusline
+/// @param maxwidth The maximum width to make the statusline
+/// @param hltab HL attributes (can be NULL)
+/// @param tabtab Tab clicks definition (can be NULL).
+///
+/// @return The final width of the statusline
+int build_stl_str_hl(
win_T *wp,
- char_u *out, /* buffer to write into != NameBuff */
- size_t outlen, /* length of out[] */
+ char_u *out,
+ size_t outlen,
char_u *fmt,
- int use_sandbox, /* "fmt" was set insecurely, use sandbox */
+ int use_sandbox,
int fillchar,
int maxwidth,
- struct stl_hlrec *hltab, /* return: HL attributes (can be NULL) */
- struct stl_hlrec *tabtab /* return: tab page nrs (can be NULL) */
+ struct stl_hlrec *hltab,
+ StlClickRecord *tabtab
)
{
- char_u *p;
- char_u *s;
- char_u *t;
- int byteval;
- win_T *o_curwin;
- buf_T *o_curbuf;
- int empty_line;
- colnr_T virtcol;
- long l;
- long n;
- int prevchar_isflag;
- int prevchar_isitem;
- int itemisflag;
- int fillable;
- char_u *str;
- long num;
- int width;
- int itemcnt;
- int curitem;
int groupitem[STL_MAX_ITEM];
- int groupdepth;
struct stl_item {
- char_u *start;
+ // Where the item starts in the status line output buffer
+ char_u *start;
+ // Function to run for ClickFunc items.
+ char *cmd;
+ // The minimum width of the item
int minwid;
+ // The maximum width of the item
int maxwid;
enum {
Normal,
@@ -2849,23 +2895,16 @@ build_stl_str_hl (
Middle,
Highlight,
TabPage,
+ ClickFunc,
Trunc
- } type;
- } item[STL_MAX_ITEM];
- int minwid;
- int maxwid;
- int zeropad;
- char_u base;
- char_u opt;
+ } type;
+ } item[STL_MAX_ITEM];
#define TMPLEN 70
char_u tmp[TMPLEN];
char_u *usefmt = fmt;
- struct stl_hlrec *sp;
- /*
- * When the format starts with "%!" then evaluate it as an expression and
- * use the result as the actual format string.
- */
+ // When the format starts with "%!" then evaluate it as an expression and
+ // use the result as the actual format string.
if (fmt[0] == '%' && fmt[1] == '!') {
usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
if (usefmt == NULL)
@@ -2874,175 +2913,280 @@ build_stl_str_hl (
if (fillchar == 0)
fillchar = ' ';
- /* Can't handle a multi-byte fill character yet. */
+ // Can't handle a multi-byte fill character yet.
else if (mb_char2len(fillchar) > 1)
fillchar = '-';
- /* Get line & check if empty (cursorpos will show "0-1"). Note that
- * p will become invalid when getting another buffer line. */
- p = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE);
- empty_line = (*p == NUL);
+ // Get line & check if empty (cursorpos will show "0-1").
+ char_u *line_ptr = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false);
+ bool empty_line = (*line_ptr == NUL);
- /* Get the byte value now, in case we need it below. This is more
- * efficient than making a copy of the line. */
- if (wp->w_cursor.col > (colnr_T)STRLEN(p))
+ // 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))
byteval = 0;
else
- byteval = (*mb_ptr2char)(p + wp->w_cursor.col);
-
- groupdepth = 0;
- p = out;
- curitem = 0;
- prevchar_isflag = TRUE;
- prevchar_isitem = FALSE;
- for (s = usefmt; *s; ) {
+ byteval = (*mb_ptr2char)(line_ptr + wp->w_cursor.col);
+
+ int groupdepth = 0;
+
+ int curitem = 0;
+ bool prevchar_isflag = true;
+ bool prevchar_isitem = false;
+
+ // out_p is the current position in the output buffer
+ char_u *out_p = out;
+
+ // out_end_p is the last valid character in the output buffer
+ // Note: The null termination character must occur here or earlier,
+ // so any user-visible characters must occur before here.
+ char_u *out_end_p = (out + outlen) - 1;
+
+
+ // Proceed character by character through the statusline format string
+ // fmt_p is the current positon in the input buffer
+ for (char_u *fmt_p = usefmt; *fmt_p; ) {
if (curitem == STL_MAX_ITEM) {
- /* There are too many items. Add the error code to the statusline
- * to give the user a hint about what went wrong. */
- if (p + 6 < out + outlen) {
- memmove(p, " E541", (size_t)5);
- p += 5;
+ // There are too many items. Add the error code to the statusline
+ // to give the user a hint about what went wrong.
+ if (out_p + 5 < out_end_p) {
+ memmove(out_p, " E541", (size_t)5);
+ out_p += 5;
}
break;
}
- if (*s != NUL && *s != '%')
- prevchar_isflag = prevchar_isitem = FALSE;
+ if (*fmt_p != NUL && *fmt_p != '%') {
+ prevchar_isflag = prevchar_isitem = false;
+ }
- /*
- * Handle up to the next '%' or the end.
- */
- while (*s != NUL && *s != '%' && p + 1 < out + outlen)
- *p++ = *s++;
- if (*s == NUL || p + 1 >= out + outlen)
+ // Copy the formatting verbatim until we reach the end of the string
+ // or find a formatting item (denoted by `%`)
+ // or run out of room in our output buffer.
+ while (*fmt_p != NUL && *fmt_p != '%' && out_p < out_end_p)
+ *out_p++ = *fmt_p++;
+
+ // If we have processed the entire format string or run out of
+ // room in our output buffer, exit the loop.
+ if (*fmt_p == NUL || out_p >= out_end_p)
break;
- /*
- * Handle one '%' item.
- */
- s++;
- if (*s == NUL) /* ignore trailing % */
+ // The rest of this loop will handle a single `%` item.
+ // Note: We increment here to skip over the `%` character we are currently
+ // on so we can process the item's contents.
+ fmt_p++;
+
+ // Ignore `%` at the end of the format string
+ if (*fmt_p == NUL) {
break;
- if (*s == '%') {
- if (p + 1 >= out + outlen)
+ }
+
+ // 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;
- *p++ = *s++;
- prevchar_isflag = prevchar_isitem = FALSE;
+ *out_p++ = *fmt_p++;
+ prevchar_isflag = prevchar_isitem = false;
continue;
}
- if (*s == STL_MIDDLEMARK) {
- s++;
- if (groupdepth > 0)
+
+ // STL_MIDDLEMARK: Separation place between left and right aligned items.
+ if (*fmt_p == STL_MIDDLEMARK) {
+ fmt_p++;
+ // Ignored when we are inside of a grouping
+ if (groupdepth > 0) {
continue;
+ }
item[curitem].type = Middle;
- item[curitem++].start = p;
+ item[curitem++].start = out_p;
continue;
}
- if (*s == STL_TRUNCMARK) {
- s++;
+
+ // 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 = p;
+ item[curitem++].start = out_p;
continue;
}
- if (*s == ')') {
- s++;
- if (groupdepth < 1)
+
+ // The end of a grouping
+ if (*fmt_p == ')') {
+ fmt_p++;
+ // Ignore if we are not actually inside a group currently
+ if (groupdepth < 1) {
continue;
+ }
groupdepth--;
- t = item[groupitem[groupdepth]].start;
- *p = NUL;
- l = vim_strsize(t);
+ // 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;
+ *out_p = NUL;
+ long group_len = vim_strsize(t);
+
+ // If the group contained internal items
+ // and the group did not have a minimum width,
+ // and if there were no normal items in the group,
+ // 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) {
- /* remove group if all items are empty */
- for (n = groupitem[groupdepth] + 1; n < curitem; n++)
- if (item[n].type == Normal)
+ bool has_normal_items = false;
+ for (long n = groupitem[groupdepth] + 1; n < curitem; n++) {
+ if (item[n].type == Normal || item[n].type == Highlight) {
+ has_normal_items = true;
break;
- if (n == curitem) {
- p = t;
- l = 0;
+ }
+ }
+
+ if (!has_normal_items) {
+ out_p = t;
+ group_len = 0;
}
}
- if (l > item[groupitem[groupdepth]].maxwid) {
- /* truncate, remove n bytes of text at the start */
+
+ // 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) {
+ // { Determine the number of bytes to remove
+ long n;
if (has_mbyte) {
/* Find the first character that should be included. */
n = 0;
- while (l >= item[groupitem[groupdepth]].maxwid) {
- l -= ptr2cells(t + n);
+ while (group_len >= item[groupitem[groupdepth]].maxwid) {
+ group_len -= ptr2cells(t + n);
n += (*mb_ptr2len)(t + n);
}
- } else
- n = (long)(p - t) - item[groupitem[groupdepth]].maxwid + 1;
+ } else {
+ n = (long)(out_p - t) - item[groupitem[groupdepth]].maxwid + 1;
+ }
+ // }
+ // Prepend the `<` to indicate that the output was truncated.
*t = '<';
- memmove(t + 1, t + n, (size_t)(p - (t + n)));
- p = p - n + 1;
+
+ // { 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 (++l < item[groupitem[groupdepth]].minwid)
- *p++ = fillchar;
+ while (++group_len < item[groupitem[groupdepth]].minwid)
+ *out_p++ = fillchar;
+ // }
/* correct the start of the items for the truncation */
- for (l = groupitem[groupdepth] + 1; l < curitem; l++) {
- item[l].start -= n;
- if (item[l].start < t)
- item[l].start = t;
+ for (int idx = groupitem[groupdepth] + 1; idx < curitem; idx++) {
+ // Shift everything back by the number of removed bytes
+ item[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;
+ }
}
- } else if (abs(item[groupitem[groupdepth]].minwid) > l) {
- /* fill */
- n = item[groupitem[groupdepth]].minwid;
- if (n < 0) {
- /* fill by appending characters */
- n = 0 - n;
- while (l++ < n && p + 1 < out + outlen)
- *p++ = fillchar;
+ // 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;
+ // If the group is left-aligned, add characters to the right.
+ if (min_group_width < 0) {
+ min_group_width = 0 - min_group_width;
+ while (group_len++ < min_group_width && out_p < out_end_p)
+ *out_p++ = fillchar;
+ // If the group is right-aligned, shift everything to the right and
+ // prepend with filler characters.
} else {
- /* fill by inserting characters */
- memmove(t + n - l, t, (size_t)(p - t));
- l = n - l;
- if (p + l >= out + outlen)
- l = (long)((out + outlen) - p - 1);
- p += l;
- for (n = groupitem[groupdepth] + 1; n < curitem; n++)
- item[n].start += l;
- for (; l > 0; l--)
+ // { Move the group to the right
+ memmove(t + min_group_width - group_len, t, (size_t)(out_p - t));
+ group_len = min_group_width - group_len;
+ if (out_p + group_len >= (out_end_p + 1)) {
+ group_len = (long)(out_end_p - out_p);
+ }
+ out_p += group_len;
+ // }
+
+ // Adjust item start positions
+ for (int n = groupitem[groupdepth] + 1; n < curitem; n++) {
+ item[n].start += group_len;
+ }
+
+ // Prepend the fill characters
+ for (; group_len > 0; group_len--) {
*t++ = fillchar;
+ }
}
}
continue;
}
- minwid = 0;
- maxwid = 9999;
- zeropad = FALSE;
- l = 1;
- if (*s == '0') {
- s++;
- zeropad = TRUE;
+ int minwid = 0;
+ int maxwid = 9999;
+ bool left_align = false;
+
+ // Denotes that numbers should be left-padded with zeros
+ bool zeropad = (*fmt_p == '0');
+ if (zeropad) {
+ fmt_p++;
}
- if (*s == '-') {
- s++;
- l = -1;
+
+ // Denotes that the item should be left-aligned.
+ // This is tracked by using a negative length.
+ if (*fmt_p == '-') {
+ fmt_p++;
+ left_align = true;
}
- if (ascii_isdigit(*s)) {
- minwid = getdigits_int(&s);
+
+ // The first digit group is the item's min width
+ if (ascii_isdigit(*fmt_p)) {
+ minwid = getdigits_int(&fmt_p);
if (minwid < 0) /* overflow */
minwid = 0;
}
- if (*s == STL_USER_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 = p;
+ item[curitem].start = out_p;
item[curitem].minwid = minwid > 9 ? 1 : minwid;
- s++;
+ fmt_p++;
curitem++;
continue;
}
- if (*s == STL_TABPAGENR || *s == STL_TABCLOSENR) {
- if (*s == STL_TABCLOSENR) {
+
+ // TABPAGE pairs are used to denote a region that when clicked will
+ // either switch to or close a tab.
+ //
+ // Ex: tabline=%0Ttab\ zero%X
+ // This tabline has a TABPAGENR item with minwid `0`,
+ // which is then closed with a TABCLOSENR item.
+ // Clicking on this region with mouse enabled will switch to tab 0.
+ // Setting the minwid to a different value will switch
+ // to that tab, if it exists
+ //
+ // Ex: tabline=%1Xtab\ one%X
+ // This tabline has a TABCLOSENR item with minwid `1`,
+ // which is then closed with a TABCLOSENR item.
+ // Clicking on this region with mouse enabled will close tab 0.
+ // This is determined by the following formula:
+ // tab to close = (1 - minwid)
+ // This is because for TABPAGENR we use `minwid` = `tab number`.
+ // For TABCLOSENR we store the tab number as a negative value.
+ // Because 0 is a valid TABPAGENR value, we have to
+ // start our numbering at `-1`.
+ // So, `-1` corresponds to us wanting to close tab `0`
+ //
+ // Note: These options are only valid when creating a tabline.
+ 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 (n = curitem - 1; n >= 0; --n)
+ for (long n = curitem - 1; n >= 0; --n)
if (item[n].type == TabPage && item[n].minwid >= 0) {
minwid = item[n].minwid;
break;
@@ -3052,54 +3196,88 @@ build_stl_str_hl (
minwid = -minwid;
}
item[curitem].type = TabPage;
- item[curitem].start = p;
+ item[curitem].start = out_p;
+ item[curitem].minwid = minwid;
+ fmt_p++;
+ curitem++;
+ continue;
+ }
+
+ if (*fmt_p == STL_CLICK_FUNC) {
+ fmt_p++;
+ char *t = (char *) fmt_p;
+ while (*fmt_p != STL_CLICK_FUNC && *fmt_p) {
+ fmt_p++;
+ }
+ 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;
- s++;
+ fmt_p++;
curitem++;
continue;
}
- if (*s == '.') {
- s++;
- if (ascii_isdigit(*s)) {
- maxwid = getdigits_int(&s);
+
+ // Denotes the end of the minwid
+ // the maxwid may follow immediately after
+ if (*fmt_p == '.') {
+ fmt_p++;
+ if (ascii_isdigit(*fmt_p)) {
+ maxwid = getdigits_int(&fmt_p);
if (maxwid <= 0) /* overflow */
maxwid = 50;
}
}
- minwid = (minwid > 50 ? 50 : minwid) * l;
- if (*s == '(') {
+
+ // Bound the minimum width at 50.
+ // Make the number negative to denote left alignment of the item
+ minwid = (minwid > 50 ? 50 : minwid) * (left_align ? -1 : 1);
+
+ // Denotes the start of a new group
+ if (*fmt_p == '(') {
groupitem[groupdepth++] = curitem;
item[curitem].type = Group;
- item[curitem].start = p;
+ item[curitem].start = out_p;
item[curitem].minwid = minwid;
item[curitem].maxwid = maxwid;
- s++;
+ fmt_p++;
curitem++;
continue;
}
- if (vim_strchr(STL_ALL, *s) == NULL) {
- s++;
+
+ // An invalid item was specified.
+ // Continue processing on the next character of the format string.
+ if (vim_strchr(STL_ALL, *fmt_p) == NULL) {
+ fmt_p++;
continue;
}
- opt = *s++;
-
- /* OK - now for the real work */
- base = 'D';
- itemisflag = FALSE;
- fillable = TRUE;
- num = -1;
- str = NULL;
+
+ // The status line item type
+ char_u opt = *fmt_p++;
+
+ // OK - now for the real work
+ NumberBase base = kNumBaseDecimal;
+ bool itemisflag = false;
+ bool fillable = true;
+ long num = -1;
+ char_u *str = NULL;
switch (opt) {
case STL_FILEPATH:
case STL_FULLPATH:
case STL_FILENAME:
- fillable = FALSE; /* don't change ' ' to fillchar */
- if (buf_spname(wp->w_buffer) != NULL)
+ {
+ // Set fillable to false to that ' ' in the filename will not
+ // get replaced with the fillchar
+ fillable = false;
+ if (buf_spname(wp->w_buffer) != NULL) {
STRLCPY(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
- else {
- t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
- : wp->w_buffer->b_fname;
- home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, TRUE);
+ } else {
+ char_u *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
+ : wp->w_buffer->b_fname;
+ home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, true);
}
trans_characters(NameBuff, MAXPATHL);
if (opt != STL_FILENAME)
@@ -3107,41 +3285,59 @@ build_stl_str_hl (
else
str = path_tail(NameBuff);
break;
-
+ }
case STL_VIM_EXPR: /* '{' */
- itemisflag = TRUE;
- t = p;
- while (*s != '}' && *s != NUL && p + 1 < out + outlen)
- *p++ = *s++;
- if (*s != '}') /* missing '}' or out of space */
+ {
+ itemisflag = true;
+
+ // Attempt to copy the expression to evaluate into
+ // the output buffer as a null-terminated string.
+ char_u *t = out_p;
+ while (*fmt_p != '}' && *fmt_p != NUL && out_p < out_end_p)
+ *out_p++ = *fmt_p++;
+ if (*fmt_p != '}') /* missing '}' or out of space */
break;
- s++;
- *p = 0;
- p = t;
+ fmt_p++;
+ *out_p = 0;
+
+ // Move our position in the output buffer
+ // to the beginning of the expression
+ out_p = t;
+
+ // { Evaluate the expression
+ // 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);
- o_curbuf = curbuf;
- o_curwin = curwin;
+ buf_T *o_curbuf = curbuf;
+ win_T *o_curwin = curwin;
curwin = wp;
curbuf = wp->w_buffer;
- str = eval_to_string_safe(p, &t, use_sandbox);
+ // Note: The result stored in `t` is unused.
+ str = eval_to_string_safe(out_p, &t, use_sandbox);
curwin = o_curwin;
curbuf = o_curbuf;
- do_unlet((char_u *)"g:actual_curbuf", TRUE);
+ // Remove the variable we just stored
+ do_unlet((char_u *)"g:actual_curbuf", true);
+
+ // }
+
+ // Check if the evaluated result is a number.
+ // If so, convert the number to an int and free the string.
if (str != NULL && *str != 0) {
if (*skipdigits(str) == NUL) {
num = atoi((char *)str);
xfree(str);
str = NULL;
- itemisflag = FALSE;
+ itemisflag = false;
}
}
break;
+ }
case STL_LINE:
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
@@ -3159,21 +3355,23 @@ build_stl_str_hl (
case STL_VIRTCOL:
case STL_VIRTCOL_ALT:
- /* In list mode virtcol needs to be recomputed */
- virtcol = wp->w_virtcol;
+ {
+ // In list mode virtcol needs to be recomputed
+ colnr_T virtcol = wp->w_virtcol;
if (wp->w_p_list && lcs_tab1 == NUL) {
wp->w_p_list = FALSE;
getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
wp->w_p_list = TRUE;
}
++virtcol;
- /* Don't display %V if it's the same as %c. */
+ // Don't display %V if it's the same as %c.
if (opt == STL_VIRTCOL_ALT
&& (virtcol == (colnr_T)(!(State & INSERT) && empty_line
? 0 : (int)wp->w_cursor.col + 1)))
break;
num = (long)virtcol;
break;
+ }
case STL_PERCENTAGE:
num = (int)(((long)wp->w_cursor.lnum * 100L) /
@@ -3181,19 +3379,31 @@ build_stl_str_hl (
break;
case STL_ALTPERCENT:
+ // Store the position percentage in our temporary buffer.
+ // Note: We cannot store the value in `num` because
+ // `get_rel_pos` can return a named position. Ex: "Top"
+ get_rel_pos(wp, tmp, TMPLEN);
str = tmp;
- get_rel_pos(wp, str, TMPLEN);
break;
case STL_ARGLISTSTAT:
- fillable = FALSE;
+ fillable = false;
+
+ // Note: This is important because `append_arg_number` starts appending
+ // at the end of the null-terminated string.
+ // Setting the first byte to null means it will place the argument
+ // number string at the beginning of the buffer.
tmp[0] = 0;
- if (append_arg_number(wp, tmp, (int)sizeof(tmp), FALSE))
+
+ // Note: The call will only return true if it actually
+ // appended data to the `tmp` buffer.
+ if (append_arg_number(wp, tmp, (int)sizeof(tmp), false)) {
str = tmp;
+ }
break;
case STL_KEYMAP:
- fillable = FALSE;
+ fillable = false;
if (get_keymap_str(wp, tmp, TMPLEN))
str = tmp;
break;
@@ -3206,16 +3416,17 @@ build_stl_str_hl (
break;
case STL_OFFSET_X:
- base = 'X';
+ base = kNumBaseHexadecimal;
case STL_OFFSET:
- 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);
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);
break;
-
+ }
case STL_BYTEVAL_X:
- base = 'X';
+ base = kNumBaseHexadecimal;
case STL_BYTEVAL:
num = byteval;
if (num == NL)
@@ -3226,20 +3437,23 @@ build_stl_str_hl (
case STL_ROFLAG:
case STL_ROFLAG_ALT:
- itemisflag = TRUE;
+ itemisflag = true;
if (wp->w_buffer->b_p_ro)
str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"));
break;
case STL_HELPFLAG:
case STL_HELPFLAG_ALT:
- itemisflag = TRUE;
+ itemisflag = true;
if (wp->w_buffer->b_help)
str = (char_u *)((opt == STL_HELPFLAG_ALT) ? ",HLP"
: _("[Help]"));
break;
case STL_FILETYPE:
+ // Copy the filetype if it is not null and the formatted string will fit
+ // in the temporary buffer
+ // (including the brackets and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) {
vim_snprintf((char *)tmp, sizeof(tmp), "[%s]",
@@ -3249,20 +3463,26 @@ build_stl_str_hl (
break;
case STL_FILETYPE_ALT:
- itemisflag = TRUE;
+ {
+ itemisflag = true;
+ // Copy the filetype if it is not null and the formatted string will fit
+ // in the temporary buffer
+ // (including the comma and null terminating character)
if (*wp->w_buffer->b_p_ft != NUL
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
vim_snprintf((char *)tmp, sizeof(tmp), ",%s",
wp->w_buffer->b_p_ft);
- for (t = tmp; *t != 0; t++)
+ // Uppercase the file extension
+ for (char_u *t = tmp; *t != 0; t++) {
*t = TOUPPER_LOC(*t);
+ }
str = tmp;
}
break;
-
+ }
case STL_PREVIEWFLAG:
case STL_PREVIEWFLAG_ALT:
- itemisflag = TRUE;
+ itemisflag = true;
if (wp->w_p_pvw)
str = (char_u *)((opt == STL_PREVIEWFLAG_ALT) ? ",PRV"
: _("[Preview]"));
@@ -3277,7 +3497,7 @@ build_stl_str_hl (
case STL_MODIFIED:
case STL_MODIFIED_ALT:
- itemisflag = TRUE;
+ itemisflag = true;
switch ((opt == STL_MODIFIED_ALT)
+ bufIsChanged(wp->w_buffer) * 2
+ (!MODIFIABLE(wp->w_buffer)) * 4) {
@@ -3291,212 +3511,362 @@ build_stl_str_hl (
break;
case STL_HIGHLIGHT:
- t = s;
- while (*s != '#' && *s != NUL)
- ++s;
- if (*s == '#') {
+ {
+ // { The name of the highlight is surrounded by `#`
+ char_u *t = fmt_p;
+ while (*fmt_p != '#' && *fmt_p != NUL) {
+ ++fmt_p;
+ }
+ // }
+
+ // Create a highlight item based on the name
+ if (*fmt_p == '#') {
item[curitem].type = Highlight;
- item[curitem].start = p;
- item[curitem].minwid = -syn_namen2id(t, (int)(s - t));
+ item[curitem].start = out_p;
+ item[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t));
curitem++;
+ fmt_p++;
}
- if (*s != NUL)
- ++s;
continue;
}
+ }
- item[curitem].start = p;
+ // 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;
+
+ // Copy the item string into the output buffer
if (str != NULL && *str) {
- t = str;
+ // { Skip the leading `,` or ` ` if the item is a flag
+ // and the proper conditions are met
+ char_u *t = str;
if (itemisflag) {
if ((t[0] && t[1])
&& ((!prevchar_isitem && *t == ',')
|| (prevchar_isflag && *t == ' ')))
t++;
- prevchar_isflag = TRUE;
+ prevchar_isflag = true;
+ }
+ // }
+
+ long l = vim_strsize(t);
+
+ // If this item is non-empty, record that the last thing
+ // we put in the output buffer was an item
+ if (l > 0) {
+ prevchar_isitem = true;
}
- l = vim_strsize(t);
- if (l > 0)
- prevchar_isitem = TRUE;
+
+ // If the item is too wide, truncate it from the beginning
if (l > maxwid) {
while (l >= maxwid)
if (has_mbyte) {
l -= ptr2cells(t);
t += (*mb_ptr2len)(t);
- } else
+ } else {
l -= byte2cells(*t++);
- if (p + 1 >= out + outlen)
+ }
+
+ // Early out if there isn't enough room for the truncation marker
+ if (out_p >= out_end_p)
break;
- *p++ = '<';
+
+ // Add the truncation marker
+ *out_p++ = '<';
}
+
+ // If the item is right aligned and not wide enough,
+ // pad with fill characters.
if (minwid > 0) {
- for (; l < minwid && p + 1 < out + outlen; l++) {
- /* Don't put a "-" in front of a digit. */
+ for (; l < minwid && out_p < out_end_p; l++) {
+ // Don't put a "-" in front of a digit.
if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t))
- *p++ = ' ';
+ *out_p++ = ' ';
else
- *p++ = fillchar;
+ *out_p++ = fillchar;
}
minwid = 0;
- } else
+ } else {
+ // Note: The negative value denotes a left aligned item.
+ // Here we switch the minimum width back to a positive value.
minwid *= -1;
- while (*t && p + 1 < out + outlen) {
- *p++ = *t++;
- /* Change a space by fillchar, unless fillchar is '-' and a
- * digit follows. */
- if (fillable && p[-1] == ' '
+ }
+
+ // { Copy the string text into the output buffer
+ while (*t && out_p < out_end_p) {
+ *out_p++ = *t++;
+ // Change a space by fillchar, unless fillchar is '-' and a
+ // digit follows.
+ if (fillable && out_p[-1] == ' '
&& (!ascii_isdigit(*t) || fillchar != '-'))
- p[-1] = fillchar;
+ out_p[-1] = fillchar;
}
- for (; l < minwid && p + 1 < out + outlen; l++)
- *p++ = fillchar;
- } else if (num >= 0) {
- int nbase = (base == 'D' ? 10 : (base == 'O' ? 8 : 16));
- char_u nstr[20];
+ // }
- if (p + 20 >= out + outlen)
+ // For left-aligned items, fill any remaining space with the fillchar
+ for (; l < minwid && out_p < out_end_p; l++) {
+ *out_p++ = fillchar;
+ }
+
+ // Otherwise if the item is a number, copy that to the output buffer.
+ } else if (num >= 0) {
+ if (out_p + 20 > out_end_p)
break; /* not sufficient space */
- prevchar_isitem = TRUE;
- t = nstr;
+ prevchar_isitem = true;
+
+ // { Build the formatting string
+ char_u nstr[20];
+ char_u *t = nstr;
if (opt == STL_VIRTCOL_ALT) {
*t++ = '-';
minwid--;
}
*t++ = '%';
- if (zeropad)
+ if (zeropad) {
*t++ = '0';
+ }
+
+ // Note: The `*` means we take the width as one of the arguments
*t++ = '*';
- *t++ = nbase == 16 ? base : (char_u)(nbase == 8 ? 'o' : 'd');
+ *t++ = (char_u) (base == kNumBaseHexadecimal ? 'X'
+ : (base == kNumBaseOctal ? 'o'
+ : 'd'));
*t = 0;
+ // }
+
+ // { Determine how many characters the number will take up when printed
+ // Note: We have to cast the base because the compiler uses
+ // unsigned ints for the enum values.
+ long num_chars = 1;
+ for (long n = num; n >= (int) base; n /= (int) base) {
+ num_chars++;
+ }
- for (n = num, l = 1; n >= nbase; n /= nbase)
- l++;
- if (opt == STL_VIRTCOL_ALT)
- l++;
- if (l > maxwid) {
- l += 2;
- n = l - maxwid;
- while (l-- > maxwid)
- num /= nbase;
+ // VIRTCOL_ALT takes up an extra character because
+ // of the `-` we added above.
+ if (opt == STL_VIRTCOL_ALT) {
+ num_chars++;
+ }
+ // }
+
+ size_t remaining_buf_len = (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.
+ // Ex: 14532 with maxwid of 4 -> '14>3'
+ if (num_chars > maxwid) {
+ // Add two to the width because the power piece will take
+ // two extra characters
+ num_chars += 2;
+
+ // How many extra characters there are
+ long n = num_chars - maxwid;
+
+ // { Reduce the number by base^n
+ while (num_chars-- > maxwid) {
+ num /= base;
+ }
+ // }
+
+ // { Add the format string for the exponent bit
*t++ = '>';
*t++ = '%';
+ // Use the same base as the first number
*t = t[-3];
*++t = 0;
- vim_snprintf((char *)p, outlen - (p - out), (char *)nstr,
+ // }
+
+ vim_snprintf((char *)out_p, remaining_buf_len, (char *)nstr,
0, num, n);
- } else
- vim_snprintf((char *)p, outlen - (p - out), (char *)nstr,
+ } else {
+ vim_snprintf((char *)out_p, remaining_buf_len, (char *)nstr,
minwid, num);
- p += STRLEN(p);
- } else
+ }
+
+ // Advance the output buffer position to the end of the
+ // number we just printed
+ out_p += STRLEN(out_p);
+
+ // Otherwise, there was nothing to print so mark the item as empty
+ } else {
item[curitem].type = Empty;
+ }
- if (opt == STL_VIM_EXPR)
+ // Only free the string buffer if we allocated it.
+ // Note: This is not needed if `str` is pointing at `tmp`
+ if (opt == STL_VIM_EXPR) {
xfree(str);
+ }
if (num >= 0 || (!itemisflag && str && *str))
- prevchar_isflag = FALSE; /* Item not NULL, but not a flag */
+ prevchar_isflag = false; /* Item not NULL, but not a flag */
+
+ // Item processed, move to the next
curitem++;
}
- *p = NUL;
- itemcnt = curitem;
- if (usefmt != fmt)
+ *out_p = NUL;
+ int itemcnt = curitem;
+
+ // Free the format buffer if we allocated it internally
+ if (usefmt != fmt) {
xfree(usefmt);
+ }
+
+ // We have now processed the entire statusline format string.
+ // What follows is post-processing to handle alignment and
+ // highlighting factors.
- width = vim_strsize(out);
+ int width = vim_strsize(out);
if (maxwidth > 0 && width > maxwidth) {
- /* Result is too long, must truncate somewhere. */
- l = 0;
- if (itemcnt == 0)
- s = out;
- else {
- for (; l < itemcnt; l++)
- if (item[l].type == Trunc) {
- /* Truncate at %< item. */
- s = item[l].start;
+ // Result is too long, must truncate somewhere.
+ int item_idx = 0;
+ char_u *trunc_p;
+
+ // If there are no items, truncate from beginning
+ if (itemcnt == 0) {
+ trunc_p = out;
+
+ // Otherwise, look for the truncation item
+ } else {
+ // Default to truncating at the first item
+ trunc_p = item[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;
+ item_idx = i;
break;
}
- if (l == itemcnt) {
- /* No %< item, truncate first item. */
- s = item[0].start;
- l = 0;
- }
}
- if (width - vim_strsize(s) >= maxwidth) {
- /* Truncation mark is beyond max length */
+ // If the truncation point we found is beyond the maximum
+ // length of the string, truncate the end of the string.
+ if (width - vim_strsize(trunc_p) >= maxwidth) {
+ // If we are using a multi-byte encoding, walk from the beginning of the
+ // string to find the last character that will fit.
if (has_mbyte) {
- s = out;
+ trunc_p = out;
width = 0;
for (;; ) {
- width += ptr2cells(s);
+ width += ptr2cells(trunc_p);
if (width >= maxwidth)
break;
- s += (*mb_ptr2len)(s);
+
+ // Note: Only advance the pointer if the next
+ // character will fit in the available output space
+ trunc_p += (*mb_ptr2len)(trunc_p);
}
- /* Fill up for half a double-wide character. */
- while (++width < maxwidth)
- *s++ = fillchar;
- } else
- s = out + maxwidth - 1;
- for (l = 0; l < itemcnt; l++)
- if (item[l].start > s)
+
+ // Otherwise put the truncation point at the end, leaving enough room
+ // for a single-character truncation marker
+ } else {
+ trunc_p = out + maxwidth - 1;
+ }
+
+ // 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) {
+ itemcnt = i;
break;
- itemcnt = l;
- *s++ = '>';
- *s = 0;
+ }
+ }
+
+ // Truncate the output
+ *trunc_p++ = '>';
+ *trunc_p = 0;
+
+ // Truncate at the truncation point we found
} else {
+ // { Determine how many bytes to remove
+ long trunc_len;
if (has_mbyte) {
- n = 0;
+ trunc_len = 0;
while (width >= maxwidth) {
- width -= ptr2cells(s + n);
- n += (*mb_ptr2len)(s + n);
+ width -= ptr2cells(trunc_p + trunc_len);
+ trunc_len += (*mb_ptr2len)(trunc_p + trunc_len);
}
- } else
- n = width - maxwidth + 1;
- p = s + n;
- STRMOVE(s + 1, p);
- *s = '<';
+ } else {
+ // Truncate an extra character so we can insert our `<`.
+ trunc_len = (width - maxwidth) + 1;
+ }
+ // }
- /* Fill up for half a double-wide character. */
- while (++width < maxwidth) {
- s = s + STRLEN(s);
- *s++ = fillchar;
- *s = NUL;
+ // { Truncate the string
+ char_u *trunc_end_p = trunc_p + trunc_len;
+ STRMOVE(trunc_p + 1, trunc_end_p);
+
+ // Put a `<` to mark where we truncated at
+ *trunc_p = '<';
+
+ if (width + 1 < maxwidth) {
+ // Advance the pointer to the end of the string
+ trunc_p = trunc_p + STRLEN(trunc_p);
}
- --n; /* count the '<' */
- for (; l < itemcnt; l++) {
- if (item[l].start - n >= s)
- item[l].start -= n;
- else
- item[l].start = s;
+ // Fill up for half a double-wide character.
+ while (++width < maxwidth) {
+ *trunc_p++ = fillchar;
+ *trunc_p = NUL;
}
+ // }
+
+ // { Change the start point for items based on
+ // their position relative to our truncation point
+
+ // Note: The offset is one less than the truncation length because
+ // the truncation marker `<` is not counted.
+ long item_offset = trunc_len - 1;
+
+ 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;
+ // Anything inside the truncated area is set to start
+ // at the `<` truncation character.
+ } else {
+ item[i].start = trunc_p;
+ }
+ }
+ // }
}
width = maxwidth;
- } else if (width < maxwidth && STRLEN(out) + maxwidth - width + 1 <
- outlen) {
- /* Apply STL_MIDDLE if any */
- for (l = 0; l < itemcnt; l++)
- if (item[l].type == Middle)
+
+ // If there is room left in our statusline, and room left in our buffer,
+ // add characters at the middle marker (if there is one) to
+ // fill up the available space.
+ } else if (width < maxwidth
+ && STRLEN(out) + maxwidth - width + 1 < outlen) {
+ for (int item_idx = 0; item_idx < itemcnt; item_idx++) {
+ if (item[item_idx].type == Middle) {
+ // Move the statusline to make room for the middle characters
+ char_u *middle_end = item[item_idx].start + (maxwidth - width);
+ STRMOVE(middle_end, item[item_idx].start);
+
+ // Fill the middle section with our fill character
+ for (char_u *s = item[item_idx].start; s < middle_end; s++)
+ *s = fillchar;
+
+ // Adjust the offset of any items after the middle
+ for (item_idx++; item_idx < itemcnt; item_idx++)
+ item[item_idx].start += maxwidth - width;
+
+ width = maxwidth;
break;
- if (l < itemcnt) {
- p = item[l].start + maxwidth - width;
- STRMOVE(p, item[l].start);
- for (s = item[l].start; s < p; s++)
- *s = fillchar;
- for (l++; l < itemcnt; l++)
- item[l].start += maxwidth - width;
- width = maxwidth;
+ }
}
}
- /* Store the info about highlighting. */
+ // Store the info about highlighting.
if (hltab != NULL) {
- sp = hltab;
- for (l = 0; l < itemcnt; l++) {
+ 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;
@@ -3507,18 +3877,39 @@ build_stl_str_hl (
sp->userhl = 0;
}
- /* Store the info about tab pages labels. */
+ // Store the info about tab pages labels.
if (tabtab != NULL) {
- sp = tabtab;
- for (l = 0; l < itemcnt; l++) {
+ StlClickRecord *cur_tab_rec = tabtab;
+ for (long l = 0; l < itemcnt; l++) {
if (item[l].type == TabPage) {
- sp->start = item[l].start;
- sp->userhl = item[l].minwid;
- sp++;
+ cur_tab_rec->start = (char *) item[l].start;
+ if (item[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) {
+ cur_tab_rec->def.type = kStlClickTabSwitch;
+ } else {
+ cur_tab_rec->def.type = kStlClickTabClose;
+ tabnr = -tabnr;
+ }
+ cur_tab_rec->def.tabnr = tabnr;
+ }
+ cur_tab_rec->def.func = NULL;
+ cur_tab_rec++;
+ } else if (item[l].type == ClickFunc) {
+ cur_tab_rec->start = (char *) item[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++;
}
}
- sp->start = NULL;
- sp->userhl = 0;
+ cur_tab_rec->start = NULL;
+ cur_tab_rec->def.type = kStlClickDisabled;
+ cur_tab_rec->def.tabnr = 0;
+ cur_tab_rec->def.func = NULL;
}
return width;
@@ -3540,6 +3931,11 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
above = wp->w_topline - 1;
above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill;
+ if (wp->w_topline == 1 && wp->w_topfill >= 1) {
+ // All buffer lines are displayed and there is an indication
+ // of filler lines, that can be considered seeing all lines.
+ above = 0;
+ }
below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
if (below <= 0)
STRLCPY(buf, (above == 0 ? _("All") : _("Bot")), buflen);
@@ -3551,26 +3947,29 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen)
: (int)(above * 100L / (above + below)));
}
-/*
- * Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
- * Return TRUE if it was appended.
- */
-static int
-append_arg_number (
- win_T *wp,
- char_u *buf,
- int buflen,
- int add_file /* Add "file" before the arg number */
-)
+/// Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
+///
+/// @param wp window whose buffers to check
+/// @param[in,out] buf string buffer to add the text to
+/// @param buflen length of the string buffer
+/// @param add_file if true, add "file" before the arg number
+///
+/// @return true if it was appended.
+static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file)
+ FUNC_ATTR_NONNULL_ALL
{
- char_u *p;
+ // Nothing to do
+ if (ARGCOUNT <= 1) {
+ return false;
+ }
- if (ARGCOUNT <= 1) /* nothing to do */
- return FALSE;
+ char_u *p = buf + STRLEN(buf); // go to the end of the buffer
+
+ // Early out if the string is getting too long
+ if (p - buf + 35 >= buflen) {
+ return false;
+ }
- p = buf + STRLEN(buf); /* go to the end of the buffer */
- if (p - buf + 35 >= buflen) /* getting too long */
- return FALSE;
*p++ = ' ';
*p++ = '(';
if (add_file) {
@@ -3578,9 +3977,10 @@ append_arg_number (
p += 5;
}
vim_snprintf((char *)p, (size_t)(buflen - (p - buf)),
- wp->w_arg_idx_invalid ? "(%d) of %d)"
- : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT);
- return TRUE;
+ wp->w_arg_idx_invalid
+ ? "(%d) of %d)"
+ : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT);
+ return true;
}
/*
@@ -4214,11 +4614,16 @@ char_u *buf_spname(buf_T *buf)
return NULL;
}
-/*
- * Find a window for buffer "buf".
- * If found true is returned and "wp" and "tp" are set to the window and tabpage.
- * If not found false is returned.
- */
+/// Find a window for buffer "buf".
+/// If found true is returned and "wp" and "tp" are set to
+/// the window and tabpage.
+/// If not found, false is returned.
+///
+/// @param buf buffer to find a window for
+/// @param[out] wp stores the found window
+/// @param[out] tp stores the found tabpage
+///
+/// @return true if a window was found for the buffer.
bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp)
{
*wp = NULL;
@@ -4500,6 +4905,224 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a
}
}
+// bufhl: plugin highlights associated with a buffer
+
+/// Adds a highlight to buffer.
+///
+/// Unlike matchaddpos() highlights follow changes to line numbering (as lines
+/// are inserted/removed above the highlighted line), like signs and marks do.
+///
+/// When called with "src_id" set to 0, a unique source id is generated and
+/// returned. Succesive calls can pass it in as "src_id" to add new highlights
+/// to the same source group. All highlights in the same group can be cleared
+/// at once. If the highlight never will be manually deleted pass in -1 for
+/// "src_id"
+///
+/// if "hl_id" or "lnum" is invalid no highlight is added, but a new src_id
+/// is still returned.
+///
+/// @param buf The 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 Id of the highlight group to use
+/// @param lnum The line to highlight
+/// @param col_start First column to highlight
+/// @param col_end The last column to highlight,
+/// or -1 to highlight to end of line
+/// @return The src_id that was used
+int bufhl_add_hl(buf_T *buf,
+ int src_id,
+ int hl_id,
+ linenr_T lnum,
+ colnr_T col_start,
+ colnr_T col_end) {
+ static int next_src_id = 1;
+ if (src_id == 0) {
+ src_id = next_src_id++;
+ }
+ 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(bufhl_hl_item_T, *lineinfo);
+ 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);
+ }
+ return src_id;
+}
+
+/// Clear bufhl highlights from a given source group and range of lines.
+///
+/// @param buf The buffer to remove highlights from
+/// @param src_id highlight source group to clear, or -1 to clear all groups.
+/// @param line_start first line to clear
+/// @param line_end last line to clear or MAXLNUM to clear to end of file.
+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;
+ }
+ }
+ }
+ })
+
+ if (last_changed != -1) {
+ changed_lines_buf(buf, first_changed, last_changed+1, 0);
+ redraw_buf_later(buf, VALID);
+ }
+}
+
+/// Clear bufhl highlights from a given source group and given line
+///
+/// @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);
+ if (src_id < 0) {
+ kv_size(*lineinfo) = 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);
+ }
+ newind++;
+ }
+ }
+ kv_size(*lineinfo) = newind;
+ }
+
+ if (kv_size(*lineinfo) == 0) {
+ kv_destroy(*lineinfo);
+ map_del(linenr_T, bufhl_vec_T)(bufhl_info, lnum);
+ }
+ return kv_size(*lineinfo) != oldsize;
+}
+
+/// Remove all highlights and free the highlight data
+void bufhl_clear_all(buf_T* buf) {
+ if (!buf->b_bufhl_info) {
+ return;
+ }
+ bufhl_clear_line_range(buf, -1, 1, MAXLNUM);
+ map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info);
+ buf->b_bufhl_info = NULL;
+}
+
+/// Adjust a placed highlight for inserted/deleted lines.
+void bufhl_mark_adjust(buf_T* buf,
+ linenr_T line1,
+ linenr_T line2,
+ long amount,
+ long amount_after) {
+ if (!buf->b_bufhl_info) {
+ 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 (amount == MAXLNUM) {
+ bufhl_clear_line(buf->b_bufhl_info, -1, line);
+ continue;
+ } else {
+ line += amount;
+ }
+ } else if (line > line2) {
+ 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;
+}
+
+
+/// Get highlights to display at a specific line
+///
+/// @param buf The buffer handle
+/// @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) {
+ 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;
+}
+
+/// get highlighting at column col
+///
+/// It is is assumed this will be called with
+/// non-decreasing column nrs, so that it is
+/// possible to only recalculate highlights
+/// at endpoints.
+///
+/// @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) {
+ 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);
+ if (entry.start <= col && col <= entry.stop) {
+ int entry_attr = syn_id2attr(entry.hl_id);
+ attr = hl_combine_attr(attr, entry_attr);
+ if (entry.stop < info->valid_to) {
+ info->valid_to = entry.stop;
+ }
+ } else if (col < entry.start && entry.start-1 < info->valid_to) {
+ info->valid_to = entry.start-1;
+ }
+ }
+ info->current = attr;
+ return attr;
+}
+
+
/*
* Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
*/
@@ -4514,50 +5137,54 @@ void set_buflisted(int on)
}
}
-/*
- * Read the file for "buf" again and check if the contents changed.
- * Return TRUE if it changed or this could not be checked.
- */
-int buf_contents_changed(buf_T *buf)
+/// Read the file for "buf" again and check if the contents changed.
+/// Return true if it changed or this could not be checked.
+///
+/// @param buf buffer to check
+///
+/// @return true if the buffer's contents have changed
+bool buf_contents_changed(buf_T *buf)
+ FUNC_ATTR_NONNULL_ALL
{
- buf_T *newbuf;
- int differ = TRUE;
- linenr_T lnum;
- aco_save_T aco;
- exarg_T ea;
+ bool differ = true;
- /* Allocate a buffer without putting it in the buffer list. */
- newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
- if (newbuf == NULL)
- return TRUE;
+ // Allocate a buffer without putting it in the buffer list.
+ buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
+ if (newbuf == NULL) {
+ return true;
+ }
- /* Force the 'fileencoding' and 'fileformat' to be equal. */
+ // Force the 'fileencoding' and 'fileformat' to be equal.
+ exarg_T ea;
prep_exarg(&ea, buf);
- /* set curwin/curbuf to buf and save a few things */
+ // set curwin/curbuf to buf and save a few things
+ aco_save_T aco;
aucmd_prepbuf(&aco, newbuf);
if (ml_open(curbuf) == OK
&& readfile(buf->b_ffname, buf->b_fname,
- (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
- &ea, READ_NEW | READ_DUMMY) == OK) {
- /* compare the two files line by line */
+ (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
+ &ea, READ_NEW | READ_DUMMY) == OK) {
+ // compare the two files line by line
if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) {
- differ = FALSE;
- for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
- if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0) {
- differ = TRUE;
+ differ = false;
+ for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
+ if (STRCMP(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) {
+ differ = true;
break;
}
+ }
}
}
xfree(ea.cmd);
- /* restore curwin/curbuf and a few other things */
+ // restore curwin/curbuf and a few other things
aucmd_restbuf(&aco);
- if (curbuf != newbuf) /* safety check */
- wipe_buffer(newbuf, FALSE);
+ if (curbuf != newbuf) { // safety check
+ wipe_buffer(newbuf, false);
+ }
return differ;
}