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.
/// 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
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
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
// width of the status column by building with "w_nrwidth_line_count". Add
// potentially truncated width and rebuild before drawing anything.
if (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);
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) {
// Add truncated width to avoid unnecessary redraws
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);
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
if (*wp->w_p_stc == NUL || (stcp->truncate > 0 && wp->w_nrwidth < MAX_NUMBERWIDTH)) {
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;
}
// Reset text/highlight pointer and current attr for new line
stcp->textp = stcp->text;
stcp->hlrecp = stcp->hlrec;
stcp->cur_attr = stcp->num_attr;
stcp->text_end = stcp->text + strlen(stcp->text);
char *p = buf;
char transbuf[MAXPATHL];
int attr = stcp->num_attr;
size_t len = strlen(buf);
int fill = stcp->width - width;
if (fill > 0) {
// Fill up with ' '
memset(stcp->text_end, ' ', (size_t)fill);
*(stcp->text_end += fill) = NUL;
}
}
/// Get information needed to display the next segment in the 'statuscolumn'.
///
/// @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 each segment with the specified highlighting.
for (stl_hlrec_t *sp = stcp->hlrec; sp->start != NULL; sp++) {
ptrdiff_t textlen = sp->start - p;
// Make all characters printable.
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;
}
} while (stcp->textp < stcp->text_end);
size_t translen = transstr_buf(p, buf + len - p, transbuf, MAXPATHL, true);
draw_col_buf(wp, wlv, transbuf, translen, attr, false);
// Fill up with ' '
draw_col_fill(wlv, ' ', stcp->width - width, stcp->num_attr);
}
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) {
statuscol.num_attr = get_line_number_attr(wp, &wlv);
}
if (statuscol.textp == NULL) {
v = (ptr - line);
get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol);
draw_statuscol(wp, &wlv, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol);
if (wp->w_redr_statuscol) {
break;
}
if (!end_fill) {
// Get the line again as evaluating 'statuscolumn' may free it.
line = ml_get_buf(wp->w_buffer, lnum);
ptr = line + v;
}
if (wp->w_redr_statuscol) {
break;
}
}
draw_statuscol(wp, &statuscol, &wlv);
} else {
// draw builtin info columns: fold, sign, number
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) {
wlv.need_showbreak = true;
}
if (statuscol.draw) {
if (vim_strchr(p_cpo, CPO_NUMCOL) && wlv.row > startrow + wlv.filler_lines) {
if (statuscol.draw && vim_strchr(p_cpo, CPO_NUMCOL)
&& wlv.row > startrow + wlv.filler_lines) {
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--;
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.
///
/// @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.
// 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;
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);
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);
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, stcp->text, stcp->width, false);
stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, buf, stcp->width, false);
}
return width;

View File

@ -57,21 +57,14 @@ struct stl_item {
};
/// Struct to hold info for 'statuscolumn'
typedef struct statuscol statuscol_T;
struct statuscol {
typedef struct {
int width; ///< width of the status column
int cur_attr; ///< current attributes in text
int num_attr; ///< default highlight attr
int sign_cul_id; ///< cursorline sign highlight id
int truncate; ///< truncated width
bool draw; ///< whether to draw the statuscolumn
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 *hlrecp; ///< current highlight group
foldinfo_T foldinfo; ///< fold information
SignTextAttrs *sattrs; ///< sign attributes
};
} statuscol_T;