mirror of
https://github.com/neovim/neovim
synced 2025-07-16 09:11:51 +00:00
fix(marks): truncate double-width inline virt_text consistently (#32560)
- Fix wrong cursor position with 'listchars' "precedes". - Always show the '<' truncation character.
This commit is contained in:
@ -930,28 +930,22 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
|
||||
if (wlv->skip_cells > 0) {
|
||||
int virt_text_width = (int)mb_string2cells(wlv->p_extra);
|
||||
if (virt_text_width > wlv->skip_cells) {
|
||||
int cells_to_skip = wlv->skip_cells;
|
||||
int skip_cells_remaining = wlv->skip_cells;
|
||||
// Skip cells in the text.
|
||||
while (cells_to_skip > 0) {
|
||||
while (skip_cells_remaining > 0) {
|
||||
int cells = utf_ptr2cells(wlv->p_extra);
|
||||
if (cells > skip_cells_remaining) {
|
||||
break;
|
||||
}
|
||||
int c_len = utfc_ptr2len(wlv->p_extra);
|
||||
cells_to_skip -= utf_ptr2cells(wlv->p_extra);
|
||||
skip_cells_remaining -= cells;
|
||||
wlv->p_extra += c_len;
|
||||
wlv->n_extra -= c_len;
|
||||
wlv->n_attr--;
|
||||
}
|
||||
// If a double-width char doesn't fit, pad with space.
|
||||
if (cells_to_skip < 0) {
|
||||
int pad_len = -cells_to_skip;
|
||||
char *padded = get_extra_buf((size_t)(wlv->n_extra + pad_len) + 1);
|
||||
memset(padded, ' ', (size_t)pad_len);
|
||||
xmemcpyz(padded + pad_len, wlv->p_extra, (size_t)wlv->n_extra);
|
||||
wlv->p_extra = padded;
|
||||
wlv->n_extra += pad_len;
|
||||
wlv->n_attr += pad_len;
|
||||
}
|
||||
// Skipped cells needed to be accounted for in vcol.
|
||||
wlv->skipped_cells += wlv->skip_cells;
|
||||
wlv->skip_cells = 0;
|
||||
wlv->skipped_cells += wlv->skip_cells - skip_cells_remaining;
|
||||
wlv->skip_cells = skip_cells_remaining;
|
||||
} else {
|
||||
// The whole text is left of the window, drop
|
||||
// it and advance to the next one.
|
||||
@ -1076,6 +1070,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
bool in_multispace = false; // in multiple consecutive spaces
|
||||
int multispace_pos = 0; // position in lcs-multispace string
|
||||
|
||||
int n_extra_next = 0; // n_extra to use after current extra chars
|
||||
int extra_attr_next = -1; // extra_attr to use after current extra chars
|
||||
|
||||
bool search_attr_from_match = false; // if search_attr is from :match
|
||||
bool has_decor = false; // this buffer has decoration
|
||||
|
||||
@ -1950,7 +1947,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
if (wlv.col >= grid->cols - 1 && schar_cells(mb_schar) == 2) {
|
||||
mb_c = '>';
|
||||
mb_l = 1;
|
||||
(void)mb_l;
|
||||
mb_schar = schar_from_ascii(mb_c);
|
||||
multi_attr = win_hl_attr(wp, HLF_AT);
|
||||
|
||||
@ -1963,11 +1959,28 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
wlv.n_extra -= mb_l;
|
||||
wlv.p_extra += mb_l;
|
||||
}
|
||||
|
||||
// If a double-width char doesn't fit at the left side display a '<'.
|
||||
if (wlv.filler_todo <= 0 && wlv.skip_cells > 0 && mb_l > 1) {
|
||||
if (wlv.n_extra > 0) {
|
||||
n_extra_next = wlv.n_extra;
|
||||
extra_attr_next = wlv.extra_attr;
|
||||
}
|
||||
wlv.n_extra = 1;
|
||||
wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR);
|
||||
wlv.sc_final = NUL;
|
||||
mb_schar = schar_from_ascii(' ');
|
||||
mb_c = ' ';
|
||||
mb_l = 1;
|
||||
(void)mb_l;
|
||||
wlv.n_attr++;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
|
||||
}
|
||||
}
|
||||
|
||||
// Only restore search_attr and area_attr after "n_extra" in
|
||||
// the next screen line is also done.
|
||||
if (wlv.n_extra <= 0) {
|
||||
// Only restore search_attr and area_attr when there is no "n_extra" to show.
|
||||
if (n_extra_next <= 0) {
|
||||
if (search_attr == 0) {
|
||||
search_attr = saved_search_attr;
|
||||
saved_search_attr = 0;
|
||||
@ -1980,13 +1993,24 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
decor_attr = saved_decor_attr;
|
||||
saved_decor_attr = 0;
|
||||
}
|
||||
|
||||
if (wlv.extra_for_extmark) {
|
||||
// wlv.extra_attr should be used at this position but not
|
||||
// any further.
|
||||
// wlv.extra_attr should be used at this position but not any further.
|
||||
wlv.reset_extra_attr = true;
|
||||
extra_attr_next = -1;
|
||||
}
|
||||
wlv.extra_for_extmark = false;
|
||||
} else {
|
||||
assert(wlv.sc_extra != NUL || wlv.sc_final != NUL);
|
||||
assert(wlv.p_extra != NULL);
|
||||
wlv.sc_extra = NUL;
|
||||
wlv.sc_final = NUL;
|
||||
wlv.n_extra = n_extra_next;
|
||||
n_extra_next = 0;
|
||||
// wlv.extra_attr should be used at this position, but extra_attr_next
|
||||
// should be used after that.
|
||||
wlv.reset_extra_attr = true;
|
||||
assert(extra_attr_next >= 0);
|
||||
}
|
||||
}
|
||||
} else if (wlv.filler_todo > 0) {
|
||||
// Wait with reading text until filler lines are done. Still need to
|
||||
@ -2563,16 +2587,21 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
|
||||
}
|
||||
|
||||
// Don't override visual selection highlighting.
|
||||
// Use "wlv.extra_attr", but don't override visual selection highlighting.
|
||||
if (wlv.n_attr > 0 && !search_attr_from_match) {
|
||||
wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr);
|
||||
if (wlv.reset_extra_attr) {
|
||||
wlv.reset_extra_attr = false;
|
||||
if (extra_attr_next >= 0) {
|
||||
wlv.extra_attr = extra_attr_next;
|
||||
extra_attr_next = -1;
|
||||
} else {
|
||||
wlv.extra_attr = 0;
|
||||
// search_attr_from_match can be restored now that the extra_attr has been applied
|
||||
search_attr_from_match = saved_search_attr_from_match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the case where we are in column 0 but not on the first
|
||||
// character of the line and the user wants us to show us a
|
||||
@ -2584,14 +2613,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
|
||||
&& wlv.skip_cells <= 0
|
||||
&& mb_schar != NUL) {
|
||||
lcs_prec_todo = NUL;
|
||||
// TODO(zeertzjq): handle the n_extra > 0 case
|
||||
if (schar_cells(mb_schar) > 1 && wlv.n_extra == 0) {
|
||||
if (schar_cells(mb_schar) > 1) {
|
||||
// Double-width character being overwritten by the "precedes"
|
||||
// character, need to fill up half the character.
|
||||
wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR);
|
||||
wlv.sc_final = NUL;
|
||||
wlv.n_extra = 1;
|
||||
if (wlv.n_extra > 0) {
|
||||
assert(wlv.p_extra != NULL);
|
||||
n_extra_next = wlv.n_extra;
|
||||
extra_attr_next = wlv.extra_attr;
|
||||
wlv.n_attr = MAX(wlv.n_attr + 1, 2);
|
||||
} else {
|
||||
wlv.n_attr = 2;
|
||||
}
|
||||
wlv.n_extra = 1;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
|
||||
}
|
||||
mb_schar = wp->w_p_lcs_chars.prec;
|
||||
|
@ -3368,17 +3368,24 @@ describe('decorations: inline virtual text', function()
|
||||
command("set nowrap")
|
||||
api.nvim_buf_set_extmark(0, ns, 0, 2, { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' })
|
||||
feed('$')
|
||||
screen:expect{grid=[[
|
||||
screen:expect([[
|
||||
{10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i|
|
||||
{1:~ }|
|
||||
|
|
||||
]]}
|
||||
]])
|
||||
command('set list listchars+=precedes:!')
|
||||
screen:expect([[
|
||||
{1:!}{10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}cdefgh^i|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('draws correctly with no wrap and multibyte virtual text', function()
|
||||
insert('12345678')
|
||||
command('set nowrap')
|
||||
api.nvim_buf_set_extmark(0, ns, 0, 2, {
|
||||
hl_mode = 'replace',
|
||||
virt_text = { { 'α口β̳γ̲=', 'Special' }, { '❤️', 'Special' } },
|
||||
virt_text_pos = 'inline',
|
||||
})
|
||||
@ -3405,9 +3412,33 @@ describe('decorations: inline virtual text', function()
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
feed('V')
|
||||
screen:expect([[
|
||||
{10:口β̳γ̲=❤️}{7:34567}^8 |
|
||||
{1:~ }|
|
||||
{8:-- VISUAL LINE --} |
|
||||
]])
|
||||
command('set list listchars+=precedes:!')
|
||||
screen:expect([[
|
||||
{1:!<}{10:β̳γ̲=❤️}{7:34567}^8 |
|
||||
{1:~ }|
|
||||
{8:-- VISUAL LINE --} |
|
||||
]])
|
||||
feed('zl')
|
||||
screen:expect([[
|
||||
{10: β̳γ̲=❤️}34567^8 |
|
||||
{1:!}{10:β̳γ̲=❤️}{7:34567}^8 |
|
||||
{1:~ }|
|
||||
{8:-- VISUAL LINE --} |
|
||||
]])
|
||||
command('set nolist')
|
||||
screen:expect([[
|
||||
{1:<}{10:β̳γ̲=❤️}{7:34567}^8 |
|
||||
{1:~ }|
|
||||
{8:-- VISUAL LINE --} |
|
||||
]])
|
||||
feed('<Esc>')
|
||||
screen:expect([[
|
||||
{1:<}{10:β̳γ̲=❤️}34567^8 |
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
@ -3435,9 +3466,21 @@ describe('decorations: inline virtual text', function()
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
command('set list')
|
||||
screen:expect([[
|
||||
{1:!<}34567^8 |
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
feed('zl')
|
||||
screen:expect([[
|
||||
{10: }34567^8 |
|
||||
{1:!}34567^8 |
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
command('set nolist')
|
||||
screen:expect([[
|
||||
{1:<}34567^8 |
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
|
Reference in New Issue
Block a user