From ee3d4f6b90d0902aa17936d1f0944755516569a1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 9 Jan 2024 05:37:08 +0800 Subject: [PATCH] fix(extmarks): handle overwriting right half of wide char (#26951) --- src/nvim/drawline.c | 19 +++++++++++++-- test/functional/ui/decorations_spec.lua | 32 ++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index dc4ef72e38..7ee7e1911f 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -206,6 +206,9 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col) /// Handles composing chars static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells, int vcol) { + // Caller should handle overwriting the left half of a double-width char. + assert(dest[0] != 0); + const char *p = *pp; int cells = utf_ptr2cells(p); int c_len = utfc_ptr2len(p); @@ -219,6 +222,7 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells); } + // When overwriting the left half of a double-width char, clear the right half. if (cells < maxcells && dest[cells] == 0) { dest[cells] = schar_from_ascii(' '); } @@ -330,8 +334,15 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, } else { attr = virt_attr; } - schar_T dummy[2]; + schar_T dummy[2] = { schar_from_ascii(' '), schar_from_ascii(' ') }; int maxcells = max_col - col; + // When overwriting the right half of a double-width char, clear the left half. + if (linebuf_char[col] == 0) { + assert(col > 0); + linebuf_char[col - 1] = schar_from_ascii(' '); + // Clear the right half as well for the assertion in line_putchar(). + linebuf_char[col] = schar_from_ascii(' '); + } int cells = line_putchar(buf, &p, through ? dummy : &linebuf_char[col], maxcells, vcol); for (int c = 0; c < cells; c++) { @@ -867,7 +878,11 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra) wlv->col = 0; wlv->off = 0; wlv->need_lbr = false; - memset(linebuf_vcol, -1, (size_t)wp->w_grid.cols * sizeof(*linebuf_vcol)); + for (int i = 0; i < wp->w_grid.cols; i++) { + linebuf_char[i] = schar_from_ascii(' '); + linebuf_attr[i] = -1; + linebuf_vcol[i] = -1; + } } static void fix_for_boguscols(winlinevars_T *wlv) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 48df6b3295..4860009e38 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -2082,7 +2082,37 @@ describe('extmark decorations', function() ]]} end) - it('works with double width char and rightleft', function() + it('virtual text overwrites double-width char properly', function() + screen:try_resize(50, 3) + insert('abcdefghij口klmnopqrstu口vwx口yz') + feed('0') + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'!!!!!', 'Underlined'}}, virt_text_win_col = 11 }) + screen:expect{grid=[[ + ^abcdefghij {28:!!!!!}opqrstu口vwx口yz | + {1:~ }| + | + ]]} + feed('8x') + screen:expect{grid=[[ + ^ij口klmnopq{28:!!!!!} vwx口yz | + {1:~ }| + | + ]]} + feed('3l5x') + screen:expect{grid=[[ + ij口^pqrstu {28:!!!!!} yz | + {1:~ }| + | + ]]} + feed('5x') + screen:expect{grid=[[ + ij口^u口vwx {28:!!!!!} | + {1:~ }| + | + ]]} + end) + + it('virtual text works with double-width char and rightleft', function() screen:try_resize(50, 3) insert('abcdefghij口klmnopqrstu口vwx口yz') feed('0')