diff options
author | BjΓΆrn Linse <bjorn.linse@gmail.com> | 2020-01-24 09:48:58 +0100 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2023-02-14 15:56:14 +0100 |
commit | 39f8aaeb815c2e31cffec12ef36ad4f25df91602 (patch) | |
tree | d8f729d4cbf87a80cf9a78270ba921baf1b6bb2c | |
parent | 53968082675cd3b8d1809e53a47c0311b7347ef9 (diff) | |
download | rneovim-39f8aaeb815c2e31cffec12ef36ad4f25df91602.tar.gz rneovim-39f8aaeb815c2e31cffec12ef36ad4f25df91602.tar.bz2 rneovim-39f8aaeb815c2e31cffec12ef36ad4f25df91602.zip |
fix(status): handle unprintable chars in the statusline
-rw-r--r-- | src/nvim/buffer.c | 2 | ||||
-rw-r--r-- | src/nvim/charset.c | 11 | ||||
-rw-r--r-- | src/nvim/grid.c | 16 | ||||
-rw-r--r-- | src/nvim/statusline.c | 37 | ||||
-rw-r--r-- | test/functional/ui/multibyte_spec.lua | 24 |
5 files changed, 60 insertions, 30 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 5dcb10751f..129a8c6fb2 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3311,7 +3311,7 @@ void maketitle(void) buf_p += MIN(size, SPACE_FOR_FNAME); } else { buf_p += transstr_buf((const char *)path_tail(curbuf->b_fname), - buf_p, SPACE_FOR_FNAME + 1, true); + -1, buf_p, SPACE_FOR_FNAME + 1, true); } switch (bufIsChanged(curbuf) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 5aec9ccf9d..c4c7c5f387 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -354,14 +354,15 @@ size_t transstr_len(const char *const s, bool untab) /// @param[in] untab remove tab characters /// /// @return length of the resulting string, without the NUL byte. -size_t transstr_buf(const char *const s, char *const buf, const size_t len, bool untab) +size_t transstr_buf(const char *const s, const ssize_t slen, char *const buf, const size_t buflen, + bool untab) FUNC_ATTR_NONNULL_ALL { const char *p = s; char *buf_p = buf; - char *const buf_e = buf_p + len - 1; + char *const buf_e = buf_p + buflen - 1; - while (*p != NUL && buf_p < buf_e) { + while ((slen < 0 || (p - s) < slen) && *p != NUL && buf_p < buf_e) { const size_t l = (size_t)utfc_ptr2len(p); if (l > 1) { if (buf_p + l > buf_e) { @@ -416,7 +417,7 @@ char *transstr(const char *const s, bool untab) // multi-byte characters. const size_t len = transstr_len(s, untab) + 1; char *const buf = xmalloc(len); - transstr_buf(s, buf, len, untab); + transstr_buf(s, -1, buf, len, untab); return buf; } @@ -431,7 +432,7 @@ size_t kv_transstr(StringBuilder *str, const char *const s, bool untab) // multi-byte characters. const size_t len = transstr_len(s, untab); kv_ensure_space(*str, len + 1); - transstr_buf(s, str->items + str->size, len + 1, untab); + transstr_buf(s, -1, str->items + str->size, len + 1, untab); str->size += len; // do not include NUL byte return len; } diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 16ebbfbb90..efbeac4f3f 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -158,9 +158,9 @@ void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp) /// attributes 'attr', and update chars[] and attrs[]. /// Note: only outputs within one row, message is truncated at grid boundary! /// Note: if grid, row and/or col is invalid, nothing is done. -void grid_puts(ScreenGrid *grid, char *text, int row, int col, int attr) +int grid_puts(ScreenGrid *grid, char *text, int row, int col, int attr) { - grid_puts_len(grid, text, -1, row, col, attr); + return grid_puts_len(grid, text, -1, row, col, attr); } static ScreenGrid *put_dirty_grid = NULL; @@ -197,7 +197,7 @@ void grid_put_schar(ScreenGrid *grid, int row, int col, char *schar, int attr) /// like grid_puts(), but output "text[len]". When "len" is -1 output up to /// a NUL. -void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, int attr) +int grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, int attr) { size_t off; char *ptr = text; @@ -218,7 +218,7 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, if (grid->chars == NULL || row >= grid->rows || row < 0 || col >= grid->cols || col < 0) { - return; + return 0; } if (put_dirty_row == -1) { @@ -230,6 +230,7 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, } } off = grid->line_offset[row] + (size_t)col; + int start_col = col; // When drawing over the right half of a double-wide char clear out the // left half. Only needed in a terminal. @@ -252,6 +253,12 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr)) : utfc_ptr2char(ptr, u8cc); int mbyte_cells = utf_char2cells(u8c); + if (mbyte_cells > 2) { + mbyte_cells = 1; + u8c = 0xFFFD; + u8cc[0] = 0; + } + if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { // Do Arabic shaping. if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) { @@ -336,6 +343,7 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, if (do_flush) { grid_puts_line_flush(true); } + return col - start_col; } /// End a group of grid_puts_len calls and send the screen buffer to the UI diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 6414b2bb74..c880de5060 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -152,8 +152,8 @@ void win_redr_status(win_T *wp) row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); col = is_stl_global ? 0 : wp->w_wincol; - grid_puts(&default_grid, p, row, col, attr); - grid_fill(&default_grid, row, row + 1, len + col, + int width = grid_puts(&default_grid, p, row, col, attr); + grid_fill(&default_grid, row, row + 1, width + col, this_ru_col + col, fillchar, fillchar, attr); if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL) @@ -266,6 +266,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) int n; int fillchar; char buf[MAXPATHL]; + char transbuf[MAXPATHL]; char *stl; char *opt_name; int opt_scope = 0; @@ -370,34 +371,25 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) // Make a copy, because the statusline may include a function call that // might change the option value and free the memory. stl = xstrdup(stl); - int width = build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope, - fillchar, maxwidth, &hltab, &tabtab, NULL); + build_stl_str_hl(ewp, buf, sizeof(buf), stl, opt_name, opt_scope, + fillchar, maxwidth, &hltab, &tabtab, NULL); xfree(stl); ewp->w_p_crb = p_crb_save; - // Make all characters printable. - char *p = transstr(buf, true); - int len = (int)xstrlcpy(buf, p, sizeof(buf)); - len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; - xfree(p); - - // fill up with "fillchar" - while (width < maxwidth && len < (int)sizeof(buf) - 1) { - len += utf_char2bytes(fillchar, buf + len); - width++; - } - buf[len] = NUL; + int len = (int)strlen(buf); + int start_col = col; // Draw each snippet with the specified highlighting. grid_puts_line_start(grid, row); int curattr = attr; - p = buf; + char *p = buf; for (n = 0; hltab[n].start != NULL; n++) { int textlen = (int)(hltab[n].start - p); - grid_puts_len(grid, p, textlen, row, col, curattr); - col += vim_strnsize(p, textlen); + // Make all characters printable. + size_t tsize = transstr_buf(p, textlen, transbuf, sizeof transbuf, true); + col += grid_puts_len(grid, transbuf, (int)tsize, row, col, curattr); p = hltab[n].start; if (hltab[n].userhl == 0) { @@ -411,7 +403,12 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) } } // Make sure to use an empty string instead of p, if p is beyond buf + len. - grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr); + size_t tsize = transstr_buf(p >= buf + len ? "" : p, -1, transbuf, sizeof transbuf, true); + col += grid_puts_len(grid, transbuf, (int)tsize, row, col, curattr); + int maxcol = start_col + maxwidth; + + // fill up with "fillchar" + grid_fill(grid, row, row + 1, col, maxcol, fillchar, fillchar, curattr); grid_puts_line_flush(false); diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua index d4e237bcb4..9f413f8bff 100644 --- a/test/functional/ui/multibyte_spec.lua +++ b/test/functional/ui/multibyte_spec.lua @@ -158,6 +158,7 @@ describe('multibyte rendering: statusline', function() screen:set_default_attr_ids({ [1] = {bold = true, foreground = Screen.colors.Blue1}, [2] = {bold = true, reverse = true}, + [3] = {background = Screen.colors.Red, foreground = Screen.colors.Gray100}; }) screen:attach() command('set laststatus=2') @@ -220,4 +221,27 @@ describe('multibyte rendering: statusline', function() | ]]} end) + + it('unprintable chars in filename with default stl', function() + command("file π§βπ»") + -- TODO: this is wrong but avoids a crash + screen:expect{grid=[[ + ^ | + {1:~ }| + {2:π§οΏ½π» }| + | + ]]} + end) + + it('unprintable chars in filename with custom stl', function() + command('set statusline=xx%#ErrorMsg#%f%##yy') + command("file π§βπ»") + -- TODO: this is also wrong but also avoids a crash + screen:expect{grid=[[ + ^ | + {1:~ }| + {2:xx}{3:π§<200d>π»}{2:yy }| + | + ]]} + end) end) |