feat(decorations): support virtual lines (for now: only one block at a time)

This commit is contained in:
Björn Linse
2021-08-08 17:36:54 +02:00
parent b3b02eb529
commit 392c658d4d
21 changed files with 885 additions and 191 deletions

View File

@@ -1003,8 +1003,7 @@ static void win_update(win_T *wp, Providers *providers)
i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
// insert extra lines for previously invisible filler lines
if (wp->w_lines[0].wl_lnum != wp->w_topline) {
i += diff_check_fill(wp, wp->w_lines[0].wl_lnum)
- wp->w_old_topfill;
i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill;
}
if (i != 0 && i < wp->w_grid.Rows - 2) { // less than a screen off
// Try to insert the correct number of lines.
@@ -1067,7 +1066,7 @@ static void win_update(win_T *wp, Providers *providers)
if (wp->w_lines[0].wl_lnum == wp->w_topline) {
row += wp->w_old_topfill;
} else {
row += diff_check_fill(wp, wp->w_topline);
row += win_get_fill(wp, wp->w_topline);
}
// ... but don't delete new filler lines.
row -= wp->w_topfill;
@@ -1101,12 +1100,12 @@ static void win_update(win_T *wp, Providers *providers)
break;
}
}
/* Correct the first entry for filler lines at the top
* when it won't get updated below. */
if (wp->w_p_diff && bot_start > 0) {
wp->w_lines[0].wl_size =
plines_win_nofill(wp, wp->w_topline, true)
+ wp->w_topfill;
// Correct the first entry for filler lines at the top
// when it won't get updated below.
if (win_may_fill(wp) && bot_start > 0) {
wp->w_lines[0].wl_size = (plines_win_nofill(wp, wp->w_topline, true)
+ wp->w_topfill);
}
}
}
@@ -1564,7 +1563,7 @@ static void win_update(win_T *wp, Providers *providers)
&& lnum > wp->w_topline
&& !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
&& srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows
&& diff_check_fill(wp, lnum) == 0) {
&& win_get_fill(wp, lnum) == 0) {
// This line is not going to fit. Don't draw anything here,
// will draw "@ " lines below.
row = wp->w_grid.Rows + 1;
@@ -1664,7 +1663,7 @@ static void win_update(win_T *wp, Providers *providers)
* Don't overwrite it, it can be edited.
*/
wp->w_botline = lnum + 1;
} else if (diff_check_fill(wp, lnum) >= wp->w_grid.Rows - srow) {
} else if (win_get_fill(wp, lnum) >= wp->w_grid.Rows - srow) {
// Window ends in filler lines.
wp->w_botline = lnum;
wp->w_filler_rows = wp->w_grid.Rows - srow;
@@ -1691,7 +1690,7 @@ static void win_update(win_T *wp, Providers *providers)
} else {
if (eof) { // we hit the end of the file
wp->w_botline = buf->b_ml.ml_line_count + 1;
j = diff_check_fill(wp, wp->w_botline);
j = win_get_fill(wp, wp->w_botline);
if (j > 0 && !wp->w_botfill) {
// Display filler text below last line. win_line() will check
// for ml_line_count+1 and only draw filler lines
@@ -1866,7 +1865,7 @@ static int compute_foldcolumn(win_T *wp, int col)
/// Put a single char from an UTF-8 buffer into a line buffer.
///
/// Handles composing chars and arabic shaping state.
static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl)
static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol)
{
const char_u *p = (char_u *)s->p;
int cells = utf_ptr2cells(p);
@@ -1876,7 +1875,13 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl)
return -1;
}
u8c = utfc_ptr2char(p, u8cc);
if (*p < 0x80 && u8cc[0] == 0) {
if (*p == TAB) {
cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells);
for (int c = 0; c < cells; c++) {
schar_from_ascii(dest[c], ' ');
}
goto done;
} else if (*p < 0x80 && u8cc[0] == 0) {
schar_from_ascii(dest[0], *p);
s->prev_c = u8c;
} else {
@@ -1909,6 +1914,7 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl)
if (cells > 1) {
dest[1][0] = 0;
}
done:
s->p += c_len;
return cells;
}
@@ -2366,8 +2372,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
filler_lines = 0;
area_highlighting = true;
}
int virtual_lines = decor_virtual_lines(wp, lnum);
filler_lines += virtual_lines;
if (lnum == wp->w_topline) {
filler_lines = wp->w_topfill;
virtual_lines = MIN(virtual_lines, filler_lines);
}
filler_todo = filler_lines;
@@ -2895,7 +2904,18 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
if (draw_state == WL_SBR - 1 && n_extra == 0) {
draw_state = WL_SBR;
if (filler_todo > 0) {
if (filler_todo > filler_lines - virtual_lines) {
// TODO(bfredl): check this doesn't inhibit TUI-style
// clear-to-end-of-line.
c_extra = ' ';
c_final = NUL;
if (wp->w_p_rl) {
n_extra = col + 1;
} else {
n_extra = grid->Columns - col;
}
char_attr = 0;
} else if (filler_todo > 0) {
// draw "deleted" diff line(s)
if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
c_extra = '-';
@@ -4402,7 +4422,19 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
&& !wp->w_p_rl; // Not right-to-left.
int draw_col = col - boguscols;
draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns);
if (filler_todo > 0) {
int index = filler_todo - (filler_lines - virtual_lines);
if (index > 0) {
int fpos = kv_size(buf->b_virt_lines) - index;
assert(fpos >= 0);
int offset = buf->b_virt_line_leftcol ? 0 : win_col_offset;
draw_virt_text_item(buf, offset, kv_A(buf->b_virt_lines, fpos),
kHlModeReplace, grid->Columns, offset);
}
} else {
draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns);
}
grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl,
wp, wp->w_hl_attr_normal, wrap);
if (wrap) {
@@ -4485,69 +4517,82 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col)
bool do_eol = state->eol_col > -1;
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange *item = &kv_A(state->active, i);
if (item->start_row == state->row && kv_size(item->decor.virt_text)) {
if (item->win_col == -1) {
if (item->decor.virt_text_pos == kVTRightAlign) {
right_pos -= item->decor.virt_text_width;
item->win_col = right_pos;
} else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
item->win_col = state->eol_col;
state->eol_col += item->decor.virt_text_width;
} else if (item->decor.virt_text_pos == kVTWinCol) {
item->win_col = MAX(item->decor.col+col_off, 0);
}
}
if (item->win_col < 0) {
continue;
}
VirtText vt = item->decor.virt_text;
HlMode hl_mode = item->decor.hl_mode;
LineState s = LINE_STATE("");
int virt_attr = 0;
int col = item->win_col;
size_t virt_pos = 0;
item->win_col = -2; // deactivate
while (col < max_col) {
if (!*s.p) {
if (virt_pos >= kv_size(vt)) {
break;
}
virt_attr = 0;
do {
s.p = kv_A(vt, virt_pos).text;
int hl_id = kv_A(vt, virt_pos).hl_id;
virt_attr = hl_combine_attr(virt_attr,
hl_id > 0 ? syn_id2attr(hl_id) : 0);
virt_pos++;
} while (!s.p && virt_pos < kv_size(vt));
if (!s.p) {
break;
}
}
int attr;
bool through = false;
if (hl_mode == kHlModeCombine) {
attr = hl_combine_attr(linebuf_attr[col], virt_attr);
} else if (hl_mode == kHlModeBlend) {
through = (*s.p == ' ');
attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
} else {
attr = virt_attr;
}
schar_T dummy[2];
int cells = line_putchar(&s, through ? dummy : &linebuf_char[col],
max_col-col, false);
linebuf_attr[col++] = attr;
if (cells > 1) {
linebuf_attr[col++] = attr;
}
}
*end_col = MAX(*end_col, col);
if (!(item->start_row == state->row && kv_size(item->decor.virt_text))) {
continue;
}
if (item->win_col == -1) {
if (item->decor.virt_text_pos == kVTRightAlign) {
right_pos -= item->decor.virt_text_width;
item->win_col = right_pos;
} else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
item->win_col = state->eol_col;
} else if (item->decor.virt_text_pos == kVTWinCol) {
item->win_col = MAX(item->decor.col+col_off, 0);
}
}
if (item->win_col < 0) {
continue;
}
int col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
item->decor.hl_mode, max_col, item->win_col-col_off);
item->win_col = -2; // deactivate
if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
state->eol_col = col+1;
}
*end_col = MAX(*end_col, col);
}
}
static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
int max_col, int vcol)
{
LineState s = LINE_STATE("");
int virt_attr = 0;
size_t virt_pos = 0;
while (col < max_col) {
if (!*s.p) {
if (virt_pos >= kv_size(vt)) {
break;
}
virt_attr = 0;
do {
s.p = kv_A(vt, virt_pos).text;
int hl_id = kv_A(vt, virt_pos).hl_id;
virt_attr = hl_combine_attr(virt_attr,
hl_id > 0 ? syn_id2attr(hl_id) : 0);
virt_pos++;
} while (!s.p && virt_pos < kv_size(vt));
if (!s.p) {
break;
}
}
int attr;
bool through = false;
if (hl_mode == kHlModeCombine) {
attr = hl_combine_attr(linebuf_attr[col], virt_attr);
} else if (hl_mode == kHlModeBlend) {
through = (*s.p == ' ');
attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
} else {
attr = virt_attr;
}
schar_T dummy[2];
int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col],
max_col-col, false, vcol);
// if we failed to emit a char, we still need to advance
cells = MAX(cells, 1);
for (int c = 0; c < cells; c++) {
linebuf_attr[col++] = attr;
}
vcol += cells;
}
return col;
}
/// Determine if dedicated window grid should be used or the default_grid
///
/// If UI did not request multigrid support, draw all windows on the
@@ -5489,7 +5534,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler)
ewp->w_p_crb = p_crb_save;
// Make all characters printable.
p = (char_u *)transstr((const char *)buf);
p = (char_u *)transstr((const char *)buf, true);
len = STRLCPY(buf, p, sizeof(buf));
len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
xfree(p);