diff options
Diffstat (limited to 'src/nvim/buffer.c')
-rw-r--r-- | src/nvim/buffer.c | 1194 |
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 |