perf(extmark): fix performance regression for non-wrapped virt lines

This commit is contained in:
zeertzjq
2026-06-08 22:13:50 +08:00
parent d9aa06eed8
commit 5e8fbfd043
2 changed files with 69 additions and 15 deletions

View File

@@ -1124,6 +1124,11 @@ bool decor_redraw_eol(win_T *wp, DecorState *state, int *eol_attr, int eol_col)
static const uint32_t lines_filter[kMTMetaCount] = {[kMTMetaLines] = kMTFilterSelect };
static inline bool decor_virt_line_wrap(win_T *wp, VirtLineOverflow overflow)
{
return overflow == kVLOverflowWrap || (overflow == kVLOverflowAuto && wp->w_p_wrap);
}
/// Counts the number of rows occupied by a virtual line. When skip_cells is non-NULL, sets it to
/// the cell offset where target_row begins.
///
@@ -1133,13 +1138,14 @@ static const uint32_t lines_filter[kMTMetaCount] = {[kMTMetaLines] = kMTFilterSe
/// @return Number of rows occupied by the virtual line.
int decor_virt_line_rows(win_T *wp, const struct virt_line *vl, int target_row, int *skip_cells)
{
VirtLineOverflow overflow = vl->overflow;
bool wrap = (overflow == kVLOverflowWrap) || ((overflow == kVLOverflowAuto) && wp->w_p_wrap);
int row_width = wp->w_view_width - ((vl->flags & kVLLeftcol) ? 0 : win_col_off(wp));
if (skip_cells != NULL) {
*skip_cells = 0;
}
if (!wrap || row_width <= 0) {
if (!decor_virt_line_wrap(wp, vl->overflow)) {
return 1;
}
int row_width = wp->w_view_width - ((vl->flags & kVLLeftcol) ? 0 : win_col_off(wp));
if (row_width <= 0) {
return 1;
}
VirtText vt = vl->line;
@@ -1196,30 +1202,40 @@ int decor_virt_lines(win_T *wp, int start_row, int end_row, int *num_below, Virt
assert(start_row >= 0);
int virt_lines = 0;
int n_virt_lines = 0;
while (true) {
MTKey mark = marktree_itr_current(itr);
DecorVirtText *vt = mt_decor_virt(mark);
if (!mt_invalid(mark) && ns_in_win(mark.ns, wp)) {
while (vt) {
if (vt->flags & kVTIsLines) {
VirtLines virt_lines = vt->data.virt_lines;
if ((vt->flags & kVTIsLines) && kv_size(virt_lines) > 0) {
bool above = vt->flags & kVTLinesAbove;
int mrow = mark.pos.row;
int draw_row = mrow + (above ? 0 : 1);
if (draw_row >= start_row && draw_row < end_row
&& (!apply_folds || !(hasFolding(wp, mrow + 1, NULL, NULL)
|| decor_conceal_line(wp, mrow, false)))) {
// Iterates over each virtual line summing number of rows.
// Rows belonging to previous line are accumulated in num_below.
for (int i = 0; i < (int)kv_size(vt->data.virt_lines); i++) {
int rows = decor_virt_line_rows(wp, &kv_A(vt->data.virt_lines, i), 0, NULL);
virt_lines += rows;
// All virtual lines from the same extmark have the same overflow flag.
if (decor_virt_line_wrap(wp, kv_A(virt_lines, 0).overflow)) {
// Iterates over each virtual line summing number of rows.
// Rows belonging to previous line are accumulated in num_below.
for (int i = 0; i < (int)kv_size(virt_lines); i++) {
int rows = decor_virt_line_rows(wp, &kv_A(virt_lines, i), 0, NULL);
n_virt_lines += rows;
if (num_below && !above) {
(*num_below) += rows;
}
}
} else {
// If virtual lines don't wrap there is no need to iterate over them.
n_virt_lines += (int)kv_size(virt_lines);
if (num_below && !above) {
(*num_below) += rows;
(*num_below) += (int)kv_size(virt_lines);
}
}
if (lines) {
kv_splice(*lines, vt->data.virt_lines);
kv_splice(*lines, virt_lines);
}
}
}
@@ -1232,7 +1248,7 @@ int decor_virt_lines(win_T *wp, int start_row, int end_row, int *num_below, Virt
}
}
return virt_lines;
return n_virt_lines;
}
/// This assumes maximum one entry of each kind, which will not always be the case.

View File

@@ -6759,7 +6759,7 @@ if (h->n_buckets < new_n_buckets) { // expand
}
end)
it('scrolls horizontally with virt_lines_overflow = "scroll" #31000', function()
it('scrolls horizontally with virt_lines_overflow=scroll #31000', function()
command('set nowrap signcolumn=yes')
insert('abcdefghijklmnopqrstuvwxyz')
api.nvim_buf_set_extmark(0, ns, 0, 0, {
@@ -7178,6 +7178,44 @@ if (h->n_buckets < new_n_buckets) { // expand
]])
end)
it('mixing virt_lines_overflow=wrap and virt_lines_overflow=scroll', function()
command('set nowrap signcolumn=yes')
insert('line1\nline2\nline3\n')
api.nvim_buf_set_extmark(0, ns, 0, 0, {
virt_lines = { { { 'BELOW ' .. string.rep('a', 60), 'Special' } } },
virt_lines_overflow = 'wrap',
})
api.nvim_buf_set_extmark(0, ns, 1, 0, {
virt_lines = { { { 'ABOVE ' .. string.rep('b', 60), 'Special' } } },
virt_lines_overflow = 'scroll',
virt_lines_above = true,
})
api.nvim_buf_set_extmark(0, ns, 1, 0, {
virt_lines = { { { 'BELOW ' .. string.rep('c', 60), 'Special' } } },
virt_lines_overflow = 'scroll',
})
api.nvim_buf_set_extmark(0, ns, 2, 0, {
virt_lines = { { { 'ABOVE ' .. string.rep('d', 60), 'Special' } } },
virt_lines_overflow = 'wrap',
virt_lines_above = true,
})
feed('2ggzlzl')
screen:expect([[
{7: }ne1 |
{7: }{16:BELOW aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
{7: }{16:aaaaaaaaaaaaaaaaaa} |
{7: }{16:OVE bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}|
{7: }^ne2 |
{7: }{16:LOW cccccccccccccccccccccccccccccccccccccccccccc}|
{7: }{16:ABOVE dddddddddddddddddddddddddddddddddddddddddd}|
{7: }{16:dddddddddddddddddd} |
{7: }ne3 |
{7: } |
{1:~ }|
|
]])
end)
it('does not show twice if end_row or end_col is specified #18622', function()
screen:try_resize(50, 8)
insert([[