fix(marks): handle double-with inline virt_text with 'nowrap' (#32476)

This commit is contained in:
zeertzjq 2025-02-16 22:05:13 +08:00 committed by GitHub
parent efe92f9dff
commit 8452032554
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 106 additions and 30 deletions

View File

@ -909,25 +909,39 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
// If the text didn't reach until the first window // If the text didn't reach until the first window
// column we need to skip cells. // column we need to skip cells.
if (wlv->skip_cells > 0) { if (wlv->skip_cells > 0) {
// FIXME: this should use virt_text_width instead int virt_text_width = (int)mb_string2cells(wlv->p_extra);
int virt_text_len = wlv->n_attr; if (virt_text_width > wlv->skip_cells) {
if (virt_text_len > wlv->skip_cells) { int cells_to_skip = wlv->skip_cells;
int len = mb_charlen2bytelen(wlv->p_extra, wlv->skip_cells); // Skip cells in the text.
wlv->n_extra -= len; while (cells_to_skip > 0) {
wlv->p_extra += len; int clen = utf_ptr2len(wlv->p_extra);
wlv->n_attr -= wlv->skip_cells; cells_to_skip -= utf_ptr2cells(wlv->p_extra);
wlv->p_extra += clen;
wlv->n_extra -= clen;
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. // Skipped cells needed to be accounted for in vcol.
wlv->skipped_cells += wlv->skip_cells; wlv->skipped_cells += wlv->skip_cells;
wlv->skip_cells = 0; wlv->skip_cells = 0;
} else { } else {
// the whole text is left of the window, drop // The whole text is left of the window, drop
// it and advance to the next one // it and advance to the next one.
wlv->skip_cells -= virt_text_len; wlv->skip_cells -= virt_text_width;
// Skipped cells needed to be accounted for in vcol. // Skipped cells needed to be accounted for in vcol.
wlv->skipped_cells += virt_text_len; wlv->skipped_cells += virt_text_width;
wlv->n_attr = 0; wlv->n_attr = 0;
wlv->n_extra = 0; wlv->n_extra = 0;
// go to the start so the next virtual text chunk can be selected. // Go to the start so the next virtual text chunk can be selected.
continue; continue;
} }
} }

View File

@ -2251,24 +2251,6 @@ int mb_charlen(const char *str)
return count; return count;
} }
int mb_charlen2bytelen(const char *str, int charlen)
{
const char *p = str;
int count = 0;
if (p == NULL) {
return 0;
}
for (int i = 0; *p != NUL && i < charlen; i++) {
int b = utfc_ptr2len(p);
p += b;
count += b;
}
return count;
}
/// Like mb_charlen() but for a string with specified length. /// Like mb_charlen() but for a string with specified length.
int mb_charlen_len(const char *str, int len) int mb_charlen_len(const char *str, int len)
{ {

View File

@ -3375,6 +3375,86 @@ describe('decorations: inline virtual text', function()
]]} ]]}
end) 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, {
virt_text = { { 'αβγ口=', 'Special' }, { '', 'Special' } },
virt_text_pos = 'inline',
})
screen:expect([[
12{10:αβγ=}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
2{10:αβγ=}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
{10:αβγ=}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
{10:βγ=}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
{10:γ=}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
{10:=}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
{10: =}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
{10:=}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
{10:}34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
{10: }34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
34567^8 |
{1:~ }|
|
]])
feed('zl')
screen:expect([[
4567^8 |
{1:~ }|
|
]])
end)
it('tabs are the correct length with no wrap following virtual text', function() it('tabs are the correct length with no wrap following virtual text', function()
command('set nowrap') command('set nowrap')
feed('itest<TAB>a<ESC>') feed('itest<TAB>a<ESC>')