mirror of
https://github.com/neovim/neovim.git
synced 2025-10-05 17:36:29 +00:00
vim-patch:9.0.0179: cursor pos wrong with wrapping virtual text in empty line
Problem: Cursor position wrong with wrapping virtual text in empty line.
Solution: Adjust handling of an empty line. (closes vim/vim#10875)
49a90792d9
Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
@@ -989,6 +989,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
|
|||||||
}
|
}
|
||||||
|
|
||||||
chartabsize_T cts;
|
chartabsize_T cts;
|
||||||
|
bool on_NUL = false;
|
||||||
init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line);
|
init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line);
|
||||||
|
|
||||||
// This function is used very often, do some speed optimizations.
|
// 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) {
|
if (*cts.cts_ptr == NUL) {
|
||||||
// NUL at end of line only takes one column
|
// NUL at end of line only takes one column
|
||||||
incr = 1;
|
incr = 1;
|
||||||
|
if (cts.cts_cur_text_width > 0) {
|
||||||
|
incr = cts.cts_cur_text_width;
|
||||||
|
}
|
||||||
|
on_NUL = true;
|
||||||
break;
|
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 (cursor != NULL) {
|
||||||
if ((State & MODE_INSERT) == 0) {
|
if ((State & MODE_INSERT) == 0 && !on_NUL) {
|
||||||
// cursor is after inserted text
|
// cursor is after inserted text, unless on the NUL
|
||||||
vcol += cts.cts_cur_text_width;
|
vcol += cts.cts_cur_text_width;
|
||||||
}
|
}
|
||||||
if ((*ptr == TAB)
|
if ((*ptr == TAB)
|
||||||
|
@@ -102,12 +102,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
|
|||||||
char *s;
|
char *s;
|
||||||
unsigned col;
|
unsigned col;
|
||||||
int width;
|
int width;
|
||||||
|
chartabsize_T cts;
|
||||||
|
|
||||||
s = ml_get_buf(wp->w_buffer, lnum, false);
|
s = ml_get_buf(wp->w_buffer, lnum, false);
|
||||||
if (*s == NUL) { // empty line
|
init_chartabsize_arg(&cts, wp, lnum, 0, s, s);
|
||||||
return 1;
|
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
|
// If list mode is on, then the '$' at the end of the line may take up one
|
||||||
// extra column.
|
// extra column.
|
||||||
@@ -262,6 +266,11 @@ int linetabsize_col(int startcol, char *s)
|
|||||||
while (*cts.cts_ptr != NUL) {
|
while (*cts.cts_ptr != NUL) {
|
||||||
cts.cts_vcol += lbr_chartabsize_adv(&cts);
|
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);
|
clear_chartabsize_arg(&cts);
|
||||||
return cts.cts_vcol;
|
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;
|
chartabsize_T cts;
|
||||||
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
|
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
|
||||||
for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len);
|
win_linetabsize_cts(&cts, len);
|
||||||
MB_PTR_ADV(cts.cts_ptr)) {
|
|
||||||
cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
|
|
||||||
}
|
|
||||||
clear_chartabsize_arg(&cts);
|
clear_chartabsize_arg(&cts);
|
||||||
return (unsigned)cts.cts_vcol;
|
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);
|
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.
|
/// Prepare the structure passed to chartabsize functions.
|
||||||
///
|
///
|
||||||
/// "line" is the start of the line, "ptr" is the first relevant character.
|
/// "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
|
// First get normal size, without 'linebreak' or virtual text
|
||||||
int size = win_chartabsize(wp, s, vcol);
|
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);
|
int col = (int)(s - line);
|
||||||
while (true) {
|
while (true) {
|
||||||
mtkey_t mark = marktree_itr_current(cts->cts_iter);
|
mtkey_t mark = marktree_itr_current(cts->cts_iter);
|
||||||
if (mark.pos.row != cts->cts_row || mark.pos.col > col) {
|
if (mark.pos.row != cts->cts_row || mark.pos.col > col) {
|
||||||
break;
|
break;
|
||||||
} else if (mark.pos.col
|
} else if (mark.pos.col >= col
|
||||||
== col) { // TODO(bfredl): or maybe unconditionally, what if byte-misaligned?
|
&& mark.pos.col < col + charlen) { // TODO(bfredl): or maybe unconditionally, what
|
||||||
|
// if byte-misaligned?
|
||||||
if (!mt_end(mark)) {
|
if (!mt_end(mark)) {
|
||||||
Decoration decor = get_decor(mark);
|
Decoration decor = get_decor(mark);
|
||||||
if (decor.virt_text_pos == kVTInline) {
|
if (decor.virt_text_pos == kVTInline) {
|
||||||
|
@@ -1390,6 +1390,92 @@ end]]
|
|||||||
|
|
|
|
||||||
]]}
|
]]}
|
||||||
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)
|
end)
|
||||||
|
|
||||||
describe('decorations: virtual lines', function()
|
describe('decorations: virtual lines', function()
|
||||||
|
Reference in New Issue
Block a user