diff options
author | zeertzjq <zeertzjq@outlook.com> | 2025-02-16 22:05:13 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-16 14:05:13 +0000 |
commit | 84520325547d6ab3a047a09b5a0e55e8b216a9fb (patch) | |
tree | f301872e208ea39883a05c24f4f56117de09de1f | |
parent | efe92f9dff9383cf78a2d0c857448a14514c7043 (diff) | |
download | rneovim-84520325547d6ab3a047a09b5a0e55e8b216a9fb.tar.gz rneovim-84520325547d6ab3a047a09b5a0e55e8b216a9fb.tar.bz2 rneovim-84520325547d6ab3a047a09b5a0e55e8b216a9fb.zip |
fix(marks): handle double-with inline virt_text with 'nowrap' (#32476)
-rw-r--r-- | src/nvim/drawline.c | 38 | ||||
-rw-r--r-- | src/nvim/mbyte.c | 18 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 80 |
3 files changed, 106 insertions, 30 deletions
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index e4fe50ad28..f25da1794c 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -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 // column we need to skip cells. if (wlv->skip_cells > 0) { - // FIXME: this should use virt_text_width instead - int virt_text_len = wlv->n_attr; - if (virt_text_len > wlv->skip_cells) { - int len = mb_charlen2bytelen(wlv->p_extra, wlv->skip_cells); - wlv->n_extra -= len; - wlv->p_extra += len; - wlv->n_attr -= wlv->skip_cells; + int virt_text_width = (int)mb_string2cells(wlv->p_extra); + if (virt_text_width > wlv->skip_cells) { + int cells_to_skip = wlv->skip_cells; + // Skip cells in the text. + while (cells_to_skip > 0) { + int clen = utf_ptr2len(wlv->p_extra); + 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. wlv->skipped_cells += wlv->skip_cells; wlv->skip_cells = 0; } else { - // the whole text is left of the window, drop - // it and advance to the next one - wlv->skip_cells -= virt_text_len; + // The whole text is left of the window, drop + // it and advance to the next one. + wlv->skip_cells -= virt_text_width; // 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_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; } } diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index ffd4472b8a..add650e7a9 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2251,24 +2251,6 @@ int mb_charlen(const char *str) 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. int mb_charlen_len(const char *str, int len) { diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 7969dd5d3b..fb2e6ea65f 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -3375,6 +3375,86 @@ describe('decorations: inline virtual text', function() ]]} 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() command('set nowrap') feed('itest<TAB>a<ESC>') |