aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/charset.c9
-rw-r--r--src/nvim/plines.c42
-rw-r--r--test/functional/ui/decorations_spec.lua86
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()