aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2025-02-16 22:05:13 +0800
committerGitHub <noreply@github.com>2025-02-16 14:05:13 +0000
commit84520325547d6ab3a047a09b5a0e55e8b216a9fb (patch)
treef301872e208ea39883a05c24f4f56117de09de1f
parentefe92f9dff9383cf78a2d0c857448a14514c7043 (diff)
downloadrneovim-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.c38
-rw-r--r--src/nvim/mbyte.c18
-rw-r--r--test/functional/ui/decorations_spec.lua80
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>')