refactor(drawline): avoid storing info to draw 'statuscolumn' (#26712)

We no longer return to the main loop in win_line() to put column
characters on the screen. Simplify and sync statuscolumn drawing
logic with that of statusline.
This commit is contained in:
luukvbaal 2023-12-22 23:31:07 +01:00 committed by GitHub
parent 2b0acacb3c
commit e632396bab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 71 deletions

View File

@ -604,24 +604,24 @@ static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int
} }
} }
/// Prepare and build the 'statuscolumn' string for line "lnum" in window "wp". /// Build and draw the 'statuscolumn' string for line "lnum" in window "wp".
/// Fill "stcp" with the built status column string and attributes. /// Fill "stcp" with the built status column string and attributes.
/// This can be called three times per win_line(), once for virt_lines, once for
/// the start of the buffer line "lnum" and once for the wrapped lines.
/// ///
/// @param[out] stcp Status column attributes /// @param[out] stcp Status column attributes
static void get_statuscol_str(win_T *wp, linenr_T lnum, int virtnum, statuscol_T *stcp) static void draw_statuscol(win_T *wp, winlinevars_T *wlv, linenr_T lnum, int virtnum,
statuscol_T *stcp)
{ {
// When called for the first non-filler row of line "lnum" set num v:vars // When called for the first non-filler row of line "lnum" set num v:vars
linenr_T relnum = virtnum == 0 ? abs(get_cursor_rel_lnum(wp, lnum)) : -1; linenr_T relnum = virtnum == 0 ? abs(get_cursor_rel_lnum(wp, lnum)) : -1;
char buf[MAXPATHL];
// When a buffer's line count has changed, make a best estimate for the full // When a buffer's line count has changed, make a best estimate for the full
// width of the status column by building with "w_nrwidth_line_count". Add // width of the status column by building with "w_nrwidth_line_count". Add
// potentially truncated width and rebuild before drawing anything. // potentially truncated width and rebuild before drawing anything.
if (wp->w_statuscol_line_count != wp->w_nrwidth_line_count) { if (wp->w_statuscol_line_count != wp->w_nrwidth_line_count) {
wp->w_statuscol_line_count = wp->w_nrwidth_line_count; wp->w_statuscol_line_count = wp->w_nrwidth_line_count;
set_vim_var_nr(VV_VIRTNUM, 0); set_vim_var_nr(VV_VIRTNUM, 0);
build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, stcp); build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, buf, stcp);
if (stcp->truncate > 0) { if (stcp->truncate > 0) {
// Add truncated width to avoid unnecessary redraws // Add truncated width to avoid unnecessary redraws
int addwidth = MIN(stcp->truncate, MAX_NUMBERWIDTH - wp->w_nrwidth); int addwidth = MIN(stcp->truncate, MAX_NUMBERWIDTH - wp->w_nrwidth);
@ -634,7 +634,7 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int virtnum, statuscol_T
} }
set_vim_var_nr(VV_VIRTNUM, virtnum); set_vim_var_nr(VV_VIRTNUM, virtnum);
int width = build_statuscol_str(wp, lnum, relnum, stcp); int width = build_statuscol_str(wp, lnum, relnum, buf, stcp);
// Force a redraw in case of error or when truncated // Force a redraw in case of error or when truncated
if (*wp->w_p_stc == NUL || (stcp->truncate > 0 && wp->w_nrwidth < MAX_NUMBERWIDTH)) { if (*wp->w_p_stc == NUL || (stcp->truncate > 0 && wp->w_nrwidth < MAX_NUMBERWIDTH)) {
if (stcp->truncate > 0) { // Avoid truncating 'statuscolumn' if (stcp->truncate > 0) { // Avoid truncating 'statuscolumn'
@ -648,44 +648,26 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int virtnum, statuscol_T
return; return;
} }
// Reset text/highlight pointer and current attr for new line char *p = buf;
stcp->textp = stcp->text; char transbuf[MAXPATHL];
stcp->hlrecp = stcp->hlrec; int attr = stcp->num_attr;
stcp->cur_attr = stcp->num_attr; size_t len = strlen(buf);
stcp->text_end = stcp->text + strlen(stcp->text);
int fill = stcp->width - width; // Draw each segment with the specified highlighting.
if (fill > 0) { for (stl_hlrec_t *sp = stcp->hlrec; sp->start != NULL; sp++) {
// Fill up with ' ' ptrdiff_t textlen = sp->start - p;
memset(stcp->text_end, ' ', (size_t)fill); // Make all characters printable.
*(stcp->text_end += fill) = NUL; size_t translen = transstr_buf(p, textlen, transbuf, MAXPATHL, true);
draw_col_buf(wp, wlv, transbuf, translen, attr, false);
p = sp->start;
int hl = sp->userhl;
attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr;
} }
} size_t translen = transstr_buf(p, buf + len - p, transbuf, MAXPATHL, true);
draw_col_buf(wp, wlv, transbuf, translen, attr, false);
/// Get information needed to display the next segment in the 'statuscolumn'. // Fill up with ' '
/// draw_col_fill(wlv, ' ', stcp->width - width, stcp->num_attr);
/// @param stcp Status column attributes
/// @param[in,out] wlv
static void draw_statuscol(win_T *wp, statuscol_T *stcp, winlinevars_T *wlv)
{
do {
int attr = stcp->cur_attr;
char *start = stcp->textp;
stcp->textp = stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end;
ptrdiff_t len = stcp->textp - start;
// Prepare for next highlight section if not yet at the end
if (stcp->textp < stcp->text_end) {
int hl = stcp->hlrecp->userhl;
stcp->cur_attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr;
stcp->hlrecp++;
}
// Skip over empty highlight sections
if (len) {
static char transbuf[(MAX_NUMBERWIDTH + 9 + 9 * SIGN_WIDTH) * MB_MAXBYTES + 1];
size_t translen = transstr_buf(start, len, transbuf, sizeof transbuf, true);
draw_col_buf(wp, wlv, transbuf, translen, attr, false);
}
} while (stcp->textp < stcp->text_end);
} }
static void handle_breakindent(win_T *wp, winlinevars_T *wlv) static void handle_breakindent(win_T *wp, winlinevars_T *wlv)
@ -1550,19 +1532,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (sign_num_attr == 0) { if (sign_num_attr == 0) {
statuscol.num_attr = get_line_number_attr(wp, &wlv); statuscol.num_attr = get_line_number_attr(wp, &wlv);
} }
if (statuscol.textp == NULL) { v = (ptr - line);
v = (ptr - line); draw_statuscol(wp, &wlv, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol);
get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol); if (wp->w_redr_statuscol) {
if (!end_fill) { break;
// Get the line again as evaluating 'statuscolumn' may free it. }
line = ml_get_buf(wp->w_buffer, lnum); if (!end_fill) {
ptr = line + v; // Get the line again as evaluating 'statuscolumn' may free it.
} line = ml_get_buf(wp->w_buffer, lnum);
if (wp->w_redr_statuscol) { ptr = line + v;
break;
}
} }
draw_statuscol(wp, &statuscol, &wlv);
} else { } else {
// draw builtin info columns: fold, sign, number // draw builtin info columns: fold, sign, number
draw_foldcolumn(wp, &wlv); draw_foldcolumn(wp, &wlv);
@ -2906,12 +2885,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (wlv.filler_todo <= 0) { if (wlv.filler_todo <= 0) {
wlv.need_showbreak = true; wlv.need_showbreak = true;
} }
if (statuscol.draw) { if (statuscol.draw && vim_strchr(p_cpo, CPO_NUMCOL)
if (vim_strchr(p_cpo, CPO_NUMCOL) && wlv.row > startrow + wlv.filler_lines) { && wlv.row > startrow + wlv.filler_lines) {
statuscol.draw = false; // don't draw status column if "n" is in 'cpo' statuscol.draw = false; // don't draw status column if "n" is in 'cpo'
} else {
statuscol.textp = NULL; // re-evaluate with new v:virtnum
}
} }
wlv.filler_todo--; wlv.filler_todo--;
virt_line_offset = -1; virt_line_offset = -1;

View File

@ -867,7 +867,7 @@ void draw_tabline(void)
/// the v:lnum and v:relnum variables don't have to be updated. /// the v:lnum and v:relnum variables don't have to be updated.
/// ///
/// @return The width of the built status column string for line "lnum" /// @return The width of the built status column string for line "lnum"
int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, statuscol_T *stcp) int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, char *buf, statuscol_T *stcp)
{ {
// Only update click definitions once per window per redraw. // 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. // Don't update when current width is 0, since it will be redrawn again if not empty.
@ -880,7 +880,7 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, statuscol_T *
StlClickRecord *clickrec; StlClickRecord *clickrec;
char *stc = xstrdup(wp->w_p_stc); char *stc = xstrdup(wp->w_p_stc);
int width = build_stl_str_hl(wp, stcp->text, MAXPATHL, stc, kOptStatuscolumn, OPT_LOCAL, ' ', int width = build_stl_str_hl(wp, buf, MAXPATHL, stc, kOptStatuscolumn, OPT_LOCAL, ' ',
stcp->width, &stcp->hlrec, fillclick ? &clickrec : NULL, stcp); stcp->width, &stcp->hlrec, fillclick ? &clickrec : NULL, stcp);
xfree(stc); xfree(stc);
@ -888,7 +888,7 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, statuscol_T *
stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size); 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, stcp->width, wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, stcp->width,
&wp->w_statuscol_click_defs_size); &wp->w_statuscol_click_defs_size);
stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, stcp->text, stcp->width, false); stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, buf, stcp->width, false);
} }
return width; return width;

View File

@ -57,21 +57,14 @@ struct stl_item {
}; };
/// Struct to hold info for 'statuscolumn' /// Struct to hold info for 'statuscolumn'
typedef struct statuscol statuscol_T; typedef struct {
struct statuscol {
int width; ///< width of the status column int width; ///< width of the status column
int cur_attr; ///< current attributes in text
int num_attr; ///< default highlight attr int num_attr; ///< default highlight attr
int sign_cul_id; ///< cursorline sign highlight id int sign_cul_id; ///< cursorline sign highlight id
int truncate; ///< truncated width int truncate; ///< truncated width
bool draw; ///< whether to draw the statuscolumn bool draw; ///< whether to draw the statuscolumn
bool use_cul; ///< whether to use cursorline attrs bool use_cul; ///< whether to use cursorline attrs
char text[MAXPATHL]; ///< text in status column
char *textp; ///< current position in text
char *text_end; ///< end of text (the NUL byte)
stl_hlrec_t *hlrec; ///< highlight groups stl_hlrec_t *hlrec; ///< highlight groups
stl_hlrec_t *hlrecp; ///< current highlight group
foldinfo_T foldinfo; ///< fold information foldinfo_T foldinfo; ///< fold information
SignTextAttrs *sattrs; ///< sign attributes SignTextAttrs *sattrs; ///< sign attributes
}; } statuscol_T;