diff options
-rw-r--r-- | src/nvim/charset.c | 9 | ||||
-rw-r--r-- | src/nvim/plines.c | 42 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 86 |
3 files changed, 125 insertions, 12 deletions
diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 48a38dd3d3..3b48392d49 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -989,6 +989,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } chartabsize_T cts; + bool on_NUL = false; init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line); // This function is used very often, do some speed optimizations. @@ -1054,6 +1055,10 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en if (*cts.cts_ptr == NUL) { // NUL at end of line only takes one column incr = 1; + if (cts.cts_cur_text_width > 0) { + incr = cts.cts_cur_text_width; + } + on_NUL = true; break; } @@ -1079,8 +1084,8 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en } if (cursor != NULL) { - if ((State & MODE_INSERT) == 0) { - // cursor is after inserted text + if ((State & MODE_INSERT) == 0 && !on_NUL) { + // cursor is after inserted text, unless on the NUL vcol += cts.cts_cur_text_width; } if ((*ptr == TAB) diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 1c6ed4f4c1..59c45d3615 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -102,12 +102,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) char *s; unsigned col; int width; + chartabsize_T cts; s = ml_get_buf(wp->w_buffer, lnum, false); - if (*s == NUL) { // empty line - return 1; + init_chartabsize_arg(&cts, wp, lnum, 0, s, s); + if (*s == NUL && !cts.cts_has_virt_text) { + return 1; // be quick for an empty line } - col = win_linetabsize(wp, lnum, s, MAXCOL); + win_linetabsize_cts(&cts, (colnr_T)MAXCOL); + clear_chartabsize_arg(&cts); + col = (unsigned)cts.cts_vcol; // If list mode is on, then the '$' at the end of the line may take up one // extra column. @@ -262,6 +266,11 @@ int linetabsize_col(int startcol, char *s) while (*cts.cts_ptr != NUL) { cts.cts_vcol += lbr_chartabsize_adv(&cts); } + if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) { + // check for virtual text in an empty line + (void)lbr_chartabsize_adv(&cts); + cts.cts_vcol += cts.cts_cur_text_width; + } clear_chartabsize_arg(&cts); return cts.cts_vcol; } @@ -277,10 +286,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) { chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len); - MB_PTR_ADV(cts.cts_ptr)) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); - } + win_linetabsize_cts(&cts, len); clear_chartabsize_arg(&cts); return (unsigned)cts.cts_vcol; } @@ -292,6 +298,20 @@ unsigned linetabsize(win_T *wp, linenr_T lnum) return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); } +void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) +{ + for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len); + MB_PTR_ADV(cts->cts_ptr)) { + cts->cts_vcol += win_lbr_chartabsize(cts, NULL); + } + // check for a virtual text on an empty line + if (cts->cts_has_virt_text && *cts->cts_ptr == NUL + && cts->cts_ptr == cts->cts_line) { + (void)win_lbr_chartabsize(cts, NULL); + cts->cts_vcol += cts->cts_cur_text_width; + } +} + /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. @@ -390,14 +410,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); - if (cts->cts_has_virt_text && *line != NUL) { + if (cts->cts_has_virt_text) { + int charlen = *s == NUL ? 1 : utf_ptr2len(s); int col = (int)(s - line); while (true) { mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; - } else if (mark.pos.col - == col) { // TODO(bfredl): or maybe unconditionally, what if byte-misaligned? + } else if (mark.pos.col >= col + && mark.pos.col < col + charlen) { // TODO(bfredl): or maybe unconditionally, what + // if byte-misaligned? if (!mt_end(mark)) { Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index a2b0138da3..635666c941 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1390,6 +1390,92 @@ end]] | ]]} end) + + it('text is drawn correctly when inserting a wrapping virtual text on an empty line', function() + feed('o<esc>') + insert([[aaaaaaa + +bbbbbbb]]) + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { string.rep('X', 51), 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 2, 0, + { virt_text = { { string.rep('X', 50), 'Special' } }, virt_text_pos = 'inline' }) + feed('gg0') + screen:expect { grid = [[ + {28:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {28:X} | + aaaaaaa | + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {28:X} | + ^aaaaaaa | + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {28:X} | + aaaaaaa | + {28:^XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('j') + screen:expect { grid = [[ + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {28:X} | + aaaaaaa | + {28:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + ^bbbbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() |