aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjΓΆrn Linse <bjorn.linse@gmail.com>2020-01-24 09:48:58 +0100
committerbfredl <bjorn.linse@gmail.com>2023-02-14 15:56:14 +0100
commit39f8aaeb815c2e31cffec12ef36ad4f25df91602 (patch)
treed8f729d4cbf87a80cf9a78270ba921baf1b6bb2c
parent53968082675cd3b8d1809e53a47c0311b7347ef9 (diff)
downloadrneovim-39f8aaeb815c2e31cffec12ef36ad4f25df91602.tar.gz
rneovim-39f8aaeb815c2e31cffec12ef36ad4f25df91602.tar.bz2
rneovim-39f8aaeb815c2e31cffec12ef36ad4f25df91602.zip
fix(status): handle unprintable chars in the statusline
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/charset.c11
-rw-r--r--src/nvim/grid.c16
-rw-r--r--src/nvim/statusline.c37
-rw-r--r--test/functional/ui/multibyte_spec.lua24
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)