diff options
Diffstat (limited to 'src/nvim/statusline.c')
-rw-r--r-- | src/nvim/statusline.c | 650 |
1 files changed, 331 insertions, 319 deletions
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 6ad1f31143..4dac1b1451 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -1,7 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// - #include <assert.h> #include <inttypes.h> #include <stdbool.h> @@ -12,10 +8,12 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/digraph.h" +#include "nvim/drawline.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" @@ -25,26 +23,25 @@ #include "nvim/grid.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/os.h" #include "nvim/path.h" -#include "nvim/pos.h" -#include "nvim/screen.h" -#include "nvim/sign_defs.h" +#include "nvim/plines.h" +#include "nvim/pos_defs.h" +#include "nvim/sign.h" +#include "nvim/state_defs.h" #include "nvim/statusline.h" #include "nvim/strings.h" -#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/undo.h" -#include "nvim/vim.h" #include "nvim/window.h" // Determines how deeply nested %{} blocks will be evaluated in statusline. @@ -62,13 +59,8 @@ typedef enum { /// If inversion is possible we use it. Else '=' characters are used. void win_redr_status(win_T *wp) { - int row; - int col; - char *p; - int len; int fillchar; int attr; - int this_ru_col; bool is_stl_global = global_stl_height() > 0; static bool busy = false; @@ -97,8 +89,8 @@ void win_redr_status(win_T *wp) const int stl_width = is_stl_global ? Columns : wp->w_width; get_trans_bufname(wp->w_buffer); - p = NameBuff; - len = (int)strlen(p); + char *p = NameBuff; + int len = (int)strlen(p); if ((bt_help(wp->w_buffer) || wp->w_p_pvw @@ -124,7 +116,7 @@ void win_redr_status(win_T *wp) // len += (int)strlen(p + len); // dead assignment } - this_ru_col = ru_col - (Columns - stl_width); + int this_ru_col = ru_col - (Columns - stl_width); if (this_ru_col < (stl_width + 1) / 2) { this_ru_col = (stl_width + 1) / 2; } @@ -132,10 +124,10 @@ void win_redr_status(win_T *wp) p = "<"; // No room for file name! len = 1; } else { - int clen = 0, i; + int i; // Count total number of display cells. - clen = (int)mb_string2cells(p); + int clen = (int)mb_string2cells(p); // Find first character that will fit. // Going from start to end is much faster for DBCS. @@ -151,29 +143,29 @@ void win_redr_status(win_T *wp) } } - row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); - col = is_stl_global ? 0 : wp->w_wincol; - grid_puts(&default_grid, p, row, col, attr); - grid_fill(&default_grid, row, row + 1, len + col, - this_ru_col + col, fillchar, fillchar, attr); + grid_line_start(&default_grid, is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp)); + int col = is_stl_global ? 0 : wp->w_wincol; + + int width = grid_line_puts(col, p, -1, attr); + grid_line_fill(width + col, this_ru_col + col, fillchar, attr); if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL) && this_ru_col - len > (int)(strlen(NameBuff) + 1)) { - grid_puts(&default_grid, NameBuff, row, - (int)((size_t)this_ru_col - strlen(NameBuff) - 1), attr); + grid_line_puts((int)((size_t)this_ru_col - strlen(NameBuff) - 1), NameBuff, -1, attr); } - win_redr_ruler(wp, true); + win_redr_ruler(wp); // Draw the 'showcmd' information if 'showcmdloc' == "statusline". if (p_sc && *p_sloc == 's') { const int sc_width = MIN(10, this_ru_col - len - 2); if (sc_width > 0) { - grid_puts_len(&default_grid, showcmd_buf, sc_width, row, - wp->w_wincol + this_ru_col - sc_width - 1, attr); + grid_line_puts(wp->w_wincol + this_ru_col - sc_width - 1, showcmd_buf, sc_width, attr); } } + + grid_line_flush(); } // May need to draw the character below the vertical separator. @@ -181,13 +173,48 @@ void win_redr_status(win_T *wp) if (stl_connected(wp)) { fillchar = fillchar_status(&attr, wp); } else { - fillchar = fillchar_vsep(wp, &attr); + attr = win_hl_attr(wp, HLF_C); + fillchar = wp->w_p_fcs_chars.vert; } - grid_putchar(&default_grid, fillchar, W_ENDROW(wp), W_ENDCOL(wp), attr); + grid_line_start(&default_grid, W_ENDROW(wp)); + grid_line_put_schar(W_ENDCOL(wp), schar_from_char(fillchar), attr); + grid_line_flush(); } busy = false; } +void get_trans_bufname(buf_T *buf) +{ + if (buf_spname(buf) != NULL) { + xstrlcpy(NameBuff, buf_spname(buf), MAXPATHL); + } else { + home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, true); + } + trans_characters(NameBuff, MAXPATHL); +} + +/// Only call if (wp->w_vsep_width != 0). +/// +/// @return true if the status line of window "wp" is connected to the status +/// line of the window right of it. If not, then it's a vertical separator. +bool stl_connected(win_T *wp) +{ + frame_T *fr = wp->w_frame; + while (fr->fr_parent != NULL) { + if (fr->fr_parent->fr_layout == FR_COL) { + if (fr->fr_next != NULL) { + break; + } + } else { + if (fr->fr_next != NULL) { + return true; + } + } + fr = fr->fr_parent; + } + return false; +} + /// Clear status line, window bar or tab page line click definition table /// /// @param[out] tpcd Table to clear. @@ -205,7 +232,7 @@ void stl_clear_click_defs(StlClickDefinition *const click_defs, const size_t cli } /// Allocate or resize the click definitions array if needed. -StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, size_t *size) +StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, int width, size_t *size) { if (*size < (size_t)width) { xfree(cdp); @@ -216,8 +243,8 @@ StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, si } /// Fill the click definitions array if needed. -void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, char *buf, - int width, bool tabline) +void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, + const char *buf, int width, bool tabline) { if (click_defs == NULL) { return; @@ -231,6 +258,7 @@ void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_r }; for (int i = 0; click_recs[i].start != NULL; i++) { len += vim_strnsize(buf, (int)(click_recs[i].start - buf)); + assert(len <= width); if (col < len) { while (col < len) { click_defs[col++] = cur_click_def; @@ -238,7 +266,7 @@ void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_r } else { xfree(cur_click_def.func); } - buf = (char *)click_recs[i].start; + buf = click_recs[i].start; cur_click_def = click_recs[i].def; if (!tabline && !(cur_click_def.type == kStlClickDisabled || cur_click_def.type == kStlClickFuncRun)) { @@ -261,23 +289,17 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) { static bool entered = false; int attr; - int curattr; int row; int col = 0; int maxwidth; - int width; - int n; - int len; int fillchar; char buf[MAXPATHL]; + char transbuf[MAXPATHL]; char *stl; - char *p; char *opt_name; int opt_scope = 0; stl_hlrec_t *hltab; StlClickRecord *tabtab; - win_T *ewp; - int p_crb_save; bool is_stl_global = global_stl_height() > 0; ScreenGrid *grid = &default_grid; @@ -309,7 +331,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) grid_adjust(&grid, &row, &col); if (row < 0) { - return; + goto theend; } fillchar = wp->w_p_fcs_chars.wbr; @@ -321,7 +343,8 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) } else { row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); - maxwidth = is_stl_global ? Columns : wp->w_width; + const bool in_status_line = wp->w_status_height != 0 || is_stl_global; + maxwidth = in_status_line && !is_stl_global ? wp->w_width : Columns; stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth, &wp->w_status_click_defs_size); @@ -347,8 +370,8 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) if (col < (maxwidth + 1) / 2) { col = (maxwidth + 1) / 2; } - maxwidth = maxwidth - col; - if (!wp->w_status_height && !is_stl_global) { + maxwidth -= col; + if (!in_status_line) { grid = &msg_grid_adj; row = Rows - 1; maxwidth--; // writing in last column may cause scrolling @@ -361,7 +384,9 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) opt_scope = ((*wp->w_p_stl != NUL) ? OPT_LOCAL : 0); } - col += is_stl_global ? 0 : wp->w_wincol; + if (in_status_line && !is_stl_global) { + col += wp->w_wincol; + } } if (maxwidth <= 0) { @@ -370,41 +395,34 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) // Temporarily reset 'cursorbind', we don't want a side effect from moving // the cursor away and back. - ewp = wp == NULL ? curwin : wp; - p_crb_save = ewp->w_p_crb; + win_T *ewp = wp == NULL ? curwin : wp; + int p_crb_save = ewp->w_p_crb; ewp->w_p_crb = false; // Make a copy, because the statusline may include a function call that // might change the option value and free the memory. stl = xstrdup(stl); - width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope, - fillchar, maxwidth, &hltab, &tabtab, NULL); + build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope, + fillchar, maxwidth, &hltab, &tabtab, NULL); xfree(stl); ewp->w_p_crb = p_crb_save; - // Make all characters printable. - p = transstr(buf, true); - len = (int)xstrlcpy(buf, p, sizeof(buf)); - len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; - xfree(p); - - // fill up with "fillchar" - while (width < maxwidth && len < (int)sizeof(buf) - 1) { - len += utf_char2bytes(fillchar, buf + len); - width++; - } - buf[len] = NUL; + int len = (int)strlen(buf); + int start_col = col; // Draw each snippet with the specified highlighting. - grid_puts_line_start(grid, row); + if (!draw_ruler) { + grid_line_start(grid, row); + } - curattr = attr; - p = buf; - for (n = 0; hltab[n].start != NULL; n++) { + int curattr = attr; + char *p = buf; + for (int n = 0; hltab[n].start != NULL; n++) { int textlen = (int)(hltab[n].start - p); - grid_puts_len(grid, p, textlen, row, col, curattr); - col += vim_strnsize(p, textlen); + // Make all characters printable. + size_t tsize = transstr_buf(p, textlen, transbuf, sizeof transbuf, true); + col += grid_line_puts(col, transbuf, (int)tsize, curattr); p = hltab[n].start; if (hltab[n].userhl == 0) { @@ -418,9 +436,16 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) } } // Make sure to use an empty string instead of p, if p is beyond buf + len. - grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr); + size_t tsize = transstr_buf(p >= buf + len ? "" : p, -1, transbuf, sizeof transbuf, true); + col += grid_line_puts(col, transbuf, (int)tsize, curattr); + int maxcol = start_col + maxwidth; - grid_puts_line_flush(false); + // fill up with "fillchar" + grid_line_fill(col, maxcol, fillchar, curattr); + + if (!draw_ruler) { + grid_line_flush(); + } // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking // in the tab page line, status line or window bar @@ -453,7 +478,8 @@ void win_redr_winbar(win_T *wp) entered = false; } -void win_redr_ruler(win_T *wp, bool always) +/// must be called after a grid_line_start() at the intended row +void win_redr_ruler(win_T *wp) { bool is_stl_global = global_stl_height() > 0; static bool did_show_ext_ruler = false; @@ -471,7 +497,8 @@ void win_redr_ruler(win_T *wp, bool always) // Don't draw the ruler while doing insert-completion, it might overwrite // the (long) mode message. - if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) { + win_T *ruler_win = curwin->w_status_height == 0 ? curwin : lastwin_nofloating(); + if (wp == ruler_win && ruler_win->w_status_height == 0 && !is_stl_global) { if (edit_submode != NULL) { return; } @@ -483,138 +510,105 @@ void win_redr_ruler(win_T *wp, bool always) } // Check if not in Insert mode and the line is empty (will show "0-1"). - int empty_line = false; - if ((State & MODE_INSERT) == 0 && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false) == NUL) { - empty_line = true; - } + int empty_line = (State & MODE_INSERT) == 0 + && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) == NUL; - // Only draw the ruler when something changed. - validate_virtcol_win(wp); - if (redraw_cmdline - || always - || wp->w_cursor.lnum != wp->w_ru_cursor.lnum - || wp->w_cursor.col != wp->w_ru_cursor.col - || wp->w_virtcol != wp->w_ru_virtcol - || wp->w_cursor.coladd != wp->w_ru_cursor.coladd - || wp->w_topline != wp->w_ru_topline - || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count - || wp->w_topfill != wp->w_ru_topfill - || empty_line != wp->w_ru_empty) { - int width; - int row; - int fillchar; - int attr; - int off; - bool part_of_status = false; - - if (wp->w_status_height) { - row = W_ENDROW(wp); - fillchar = fillchar_status(&attr, wp); - off = wp->w_wincol; - width = wp->w_width; - part_of_status = true; - } else if (is_stl_global) { - row = Rows - (int)p_ch - 1; - fillchar = fillchar_status(&attr, wp); - off = 0; - width = Columns; - part_of_status = true; - } else { - row = Rows - 1; - fillchar = ' '; - attr = HL_ATTR(HLF_MSG); - width = Columns; - off = 0; - } + int width; + int fillchar; + int attr; + int off; + bool part_of_status = false; - if (!part_of_status && p_ch == 0 && !ui_has(kUIMessages)) { - return; - } + if (wp->w_status_height) { + fillchar = fillchar_status(&attr, wp); + off = wp->w_wincol; + width = wp->w_width; + part_of_status = true; + } else if (is_stl_global) { + fillchar = fillchar_status(&attr, wp); + off = 0; + width = Columns; + part_of_status = true; + } else { + fillchar = ' '; + attr = HL_ATTR(HLF_MSG); + width = Columns; + off = 0; + } - // In list mode virtcol needs to be recomputed - colnr_T virtcol = wp->w_virtcol; - if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) { - wp->w_p_list = false; - getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL); - wp->w_p_list = true; - } + // In list mode virtcol needs to be recomputed + colnr_T virtcol = wp->w_virtcol; + if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) { + wp->w_p_list = false; + getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL); + wp->w_p_list = true; + } #define RULER_BUF_LEN 70 - char buffer[RULER_BUF_LEN]; - - // Some sprintfs return the length, some return a pointer. - // To avoid portability problems we use strlen() here. - vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",", - (wp->w_buffer->b_ml.ml_flags & - ML_EMPTY) ? (int64_t)0L : (int64_t)wp->w_cursor.lnum); - size_t len = strlen(buffer); - col_print(buffer + len, RULER_BUF_LEN - len, - empty_line ? 0 : (int)wp->w_cursor.col + 1, - (int)virtcol + 1); - - // Add a "50%" if there is room for it. - // On the last line, don't print in the last column (scrolls the - // screen up on some terminals). - int i = (int)strlen(buffer); - get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1); - int o = i + vim_strsize(buffer + i + 1); - if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen + char buffer[RULER_BUF_LEN]; + + // Some sprintfs return the length, some return a pointer. + // To avoid portability problems we use strlen() here. + vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",", + (wp->w_buffer->b_ml.ml_flags & + ML_EMPTY) ? 0 : (int64_t)wp->w_cursor.lnum); + size_t len = strlen(buffer); + col_print(buffer + len, RULER_BUF_LEN - len, + empty_line ? 0 : (int)wp->w_cursor.col + 1, + (int)virtcol + 1); + + // Add a "50%" if there is room for it. + // On the last line, don't print in the last column (scrolls the + // screen up on some terminals). + int i = (int)strlen(buffer); + get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1); + int o = i + vim_strsize(buffer + i + 1); + if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen + o++; + } + int this_ru_col = ru_col - (Columns - width); + if (this_ru_col < 0) { + this_ru_col = 0; + } + // Never use more than half the window/screen width, leave the other half + // for the filename. + if (this_ru_col < (width + 1) / 2) { + this_ru_col = (width + 1) / 2; + } + if (this_ru_col + o < width) { + // Need at least 3 chars left for get_rel_pos() + NUL. + while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) { + i += utf_char2bytes(fillchar, buffer + i); o++; } - int this_ru_col = ru_col - (Columns - width); - if (this_ru_col < 0) { - this_ru_col = 0; - } - // Never use more than half the window/screen width, leave the other half - // for the filename. - if (this_ru_col < (width + 1) / 2) { - this_ru_col = (width + 1) / 2; - } - if (this_ru_col + o < width) { - // Need at least 3 chars left for get_rel_pos() + NUL. - while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) { - i += utf_char2bytes(fillchar, buffer + i); - o++; - } - get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i); - } + get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i); + } - if (ui_has(kUIMessages) && !part_of_status) { - MAXSIZE_TEMP_ARRAY(content, 1); - MAXSIZE_TEMP_ARRAY(chunk, 2); - ADD_C(chunk, INTEGER_OBJ(attr)); - ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer))); - ADD_C(content, ARRAY_OBJ(chunk)); - ui_call_msg_ruler(content); - did_show_ext_ruler = true; - } else { - if (did_show_ext_ruler) { - ui_call_msg_ruler((Array)ARRAY_DICT_INIT); - did_show_ext_ruler = false; - } - // Truncate at window boundary. - o = 0; - for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) { - o += utf_ptr2cells(buffer + i); - if (this_ru_col + o > width) { - buffer[i] = NUL; - break; - } + if (ui_has(kUIMessages) && !part_of_status) { + MAXSIZE_TEMP_ARRAY(content, 1); + MAXSIZE_TEMP_ARRAY(chunk, 2); + ADD_C(chunk, INTEGER_OBJ(attr)); + ADD_C(chunk, CSTR_AS_OBJ(buffer)); + ADD_C(content, ARRAY_OBJ(chunk)); + ui_call_msg_ruler(content); + did_show_ext_ruler = true; + } else { + if (did_show_ext_ruler) { + ui_call_msg_ruler((Array)ARRAY_DICT_INIT); + did_show_ext_ruler = false; + } + // Truncate at window boundary. + o = 0; + for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) { + o += utf_ptr2cells(buffer + i); + if (this_ru_col + o > width) { + buffer[i] = NUL; + break; } - - ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj; - grid_puts(grid, buffer, row, this_ru_col + off, attr); - grid_fill(grid, row, row + 1, - this_ru_col + off + (int)strlen(buffer), off + width, fillchar, - fillchar, attr); } - wp->w_ru_cursor = wp->w_cursor; - wp->w_ru_virtcol = wp->w_virtcol; - wp->w_ru_empty = (char)empty_line; - wp->w_ru_topline = wp->w_topline; - wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count; - wp->w_ru_topfill = wp->w_topfill; + int w = grid_line_puts(this_ru_col + off, buffer, -1, attr); + grid_line_fill(this_ru_col + off + w, off + width, fillchar, attr); } } @@ -630,18 +624,7 @@ int fillchar_status(int *attr, win_T *wp) *attr = win_hl_attr(wp, HLF_SNC); fill = wp->w_p_fcs_chars.stlnc; } - // Use fill when there is highlighting, and highlighting of current - // window differs, or the fillchars differ, or this is not the - // current window - if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC) - || !is_curwin || ONE_WINDOW) - || (wp->w_p_fcs_chars.stl != wp->w_p_fcs_chars.stlnc))) { - return fill; - } - if (is_curwin) { - return '^'; - } - return '='; + return fill; } /// Redraw the status line according to 'statusline' and take care of any @@ -710,21 +693,9 @@ static void ui_ext_tabline_update(void) /// Draw the tab pages line at the top of the Vim window. void draw_tabline(void) { - int tabcount = 0; - int tabwidth = 0; - int col = 0; - int scol = 0; - int attr; win_T *wp; - win_T *cwp; - int wincount; - int modified; - int c; - int len; int attr_nosel = HL_ATTR(HLF_TP); int attr_fill = HL_ATTR(HLF_TPF); - char *p; - int room; int use_sep_chars = (t_colors < 8); if (default_grid.chars == NULL) { @@ -749,6 +720,15 @@ void draw_tabline(void) if (*p_tal != NUL) { win_redr_custom(NULL, false, false); } else { + int tabcount = 0; + int tabwidth = 0; + int col = 0; + win_T *cwp; + int wincount; + int c; + int len; + char *p; + grid_line_start(&default_grid, 0); FOR_ALL_TABS(tp) { tabcount++; } @@ -761,7 +741,7 @@ void draw_tabline(void) tabwidth = 6; } - attr = attr_nosel; + int attr = attr_nosel; tabcount = 0; FOR_ALL_TABS(tp) { @@ -769,7 +749,7 @@ void draw_tabline(void) break; } - scol = col; + int scol = col; if (tp == curtab) { cwp = curwin; @@ -783,16 +763,16 @@ void draw_tabline(void) attr = win_hl_attr(cwp, HLF_TPS); } if (use_sep_chars && col > 0) { - grid_putchar(&default_grid, '|', 0, col++, attr); + grid_line_put_schar(col++, schar_from_ascii('|'), attr); } if (tp->tp_topframe != topframe) { attr = win_hl_attr(cwp, HLF_TP); } - grid_putchar(&default_grid, ' ', 0, col++, attr); + grid_line_put_schar(col++, schar_from_ascii(' '), attr); - modified = false; + int modified = false; for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) { if (bufIsChanged(wp->w_buffer)) { @@ -807,17 +787,17 @@ void draw_tabline(void) if (col + len >= Columns - 3) { break; } - grid_puts_len(&default_grid, NameBuff, len, 0, col, - hl_combine_attr(attr, win_hl_attr(cwp, HLF_T))); + grid_line_puts(col, NameBuff, len, + hl_combine_attr(attr, win_hl_attr(cwp, HLF_T))); col += len; } if (modified) { - grid_puts_len(&default_grid, "+", 1, 0, col++, attr); + grid_line_put_schar(col++, schar_from_ascii('+'), attr); } - grid_putchar(&default_grid, ' ', 0, col++, attr); + grid_line_put_schar(col++, schar_from_ascii(' '), attr); } - room = scol - col + tabwidth - 1; + int room = scol - col + tabwidth - 1; if (room > 0) { // Get buffer name in NameBuff[] get_trans_bufname(cwp->w_buffer); @@ -832,10 +812,10 @@ void draw_tabline(void) len = Columns - col - 1; } - grid_puts_len(&default_grid, p, (int)strlen(p), 0, col, attr); + grid_line_puts(col, p, -1, attr); col += len; } - grid_putchar(&default_grid, ' ', 0, col++, attr); + grid_line_put_schar(col++, schar_from_ascii(' '), attr); // Store the tab page number in tab_page_click_defs[], so that // jump_to_mouse() knows where each one is. @@ -854,27 +834,29 @@ void draw_tabline(void) } else { c = ' '; } - grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill); + grid_line_fill(col, Columns, c, attr_fill); // Draw the 'showcmd' information if 'showcmdloc' == "tabline". if (p_sc && *p_sloc == 't') { const int sc_width = MIN(10, (int)Columns - col - (tabcount > 1) * 3); if (sc_width > 0) { - grid_puts_len(&default_grid, showcmd_buf, sc_width, 0, - Columns - sc_width - (tabcount > 1) * 2, attr_nosel); + grid_line_puts(Columns - sc_width - (tabcount > 1) * 2, + showcmd_buf, sc_width, attr_nosel); } } // Put an "X" for closing the current tab if there are several. if (tabcount > 1) { - grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel); + grid_line_put_schar(Columns - 1, schar_from_ascii('X'), attr_nosel); tab_page_click_defs[Columns - 1] = (StlClickDefinition) { .type = kStlClickTabClose, .tabnr = 999, .func = NULL, }; } + + grid_line_flush(); } // Reset the flag here again, in case evaluating 'tabline' causes it to be @@ -885,13 +867,12 @@ void draw_tabline(void) /// Build the 'statuscolumn' string for line "lnum". When "relnum" == -1, /// the v:lnum and v:relnum variables don't have to be updated. /// -/// @param hlrec HL attributes (can be NULL) -/// @param stcp Status column attributes (can be NULL) /// @return The width of the built status column string for line "lnum" -int build_statuscol_str(win_T *wp, linenr_T lnum, long relnum, int maxwidth, int fillchar, - char *buf, stl_hlrec_t **hlrec, statuscol_T *stcp) +int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, statuscol_T *stcp) { - bool fillclick = relnum >= 0 && lnum == wp->w_topline; + // Only update click definitions once per window per redraw. + // Don't update when current width is 0, since it will be redrawn again if not empty. + const bool fillclick = relnum >= 0 && stcp->width > 0 && lnum == wp->w_topline; if (relnum >= 0) { set_vim_var_nr(VV_LNUM, lnum); @@ -900,16 +881,15 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, long relnum, int maxwidth, int StlClickRecord *clickrec; char *stc = xstrdup(wp->w_p_stc); - int width = build_stl_str_hl(wp, buf, MAXPATHL, stc, "statuscolumn", OPT_LOCAL, fillchar, - maxwidth, hlrec, fillclick ? &clickrec : NULL, stcp); + int width = build_stl_str_hl(wp, stcp->text, MAXPATHL, stc, "statuscolumn", OPT_LOCAL, ' ', + stcp->width, &stcp->hlrec, fillclick ? &clickrec : NULL, stcp); xfree(stc); - // Only update click definitions once per window per redraw if (fillclick) { stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size); - wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, width, + wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, stcp->width, &wp->w_statuscol_click_defs_size); - stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, buf, width, false); + stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, stcp->text, stcp->width, false); } return width; @@ -971,7 +951,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // 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_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); @@ -991,7 +971,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n }; set_var(S_LEN("g:statusline_winid"), &tv, false); - usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox); + usefmt = eval_to_string_safe(fmt + 2, use_sandbox); if (usefmt == NULL) { usefmt = fmt; } @@ -1012,7 +992,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } // Get line & check if empty (cursorpos will show "0-1"). - const char *line_ptr = ml_get_buf(wp->w_buffer, lnum, false); + const char *line_ptr = ml_get_buf(wp->w_buffer, lnum); bool empty_line = (*line_ptr == NUL); // Get the byte value now, in case we need it below. This is more @@ -1030,9 +1010,10 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } int groupdepth = 0; - int evaldepth = 0; + int evaldepth = 0; int curitem = 0; + int foldsignitem = -1; bool prevchar_isflag = true; bool prevchar_isitem = false; @@ -1095,7 +1076,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n continue; } - // STL_SEPARATE: Separation place between left and right aligned items. + // STL_SEPARATE: Separation between items, filled with white space. if (*fmt_p == STL_SEPARATE) { fmt_p++; // Ignored when we are inside of a grouping @@ -1182,7 +1163,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // { Determine the number of bytes to remove // Find the first character that should be included. - long n = 0; + int n = 0; while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) { group_len -= ptr2cells(t + n); n += utfc_ptr2len(t + n); @@ -1307,7 +1288,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n 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--) { + for (int n = curitem - 1; n >= 0; n--) { if (stl_items[n].type == TabPage && stl_items[n].minwid >= 0) { minwid = stl_items[n].minwid; break; @@ -1379,6 +1360,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // An invalid item was specified. // Continue processing on the next character of the format string. if (vim_strchr(STL_ALL, (uint8_t)(*fmt_p)) == NULL) { + if (*fmt_p == NUL) { // can happen with "%0" + break; + } fmt_p++; continue; } @@ -1390,7 +1374,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n NumberBase base = kNumBaseDecimal; bool itemisflag = false; bool fillable = true; - long num = -1; + int num = -1; char *str = NULL; switch (opt) { case STL_FILEPATH: @@ -1403,7 +1387,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n xstrlcpy(NameBuff, buf_spname(wp->w_buffer), MAXPATHL); } else { char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname - : wp->w_buffer->b_fname; + : wp->w_buffer->b_fname; home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, true); } trans_characters(NameBuff, MAXPATHL); @@ -1449,8 +1433,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // 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); + vim_snprintf(win_tmp, sizeof(win_tmp), "%d", curwin->handle); + set_internal_string_var("g:actual_curwin", win_tmp); buf_T *const save_curbuf = curbuf; win_T *const save_curwin = curwin; @@ -1463,7 +1447,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } // Note: The result stored in `t` is unused. - str = eval_to_string_safe(out_p, &t, use_sandbox); + str = eval_to_string_safe(out_p, use_sandbox); curwin = save_curwin; curbuf = save_curbuf; @@ -1488,7 +1472,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // 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 + && strchr(str, '%') != NULL && evaldepth < MAX_STL_EVAL_DEPTH) { size_t parsed_usefmt = (size_t)(block_start - usefmt); size_t str_length = strlen(str); @@ -1518,12 +1502,12 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n case STL_LINE: // Overload %l with v:lnum for 'statuscolumn' - if (opt_name != NULL && strcmp(opt_name, "statuscolumn") == 0) { + if (stcp != NULL) { if (wp->w_p_nu && !get_vim_var_nr(VV_VIRTNUM)) { - num = get_vim_var_nr(VV_LNUM); + num = (int)get_vim_var_nr(VV_LNUM); } } else { - num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0L : (long)(wp->w_cursor.lnum); + num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0 : wp->w_cursor.lnum; } break; @@ -1544,13 +1528,12 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n ? 0 : (int)wp->w_cursor.col + 1))) { break; } - num = (long)virtcol; + num = virtcol; break; } case STL_PERCENTAGE: - num = (int)(((long)wp->w_cursor.lnum * 100L) / - (long)wp->w_buffer->b_ml.ml_line_count); + num = ((wp->w_cursor.lnum * 100) / wp->w_buffer->b_ml.ml_line_count); break; case STL_ALTPERCENT: @@ -1578,7 +1561,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // 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)) { + if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp))) { str = buf_tmp; } break; @@ -1601,11 +1584,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n 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); + int 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 + ? 0 : l + 1 + ((State & MODE_INSERT) == 0 && empty_line + ? 0 : (int)wp->w_cursor.col); break; } case STL_BYTEVAL_X: @@ -1623,9 +1606,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n case STL_ROFLAG: case STL_ROFLAG_ALT: // Overload %r with v:relnum for 'statuscolumn' - if (opt_name != NULL && strcmp(opt_name, "statuscolumn") == 0) { + if (stcp != NULL) { if (wp->w_p_rnu && !get_vim_var_nr(VV_VIRTNUM)) { - num = get_vim_var_nr(VV_RELNUM); + num = (int)get_vim_var_nr(VV_RELNUM); } } else { itemisflag = true; @@ -1648,25 +1631,40 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n if (stcp == NULL) { break; } - bool fold = opt == STL_FOLDCOL; - *buf_tmp = NUL; - for (int i = 0; i <= SIGN_SHOW_MAX; i++) { - char *p = fold ? stcp->fold_text : stcp->sign_text[i]; - if ((!p || !*p) && *buf_tmp == NUL) { - break; + int width = fold ? (compute_foldcolumn(wp, 0) > 0) : wp->w_scwidth; + + if (width == 0) { + break; + } + foldsignitem = curitem; + + char *p = NULL; + if (fold) { + size_t n = fill_foldcolumn(out_p, wp, stcp->foldinfo, + (linenr_T)get_vim_var_nr(VV_LNUM), NULL); + stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLF : HLF_FC) + 1); + p = out_p; + p[n] = NUL; + } + + size_t buflen = 0; + varnumber_T virtnum = get_vim_var_nr(VV_VIRTNUM); + for (int i = 0; i < width; i++) { + if (!fold) { + SignTextAttrs *sattr = virtnum ? NULL : &stcp->sattrs[i]; + p = sattr && sattr->text ? sattr->text : " "; + stl_items[curitem].minwid = -(sattr && sattr->text + ? (stcp->sign_cul_id ? stcp->sign_cul_id : sattr->hl_id) + : (stcp->use_cul ? HLF_CLS : HLF_SC) + 1); } stl_items[curitem].type = Highlight; - stl_items[curitem].start = out_p + strlen(buf_tmp); - stl_items[curitem].minwid = !p || (fold && i) ? 0 : fold ? stcp->fold_attr - : stcp->sign_attr[i]; + stl_items[curitem].start = out_p + buflen; + xstrlcpy(buf_tmp + buflen, p, TMPLEN - buflen); + buflen += strlen(p); curitem++; - if (!p || (fold && i)) { - str = buf_tmp; - break; - } - STRCAT(buf_tmp, p); } + str = buf_tmp; break; } @@ -1773,7 +1771,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } // } - long l = vim_strsize(t); + int l = vim_strsize(t); // If this item is non-empty, record that the last thing // we put in the output buffer was an item @@ -1809,6 +1807,13 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } } minwid = 0; + // For a 'statuscolumn' sign or fold item, shift the added items + if (foldsignitem >= 0) { + ptrdiff_t offset = out_p - stl_items[foldsignitem].start; + for (int i = foldsignitem; i < curitem; i++) { + stl_items[i].start += offset; + } + } } else { // Note: The negative value denotes a left aligned item. // Here we switch the minimum width back to a positive value. @@ -1828,6 +1833,14 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n } // } + // For a 'statuscolumn' sign or fold item, add an item to reset the highlight group + if (foldsignitem >= 0) { + foldsignitem = -1; + stl_items[curitem].type = Highlight; + stl_items[curitem].start = out_p; + stl_items[curitem].minwid = 0; + } + // 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); @@ -1861,8 +1874,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // { 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) { + int num_chars = 1; + for (int n = num; n >= (int)base; n /= (int)base) { num_chars++; } @@ -1885,11 +1898,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n num_chars += 2; // How many extra characters there are - long n = num_chars - maxwid; + int n = num_chars - maxwid; // { Reduce the number by base^n while (num_chars-- > maxwid) { - num /= (long)base; + num /= (int)base; } // } @@ -1942,8 +1955,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n int width = vim_strsize(out); // Return truncated width for 'statuscolumn' - if (stcp != NULL && width > maxwidth) { - stcp->truncate = width - maxwidth; + if (stcp != NULL && width > stcp->width) { + stcp->truncate = width - stcp->width; } if (maxwidth > 0 && width > maxwidth) { // Result is too long, must truncate somewhere. @@ -1977,7 +1990,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // string to find the last character that will fit. trunc_p = out; width = 0; - for (;;) { + while (true) { width += ptr2cells(trunc_p); if (width >= maxwidth) { break; @@ -2009,9 +2022,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // Truncate at the truncation point we found } else { // { Determine how many bytes to remove - long trunc_len = 0; + int trunc_len = 0; while (width >= maxwidth) { - width -= ptr2cells(trunc_p + trunc_len); + width -= ptr2cells(trunc_p + trunc_len); trunc_len += utfc_ptr2len(trunc_p + trunc_len); } // } @@ -2022,17 +2035,6 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // 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 @@ -2040,20 +2042,31 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // Note: The offset is one less than the truncation length because // the truncation marker `<` is not counted. - long item_offset = trunc_len - 1; + int 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; + } else { // Anything inside the truncated area is set to start // at the `<` truncation character. - } else { stl_items[i].start = 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; + } } width = maxwidth; @@ -2067,8 +2080,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n 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. + // Create an array of the start location for each separator mark. stl_separator_locations[num_separators] = i; num_separators++; } @@ -2080,17 +2092,17 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n 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; + for (int l = 0; l < num_separators; l++) { + int dislocation = (l == (num_separators - 1)) ? final_spaces : standard_spaces; dislocation *= utf_char2len(fillchar); - char *start = stl_items[stl_separator_locations[i]].start; + char *start = stl_items[stl_separator_locations[l]].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; + for (int item_idx = stl_separator_locations[l] + 1; item_idx < itemcnt; item_idx++) { stl_items[item_idx].start += dislocation; @@ -2105,7 +2117,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n if (hltab != NULL) { *hltab = stl_hltab; stl_hlrec_t *sp = stl_hltab; - for (long l = 0; l < itemcnt; l++) { + for (int l = 0; l < itemcnt; l++) { if (stl_items[l].type == Highlight) { sp->start = stl_items[l].start; sp->userhl = stl_items[l].minwid; @@ -2120,7 +2132,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n if (tabtab != NULL) { *tabtab = stl_tabtab; StlClickRecord *cur_tab_rec = stl_tabtab; - for (long l = 0; l < itemcnt; l++) { + for (int 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) { |