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.c1194
1 files changed, 2 insertions, 1192 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index f9bce2476f..88b7cfc8ee 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -76,6 +76,7 @@
#include "nvim/runtime.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
@@ -89,11 +90,6 @@
# include "buffer.c.generated.h"
#endif
-// Determines how deeply nested %{} blocks will be evaluated in statusline.
-#define MAX_STL_EVAL_DEPTH 100
-
-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");
static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use");
@@ -3359,1192 +3355,6 @@ 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,
- 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, %= => separation 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 *out, size_t outlen, char *fmt, int use_sandbox, int fillchar,
- int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab)
-{
- static size_t stl_items_len = 20; // Initial value, grows as needed.
- static stl_item_t *stl_items = NULL;
- static int *stl_groupitems = NULL;
- static stl_hlrec_t *stl_hltab = NULL;
- static StlClickRecord *stl_tabtab = NULL;
- static int *stl_separator_locations = NULL;
-
-#define TMPLEN 70
- char buf_tmp[TMPLEN];
- char win_tmp[TMPLEN];
- char *usefmt = fmt;
- const int save_must_redraw = must_redraw;
- const int save_redr_type = curwin->w_redr_type;
-
- if (stl_items == NULL) {
- stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len);
- stl_groupitems = xmalloc(sizeof(int) * stl_items_len);
-
- // Allocate one more, because the last element is used to indicate the
- // end of the list.
- stl_hltab = xmalloc(sizeof(stl_hlrec_t) * (stl_items_len + 1));
- stl_tabtab = xmalloc(sizeof(StlClickRecord) * (stl_items_len + 1));
-
- stl_separator_locations = xmalloc(sizeof(int) * stl_items_len);
- }
-
- // 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] == '!') {
- typval_T tv = {
- .v_type = VAR_NUMBER,
- .vval.v_number = wp->handle,
- };
- set_var(S_LEN("g:statusline_winid"), &tv, false);
-
- usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
- if (usefmt == NULL) {
- usefmt = fmt;
- }
-
- do_unlet(S_LEN("g:statusline_winid"), true);
- }
-
- if (fillchar == 0) {
- fillchar = ' ';
- }
-
- // The cursor in windows other than the current one isn't always
- // up-to-date, esp. because of autocommands and timers.
- linenr_T lnum = wp->w_cursor.lnum;
- if (lnum > wp->w_buffer->b_ml.ml_line_count) {
- lnum = wp->w_buffer->b_ml.ml_line_count;
- wp->w_cursor.lnum = lnum;
- }
-
- // Get line & check if empty (cursorpos will show "0-1").
- const char *line_ptr = (char *)ml_get_buf(wp->w_buffer, 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.
- int byteval;
- const size_t len = STRLEN(line_ptr);
- if (wp->w_cursor.col > (colnr_T)len) {
- // Line may have changed since checking the cursor column, or the lnum
- // was adjusted above.
- wp->w_cursor.col = (colnr_T)len;
- wp->w_cursor.coladd = 0;
- byteval = 0;
- } else {
- byteval = utf_ptr2char(line_ptr + wp->w_cursor.col);
- }
-
- int groupdepth = 0;
- int evaldepth = 0;
-
- int curitem = 0;
- bool prevchar_isflag = true;
- bool prevchar_isitem = false;
-
- // out_p is the current position in the output buffer
- char *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 *out_end_p = (out + outlen) - 1;
-
- // Proceed character by character through the statusline format string
- // fmt_p is the current position in the input buffer
- for (char *fmt_p = usefmt; *fmt_p != NUL;) {
- if (curitem == (int)stl_items_len) {
- size_t new_len = stl_items_len * 3 / 2;
-
- stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len);
- stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len);
- stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * (new_len + 1));
- stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * (new_len + 1));
- stl_separator_locations =
- xrealloc(stl_separator_locations, sizeof(int) * new_len);
-
- stl_items_len = new_len;
- }
-
- if (*fmt_p != '%') {
- prevchar_isflag = prevchar_isitem = false;
- }
-
- // 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;
- }
-
- // 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;
- }
-
- // Two `%` in a row is the escape sequence to print a
- // single `%` in the output buffer.
- if (*fmt_p == '%') {
- *out_p++ = *fmt_p++;
- prevchar_isflag = prevchar_isitem = false;
- continue;
- }
-
- // STL_SEPARATE: Separation place between left and right aligned items.
- if (*fmt_p == STL_SEPARATE) {
- fmt_p++;
- // Ignored when we are inside of a grouping
- if (groupdepth > 0) {
- continue;
- }
- stl_items[curitem].type = Separate;
- stl_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++;
- stl_items[curitem].type = Trunc;
- stl_items[curitem++].start = out_p;
- continue;
- }
-
- // 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--;
-
- // Determine how long the group is.
- // Note: We set the current output position to null
- // so `vim_strsize` will work.
- char *t = stl_items[stl_groupitems[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 > stl_groupitems[groupdepth] + 1
- && stl_items[stl_groupitems[groupdepth]].minwid == 0) {
- // remove group if all items are empty and highlight group
- // doesn't change
- int group_start_userhl = 0;
- int group_end_userhl = 0;
- int n;
- for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) {
- if (stl_items[n].type == Highlight) {
- group_start_userhl = group_end_userhl = stl_items[n].minwid;
- break;
- }
- }
- for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- if (stl_items[n].type == Normal) {
- break;
- }
- if (stl_items[n].type == Highlight) {
- group_end_userhl = stl_items[n].minwid;
- }
- }
- if (n == curitem && group_start_userhl == group_end_userhl) {
- // empty group
- out_p = t;
- group_len = 0;
- for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- // do not use the highlighting from the removed group
- if (stl_items[n].type == Highlight) {
- stl_items[n].type = Empty;
- }
- // adjust the start position of TabPage to the next
- // item position
- if (stl_items[n].type == TabPage) {
- stl_items[n].start = out_p;
- }
- }
- }
- }
-
- // 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 > stl_items[stl_groupitems[groupdepth]].maxwid) {
- // { Determine the number of bytes to remove
-
- // Find the first character that should be included.
- long n = 0;
- while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) {
- group_len -= ptr2cells(t + n);
- n += utfc_ptr2len(t + n);
- }
- // }
-
- // Prepend the `<` to indicate that the output was truncated.
- *t = '<';
-
- // { 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 < stl_items[stl_groupitems[groupdepth]].minwid) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- // }
-
- // correct the start of the items for the truncation
- for (int idx = stl_groupitems[groupdepth] + 1; idx < curitem; idx++) {
- // Shift everything back by the number of removed bytes
- // Minus one for the leading '<' added above.
- stl_items[idx].start -= n - 1;
-
- // If the item was partially or completely truncated, set its
- // start to the start of the group
- if (stl_items[idx].start < t) {
- stl_items[idx].start = t;
- }
- }
- // If the group is shorter than the minimum width, add padding characters.
- } else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
- long min_group_width = stl_items[stl_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;
- while (group_len++ < min_group_width && out_p < out_end_p) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- // If the group is right-aligned, shift everything to the right and
- // prepend with filler characters.
- } else {
- // { Move the group to the right
- group_len = (min_group_width - group_len) * utf_char2len(fillchar);
- memmove(t + group_len, t, (size_t)(out_p - t));
- 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 = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- stl_items[n].start += group_len;
- }
-
- // Prepend the fill characters
- for (; group_len > 0; group_len--) {
- MB_CHAR2BYTES(fillchar, t);
- }
- }
- }
- continue;
- }
- 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++;
- }
-
- // Denotes that the item should be left-aligned.
- // This is tracked by using a negative length.
- if (*fmt_p == '-') {
- fmt_p++;
- left_align = true;
- }
-
- // The first digit group is the item's min width
- if (ascii_isdigit(*fmt_p)) {
- minwid = getdigits_int(&fmt_p, false, 0);
- }
-
- // User highlight groups override the min width field
- // to denote the styling to use.
- if (*fmt_p == STL_USER_HL) {
- stl_items[curitem].type = Highlight;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid > 9 ? 1 : minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // 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 previous tab label nr.
- for (long n = curitem - 1; n >= 0; n--) {
- if (stl_items[n].type == TabPage && stl_items[n].minwid >= 0) {
- minwid = stl_items[n].minwid;
- break;
- }
- }
- } else {
- // close nrs are stored as negative values
- minwid = -minwid;
- }
- }
- stl_items[curitem].type = TabPage;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- if (*fmt_p == STL_CLICK_FUNC) {
- fmt_p++;
- char *t = fmt_p;
- while (*fmt_p != STL_CLICK_FUNC && *fmt_p) {
- fmt_p++;
- }
- if (*fmt_p != STL_CLICK_FUNC) {
- break;
- }
- stl_items[curitem].type = ClickFunc;
- stl_items[curitem].start = out_p;
- stl_items[curitem].cmd = xmemdupz(t, (size_t)(fmt_p - t));
- stl_items[curitem].minwid = minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // 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, false, 50);
- }
- }
-
- // 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 == '(') {
- stl_groupitems[groupdepth++] = curitem;
- stl_items[curitem].type = Group;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid;
- stl_items[curitem].maxwid = maxwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // Denotes end of expanded %{} block
- if (*fmt_p == '}' && evaldepth > 0) {
- fmt_p++;
- evaldepth--;
- continue;
- }
-
- // 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;
- }
-
- // The status line item type
- char opt = *fmt_p++;
-
- // OK - now for the real work
- NumberBase base = kNumBaseDecimal;
- bool itemisflag = false;
- bool fillable = true;
- long num = -1;
- char *str = NULL;
- switch (opt) {
- case STL_FILEPATH:
- case STL_FULLPATH:
- case STL_FILENAME:
- // 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) {
- STRLCPY(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
- } else {
- char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
- : wp->w_buffer->b_fname;
- home_replace(wp->w_buffer, t, (char *)NameBuff, MAXPATHL, true);
- }
- trans_characters((char *)NameBuff, MAXPATHL);
- if (opt != STL_FILENAME) {
- str = (char *)NameBuff;
- } else {
- str = path_tail((char *)NameBuff);
- }
- break;
- case STL_VIM_EXPR: // '{'
- {
- char *block_start = fmt_p - 1;
- int reevaluate = (*fmt_p == '%');
- itemisflag = true;
-
- if (reevaluate) {
- fmt_p++;
- }
-
- // Attempt to copy the expression to evaluate into
- // the output buffer as a null-terminated string.
- char *t = out_p;
- while ((*fmt_p != '}' || (reevaluate && fmt_p[-1] != '%'))
- && *fmt_p != NUL && out_p < out_end_p) {
- *out_p++ = *fmt_p++;
- }
- if (*fmt_p != '}') { // missing '}' or out of space
- break;
- }
- fmt_p++;
- if (reevaluate) {
- out_p[-1] = 0; // remove the % at the end of %{% expr %}
- } else {
- *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(buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum);
- set_internal_string_var("g:actual_curbuf", buf_tmp);
- vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle);
- set_internal_string_var("g:actual_curwin", (char *)win_tmp);
-
- buf_T *const save_curbuf = curbuf;
- win_T *const save_curwin = curwin;
- const int save_VIsual_active = VIsual_active;
- curwin = wp;
- curbuf = wp->w_buffer;
- // Visual mode is only valid in the current window.
- if (curwin != save_curwin) {
- VIsual_active = false;
- }
-
- // Note: The result stored in `t` is unused.
- str = eval_to_string_safe(out_p, &t, use_sandbox);
-
- curwin = save_curwin;
- curbuf = save_curbuf;
- VIsual_active = save_VIsual_active;
-
- // Remove the variable we just stored
- do_unlet(S_LEN("g:actual_curbuf"), true);
- do_unlet(S_LEN("g:actual_curwin"), 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(str);
- XFREE_CLEAR(str);
- itemisflag = false;
- }
- }
-
- // If the output of the expression needs to be evaluated
- // replace the %{} block with the result of evaluation
- if (reevaluate && str != NULL && *str != 0
- && strchr((const char *)str, '%') != NULL
- && evaldepth < MAX_STL_EVAL_DEPTH) {
- size_t parsed_usefmt = (size_t)(block_start - usefmt);
- size_t str_length = STRLEN(str);
- size_t fmt_length = STRLEN(fmt_p);
- size_t new_fmt_len = parsed_usefmt + str_length + fmt_length + 3;
- char *new_fmt = xmalloc(new_fmt_len * sizeof(char));
- char *new_fmt_p = new_fmt;
-
- new_fmt_p = (char *)memcpy(new_fmt_p, usefmt, parsed_usefmt) + parsed_usefmt;
- new_fmt_p = (char *)memcpy(new_fmt_p, str, str_length) + str_length;
- new_fmt_p = (char *)memcpy(new_fmt_p, "%}", 2) + 2;
- new_fmt_p = (char *)memcpy(new_fmt_p, fmt_p, fmt_length) + fmt_length;
- *new_fmt_p = 0;
- new_fmt_p = NULL;
-
- if (usefmt != fmt) {
- xfree(usefmt);
- }
- XFREE_CLEAR(str);
- usefmt = new_fmt;
- fmt_p = usefmt + parsed_usefmt;
- evaldepth++;
- continue;
- }
- break;
- }
-
- case STL_LINE:
- num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
- ? 0L : (long)(wp->w_cursor.lnum);
- break;
-
- case STL_NUMLINES:
- num = wp->w_buffer->b_ml.ml_line_count;
- break;
-
- case STL_COLUMN:
- num = (State & MODE_INSERT) == 0 && empty_line ? 0 : (int)wp->w_cursor.col + 1;
- break;
-
- case STL_VIRTCOL:
- case STL_VIRTCOL_ALT: {
- colnr_T virtcol = wp->w_virtcol + 1;
- // Don't display %V if it's the same as %c.
- if (opt == STL_VIRTCOL_ALT
- && (virtcol == (colnr_T)((State & MODE_INSERT) == 0 && 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) /
- (long)wp->w_buffer->b_ml.ml_line_count);
- 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, buf_tmp, TMPLEN);
- str = buf_tmp;
- break;
-
- case STL_ARGLISTSTAT:
- 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.
- buf_tmp[0] = 0;
-
- // Note: The call will only return true if it actually
- // appended data to the `buf_tmp` buffer.
- if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) {
- str = buf_tmp;
- }
- break;
-
- case STL_KEYMAP:
- fillable = false;
- if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN)) {
- str = buf_tmp;
- }
- break;
- case STL_PAGENUM:
- num = printer_page_num;
- break;
-
- case STL_BUFNO:
- num = wp->w_buffer->b_fnum;
- break;
-
- 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,
- false);
- num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ?
- 0L : l + 1 + ((State & MODE_INSERT) == 0 && empty_line ?
- 0 : (int)wp->w_cursor.col);
- break;
- }
- case STL_BYTEVAL_X:
- base = kNumBaseHexadecimal;
- FALLTHROUGH;
- case STL_BYTEVAL:
- num = byteval;
- if (num == NL) {
- num = 0;
- } else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC) {
- num = NL;
- }
- break;
-
- case STL_ROFLAG:
- case STL_ROFLAG_ALT:
- itemisflag = true;
- if (wp->w_buffer->b_p_ro) {
- str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]");
- }
- break;
-
- case STL_HELPFLAG:
- case STL_HELPFLAG_ALT:
- itemisflag = true;
- if (wp->w_buffer->b_help) {
- str = (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(buf_tmp, sizeof(buf_tmp), "[%s]",
- wp->w_buffer->b_p_ft);
- str = buf_tmp;
- }
- break;
-
- case STL_FILETYPE_ALT:
- 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(buf_tmp, sizeof(buf_tmp), ",%s", wp->w_buffer->b_p_ft);
- // Uppercase the file extension
- for (char *t = buf_tmp; *t != 0; t++) {
- *t = (char)TOUPPER_LOC(*t);
- }
- str = buf_tmp;
- }
- break;
- case STL_PREVIEWFLAG:
- case STL_PREVIEWFLAG_ALT:
- itemisflag = true;
- if (wp->w_p_pvw) {
- str = (opt == STL_PREVIEWFLAG_ALT) ? ",PRV" : _("[Preview]");
- }
- break;
-
- case STL_QUICKFIX:
- if (bt_quickfix(wp->w_buffer)) {
- str = wp->w_llist_ref ? _(msg_loclist) : _(msg_qflist);
- }
- break;
-
- case STL_MODIFIED:
- case STL_MODIFIED_ALT:
- itemisflag = true;
- switch ((opt == STL_MODIFIED_ALT)
- + bufIsChanged(wp->w_buffer) * 2
- + (!MODIFIABLE(wp->w_buffer)) * 4) {
- case 2:
- str = "[+]"; break;
- case 3:
- str = ",+"; break;
- case 4:
- str = "[-]"; break;
- case 5:
- str = ",-"; break;
- case 6:
- str = "[+-]"; break;
- case 7:
- str = ",+-"; break;
- }
- break;
-
- case STL_HIGHLIGHT: {
- // { The name of the highlight is surrounded by `#`
- char *t = fmt_p;
- while (*fmt_p != '#' && *fmt_p != NUL) {
- fmt_p++;
- }
- // }
-
- // Create a highlight item based on the name
- if (*fmt_p == '#') {
- stl_items[curitem].type = Highlight;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = -syn_name2id_len(t, (size_t)(fmt_p - t));
- curitem++;
- fmt_p++;
- }
- continue;
- }
- }
-
- // 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`.
- stl_items[curitem].start = out_p;
- stl_items[curitem].type = Normal;
-
- // Copy the item string into the output buffer
- if (str != NULL && *str) {
- // { Skip the leading `,` or ` ` if the item is a flag
- // and the proper conditions are met
- char *t = str;
- if (itemisflag) {
- if ((t[0] && t[1])
- && ((!prevchar_isitem && *t == ',')
- || (prevchar_isflag && *t == ' '))) {
- t++;
- }
- 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;
- }
-
- // If the item is too wide, truncate it from the beginning
- if (l > maxwid) {
- while (l >= maxwid) {
- l -= ptr2cells(t);
- t += utfc_ptr2len(t);
- }
-
- // Early out if there isn't enough room for the truncation marker
- if (out_p >= out_end_p) {
- break;
- }
-
- // 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 && out_p < out_end_p; l++) {
- // Don't put a "-" in front of a digit.
- if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) {
- *out_p++ = ' ';
- } else {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- }
- minwid = 0;
- } else {
- // Note: The negative value denotes a left aligned item.
- // Here we switch the minimum width back to a positive value.
- minwid *= -1;
- }
-
- // { Copy the string text into the output buffer
- for (; *t && out_p < out_end_p; t++) {
- // Change a space by fillchar, unless fillchar is '-' and a
- // digit follows.
- if (fillable && *t == ' '
- && (!ascii_isdigit(*(t + 1)) || fillchar != '-')) {
- MB_CHAR2BYTES(fillchar, out_p);
- } else {
- *out_p++ = *t;
- }
- }
- // }
-
- // For left-aligned items, fill any remaining space with the fillchar
- for (; l < minwid && out_p < out_end_p; l++) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
-
- // 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;
-
- // { Build the formatting string
- char nstr[20];
- char *t = nstr;
- if (opt == STL_VIRTCOL_ALT) {
- *t++ = '-';
- minwid--;
- }
- *t++ = '%';
- if (zeropad) {
- *t++ = '0';
- }
-
- // Note: The `*` means we take the width as one of the arguments
- *t++ = '*';
- *t++ = base == kNumBaseHexadecimal ? 'X' : '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++;
- }
-
- // VIRTCOL_ALT takes up an extra character because
- // of the `-` we added above.
- if (opt == STL_VIRTCOL_ALT) {
- num_chars++;
- }
- // }
-
- 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.
- // 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 /= (long)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(out_p, remaining_buf_len, nstr, 0, num, n);
- } else {
- vim_snprintf(out_p, remaining_buf_len, nstr, minwid, num);
- }
-
- // 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 {
- stl_items[curitem].type = Empty;
- }
-
- // 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_CLEAR(str);
- }
-
- if (num >= 0 || (!itemisflag && str && *str)) {
- prevchar_isflag = false; // Item not NULL, but not a flag
- }
-
- // Item processed, move to the next
- curitem++;
- }
-
- *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.
-
- int width = vim_strsize(out);
- if (maxwidth > 0 && width > maxwidth) {
- // Result is too long, must truncate somewhere.
- int item_idx = 0;
- char *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 = stl_items[0].start;
- item_idx = 0;
-
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].type == Trunc) {
- // Truncate at %< stl_items.
- trunc_p = stl_items[i].start;
- item_idx = i;
- break;
- }
- }
- }
-
- // 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) {
- // Walk from the beginning of the
- // string to find the last character that will fit.
- trunc_p = out;
- width = 0;
- for (;;) {
- width += ptr2cells(trunc_p);
- if (width >= maxwidth) {
- break;
- }
-
- // Note: Only advance the pointer if the next
- // character will fit in the available output space
- trunc_p += utfc_ptr2len(trunc_p);
- }
-
- // Ignore any items in the statusline that occur after
- // the truncation point
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].start > trunc_p) {
- itemcnt = i;
- break;
- }
- }
-
- // 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 = 0;
- while (width >= maxwidth) {
- width -= ptr2cells(trunc_p + trunc_len);
- trunc_len += utfc_ptr2len(trunc_p + trunc_len);
- }
- // }
-
- // { Truncate the string
- char *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);
- }
-
- // Fill up for half a double-wide character.
- while (++width < maxwidth) {
- MB_CHAR2BYTES(fillchar, trunc_p);
- *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 (stl_items[i].start >= trunc_end_p) {
- stl_items[i].start -= item_offset;
- // Anything inside the truncated area is set to start
- // at the `<` truncation character.
- } else {
- stl_items[i].start = trunc_p;
- }
- }
- // }
- }
- width = maxwidth;
-
- // If there is room left in our statusline, and room left in our buffer,
- // add characters at the separate marker (if there is one) to
- // fill up the available space.
- } else if (width < maxwidth
- && 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 (stl_items[i].type == Separate) {
- // Create an array of the start location for each
- // separator mark.
- stl_separator_locations[num_separators] = i;
- num_separators++;
- }
- }
-
- // If we have separated groups, then we deal with it now
- if (num_separators) {
- int standard_spaces = (maxwidth - width) / num_separators;
- int final_spaces = (maxwidth - width) -
- standard_spaces * (num_separators - 1);
-
- for (int i = 0; i < num_separators; i++) {
- int dislocation = (i == (num_separators - 1)) ? final_spaces : standard_spaces;
- dislocation *= utf_char2len(fillchar);
- char *start = stl_items[stl_separator_locations[i]].start;
- char *seploc = start + dislocation;
- STRMOVE(seploc, start);
- for (char *s = start; s < seploc;) {
- MB_CHAR2BYTES(fillchar, s);
- }
-
- for (int item_idx = stl_separator_locations[i] + 1;
- item_idx < itemcnt;
- item_idx++) {
- stl_items[item_idx].start += dislocation;
- }
- }
-
- width = maxwidth;
- }
- }
-
- // Store the info about highlighting.
- if (hltab != NULL) {
- *hltab = stl_hltab;
- stl_hlrec_t *sp = stl_hltab;
- for (long l = 0; l < itemcnt; l++) {
- if (stl_items[l].type == Highlight) {
- sp->start = stl_items[l].start;
- sp->userhl = stl_items[l].minwid;
- sp++;
- }
- }
- sp->start = NULL;
- sp->userhl = 0;
- }
-
- // Store the info about tab pages labels.
- if (tabtab != NULL) {
- *tabtab = stl_tabtab;
- StlClickRecord *cur_tab_rec = stl_tabtab;
- for (long l = 0; l < itemcnt; l++) {
- if (stl_items[l].type == TabPage) {
- cur_tab_rec->start = stl_items[l].start;
- if (stl_items[l].minwid == 0) {
- cur_tab_rec->def.type = kStlClickDisabled;
- cur_tab_rec->def.tabnr = 0;
- } else {
- int tabnr = stl_items[l].minwid;
- if (stl_items[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 (stl_items[l].type == ClickFunc) {
- cur_tab_rec->start = stl_items[l].start;
- cur_tab_rec->def.type = kStlClickFuncRun;
- cur_tab_rec->def.tabnr = stl_items[l].minwid;
- cur_tab_rec->def.func = stl_items[l].cmd;
- cur_tab_rec++;
- }
- }
- cur_tab_rec->start = NULL;
- cur_tab_rec->def.type = kStlClickDisabled;
- cur_tab_rec->def.tabnr = 0;
- cur_tab_rec->def.func = NULL;
- }
-
- // When inside update_screen we do not want redrawing a statusline, ruler,
- // title, etc. to trigger another redraw, it may cause an endless loop.
- if (updating_screen) {
- must_redraw = save_must_redraw;
- curwin->w_redr_type = save_redr_type;
- }
-
- return width;
-}
-
/// Get relative cursor position in window into "buf[buflen]", in the form 99%,
/// using "Top", "Bot" or "All" when appropriate.
void get_rel_pos(win_T *wp, char *buf, int buflen)
@@ -4584,7 +3394,7 @@ void get_rel_pos(win_T *wp, char *buf, int buflen)
/// @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 *buf, int buflen, bool add_file)
+bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
FUNC_ATTR_NONNULL_ALL
{
// Nothing to do