From 9c202b9392f3d42618cc576aab00a50ed2f7bdeb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 15 Jan 2024 23:45:11 +0800 Subject: [PATCH] fix(grid): handle clearing half a double-width char (#27023) --- src/nvim/grid.c | 15 +++++++++---- test/functional/ui/multibyte_spec.lua | 32 ++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/nvim/grid.c b/src/nvim/grid.c index e4a34ae74a..32b1d3f8eb 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -655,6 +655,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol // two-cell character in the same grid, truncate that into a '>'. if (col > 0 && grid->chars[off_to + (size_t)col] == 0) { linebuf_char[col - 1] = schar_from_ascii('>'); + linebuf_attr[col - 1] = grid->attrs[off_to + (size_t)col - 1]; col--; } @@ -676,7 +677,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol } } - redraw_next = grid_char_needs_redraw(grid, col, (size_t)col + off_to, endcol - col); + redraw_next = grid_char_needs_redraw(grid, col, off_to + (size_t)col, endcol - col); int start_dirty = -1; int end_dirty = 0; @@ -688,7 +689,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol char_cells = 2; } bool redraw_this = redraw_next; // Does character need redraw? - size_t off = (size_t)col + off_to; + size_t off = off_to + (size_t)col; redraw_next = grid_char_needs_redraw(grid, col + char_cells, off + (size_t)char_cells, endcol - col - char_cells); @@ -732,16 +733,22 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol if (clear_next) { // Clear the second half of a double-wide character of which the left // half was overwritten with a single-wide character. - grid->chars[(size_t)col + off_to] = schar_from_ascii(' '); + grid->chars[off_to + (size_t)col] = schar_from_ascii(' '); end_dirty++; } + // When clearing the left half of a double-wide char also clear the right half. + if (off_to + (size_t)clear_width < max_off_to + && grid->chars[off_to + (size_t)clear_width] == 0) { + clear_width++; + } + int clear_dirty_start = -1, clear_end = -1; // blank out the rest of the line // TODO(bfredl): we could cache winline widths col = clear_start; while (col < clear_width) { - size_t off = (size_t)col + off_to; + size_t off = off_to + (size_t)col; if (grid->chars[off] != schar_from_ascii(' ') || grid->attrs[off] != bg_attr || rdb_flags & RDB_NODELTA) { diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua index d38bc27a51..c2fc763401 100644 --- a/test/functional/ui/multibyte_spec.lua +++ b/test/functional/ui/multibyte_spec.lua @@ -22,6 +22,8 @@ describe('multibyte rendering', function() [3] = { background = Screen.colors.LightMagenta }, [4] = { bold = true }, [5] = { foreground = Screen.colors.Blue }, + [6] = { reverse = true, bold = true }, + [7] = { reverse = true }, }) end) @@ -84,7 +86,7 @@ describe('multibyte rendering', function() {4:-- INSERT --} | ]]) - -- check double-with char is temporarily hidden when overlapped + -- check double-width char is temporarily hidden when overlapped fn.complete(4, { 'xx', 'yy' }) screen:expect([[ ab xx^ | @@ -104,6 +106,34 @@ describe('multibyte rendering', function() ]]) end) + it('no stray chars when splitting left of window with double-width chars', function() + api.nvim_buf_set_lines(0, 0, -1, true, { + ('口'):rep(16), + 'a' .. ('口'):rep(16), + 'aa' .. ('口'):rep(16), + 'aaa' .. ('口'):rep(16), + 'aaaa' .. ('口'):rep(16), + }) + screen:expect([[ + ^口口口口口口口口口口口口口口口口 | + a口口口口口口口口口口口口口口口口 | + aa口口口口口口口口口口口口口口口口 | + aaa口口口口口口口口口口口口口口口口 | + aaaa口口口口口口口口口口口口口口口口 | + | + ]]) + + command('20vnew') + screen:expect([[ + ^ │口口口口口口口口口口口口口口口口 | + {1:~ }│a口口口口口口口口口口口口口口口口 | + {1:~ }│aa口口口口口口口口口口口口口口口口 | + {1:~ }│aaa口口口口口口口口口口口口口口口口 | + {6:[No Name] }{7:[No Name] [+] }| + | + ]]) + end) + it('0xffff is shown as 4 hex digits', function() command([[call setline(1, "\uFFFF!!!")]]) feed('$')