diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 9dee121519..cfc9d6d624 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -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. diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index d376e4161f..37add6f0f1 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -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([[