refactor(options): use schar_T representation for fillchars and listchars

A bit big, but practically it was a lot simpler to change over all
fillchars and all listchars at once, to not need to maintain two
parallel implementations.

This is mostly an internal refactor, but it also removes an arbitrary
limitation: that 'fillchars' and 'listchars' values can only be
single-codepoint characters. Now any character which fits into a single
screen cell can be used.
This commit is contained in:
bfredl 2024-01-03 13:31:39 +01:00
parent fbe40caa7c
commit aeb053907d
18 changed files with 474 additions and 457 deletions

View File

@ -41,7 +41,7 @@
static PMap(uint64_t) connected_uis = MAP_INIT; static PMap(uint64_t) connected_uis = MAP_INIT;
#define mpack_w(b, byte) *(*b)++ = (char)(byte); #define mpack_w(b, byte) *(*(b))++ = (char)(byte);
static void mpack_w2(char **b, uint32_t v) static void mpack_w2(char **b, uint32_t v)
{ {
*(*b)++ = (char)((v >> 8) & 0xff); *(*b)++ = (char)((v >> 8) & 0xff);
@ -98,10 +98,9 @@ static char *mpack_array_dyn16(char **buf)
return pos; return pos;
} }
static void mpack_str(char **buf, const char *str) static void mpack_str(char **buf, const char *str, size_t len)
{ {
assert(sizeof(schar_T) - 1 < 0x20); assert(sizeof(schar_T) - 1 < 0x20);
size_t len = strlen(str);
mpack_w(buf, 0xa0 | len); mpack_w(buf, 0xa0 | len);
memcpy(*buf, str, len); memcpy(*buf, str, len);
*buf += len; *buf += len;
@ -566,7 +565,7 @@ static void flush_event(UIData *data)
// [2, "redraw", [...]] // [2, "redraw", [...]]
mpack_array(buf, 3); mpack_array(buf, 3);
mpack_uint(buf, 2); mpack_uint(buf, 2);
mpack_str(buf, "redraw"); mpack_str(buf, S_LEN("redraw"));
data->nevents_pos = mpack_array_dyn16(buf); data->nevents_pos = mpack_array_dyn16(buf);
} }
} }
@ -607,7 +606,7 @@ static bool prepare_call(UI *ui, const char *name)
data->cur_event = name; data->cur_event = name;
char **buf = &data->buf_wptr; char **buf = &data->buf_wptr;
data->ncalls_pos = mpack_array_dyn16(buf); data->ncalls_pos = mpack_array_dyn16(buf);
mpack_str(buf, name); mpack_str(buf, name, strlen(name));
data->nevents++; data->nevents++;
data->ncalls = 1; data->ncalls = 1;
return true; return true;
@ -640,17 +639,18 @@ static void push_call(UI *ui, const char *name, Array args)
remote_ui_flush_buf(ui); remote_ui_flush_buf(ui);
} }
if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) { size_t name_len = strlen(name);
if (data->pack_totlen > UI_BUF_SIZE - name_len - 20) {
// TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set)
data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen); data->temp_buf = xmalloc(20 + name_len + data->pack_totlen);
data->buf_wptr = data->temp_buf; data->buf_wptr = data->temp_buf;
char **buf = &data->buf_wptr; char **buf = &data->buf_wptr;
mpack_array(buf, 3); mpack_array(buf, 3);
mpack_uint(buf, 2); mpack_uint(buf, 2);
mpack_str(buf, "redraw"); mpack_str(buf, S_LEN("redraw"));
mpack_array(buf, 1); mpack_array(buf, 1);
mpack_array(buf, 2); mpack_array(buf, 2);
mpack_str(buf, name); mpack_str(buf, name, name_len);
} else { } else {
prepare_call(ui, name); prepare_call(ui, name);
} }
@ -895,9 +895,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1); uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1);
nelem++; nelem++;
mpack_array(buf, csize); mpack_array(buf, csize);
char sc_buf[MAX_SCHAR_SIZE]; char *size_byte = (*buf)++;
schar_get(sc_buf, chunk[i]); size_t len = schar_get_adv(buf, chunk[i]);
mpack_str(buf, sc_buf); *size_byte = (char)(0xa0 | len);
if (csize >= 2) { if (csize >= 2) {
mpack_uint(buf, (uint32_t)attrs[i]); mpack_uint(buf, (uint32_t)attrs[i]);
if (csize >= 3) { if (csize >= 3) {
@ -916,7 +916,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
nelem++; nelem++;
data->ncells_pending += 1; data->ncells_pending += 1;
mpack_array(buf, 3); mpack_array(buf, 3);
mpack_str(buf, " "); mpack_str(buf, S_LEN(" "));
mpack_uint(buf, (uint32_t)clearattr); mpack_uint(buf, (uint32_t)clearattr);
mpack_uint(buf, (uint32_t)(clearcol - endcol)); mpack_uint(buf, (uint32_t)(clearcol - endcol));
} }

View File

@ -2132,7 +2132,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
Dictionary result = ARRAY_DICT_INIT; Dictionary result = ARRAY_DICT_INIT;
int maxwidth; int maxwidth;
int fillchar = 0; schar_T fillchar = 0;
int statuscol_lnum = 0; int statuscol_lnum = 0;
Window window = 0; Window window = 0;
@ -2148,11 +2148,13 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
} }
if (HAS_KEY(opts, eval_statusline, fillchar)) { if (HAS_KEY(opts, eval_statusline, fillchar)) {
VALIDATE_EXP((*opts->fillchar.data != 0 VALIDATE_EXP((*opts->fillchar.data != 0
&& ((size_t)utf_ptr2len(opts->fillchar.data) == opts->fillchar.size)), && ((size_t)utfc_ptr2len(opts->fillchar.data) == opts->fillchar.size)),
"fillchar", "single character", NULL, { "fillchar", "single character", NULL, {
return result; return result;
}); });
fillchar = utf_ptr2char(opts->fillchar.data); int c;
fillchar = utfc_ptr2schar(opts->fillchar.data, &c);
// TODO(bfredl): actually check c is single width
} }
int use_bools = (int)opts->use_winbar + (int)opts->use_tabline; int use_bools = (int)opts->use_winbar + (int)opts->use_tabline;
@ -2181,7 +2183,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 }; SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 };
if (opts->use_tabline) { if (opts->use_tabline) {
fillchar = ' '; fillchar = schar_from_ascii(' ');
} else { } else {
if (fillchar == 0) { if (fillchar == 0) {
if (opts->use_winbar) { if (opts->use_winbar) {
@ -2242,16 +2244,8 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
int p_crb_save = wp->w_p_crb; int p_crb_save = wp->w_p_crb;
wp->w_p_crb = false; wp->w_p_crb = false;
int width = build_stl_str_hl(wp, int width = build_stl_str_hl(wp, buf, sizeof(buf), str.data, -1, 0, fillchar, maxwidth,
buf, opts->highlights ? &hltab : NULL, NULL,
sizeof(buf),
str.data,
-1,
0,
fillchar,
maxwidth,
opts->highlights ? &hltab : NULL,
NULL,
statuscol_lnum ? &statuscol : NULL); statuscol_lnum ? &statuscol : NULL);
PUT(result, "width", INTEGER_OBJ(width)); PUT(result, "width", INTEGER_OBJ(width));

View File

@ -971,41 +971,41 @@ typedef struct {
/// Characters from the 'listchars' option. /// Characters from the 'listchars' option.
typedef struct { typedef struct {
int eol; schar_T eol;
int ext; schar_T ext;
int prec; schar_T prec;
int nbsp; schar_T nbsp;
int space; schar_T space;
int tab1; ///< first tab character schar_T tab1; ///< first tab character
int tab2; ///< second tab character schar_T tab2; ///< second tab character
int tab3; ///< third tab character schar_T tab3; ///< third tab character
int lead; schar_T lead;
int trail; schar_T trail;
int *multispace; schar_T *multispace;
int *leadmultispace; schar_T *leadmultispace;
int conceal; schar_T conceal;
} lcs_chars_T; } lcs_chars_T;
/// Characters from the 'fillchars' option. /// Characters from the 'fillchars' option.
typedef struct { typedef struct {
int stl; schar_T stl;
int stlnc; schar_T stlnc;
int wbr; schar_T wbr;
int horiz; schar_T horiz;
int horizup; schar_T horizup;
int horizdown; schar_T horizdown;
int vert; schar_T vert;
int vertleft; schar_T vertleft;
int vertright; schar_T vertright;
int verthoriz; schar_T verthoriz;
int fold; schar_T fold;
int foldopen; ///< when fold is open schar_T foldopen; ///< when fold is open
int foldclosed; ///< when fold is closed schar_T foldclosed; ///< when fold is closed
int foldsep; ///< continuous fold marker schar_T foldsep; ///< continuous fold marker
int diff; schar_T diff;
int msgsep; schar_T msgsep;
int eob; schar_T eob;
int lastline; schar_T lastline;
} fcs_chars_T; } fcs_chars_T;
/// Structure which contains all information that belongs to a window. /// Structure which contains all information that belongs to a window.

View File

@ -508,7 +508,7 @@ static void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int m
} }
} }
int fillchar = fillchar_status(&attr, curwin); schar_T fillchar = fillchar_status(&attr, curwin);
if (first_match == 0) { if (first_match == 0) {
*buf = NUL; *buf = NUL;

View File

@ -64,6 +64,7 @@ typedef struct {
colnr_T vcol; ///< virtual column, before wrapping colnr_T vcol; ///< virtual column, before wrapping
int col; ///< visual column on screen, after wrapping int col; ///< visual column on screen, after wrapping
int boguscols; ///< nonexistent columns added to "col" to force wrapping int boguscols; ///< nonexistent columns added to "col" to force wrapping
int old_boguscols; ///< bogus boguscols
int vcol_off; ///< offset for concealed characters int vcol_off; ///< offset for concealed characters
int off; ///< offset relative start of line int off; ///< offset relative start of line
@ -83,10 +84,10 @@ typedef struct {
int n_extra; ///< number of extra bytes int n_extra; ///< number of extra bytes
int n_attr; ///< chars with special attr int n_attr; ///< chars with special attr
char *p_extra; ///< string of extra chars, plus NUL, only used char *p_extra; ///< string of extra chars, plus NUL, only used
///< when c_extra and c_final are NUL ///< when sc_extra and sc_final are NUL
int extra_attr; ///< attributes for p_extra int extra_attr; ///< attributes for p_extra
int c_extra; ///< extra chars, all the same schar_T sc_extra; ///< extra chars, all the same
int c_final; ///< final char, mandatory if set schar_T sc_final; ///< final char, mandatory if set
bool extra_for_extmark; ///< n_extra set for inline virtual text bool extra_for_extmark; ///< n_extra set for inline virtual text
@ -409,9 +410,9 @@ void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, in
int closedcol = MIN(fdc, level); int closedcol = MIN(fdc, level);
for (int i = 0; i < fdc; i++) { for (int i = 0; i < fdc; i++) {
int symbol = 0; schar_T symbol = 0;
if (i >= level) { if (i >= level) {
symbol = ' '; symbol = schar_from_ascii(' ');
} else if (i == closedcol - 1 && closed) { } else if (i == closedcol - 1 && closed) {
symbol = wp->w_p_fcs_chars.foldclosed; symbol = wp->w_p_fcs_chars.foldclosed;
} else if (foldinfo.fi_lnum == lnum && first_level + i >= foldinfo.fi_low_level) { } else if (foldinfo.fi_lnum == lnum && first_level + i >= foldinfo.fi_low_level) {
@ -419,17 +420,17 @@ void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, in
} else if (first_level == 1) { } else if (first_level == 1) {
symbol = wp->w_p_fcs_chars.foldsep; symbol = wp->w_p_fcs_chars.foldsep;
} else if (first_level + i <= 9) { } else if (first_level + i <= 9) {
symbol = '0' + first_level + i; symbol = schar_from_ascii('0' + first_level + i);
} else { } else {
symbol = '>'; symbol = schar_from_ascii('>');
} }
if (out_buffer) { if (out_buffer) {
out_buffer[i] = schar_from_char(symbol); out_buffer[i] = symbol;
} else { } else {
linebuf_vcol[*wlv_off] = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3; linebuf_vcol[*wlv_off] = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3;
linebuf_attr[*wlv_off] = attr; linebuf_attr[*wlv_off] = attr;
linebuf_char[(*wlv_off)++] = schar_from_char(symbol); linebuf_char[(*wlv_off)++] = symbol;
} }
} }
} }
@ -441,7 +442,6 @@ void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, in
static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, int sign_cul_attr) static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, int sign_cul_attr)
{ {
SignTextAttrs sattr = wlv->sattrs[sign_idx]; SignTextAttrs sattr = wlv->sattrs[sign_idx];
wlv->c_final = NUL;
if (sattr.text[0] && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) { if (sattr.text[0] && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
int attr = (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr) int attr = (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr)
@ -453,7 +453,6 @@ static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, i
linebuf_char[sign_pos + 1] = sattr.text[1]; linebuf_char[sign_pos + 1] = sattr.text[1];
} else { } else {
assert(!nrcol); // handled in draw_lnum_col() assert(!nrcol); // handled in draw_lnum_col()
wlv->c_extra = ' ';
int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC); int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
draw_col_fill(wlv, schar_from_ascii(' '), SIGN_WIDTH, attr); draw_col_fill(wlv, schar_from_ascii(' '), SIGN_WIDTH, attr);
} }
@ -680,8 +679,8 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv)
draw_col_fill(wlv, schar_from_ascii(' '), remaining, 0); draw_col_fill(wlv, schar_from_ascii(' '), remaining, 0);
} else if (wlv->filler_todo > 0) { } else if (wlv->filler_todo > 0) {
// Draw "deleted" diff line(s) // Draw "deleted" diff line(s)
int c = (char2cells(wp->w_p_fcs_chars.diff) > 1) ? '-' : wp->w_p_fcs_chars.diff; schar_T c = wp->w_p_fcs_chars.diff;
draw_col_fill(wlv, schar_from_char(c), remaining, win_hl_attr(wp, HLF_DED)); draw_col_fill(wlv, c, remaining, win_hl_attr(wp, HLF_DED));
} }
char *const sbr = get_showbreak_value(wp); char *const sbr = get_showbreak_value(wp);
@ -790,8 +789,8 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
if (wlv->n_extra == 0) { if (wlv->n_extra == 0) {
continue; continue;
} }
wlv->c_extra = NUL; wlv->sc_extra = NUL;
wlv->c_final = NUL; wlv->sc_final = NUL;
wlv->extra_attr = attr; wlv->extra_attr = attr;
wlv->n_attr = mb_charlen(text); wlv->n_attr = mb_charlen(text);
// If the text didn't reach until the first window // If the text didn't reach until the first window
@ -871,6 +870,16 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra)
memset(linebuf_vcol, -1, (size_t)wp->w_grid.cols * sizeof(*linebuf_vcol)); memset(linebuf_vcol, -1, (size_t)wp->w_grid.cols * sizeof(*linebuf_vcol));
} }
static void fix_for_boguscols(winlinevars_T *wlv)
{
wlv->n_extra += wlv->vcol_off;
wlv->vcol -= wlv->vcol_off;
wlv->vcol_off = 0;
wlv->col -= wlv->boguscols;
wlv->old_boguscols = wlv->boguscols;
wlv->boguscols = 0;
}
/// Display line "lnum" of window "wp" on the screen. /// Display line "lnum" of window "wp" on the screen.
/// wp->w_virtcol needs to be valid. /// wp->w_virtcol needs to be valid.
/// ///
@ -932,7 +941,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
int multi_attr = 0; // attributes desired by multibyte int multi_attr = 0; // attributes desired by multibyte
int mb_l = 1; // multi-byte byte length int mb_l = 1; // multi-byte byte length
int mb_c = 0; // decoded multi-byte character int mb_c = 0; // decoded multi-byte character
schar_T mb_schar; // complete screen char schar_T mb_schar = 0; // complete screen char
int change_start = MAXCOL; // first col of changed area int change_start = MAXCOL; // first col of changed area
int change_end = -1; // last col of changed area int change_end = -1; // last col of changed area
bool in_multispace = false; // in multiple consecutive spaces bool in_multispace = false; // in multiple consecutive spaces
@ -971,17 +980,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
int conceal_attr = win_hl_attr(wp, HLF_CONCEAL); int conceal_attr = win_hl_attr(wp, HLF_CONCEAL);
bool is_concealing = false; bool is_concealing = false;
bool did_wcol = false; bool did_wcol = false;
int old_boguscols = 0;
#define vcol_hlc(wlv) ((wlv).vcol - (wlv).vcol_off) #define vcol_hlc(wlv) ((wlv).vcol - (wlv).vcol_off)
#define FIX_FOR_BOGUSCOLS \
{ \
wlv.n_extra += wlv.vcol_off; \
wlv.vcol -= wlv.vcol_off; \
wlv.vcol_off = 0; \
wlv.col -= wlv.boguscols; \
old_boguscols = wlv.boguscols; \
wlv.boguscols = 0; \
}
assert(startrow < endrow); assert(startrow < endrow);
@ -994,6 +993,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.fromcol = -10; wlv.fromcol = -10;
wlv.tocol = MAXCOL; wlv.tocol = MAXCOL;
wlv.vcol_sbr = -1; wlv.vcol_sbr = -1;
wlv.old_boguscols = 0;
buf_T *buf = wp->w_buffer; buf_T *buf = wp->w_buffer;
const bool end_fill = (lnum == buf->b_ml.ml_line_count + 1); const bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
@ -1276,8 +1276,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
colnr_T trailcol = MAXCOL; // start of trailing spaces colnr_T trailcol = MAXCOL; // start of trailing spaces
colnr_T leadcol = 0; // start of leading spaces colnr_T leadcol = 0; // start of leading spaces
int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used bool lcs_eol_todo = true; // need to keep track of this even if lcs_eol is NUL
int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used const schar_T lcs_eol = wp->w_p_lcs_chars.eol; // 'eol' value
schar_T lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used, then NUL
if (wp->w_p_list && !has_fold && !end_fill) { if (wp->w_p_list && !has_fold && !end_fill) {
if (wp->w_p_lcs_chars.space if (wp->w_p_lcs_chars.space
@ -1644,7 +1645,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// When another match, have to check for start again. // When another match, have to check for start again.
v = ptr - line; v = ptr - line;
search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl, search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl,
&has_match_conc, &match_conc, lcs_eol_one, &has_match_conc, &match_conc, lcs_eol_todo,
&on_last_col, &search_attr_from_match); &on_last_col, &search_attr_from_match);
ptr = line + v; // "line" may have been changed ptr = line + v; // "line" may have been changed
@ -1715,8 +1716,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (wlv.p_extra != buf_fold) { if (wlv.p_extra != buf_fold) {
foldtext_free = wlv.p_extra; foldtext_free = wlv.p_extra;
} }
wlv.c_extra = NUL; wlv.sc_extra = NUL;
wlv.c_final = NUL; wlv.sc_final = NUL;
wlv.p_extra[wlv.n_extra] = NUL; wlv.p_extra[wlv.n_extra] = NUL;
// Get the line again as evaluating 'foldtext' may free it. // Get the line again as evaluating 'foldtext' may free it.
@ -1726,8 +1727,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if (draw_folded && wlv.n_extra == 0 && wlv.col < grid->cols) { if (draw_folded && wlv.n_extra == 0 && wlv.col < grid->cols) {
// Fill rest of line with 'fold'. // Fill rest of line with 'fold'.
wlv.c_extra = wp->w_p_fcs_chars.fold; wlv.sc_extra = wp->w_p_fcs_chars.fold;
wlv.c_final = NUL; wlv.sc_final = NUL;
wlv.n_extra = grid->cols - wlv.col; wlv.n_extra = grid->cols - wlv.col;
} }
@ -1740,15 +1741,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// //
// The "p_extra" points to the extra stuff that is inserted to // The "p_extra" points to the extra stuff that is inserted to
// represent special characters (non-printable stuff) and other // represent special characters (non-printable stuff) and other
// things. When all characters are the same, c_extra is used. // things. When all characters are the same, sc_extra is used.
// If c_final is set, it will compulsorily be used at the end. // If sc_final is set, it will compulsorily be used at the end.
// "p_extra" must end in a NUL to avoid utfc_ptr2len() reads past // "p_extra" must end in a NUL to avoid utfc_ptr2len() reads past
// "p_extra[n_extra]". // "p_extra[n_extra]".
// For the '$' of the 'list' option, n_extra == 1, p_extra == "". // For the '$' of the 'list' option, n_extra == 1, p_extra == "".
if (wlv.n_extra > 0) { if (wlv.n_extra > 0) {
if (wlv.c_extra != NUL || (wlv.n_extra == 1 && wlv.c_final != NUL)) { if (wlv.sc_extra != NUL || (wlv.n_extra == 1 && wlv.sc_final != NUL)) {
mb_c = (wlv.n_extra == 1 && wlv.c_final != NUL) ? wlv.c_final : wlv.c_extra; mb_schar = (wlv.n_extra == 1 && wlv.sc_final != NUL) ? wlv.sc_final : wlv.sc_extra;
mb_schar = schar_from_char(mb_c); mb_c = schar_get_first_codepoint(mb_schar);
wlv.n_extra--; wlv.n_extra--;
} else { } else {
assert(wlv.p_extra != NULL); assert(wlv.p_extra != NULL);
@ -1809,7 +1810,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
mb_schar = schar_from_ascii(' '); mb_schar = schar_from_ascii(' ');
} else if (has_fold) { } else if (has_fold) {
// skip writing the buffer line itself // skip writing the buffer line itself
mb_c = NUL; mb_schar = NUL;
} else { } else {
char *prev_ptr = ptr; char *prev_ptr = ptr;
@ -1844,8 +1845,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
mb_c = mb_ptr2char_adv((const char **)&wlv.p_extra); mb_c = mb_ptr2char_adv((const char **)&wlv.p_extra);
mb_schar = schar_from_char(mb_c); mb_schar = schar_from_char(mb_c);
wlv.n_extra = (int)strlen(wlv.p_extra); wlv.n_extra = (int)strlen(wlv.p_extra);
wlv.c_extra = NUL; wlv.sc_extra = NUL;
wlv.c_final = NUL; wlv.sc_final = NUL;
if (area_attr == 0 && search_attr == 0) { if (area_attr == 0 && search_attr == 0) {
wlv.n_attr = wlv.n_extra + 1; wlv.n_attr = wlv.n_extra + 1;
wlv.extra_attr = win_hl_attr(wp, HLF_8); wlv.extra_attr = win_hl_attr(wp, HLF_8);
@ -1858,9 +1859,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// last column; the character is displayed at the start of the // last column; the character is displayed at the start of the
// next line. // next line.
if (wlv.col >= grid->cols - 1 && utf_char2cells(mb_c) == 2) { if (wlv.col >= grid->cols - 1 && utf_char2cells(mb_c) == 2) {
mb_schar = schar_from_ascii('>');
mb_c = '>'; mb_c = '>';
mb_l = 1; mb_l = 1;
mb_schar = schar_from_ascii(mb_c);
multi_attr = win_hl_attr(wp, HLF_AT); multi_attr = win_hl_attr(wp, HLF_AT);
// Put pointer back so that the character will be // Put pointer back so that the character will be
// displayed at the start of the next line. // displayed at the start of the next line.
@ -1874,11 +1875,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// the first column. Don't do this for unprintable characters. // the first column. Don't do this for unprintable characters.
if (wlv.skip_cells > 0 && mb_l > 1 && wlv.n_extra == 0) { if (wlv.skip_cells > 0 && mb_l > 1 && wlv.n_extra == 0) {
wlv.n_extra = 1; wlv.n_extra = 1;
wlv.c_extra = MB_FILLER_CHAR; wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR);
wlv.c_final = NUL; wlv.sc_final = NUL;
mb_schar = schar_from_ascii(' ');
mb_c = ' '; mb_c = ' ';
mb_l = 1; mb_l = 1;
mb_schar = schar_from_ascii(mb_c);
if (area_attr == 0 && search_attr == 0) { if (area_attr == 0 && search_attr == 0) {
wlv.n_attr = wlv.n_extra + 1; wlv.n_attr = wlv.n_extra + 1;
wlv.extra_attr = win_hl_attr(wp, HLF_AT); wlv.extra_attr = win_hl_attr(wp, HLF_AT);
@ -1922,7 +1923,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// no concealing past the end of the line, it interferes // no concealing past the end of the line, it interferes
// with line highlighting. // with line highlighting.
syntax_flags = (mb_c == 0) ? 0 : get_syntax_info(&syntax_seqnr); syntax_flags = (mb_schar == 0) ? 0 : get_syntax_info(&syntax_seqnr);
} }
if (has_decor && v > 0) { if (has_decor && v > 0) {
@ -1957,7 +1958,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
spell_attr = 0; spell_attr = 0;
// do not calculate cap_col at the end of the line or when // do not calculate cap_col at the end of the line or when
// only white space is following // only white space is following
if (mb_c != 0 && (*skipwhite(prev_ptr) != NUL) && can_spell) { if (mb_schar != 0 && (*skipwhite(prev_ptr) != NUL) && can_spell) {
char *p; char *p;
hlf_T spell_hlf = HLF_COUNT; hlf_T spell_hlf = HLF_COUNT;
v -= mb_l - 1; v -= mb_l - 1;
@ -2031,7 +2032,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// //
// So only allow to linebreak, once we have found chars not in // So only allow to linebreak, once we have found chars not in
// 'breakat' in the line. // 'breakat' in the line.
if (wp->w_p_lbr && !wlv.need_lbr && mb_c != NUL if (wp->w_p_lbr && !wlv.need_lbr && mb_schar != NUL
&& !vim_isbreak((uint8_t)(*ptr))) { && !vim_isbreak((uint8_t)(*ptr))) {
wlv.need_lbr = true; wlv.need_lbr = true;
} }
@ -2059,12 +2060,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.n_extra = tabstop_padding(wlv.vcol, wp->w_buffer->b_p_ts, wlv.n_extra = tabstop_padding(wlv.vcol, wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array) - 1; wp->w_buffer->b_p_vts_array) - 1;
} }
wlv.c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; wlv.sc_extra = schar_from_ascii(mb_off > 0 ? MB_FILLER_CHAR : ' ');
wlv.c_final = NUL; wlv.sc_final = NUL;
if (mb_c < 128 && ascii_iswhite(mb_c)) { if (mb_c < 128 && ascii_iswhite(mb_c)) {
if (mb_c == TAB) { if (mb_c == TAB) {
// See "Tab alignment" below. // See "Tab alignment" below.
FIX_FOR_BOGUSCOLS; fix_for_boguscols(&wlv);
} }
if (!wp->w_p_list) { if (!wp->w_p_list) {
mb_c = ' '; mb_c = ' ';
@ -2093,39 +2094,39 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& ptr - line >= leadcol && ptr - line >= leadcol
&& ptr - line <= trailcol))) { && ptr - line <= trailcol))) {
if (in_multispace && wp->w_p_lcs_chars.multispace != NULL) { if (in_multispace && wp->w_p_lcs_chars.multispace != NULL) {
mb_c = wp->w_p_lcs_chars.multispace[multispace_pos++]; mb_schar = wp->w_p_lcs_chars.multispace[multispace_pos++];
if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) { if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
multispace_pos = 0; multispace_pos = 0;
} }
} else { } else {
mb_c = (mb_c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp; mb_schar = (mb_c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
} }
wlv.n_attr = 1; wlv.n_attr = 1;
wlv.extra_attr = win_hl_attr(wp, HLF_0); wlv.extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = wlv.char_attr; // save current attr saved_attr2 = wlv.char_attr; // save current attr
mb_schar = schar_from_char(mb_c); mb_c = schar_get_first_codepoint(mb_schar);
} }
if (mb_c == ' ' && mb_l == 1 && ((trailcol != MAXCOL && ptr > line + trailcol) if (mb_c == ' ' && mb_l == 1 && ((trailcol != MAXCOL && ptr > line + trailcol)
|| (leadcol != 0 && ptr < line + leadcol))) { || (leadcol != 0 && ptr < line + leadcol))) {
if (leadcol != 0 && in_multispace && ptr < line + leadcol if (leadcol != 0 && in_multispace && ptr < line + leadcol
&& wp->w_p_lcs_chars.leadmultispace != NULL) { && wp->w_p_lcs_chars.leadmultispace != NULL) {
mb_c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++]; mb_schar = wp->w_p_lcs_chars.leadmultispace[multispace_pos++];
if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) { if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
multispace_pos = 0; multispace_pos = 0;
} }
} else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) { } else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) {
mb_c = wp->w_p_lcs_chars.trail; mb_schar = wp->w_p_lcs_chars.trail;
} else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) { } else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) {
mb_c = wp->w_p_lcs_chars.lead; mb_schar = wp->w_p_lcs_chars.lead;
} else if (leadcol != 0 && wp->w_p_lcs_chars.space) { } else if (leadcol != 0 && wp->w_p_lcs_chars.space) {
mb_c = wp->w_p_lcs_chars.space; mb_schar = wp->w_p_lcs_chars.space;
} }
wlv.n_attr = 1; wlv.n_attr = 1;
wlv.extra_attr = win_hl_attr(wp, HLF_0); wlv.extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = wlv.char_attr; // save current attr saved_attr2 = wlv.char_attr; // save current attr
mb_schar = schar_from_char(mb_c); mb_c = schar_get_first_codepoint(mb_schar);
} }
} }
@ -2157,8 +2158,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// there are characters to conceal // there are characters to conceal
tab_len += wlv.vcol_off; tab_len += wlv.vcol_off;
} }
// boguscols before FIX_FOR_BOGUSCOLS macro from above. // boguscols before fix_for_boguscols() from above.
if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0 if (wp->w_p_lcs_chars.tab1 && wlv.old_boguscols > 0
&& wlv.n_extra > tab_len) { && wlv.n_extra > tab_len) {
tab_len += wlv.n_extra - tab_len; tab_len += wlv.n_extra - tab_len;
} }
@ -2167,35 +2168,35 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// If wlv.n_extra > 0, it gives the number of chars // If wlv.n_extra > 0, it gives the number of chars
// to use for a tab, else we need to calculate the // to use for a tab, else we need to calculate the
// width for a tab. // width for a tab.
int tab2_len = utf_char2len(wp->w_p_lcs_chars.tab2); size_t tab2_len = schar_len(wp->w_p_lcs_chars.tab2);
int len = tab_len * tab2_len; size_t len = (size_t)tab_len * tab2_len;
if (wp->w_p_lcs_chars.tab3) { if (wp->w_p_lcs_chars.tab3) {
len += utf_char2len(wp->w_p_lcs_chars.tab3) - tab2_len; len += schar_len(wp->w_p_lcs_chars.tab3) - tab2_len;
} }
if (wlv.n_extra > 0) { if (wlv.n_extra > 0) {
len += wlv.n_extra - tab_len; len += (size_t)(wlv.n_extra - tab_len);
} }
mb_c = wp->w_p_lcs_chars.tab1; mb_schar = wp->w_p_lcs_chars.tab1;
char *p = get_extra_buf((size_t)len + 1); mb_c = schar_get_first_codepoint(mb_schar);
memset(p, ' ', (size_t)len); char *p = get_extra_buf(len + 1);
p[len] = NUL; memset(p, ' ', len);
wlv.p_extra = p; wlv.p_extra = p;
for (int i = 0; i < tab_len; i++) { for (int i = 0; i < tab_len; i++) {
if (*p == NUL) { if (*p == NUL) {
tab_len = i; tab_len = i;
break; break;
} }
int lcs = wp->w_p_lcs_chars.tab2; schar_T lcs = wp->w_p_lcs_chars.tab2;
// if tab3 is given, use it for the last char // if tab3 is given, use it for the last char
if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
lcs = wp->w_p_lcs_chars.tab3; lcs = wp->w_p_lcs_chars.tab3;
} }
p += utf_char2bytes(lcs, p); size_t slen = schar_get_adv(&p, lcs);
wlv.n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0); wlv.n_extra += (int)slen - (saved_nextra > 0 ? 1 : 0);
} }
// n_extra will be increased by FIX_FOX_BOGUSCOLS // n_extra will be increased by fix_for_boguscols()
// macro below, so need to adjust for that here // macro below, so need to adjust for that here
if (wlv.vcol_off > 0) { if (wlv.vcol_off > 0) {
wlv.n_extra -= wlv.vcol_off; wlv.n_extra -= wlv.vcol_off;
@ -2212,7 +2213,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// vcol_off and boguscols accumulated so far in the // vcol_off and boguscols accumulated so far in the
// line. Note that the tab can be longer than // line. Note that the tab can be longer than
// 'tabstop' when there are concealed characters. // 'tabstop' when there are concealed characters.
FIX_FOR_BOGUSCOLS; fix_for_boguscols(&wlv);
// Make sure, the highlighting for the tab char will be // Make sure, the highlighting for the tab char will be
// correctly set further below (effectively reverts the // correctly set further below (effectively reverts the
@ -2224,24 +2225,24 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
} }
if (wp->w_p_list) { if (wp->w_p_list) {
mb_c = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3) mb_schar = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3)
? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1; ? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1;
if (wp->w_p_lbr && wlv.p_extra != NULL && *wlv.p_extra != NUL) { if (wp->w_p_lbr && wlv.p_extra != NULL && *wlv.p_extra != NUL) {
wlv.c_extra = NUL; // using p_extra from above wlv.sc_extra = NUL; // using p_extra from above
} else { } else {
wlv.c_extra = wp->w_p_lcs_chars.tab2; wlv.sc_extra = wp->w_p_lcs_chars.tab2;
} }
wlv.c_final = wp->w_p_lcs_chars.tab3; wlv.sc_final = wp->w_p_lcs_chars.tab3;
wlv.n_attr = tab_len + 1; wlv.n_attr = tab_len + 1;
wlv.extra_attr = win_hl_attr(wp, HLF_0); wlv.extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = wlv.char_attr; // save current attr saved_attr2 = wlv.char_attr; // save current attr
} else { } else {
wlv.c_final = NUL; wlv.sc_final = NUL;
wlv.c_extra = ' '; wlv.sc_extra = schar_from_ascii(' ');
mb_c = ' '; mb_schar = schar_from_ascii(' ');
} }
mb_schar = schar_from_char(mb_c); mb_c = schar_get_first_codepoint(mb_schar);
} else if (mb_c == NUL } else if (mb_schar == NUL
&& (wp->w_p_list && (wp->w_p_list
|| ((wlv.fromcol >= 0 || fromcol_prev >= 0) || ((wlv.fromcol >= 0 || fromcol_prev >= 0)
&& wlv.tocol > wlv.vcol && wlv.tocol > wlv.vcol
@ -2250,7 +2251,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& !(noinvcur && !(noinvcur
&& lnum == wp->w_cursor.lnum && lnum == wp->w_cursor.lnum
&& wlv.vcol == wp->w_virtcol))) && wlv.vcol == wp->w_virtcol)))
&& lcs_eol_one > 0) { && lcs_eol_todo && lcs_eol != NUL) {
// Display a '$' after the line or highlight an extra // Display a '$' after the line or highlight an extra
// character if the line break is included. // character if the line break is included.
// For a diff line the highlighting continues after the "$". // For a diff line the highlighting continues after the "$".
@ -2265,16 +2266,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.n_extra = 0; wlv.n_extra = 0;
} }
if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) { if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
mb_c = wp->w_p_lcs_chars.eol; mb_schar = wp->w_p_lcs_chars.eol;
} else { } else {
mb_c = ' '; mb_schar = schar_from_ascii(' ');
} }
lcs_eol_one = -1; lcs_eol_todo = false;
ptr--; // put it back at the NUL ptr--; // put it back at the NUL
wlv.extra_attr = win_hl_attr(wp, HLF_AT); wlv.extra_attr = win_hl_attr(wp, HLF_AT);
wlv.n_attr = 1; wlv.n_attr = 1;
mb_schar = schar_from_char(mb_c); mb_c = schar_get_first_codepoint(mb_schar);
} else if (mb_c != NUL) { } else if (mb_schar != NUL) {
wlv.p_extra = transchar_buf(wp->w_buffer, mb_c); wlv.p_extra = transchar_buf(wp->w_buffer, mb_c);
if (wlv.n_extra == 0) { if (wlv.n_extra == 0) {
wlv.n_extra = byte2cells(mb_c) - 1; wlv.n_extra = byte2cells(mb_c) - 1;
@ -2282,8 +2283,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
if ((dy_flags & DY_UHEX) && wp->w_p_rl) { if ((dy_flags & DY_UHEX) && wp->w_p_rl) {
rl_mirror_ascii(wlv.p_extra, NULL); // reverse "<12>" rl_mirror_ascii(wlv.p_extra, NULL); // reverse "<12>"
} }
wlv.c_extra = NUL; wlv.sc_extra = NUL;
wlv.c_final = NUL; wlv.sc_final = NUL;
if (wp->w_p_lbr) { if (wp->w_p_lbr) {
mb_c = (uint8_t)(*wlv.p_extra); mb_c = (uint8_t)(*wlv.p_extra);
char *p = get_extra_buf((size_t)wlv.n_extra + 1); char *p = get_extra_buf((size_t)wlv.n_extra + 1);
@ -2316,7 +2317,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0) && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0)
&& !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
wlv.char_attr = conceal_attr; wlv.char_attr = conceal_attr;
bool is_conceal_char = false;
if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0) if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)
|| has_match_conc > 1 || decor_conceal > 1) || has_match_conc > 1 || decor_conceal > 1)
&& (syn_get_sub_char() != NUL && (syn_get_sub_char() != NUL
@ -2327,21 +2327,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// First time at this concealed item: display one // First time at this concealed item: display one
// character. // character.
if (has_match_conc && match_conc) { if (has_match_conc && match_conc) {
mb_c = match_conc; mb_schar = schar_from_char(match_conc);
} else if (decor_conceal && decor_state.conceal_char) { } else if (decor_conceal && decor_state.conceal_char) {
mb_schar = decor_state.conceal_char; mb_schar = decor_state.conceal_char;
mb_c = schar_get_first_codepoint(mb_schar);
is_conceal_char = true;
if (decor_state.conceal_attr) { if (decor_state.conceal_attr) {
wlv.char_attr = decor_state.conceal_attr; wlv.char_attr = decor_state.conceal_attr;
} }
} else if (syn_get_sub_char() != NUL) { } else if (syn_get_sub_char() != NUL) {
mb_c = syn_get_sub_char(); mb_schar = schar_from_char(syn_get_sub_char());
} else if (wp->w_p_lcs_chars.conceal != NUL) { } else if (wp->w_p_lcs_chars.conceal != NUL) {
mb_c = wp->w_p_lcs_chars.conceal; mb_schar = wp->w_p_lcs_chars.conceal;
} else { } else {
mb_c = ' '; mb_schar = schar_from_ascii(' ');
} }
mb_c = schar_get_first_codepoint(mb_schar);
prev_syntax_id = syntax_seqnr; prev_syntax_id = syntax_seqnr;
@ -2359,9 +2358,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
is_concealing = true; is_concealing = true;
wlv.skip_cells = 1; wlv.skip_cells = 1;
} }
if (!is_conceal_char) {
mb_schar = schar_from_char(mb_c);
}
} else { } else {
prev_syntax_id = 0; prev_syntax_id = 0;
is_concealing = false; is_concealing = false;
@ -2403,26 +2399,26 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& wp->w_p_list && wp->w_p_list
&& (wp->w_p_wrap ? (wp->w_skipcol > 0 && wlv.row == 0) : wp->w_leftcol > 0) && (wp->w_p_wrap ? (wp->w_skipcol > 0 && wlv.row == 0) : wp->w_leftcol > 0)
&& wlv.filler_todo <= 0 && wlv.filler_todo <= 0
&& mb_c != NUL) { && mb_schar != NUL) {
mb_c = wp->w_p_lcs_chars.prec; mb_schar = wp->w_p_lcs_chars.prec;
lcs_prec_todo = NUL; lcs_prec_todo = NUL;
if (utf_char2cells(mb_c) > 1) { if (utf_char2cells(mb_c) > 1) {
// Double-width character being overwritten by the "precedes" // Double-width character being overwritten by the "precedes"
// character, need to fill up half the character. // character, need to fill up half the character.
wlv.c_extra = MB_FILLER_CHAR; wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR);
wlv.c_final = NUL; wlv.sc_final = NUL;
wlv.n_extra = 1; wlv.n_extra = 1;
wlv.n_attr = 2; wlv.n_attr = 2;
wlv.extra_attr = win_hl_attr(wp, HLF_AT); wlv.extra_attr = win_hl_attr(wp, HLF_AT);
} }
mb_schar = schar_from_char(mb_c); mb_c = schar_get_first_codepoint(mb_schar);
saved_attr3 = wlv.char_attr; // save current attr saved_attr3 = wlv.char_attr; // save current attr
wlv.char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr wlv.char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr
n_attr3 = 1; n_attr3 = 1;
} }
// At end of the text line or just after the last character. // At end of the text line or just after the last character.
if (mb_c == NUL && eol_hl_off == 0) { if (mb_schar == NUL && eol_hl_off == 0) {
// flag to indicate whether prevcol equals startcol of search_hl or // flag to indicate whether prevcol equals startcol of search_hl or
// one of the matches // one of the matches
bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl, bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl,
@ -2432,7 +2428,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// highlight match at end of line. If it's beyond the last // highlight match at end of line. If it's beyond the last
// char on the screen, just overwrite that one (tricky!) Not // char on the screen, just overwrite that one (tricky!) Not
// needed when a '$' was displayed for 'list'. // needed when a '$' was displayed for 'list'.
if (wp->w_p_lcs_chars.eol == lcs_eol_one if (lcs_eol_todo
&& ((area_attr != 0 && wlv.vcol == wlv.fromcol && ((area_attr != 0 && wlv.vcol == wlv.fromcol
&& (VIsual_mode != Ctrl_V && (VIsual_mode != Ctrl_V
|| lnum == VIsual.lnum || lnum == VIsual.lnum
@ -2476,7 +2472,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
} }
// At end of the text line. // At end of the text line.
if (mb_c == NUL) { if (mb_schar == NUL) {
// Highlight 'cursorcolumn' & 'colorcolumn' past end of the line. // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
if (wp->w_p_wrap) { if (wp->w_p_wrap) {
v = wlv.startrow == 0 ? wp->w_skipcol : 0; v = wlv.startrow == 0 ? wp->w_skipcol : 0;
@ -2498,8 +2494,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
bool has_virttext = false; bool has_virttext = false;
// Make sure alignment is the same regardless // Make sure alignment is the same regardless
// if listchars=eol:X is used or not. // if listchars=eol:X is used or not.
int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0 int eol_skip = (lcs_eol_todo && eol_hl_off == 0 ? 1 : 0);
? 1 : 0);
if (has_decor) { if (has_decor) {
has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip);
@ -2601,7 +2596,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// When the window is too narrow draw all "@" lines. // When the window is too narrow draw all "@" lines.
if (leftcols_width >= wp->w_grid.cols && wp->w_p_wrap) { if (leftcols_width >= wp->w_grid.cols && wp->w_p_wrap) {
win_draw_end(wp, '@', ' ', true, wlv.row, wp->w_grid.rows, HLF_AT); win_draw_end(wp, schar_from_ascii('@'), schar_from_ascii(' '), true, wlv.row,
wp->w_grid.rows, HLF_AT);
set_empty_rows(wp, wlv.row); set_empty_rows(wp, wlv.row);
wlv.row = endrow; wlv.row = endrow;
} }
@ -2617,17 +2613,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& wlv.filler_todo <= 0 && wlv.filler_todo <= 0
&& wlv.col == grid->cols - 1 && wlv.col == grid->cols - 1
&& !has_fold) { && !has_fold) {
if (has_decor && *ptr == NUL && lcs_eol_one == 0) { if (has_decor && *ptr == NUL && lcs_eol == 0 && lcs_eol_todo) {
// Tricky: there might be a virtual text just _after_ the last char // Tricky: there might be a virtual text just _after_ the last char
decor_redraw_col(wp, (colnr_T)(ptr - line), wlv.off, false, &decor_state); decor_redraw_col(wp, (colnr_T)(ptr - line), wlv.off, false, &decor_state);
} }
if (*ptr != NUL if (*ptr != NUL
|| lcs_eol_one > 0 || (lcs_eol > 0 && lcs_eol_todo)
|| (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)) || (wlv.n_extra > 0 && (wlv.sc_extra != NUL || *wlv.p_extra != NUL))
|| has_more_inline_virt(&wlv, ptr - line)) { || has_more_inline_virt(&wlv, ptr - line)) {
mb_c = wp->w_p_lcs_chars.ext; mb_schar = wp->w_p_lcs_chars.ext;
wlv.char_attr = win_hl_attr(wp, HLF_AT); wlv.char_attr = win_hl_attr(wp, HLF_AT);
mb_schar = schar_from_char(mb_c); mb_c = schar_get_first_codepoint(mb_schar);
} }
} }
@ -2784,11 +2780,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
|| wlv.filler_todo > 0 || wlv.filler_todo > 0
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
&& wlv.p_extra != at_end_str) && wlv.p_extra != at_end_str)
|| (wlv.n_extra != 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)) || (wlv.n_extra != 0 && (wlv.sc_extra != NUL || *wlv.p_extra != NUL))
|| has_more_inline_virt(&wlv, ptr - line))) { || has_more_inline_virt(&wlv, ptr - line))) {
bool wrap = wp->w_p_wrap // Wrapping enabled. bool wrap = wp->w_p_wrap // Wrapping enabled.
&& wlv.filler_todo <= 0 // Not drawing diff filler lines. && wlv.filler_todo <= 0 // Not drawing diff filler lines.
&& lcs_eol_one != -1 // Haven't printed the lcs_eol character. && lcs_eol_todo // Haven't printed the lcs_eol character.
&& wlv.row != endrow - 1 // Not the last line being displayed. && wlv.row != endrow - 1 // Not the last line being displayed.
&& (grid->cols == Columns // Window spans the width of the screen, && (grid->cols == Columns // Window spans the width of the screen,
|| ui_has(kUIMultigrid)) // or has dedicated grid. || ui_has(kUIMultigrid)) // or has dedicated grid.
@ -2819,13 +2815,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// When not wrapping and finished diff lines, or when displayed // When not wrapping and finished diff lines, or when displayed
// '$' and highlighting until last column, break here. // '$' and highlighting until last column, break here.
if ((!wp->w_p_wrap && wlv.filler_todo <= 0) || lcs_eol_one == -1) { if ((!wp->w_p_wrap && wlv.filler_todo <= 0) || !lcs_eol_todo) {
break; break;
} }
// When the window is too narrow draw all "@" lines. // When the window is too narrow draw all "@" lines.
if (wlv.col <= leftcols_width) { if (wlv.col <= leftcols_width) {
win_draw_end(wp, '@', ' ', true, wlv.row, wp->w_grid.rows, HLF_AT); win_draw_end(wp, schar_from_ascii('@'), schar_from_ascii(' '), true, wlv.row,
wp->w_grid.rows, HLF_AT);
set_empty_rows(wp, wlv.row); set_empty_rows(wp, wlv.row);
wlv.row = endrow; wlv.row = endrow;
} }

View File

@ -546,7 +546,7 @@ int update_screen(void)
// might need to clear space on default_grid for the message area. // might need to clear space on default_grid for the message area.
if (type == UPD_NOT_VALID && clear_cmdline && !ui_has(kUIMessages)) { if (type == UPD_NOT_VALID && clear_cmdline && !ui_has(kUIMessages)) {
grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0); grid_clear(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, 0);
} }
ui_comp_set_screen_valid(true); ui_comp_set_screen_valid(true);
@ -1297,9 +1297,8 @@ static void draw_vsep_win(win_T *wp)
// draw the vertical separator right of this window // draw the vertical separator right of this window
int hl = win_hl_attr(wp, HLF_C); int hl = win_hl_attr(wp, HLF_C);
int c = wp->w_p_fcs_chars.vert; schar_T c = wp->w_p_fcs_chars.vert;
grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, c, hl);
W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
} }
/// Draw the horizontal separator below window "wp" /// Draw the horizontal separator below window "wp"
@ -1311,9 +1310,8 @@ static void draw_hsep_win(win_T *wp)
// draw the horizontal separator below this window // draw the horizontal separator below this window
int hl = win_hl_attr(wp, HLF_C); int hl = win_hl_attr(wp, HLF_C);
int c = wp->w_p_fcs_chars.horiz; schar_T c = wp->w_p_fcs_chars.horiz;
grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, wp->w_wincol, W_ENDCOL(wp), c, c, hl);
wp->w_wincol, W_ENDCOL(wp), c, c, hl);
} }
/// Get the separator connector for specified window corner of window "wp" /// Get the separator connector for specified window corner of window "wp"
@ -1321,21 +1319,19 @@ static schar_T get_corner_sep_connector(win_T *wp, WindowCorner corner)
{ {
// It's impossible for windows to be connected neither vertically nor horizontally // It's impossible for windows to be connected neither vertically nor horizontally
// So if they're not vertically connected, assume they're horizontally connected // So if they're not vertically connected, assume they're horizontally connected
int c;
if (vsep_connected(wp, corner)) { if (vsep_connected(wp, corner)) {
if (hsep_connected(wp, corner)) { if (hsep_connected(wp, corner)) {
c = wp->w_p_fcs_chars.verthoriz; return wp->w_p_fcs_chars.verthoriz;
} else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) { } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
c = wp->w_p_fcs_chars.vertright; return wp->w_p_fcs_chars.vertright;
} else { } else {
c = wp->w_p_fcs_chars.vertleft; return wp->w_p_fcs_chars.vertleft;
} }
} else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) { } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
c = wp->w_p_fcs_chars.horizdown; return wp->w_p_fcs_chars.horizdown;
} else { } else {
c = wp->w_p_fcs_chars.horizup; return wp->w_p_fcs_chars.horizup;
} }
return schar_from_char(c);
} }
/// Draw separator connecting characters on the corners of window "wp" /// Draw separator connecting characters on the corners of window "wp"
@ -2384,7 +2380,7 @@ static void win_update(win_T *wp)
// Last line isn't finished: Display "@@@" in the last screen line. // Last line isn't finished: Display "@@@" in the last screen line.
grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr); grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr);
grid_line_fill(3, wp->w_grid.cols, ' ', at_attr); grid_line_fill(3, wp->w_grid.cols, schar_from_ascii(' '), at_attr);
grid_line_flush(); grid_line_flush();
set_empty_rows(wp, srow); set_empty_rows(wp, srow);
wp->w_botline = lnum; wp->w_botline = lnum;
@ -2399,7 +2395,8 @@ static void win_update(win_T *wp)
set_empty_rows(wp, srow); set_empty_rows(wp, srow);
wp->w_botline = lnum; wp->w_botline = lnum;
} else { } else {
win_draw_end(wp, wp->w_p_fcs_chars.lastline, ' ', true, srow, wp->w_grid.rows, HLF_AT); win_draw_end(wp, wp->w_p_fcs_chars.lastline, schar_from_ascii(' '), true, srow,
wp->w_grid.rows, HLF_AT);
set_empty_rows(wp, srow); set_empty_rows(wp, srow);
wp->w_botline = lnum; wp->w_botline = lnum;
} }
@ -2432,7 +2429,8 @@ static void win_update(win_T *wp)
lastline = 0; lastline = 0;
} }
win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, MAX(lastline, row), wp->w_grid.rows, win_draw_end(wp, wp->w_p_fcs_chars.eob, schar_from_ascii(' '), false, MAX(lastline, row),
wp->w_grid.rows,
HLF_EOB); HLF_EOB);
set_empty_rows(wp, row); set_empty_rows(wp, row);
} }
@ -2519,10 +2517,9 @@ void win_scroll_lines(win_T *wp, int row, int line_count)
} }
} }
/// Call grid_fill() with columns adjusted for 'rightleft' if needed. /// Call grid_clear() with columns adjusted for 'rightleft' if needed.
/// Return the new offset. /// Return the new offset.
static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, int endrow, static int win_clear_end(win_T *wp, int off, int width, int row, int endrow, int attr)
int attr)
{ {
int nn = off + width; int nn = off + width;
const int endcol = wp->w_grid.cols; const int endcol = wp->w_grid.cols;
@ -2532,9 +2529,9 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row,
} }
if (wp->w_p_rl) { if (wp->w_p_rl) {
grid_fill(&wp->w_grid, row, endrow, endcol - nn, endcol - off, c1, c2, attr); grid_clear(&wp->w_grid, row, endrow, endcol - nn, endcol - off, attr);
} else { } else {
grid_fill(&wp->w_grid, row, endrow, off, nn, c1, c2, attr); grid_clear(&wp->w_grid, row, endrow, off, nn, attr);
} }
return nn; return nn;
@ -2543,7 +2540,8 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row,
/// Clear lines near the end of the window and mark the unused lines with "c1". /// Clear lines near the end of the window and mark the unused lines with "c1".
/// Use "c2" as filler character. /// Use "c2" as filler character.
/// When "draw_margin" is true, then draw the sign/fold/number columns. /// When "draw_margin" is true, then draw the sign/fold/number columns.
void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl) void win_draw_end(win_T *wp, schar_T c1, schar_T c2, bool draw_margin, int row, int endrow,
hlf_T hl)
{ {
assert(hl >= 0 && hl < HLF_COUNT); assert(hl >= 0 && hl < HLF_COUNT);
int n = 0; int n = 0;
@ -2552,19 +2550,16 @@ void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endr
// draw the fold column // draw the fold column
int fdc = compute_foldcolumn(wp, 0); int fdc = compute_foldcolumn(wp, 0);
if (fdc > 0) { if (fdc > 0) {
n = win_fill_end(wp, ' ', ' ', n, fdc, row, endrow, n = win_clear_end(wp, n, fdc, row, endrow, win_hl_attr(wp, HLF_FC));
win_hl_attr(wp, HLF_FC));
} }
// draw the sign column // draw the sign column
int count = wp->w_scwidth; int count = wp->w_scwidth;
if (count > 0) { if (count > 0) {
n = win_fill_end(wp, ' ', ' ', n, SIGN_WIDTH * count, row, n = win_clear_end(wp, n, SIGN_WIDTH * count, row, endrow, win_hl_attr(wp, HLF_SC));
endrow, win_hl_attr(wp, HLF_SC));
} }
// draw the number column // draw the number column
if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) { if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) == NULL) {
n = win_fill_end(wp, ' ', ' ', n, number_width(wp) + 1, row, endrow, n = win_clear_end(wp, n, number_width(wp) + 1, row, endrow, win_hl_attr(wp, HLF_N));
win_hl_attr(wp, HLF_N));
} }
} }

View File

@ -6881,15 +6881,8 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ScreenGrid *grid; ScreenGrid *grid;
screenchar_adjust(&grid, &row, &col); screenchar_adjust(&grid, &row, &col);
int c; rettv->vval.v_number = (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols)
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { ? -1 : schar_get_first_codepoint(grid_getchar(grid, row, col, NULL));
c = -1;
} else {
char buf[MAX_SCHAR_SIZE + 1];
schar_get(buf, grid_getchar(grid, row, col, NULL));
c = utf_ptr2char(buf);
}
rettv->vval.v_number = c;
} }
/// "screenchars()" function /// "screenchars()" function
@ -8383,7 +8376,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{ {
int syntax_flags = 0; int syntax_flags = 0;
int cchar;
int matchid = 0; int matchid = 0;
char str[NUMBUFLEN]; char str[NUMBUFLEN];
@ -8402,14 +8394,13 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
// get the conceal character // get the conceal character
if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) {
cchar = syn_get_sub_char(); schar_T cchar = schar_from_char(syn_get_sub_char());
if (cchar == NUL && curwin->w_p_cole == 1) { if (cchar == NUL && curwin->w_p_cole == 1) {
cchar = (curwin->w_p_lcs_chars.conceal == NUL) cchar = (curwin->w_p_lcs_chars.conceal == NUL)
? ' ' ? schar_from_ascii(' ') : curwin->w_p_lcs_chars.conceal;
: curwin->w_p_lcs_chars.conceal;
} }
if (cchar != NUL) { if (cchar != NUL) {
utf_char2bytes(cchar, str); schar_get(str, cchar);
} }
} }
} }

View File

@ -27,6 +27,7 @@
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/message.h" #include "nvim/message.h"
#include "nvim/option_vars.h" #include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/types_defs.h" #include "nvim/types_defs.h"
#include "nvim/ui.h" #include "nvim/ui.h"
@ -67,7 +68,7 @@ void grid_adjust(ScreenGrid **grid, int *row_off, int *col_off)
} }
} }
schar_T schar_from_str(char *str) schar_T schar_from_str(const char *str)
{ {
if (str == NULL) { if (str == NULL) {
return 0; return 0;
@ -120,6 +121,13 @@ void schar_cache_clear(void)
{ {
decor_check_invalid_glyphs(); decor_check_invalid_glyphs();
set_clear(glyph, &glyph_cache); set_clear(glyph, &glyph_cache);
// for char options we have stored the original strings. Regenerate
// the parsed schar_T values with the new clean cache.
// This must not return an error as cell widths have not changed.
if (check_chars_options()) {
abort();
}
} }
bool schar_high(schar_T sc) bool schar_high(schar_T sc)
@ -137,15 +145,39 @@ bool schar_high(schar_T sc)
# define schar_idx(sc) (sc >> 8) # define schar_idx(sc) (sc >> 8)
#endif #endif
void schar_get(char *buf_out, schar_T sc) /// sets final NUL
size_t schar_get(char *buf_out, schar_T sc)
{
size_t len = schar_get_adv(&buf_out, sc);
*buf_out = NUL;
return len;
}
/// advance buf_out. do NOT set final NUL
size_t schar_get_adv(char **buf_out, schar_T sc)
{
size_t len;
if (schar_high(sc)) {
uint32_t idx = schar_idx(sc);
assert(idx < glyph_cache.h.n_keys);
len = strlen(&glyph_cache.keys[idx]);
memcpy(*buf_out, &glyph_cache.keys[idx], len);
} else {
len = strnlen((char *)&sc, 4);
memcpy(*buf_out, (char *)&sc, len);
}
*buf_out += len;
return len;
}
size_t schar_len(schar_T sc)
{ {
if (schar_high(sc)) { if (schar_high(sc)) {
uint32_t idx = schar_idx(sc); uint32_t idx = schar_idx(sc);
assert(idx < glyph_cache.h.n_keys); assert(idx < glyph_cache.h.n_keys);
xstrlcpy(buf_out, &glyph_cache.keys[idx], 32); return strlen(&glyph_cache.keys[idx]);
} else { } else {
memcpy(buf_out, (char *)&sc, 4); return strnlen((char *)&sc, 4);
buf_out[4] = NUL;
} }
} }
@ -433,14 +465,13 @@ int grid_line_puts(int col, const char *text, int textlen, int attr)
return col - start_col; return col - start_col;
} }
void grid_line_fill(int start_col, int end_col, int c, int attr) void grid_line_fill(int start_col, int end_col, schar_T sc, int attr)
{ {
end_col = MIN(end_col, grid_line_maxcol); end_col = MIN(end_col, grid_line_maxcol);
if (start_col >= end_col) { if (start_col >= end_col) {
return; return;
} }
schar_T sc = schar_from_char(c);
for (int col = start_col; col < end_col; col++) { for (int col = start_col; col < end_col; col++) {
linebuf_char[col] = sc; linebuf_char[col] = sc;
linebuf_attr[col] = attr; linebuf_attr[col] = attr;
@ -532,11 +563,17 @@ void grid_line_flush_if_valid_row(void)
grid_line_flush(); grid_line_flush();
} }
void grid_clear(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int attr)
{
grid_fill(grid, start_row, end_row, start_col, end_col, schar_from_ascii(' '),
schar_from_ascii(' '), attr);
}
/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col" /// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col"
/// to "end_col" (exclusive) with character "c1" in first column followed by /// to "end_col" (exclusive) with character "c1" in first column followed by
/// "c2" in the other columns. Use attributes "attr". /// "c2" in the other columns. Use attributes "attr".
void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1, void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, schar_T c1,
int c2, int attr) schar_T c2, int attr)
{ {
int row_off = 0; int row_off = 0;
int col_off = 0; int col_off = 0;
@ -582,7 +619,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
} }
int col = start_col; int col = start_col;
schar_T sc = schar_from_char(c1); schar_T sc = c1;
for (col = start_col; col < end_col; col++) { for (col = start_col; col < end_col; col++) {
size_t off = lineoff + (size_t)col; size_t off = lineoff + (size_t)col;
if (grid->chars[off] != sc || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) { if (grid->chars[off] != sc || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) {
@ -595,7 +632,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
} }
grid->vcols[off] = -1; grid->vcols[off] = -1;
if (col == start_col) { if (col == start_col) {
sc = schar_from_char(c2); sc = c2;
} }
} }
if (dirty_last > dirty_first) { if (dirty_last > dirty_first) {
@ -683,7 +720,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol
col++; col++;
} }
if (col <= endcol) { if (col <= endcol) {
grid_fill(grid, row, row + 1, col + coloff, endcol + coloff + 1, ' ', ' ', bg_attr); grid_clear(grid, row, row + 1, col + coloff, endcol + coloff + 1, bg_attr);
} }
} }
col = endcol + 1; col = endcol + 1;

View File

@ -666,7 +666,7 @@ bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char **lin
/// is endcol. /// is endcol.
/// Return the updated search_attr. /// Return the updated search_attr.
int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char **line, match_T *search_hl, int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char **line, match_T *search_hl,
int *has_match_conc, int *match_conc, int lcs_eol_one, bool *on_last_col, int *has_match_conc, int *match_conc, bool lcs_eol_todo, bool *on_last_col,
bool *search_attr_from_match) bool *search_attr_from_match)
{ {
matchitem_T *cur = wp->w_match_head; // points to the match list matchitem_T *cur = wp->w_match_head; // points to the match list
@ -787,7 +787,7 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char **line, match_T
} }
} }
// Only highlight one character after the last column. // Only highlight one character after the last column.
if (*(*line + col) == NUL && (wp->w_p_list && lcs_eol_one == -1)) { if (*(*line + col) == NUL && (wp->w_p_list && !lcs_eol_todo)) {
search_attr = 0; search_attr = 0;
} }
return search_attr; return search_attr;

View File

@ -143,9 +143,8 @@ static int msg_grid_pos_at_flush = 0;
static void ui_ext_msg_set_pos(int row, bool scrolled) static void ui_ext_msg_set_pos(int row, bool scrolled)
{ {
char buf[MB_MAXCHAR + 1]; char buf[MAX_SCHAR_SIZE];
size_t size = (size_t)utf_char2bytes(curwin->w_p_fcs_chars.msgsep, buf); size_t size = schar_get(buf, curwin->w_p_fcs_chars.msgsep);
buf[size] = '\0';
ui_call_msg_set_pos(msg_grid.handle, row, scrolled, ui_call_msg_set_pos(msg_grid.handle, row, scrolled,
(String){ .data = buf, .size = size }); (String){ .data = buf, .size = size });
} }
@ -1798,12 +1797,12 @@ void str2specialbuf(const char *sp, char *buf, size_t len)
/// print line for :print or :list command /// print line for :print or :list command
void msg_prt_line(const char *s, bool list) void msg_prt_line(const char *s, bool list)
{ {
int c; schar_T sc;
int col = 0; int col = 0;
int n_extra = 0; int n_extra = 0;
int c_extra = 0; schar_T sc_extra = 0;
int c_final = 0; schar_T sc_final = 0;
const char *p_extra = NULL; // init to make SASC shut up const char *p_extra = NULL; // init to make SASC shut up. ASCII only!
int n; int n;
int attr = 0; int attr = 0;
const char *lead = NULL; const char *lead = NULL;
@ -1845,13 +1844,13 @@ void msg_prt_line(const char *s, bool list)
while (!got_int) { while (!got_int) {
if (n_extra > 0) { if (n_extra > 0) {
n_extra--; n_extra--;
if (n_extra == 0 && c_final) { if (n_extra == 0 && sc_final) {
c = c_final; sc = sc_final;
} else if (c_extra) { } else if (sc_extra) {
c = c_extra; sc = sc_extra;
} else { } else {
assert(p_extra != NULL); assert(p_extra != NULL);
c = (unsigned char)(*p_extra++); sc = schar_from_ascii((unsigned char)(*p_extra++));
} }
} else if ((l = utfc_ptr2len(s)) > 1) { } else if ((l = utfc_ptr2len(s)) > 1) {
col += utf_ptr2cells(s); col += utf_ptr2cells(s);
@ -1859,10 +1858,8 @@ void msg_prt_line(const char *s, bool list)
if (l >= MB_MAXBYTES) { if (l >= MB_MAXBYTES) {
xstrlcpy(buf, "?", sizeof(buf)); xstrlcpy(buf, "?", sizeof(buf));
} else if (curwin->w_p_lcs_chars.nbsp != NUL && list } else if (curwin->w_p_lcs_chars.nbsp != NUL && list
&& (utf_ptr2char(s) == 160 && (utf_ptr2char(s) == 160 || utf_ptr2char(s) == 0x202f)) {
|| utf_ptr2char(s) == 0x202f)) { schar_get(buf, curwin->w_p_lcs_chars.nbsp);
int len = utf_char2bytes(curwin->w_p_lcs_chars.nbsp, buf);
buf[len] = NUL;
} else { } else {
memmove(buf, s, (size_t)l); memmove(buf, s, (size_t)l);
buf[l] = NUL; buf[l] = NUL;
@ -1872,7 +1869,9 @@ void msg_prt_line(const char *s, bool list)
continue; continue;
} else { } else {
attr = 0; attr = 0;
c = (uint8_t)(*s++); int c = (uint8_t)(*s++);
sc_extra = NUL;
sc_final = NUL;
if (list) { if (list) {
in_multispace = c == ' ' && (*s == ' ' in_multispace = c == ' ' && (*s == ' '
|| (col > 0 && s[-2] == ' ')); || (col > 0 && s[-2] == ' '));
@ -1882,74 +1881,72 @@ void msg_prt_line(const char *s, bool list)
} }
if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) { if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) {
// tab amount depends on current column // tab amount depends on current column
n_extra = tabstop_padding(col, n_extra = tabstop_padding(col, curbuf->b_p_ts,
curbuf->b_p_ts,
curbuf->b_p_vts_array) - 1; curbuf->b_p_vts_array) - 1;
if (!list) { if (!list) {
c = ' '; sc = schar_from_ascii(' ');
c_extra = ' '; sc_extra = schar_from_ascii(' ');
c_final = NUL;
} else { } else {
c = (n_extra == 0 && curwin->w_p_lcs_chars.tab3) sc = (n_extra == 0 && curwin->w_p_lcs_chars.tab3)
? curwin->w_p_lcs_chars.tab3 ? curwin->w_p_lcs_chars.tab3
: curwin->w_p_lcs_chars.tab1; : curwin->w_p_lcs_chars.tab1;
c_extra = curwin->w_p_lcs_chars.tab2; sc_extra = curwin->w_p_lcs_chars.tab2;
c_final = curwin->w_p_lcs_chars.tab3; sc_final = curwin->w_p_lcs_chars.tab3;
attr = HL_ATTR(HLF_0); attr = HL_ATTR(HLF_0);
} }
} else if (c == 160 && list && curwin->w_p_lcs_chars.nbsp != NUL) {
c = curwin->w_p_lcs_chars.nbsp;
attr = HL_ATTR(HLF_0);
} else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) { } else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) {
p_extra = ""; p_extra = "";
c_extra = NUL;
c_final = NUL;
n_extra = 1; n_extra = 1;
c = curwin->w_p_lcs_chars.eol; sc = curwin->w_p_lcs_chars.eol;
attr = HL_ATTR(HLF_AT); attr = HL_ATTR(HLF_AT);
s--; s--;
} else if (c != NUL && (n = byte2cells(c)) > 1) { } else if (c != NUL && (n = byte2cells(c)) > 1) {
n_extra = n - 1; n_extra = n - 1;
p_extra = transchar_byte_buf(NULL, c); p_extra = transchar_byte_buf(NULL, c);
c_extra = NUL; sc = schar_from_ascii(*p_extra++);
c_final = NUL;
c = (unsigned char)(*p_extra++);
// Use special coloring to be able to distinguish <hex> from // Use special coloring to be able to distinguish <hex> from
// the same in plain text. // the same in plain text.
attr = HL_ATTR(HLF_0); attr = HL_ATTR(HLF_0);
} else if (c == ' ') { } else if (c == ' ') {
if (lead != NULL && s <= lead && in_multispace if (lead != NULL && s <= lead && in_multispace
&& curwin->w_p_lcs_chars.leadmultispace != NULL) { && curwin->w_p_lcs_chars.leadmultispace != NULL) {
c = curwin->w_p_lcs_chars.leadmultispace[multispace_pos++]; sc = curwin->w_p_lcs_chars.leadmultispace[multispace_pos++];
if (curwin->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) { if (curwin->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
multispace_pos = 0; multispace_pos = 0;
} }
attr = HL_ATTR(HLF_0); attr = HL_ATTR(HLF_0);
} else if (lead != NULL && s <= lead && curwin->w_p_lcs_chars.lead != NUL) { } else if (lead != NULL && s <= lead && curwin->w_p_lcs_chars.lead != NUL) {
c = curwin->w_p_lcs_chars.lead; sc = curwin->w_p_lcs_chars.lead;
attr = HL_ATTR(HLF_0); attr = HL_ATTR(HLF_0);
} else if (trail != NULL && s > trail) { } else if (trail != NULL && s > trail) {
c = curwin->w_p_lcs_chars.trail; sc = curwin->w_p_lcs_chars.trail;
attr = HL_ATTR(HLF_0); attr = HL_ATTR(HLF_0);
} else if (in_multispace } else if (in_multispace
&& curwin->w_p_lcs_chars.multispace != NULL) { && curwin->w_p_lcs_chars.multispace != NULL) {
c = curwin->w_p_lcs_chars.multispace[multispace_pos++]; sc = curwin->w_p_lcs_chars.multispace[multispace_pos++];
if (curwin->w_p_lcs_chars.multispace[multispace_pos] == NUL) { if (curwin->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
multispace_pos = 0; multispace_pos = 0;
} }
attr = HL_ATTR(HLF_0); attr = HL_ATTR(HLF_0);
} else if (list && curwin->w_p_lcs_chars.space != NUL) { } else if (list && curwin->w_p_lcs_chars.space != NUL) {
c = curwin->w_p_lcs_chars.space; sc = curwin->w_p_lcs_chars.space;
attr = HL_ATTR(HLF_0); attr = HL_ATTR(HLF_0);
} else {
sc = schar_from_ascii(' '); // SPACE!
} }
} else {
sc = schar_from_ascii(c);
} }
} }
if (c == NUL) { if (sc == NUL) {
break; break;
} }
msg_putchar_attr(c, attr); // TODO(bfredl): this is such baloney. need msg_put_schar
char buf[MAX_SCHAR_SIZE];
schar_get(buf, sc);
msg_puts_attr(buf, attr);
col++; col++;
} }
msg_clr_eos(); msg_clr_eos();
@ -2308,7 +2305,7 @@ void msg_scroll_up(bool may_throttle, bool zerocmd)
msg_grid.dirty_col[msg_grid.rows - 1] = 0; msg_grid.dirty_col[msg_grid.rows - 1] = 0;
} }
grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', HL_ATTR(HLF_MSG)); grid_clear(&msg_grid_adj, Rows - 1, Rows, 0, Columns, HL_ATTR(HLF_MSG));
} }
/// Send throttled message output to UI clients /// Send throttled message output to UI clients
@ -2823,16 +2820,14 @@ static bool do_more_prompt(int typed_char)
if (toscroll == -1 && !to_redraw) { if (toscroll == -1 && !to_redraw) {
grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns); grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns);
grid_fill(&msg_grid_adj, 0, 1, 0, Columns, ' ', ' ', grid_clear(&msg_grid_adj, 0, 1, 0, Columns, HL_ATTR(HLF_MSG));
HL_ATTR(HLF_MSG));
// display line at top // display line at top
disp_sb_line(0, mp); disp_sb_line(0, mp);
} else { } else {
// redisplay all lines // redisplay all lines
// TODO(bfredl): this case is not optimized (though only concerns // TODO(bfredl): this case is not optimized (though only concerns
// event fragmentation, not unnecessary scroll events). // event fragmentation, not unnecessary scroll events).
grid_fill(&msg_grid_adj, 0, Rows, 0, Columns, ' ', ' ', grid_clear(&msg_grid_adj, 0, Rows, 0, Columns, HL_ATTR(HLF_MSG));
HL_ATTR(HLF_MSG));
for (int i = 0; mp != NULL && i < Rows - 1; i++) { for (int i = 0; mp != NULL && i < Rows - 1; i++) {
mp = disp_sb_line(i, mp); mp = disp_sb_line(i, mp);
msg_scrolled++; msg_scrolled++;
@ -2858,8 +2853,7 @@ static bool do_more_prompt(int typed_char)
// scroll up, display line at bottom // scroll up, display line at bottom
msg_scroll_up(true, false); msg_scroll_up(true, false);
inc_msg_scrolled(); inc_msg_scrolled();
grid_fill(&msg_grid_adj, Rows - 2, Rows - 1, 0, Columns, ' ', ' ', grid_clear(&msg_grid_adj, Rows - 2, Rows - 1, 0, Columns, HL_ATTR(HLF_MSG));
HL_ATTR(HLF_MSG));
mp_last = disp_sb_line(Rows - 2, mp_last); mp_last = disp_sb_line(Rows - 2, mp_last);
toscroll--; toscroll--;
} }
@ -2867,8 +2861,7 @@ static bool do_more_prompt(int typed_char)
if (toscroll <= 0) { if (toscroll <= 0) {
// displayed the requested text, more prompt again // displayed the requested text, more prompt again
grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', grid_clear(&msg_grid_adj, Rows - 1, Rows, 0, Columns, HL_ATTR(HLF_MSG));
HL_ATTR(HLF_MSG));
msg_moremsg(false); msg_moremsg(false);
continue; continue;
} }
@ -2881,8 +2874,7 @@ static bool do_more_prompt(int typed_char)
} }
// clear the --more-- message // clear the --more-- message
grid_fill(&msg_grid_adj, Rows - 1, Rows, 0, Columns, ' ', ' ', grid_clear(&msg_grid_adj, Rows - 1, Rows, 0, Columns, HL_ATTR(HLF_MSG));
HL_ATTR(HLF_MSG));
redraw_cmdline = true; redraw_cmdline = true;
clear_cmdline = false; clear_cmdline = false;
mode_displayed = false; mode_displayed = false;
@ -2966,10 +2958,8 @@ void msg_clr_eos_force(void)
} }
} }
grid_fill(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, grid_clear(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, HL_ATTR(HLF_MSG));
' ', ' ', HL_ATTR(HLF_MSG)); grid_clear(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, HL_ATTR(HLF_MSG));
grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns,
' ', ' ', HL_ATTR(HLF_MSG));
redraw_cmdline = true; // overwritten the command line redraw_cmdline = true; // overwritten the command line
if (msg_row < Rows - 1 || msg_col == 0) { if (msg_row < Rows - 1 || msg_col == 0) {

View File

@ -1696,10 +1696,10 @@ static void didset_options2(void)
highlight_changed(); highlight_changed();
// Parse default for 'fillchars'. // Parse default for 'fillchars'.
set_fillchars_option(curwin, curwin->w_p_fcs, true); set_chars_option(curwin, curwin->w_p_fcs, kFillchars, true);
// Parse default for 'listchars'. // Parse default for 'listchars'.
set_listchars_option(curwin, curwin->w_p_lcs, true); set_chars_option(curwin, curwin->w_p_lcs, kListchars, true);
// Parse default for 'wildmode'. // Parse default for 'wildmode'.
check_opt_wim(); check_opt_wim();
@ -4991,8 +4991,8 @@ void didset_window_options(win_T *wp, bool valid_cursor)
check_colorcolumn(wp); check_colorcolumn(wp);
briopt_check(wp); briopt_check(wp);
fill_culopt_flags(NULL, wp); fill_culopt_flags(NULL, wp);
set_fillchars_option(wp, wp->w_p_fcs, true); set_chars_option(wp, wp->w_p_fcs, kFillchars, true);
set_listchars_option(wp, wp->w_p_lcs, true); set_chars_option(wp, wp->w_p_lcs, kListchars, true);
parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl
check_blending(wp); check_blending(wp);
set_winbar_win(wp, false, valid_cursor); set_winbar_win(wp, false, valid_cursor);

View File

@ -21,6 +21,7 @@
#include "nvim/fold.h" #include "nvim/fold.h"
#include "nvim/gettext.h" #include "nvim/gettext.h"
#include "nvim/globals.h" #include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight_group.h" #include "nvim/highlight_group.h"
#include "nvim/indent.h" #include "nvim/indent.h"
#include "nvim/indent_c.h" #include "nvim/indent_c.h"
@ -876,18 +877,15 @@ int expand_set_casemap(optexpand_T *args, int *numMatches, char ***matches)
} }
/// The global 'listchars' or 'fillchars' option is changed. /// The global 'listchars' or 'fillchars' option is changed.
static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_lcs, int opt_flags) static const char *did_set_global_chars_option(win_T *win, char *val, CharsOption what,
int opt_flags)
{ {
const char *errmsg = NULL; const char *errmsg = NULL;
char **local_ptr = opt_lcs ? &win->w_p_lcs : &win->w_p_fcs; char **local_ptr = (what == kListchars) ? &win->w_p_lcs : &win->w_p_fcs;
// only apply the global value to "win" when it does not have a // only apply the global value to "win" when it does not have a
// local value // local value
if (opt_lcs) { errmsg = set_chars_option(win, val, what, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL));
errmsg = set_listchars_option(win, val, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL));
} else {
errmsg = set_fillchars_option(win, val, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL));
}
if (errmsg != NULL) { if (errmsg != NULL) {
return errmsg; return errmsg;
} }
@ -903,14 +901,9 @@ static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_
// again, it was changed when setting the global value. // again, it was changed when setting the global value.
// If no error was returned above, we don't expect an error // If no error was returned above, we don't expect an error
// here, so ignore the return value. // here, so ignore the return value.
if (opt_lcs) { char *opt = (what == kListchars) ? wp->w_p_lcs : wp->w_p_fcs;
if (*wp->w_p_lcs == NUL) { if (*opt == NUL) {
set_listchars_option(wp, wp->w_p_lcs, true); set_chars_option(wp, opt, what, true);
}
} else {
if (*wp->w_p_fcs == NUL) {
set_fillchars_option(wp, wp->w_p_fcs, true);
}
} }
} }
@ -926,13 +919,14 @@ const char *did_set_chars_option(optset_T *args)
char **varp = (char **)args->os_varp; char **varp = (char **)args->os_varp;
const char *errmsg = NULL; const char *errmsg = NULL;
if (varp == &p_lcs // global 'listchars' if (varp == &p_lcs) { // global 'listchars'
|| varp == &p_fcs) { // global 'fillchars' errmsg = did_set_global_chars_option(win, *varp, kListchars, args->os_flags);
errmsg = did_set_global_listfillchars(win, *varp, varp == &p_lcs, args->os_flags); } else if (varp == &p_fcs) { // global 'fillchars'
errmsg = did_set_global_chars_option(win, *varp, kFillchars, args->os_flags);
} else if (varp == &win->w_p_lcs) { // local 'listchars' } else if (varp == &win->w_p_lcs) { // local 'listchars'
errmsg = set_listchars_option(win, *varp, true); errmsg = set_chars_option(win, *varp, kListchars, true);
} else if (varp == &win->w_p_fcs) { // local 'fillchars' } else if (varp == &win->w_p_fcs) { // local 'fillchars'
errmsg = set_fillchars_option(win, *varp, true); errmsg = set_chars_option(win, *varp, kFillchars, true);
} }
return errmsg; return errmsg;
@ -2603,7 +2597,7 @@ static const char e_conflicts_with_value_of_fillchars[]
/// Calls mb_cptr2char_adv(p) and returns the character. /// Calls mb_cptr2char_adv(p) and returns the character.
/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used. /// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
/// Returns 0 for invalid hex or invalid UTF-8 byte. /// Returns 0 for invalid hex or invalid UTF-8 byte.
static int get_encoded_char_adv(const char **p) static schar_T get_encoded_char_adv(const char **p)
{ {
const char *s = *p; const char *s = *p;
@ -2618,71 +2612,69 @@ static int get_encoded_char_adv(const char **p)
num = num * 256 + n; num = num * 256 + n;
} }
*p += 2; *p += 2;
return (int)num; return (char2cells((int)num) > 1) ? 0 : schar_from_char((int)num);
} }
// TODO(bfredl): use schar_T representation and utfc_ptr2len int clen = utfc_ptr2len(s);
int clen = utf_ptr2len(s); int firstc;
int c = mb_cptr2char_adv(p); schar_T c = utfc_ptr2schar(s, &firstc);
if (clen == 1 && c > 127) { // Invalid UTF-8 byte *p += clen;
return 0; // Invalid UTF-8 byte or doublewidth not allowed
} return ((clen == 1 && firstc > 127) || char2cells(firstc) > 1) ? 0 : c;
return c;
} }
struct chars_tab { struct chars_tab {
int *cp; ///< char value schar_T *cp; ///< char value
const char *name; ///< char id const char *name; ///< char id
int def; ///< default value const char *def; ///< default value
int fallback; ///< default value when "def" isn't single-width const char *fallback; ///< default value when "def" isn't single-width
}; };
static fcs_chars_T fcs_chars; static fcs_chars_T fcs_chars;
static const struct chars_tab fcs_tab[] = { static const struct chars_tab fcs_tab[] = {
{ &fcs_chars.stl, "stl", ' ', NUL }, { &fcs_chars.stl, "stl", " ", NULL },
{ &fcs_chars.stlnc, "stlnc", ' ', NUL }, { &fcs_chars.stlnc, "stlnc", " ", NULL },
{ &fcs_chars.wbr, "wbr", ' ', NUL }, { &fcs_chars.wbr, "wbr", " ", NULL },
{ &fcs_chars.horiz, "horiz", 0x2500, '-' }, // ─ { &fcs_chars.horiz, "horiz", "", "-" },
{ &fcs_chars.horizup, "horizup", 0x2534, '-' }, // ┴ { &fcs_chars.horizup, "horizup", "", "-" },
{ &fcs_chars.horizdown, "horizdown", 0x252c, '-' }, // ┬ { &fcs_chars.horizdown, "horizdown", "", "-" },
{ &fcs_chars.vert, "vert", 0x2502, '|' }, // │ { &fcs_chars.vert, "vert", "", "|" },
{ &fcs_chars.vertleft, "vertleft", 0x2524, '|' }, // ┤ { &fcs_chars.vertleft, "vertleft", "", "|" },
{ &fcs_chars.vertright, "vertright", 0x251c, '|' }, // ├ { &fcs_chars.vertright, "vertright", "", "|" },
{ &fcs_chars.verthoriz, "verthoriz", 0x253c, '+' }, // ┼ { &fcs_chars.verthoriz, "verthoriz", "", "+" },
{ &fcs_chars.fold, "fold", 0x00b7, '-' }, // · { &fcs_chars.fold, "fold", "·", "-" },
{ &fcs_chars.foldopen, "foldopen", '-', NUL }, { &fcs_chars.foldopen, "foldopen", "-", NULL },
{ &fcs_chars.foldclosed, "foldclose", '+', NUL }, { &fcs_chars.foldclosed, "foldclose", "+", NULL },
{ &fcs_chars.foldsep, "foldsep", 0x2502, '|' }, // │ { &fcs_chars.foldsep, "foldsep", "", "|" },
{ &fcs_chars.diff, "diff", '-', NUL }, { &fcs_chars.diff, "diff", "-", NULL },
{ &fcs_chars.msgsep, "msgsep", ' ', NUL }, { &fcs_chars.msgsep, "msgsep", " ", NULL },
{ &fcs_chars.eob, "eob", '~', NUL }, { &fcs_chars.eob, "eob", "~", NULL },
{ &fcs_chars.lastline, "lastline", '@', NUL }, { &fcs_chars.lastline, "lastline", "@", NULL },
}; };
static lcs_chars_T lcs_chars; static lcs_chars_T lcs_chars;
static const struct chars_tab lcs_tab[] = { static const struct chars_tab lcs_tab[] = {
{ &lcs_chars.eol, "eol", NUL, NUL }, { &lcs_chars.eol, "eol", NULL, NULL },
{ &lcs_chars.ext, "extends", NUL, NUL }, { &lcs_chars.ext, "extends", NULL, NULL },
{ &lcs_chars.nbsp, "nbsp", NUL, NUL }, { &lcs_chars.nbsp, "nbsp", NULL, NULL },
{ &lcs_chars.prec, "precedes", NUL, NUL }, { &lcs_chars.prec, "precedes", NULL, NULL },
{ &lcs_chars.space, "space", NUL, NUL }, { &lcs_chars.space, "space", NULL, NULL },
{ &lcs_chars.tab2, "tab", NUL, NUL }, { &lcs_chars.tab2, "tab", NULL, NULL },
{ &lcs_chars.lead, "lead", NUL, NUL }, { &lcs_chars.lead, "lead", NULL, NULL },
{ &lcs_chars.trail, "trail", NUL, NUL }, { &lcs_chars.trail, "trail", NULL, NULL },
{ &lcs_chars.conceal, "conceal", NUL, NUL }, { &lcs_chars.conceal, "conceal", NULL, NULL },
{ NULL, "multispace", NUL, NUL }, { NULL, "multispace", NULL, NULL },
{ NULL, "leadmultispace", NUL, NUL }, { NULL, "leadmultispace", NULL, NULL },
}; };
/// Handle setting 'listchars' or 'fillchars'. /// Handle setting 'listchars' or 'fillchars'.
/// Assume monocell characters /// Assume monocell characters
/// ///
/// @param value points to either the global or the window-local value. /// @param value points to either the global or the window-local value.
/// @param is_listchars is true for "listchars" and false for "fillchars". /// @param what kListchars or kFillchars
/// @param apply if false, do not store the flags, only check for errors. /// @param apply if false, do not store the flags, only check for errors.
/// @return error message, NULL if it's OK. /// @return error message, NULL if it's OK.
static const char *set_chars_option(win_T *wp, const char *value, const bool is_listchars, const char *set_chars_option(win_T *wp, const char *value, CharsOption what, bool apply)
const bool apply)
{ {
const char *last_multispace = NULL; // Last occurrence of "multispace:" const char *last_multispace = NULL; // Last occurrence of "multispace:"
const char *last_lmultispace = NULL; // Last occurrence of "leadmultispace:" const char *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
@ -2691,7 +2683,7 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
const struct chars_tab *tab; const struct chars_tab *tab;
int entries; int entries;
if (is_listchars) { if (what == kListchars) {
tab = lcs_tab; tab = lcs_tab;
entries = ARRAY_SIZE(lcs_tab); entries = ARRAY_SIZE(lcs_tab);
if (wp->w_p_lcs[0] == NUL) { if (wp->w_p_lcs[0] == NUL) {
@ -2713,23 +2705,24 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
if (tab[i].cp != NULL) { if (tab[i].cp != NULL) {
// XXX: Characters taking 2 columns is forbidden (TUI limitation?). // XXX: Characters taking 2 columns is forbidden (TUI limitation?).
// Set old defaults in this case. // Set old defaults in this case.
*(tab[i].cp) = char2cells(tab[i].def) == 1 ? tab[i].def : tab[i].fallback; *(tab[i].cp) = schar_from_str((tab[i].def && ptr2cells(tab[i].def) == 1)
? tab[i].def : tab[i].fallback);
} }
} }
if (is_listchars) { if (what == kListchars) {
lcs_chars.tab1 = NUL; lcs_chars.tab1 = NUL;
lcs_chars.tab3 = NUL; lcs_chars.tab3 = NUL;
if (multispace_len > 0) { if (multispace_len > 0) {
lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int)); lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(schar_T));
lcs_chars.multispace[multispace_len] = NUL; lcs_chars.multispace[multispace_len] = NUL;
} else { } else {
lcs_chars.multispace = NULL; lcs_chars.multispace = NULL;
} }
if (lead_multispace_len > 0) { if (lead_multispace_len > 0) {
lcs_chars.leadmultispace = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int)); lcs_chars.leadmultispace = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(schar_T));
lcs_chars.leadmultispace[lead_multispace_len] = NUL; lcs_chars.leadmultispace[lead_multispace_len] = NUL;
} else { } else {
lcs_chars.leadmultispace = NULL; lcs_chars.leadmultispace = NULL;
@ -2748,15 +2741,15 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
continue; continue;
} }
if (is_listchars && strcmp(tab[i].name, "multispace") == 0) { if (what == kListchars && strcmp(tab[i].name, "multispace") == 0) {
const char *s = p + len + 1; const char *s = p + len + 1;
if (round == 0) { if (round == 0) {
// Get length of lcs-multispace string in the first round // Get length of lcs-multispace string in the first round
last_multispace = p; last_multispace = p;
multispace_len = 0; multispace_len = 0;
while (*s != NUL && *s != ',') { while (*s != NUL && *s != ',') {
int c1 = get_encoded_char_adv(&s); schar_T c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) { if (c1 == 0) {
return e_invarg; return e_invarg;
} }
multispace_len++; multispace_len++;
@ -2769,7 +2762,7 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
} else { } else {
int multispace_pos = 0; int multispace_pos = 0;
while (*s != NUL && *s != ',') { while (*s != NUL && *s != ',') {
int c1 = get_encoded_char_adv(&s); schar_T c1 = get_encoded_char_adv(&s);
if (p == last_multispace) { if (p == last_multispace) {
lcs_chars.multispace[multispace_pos++] = c1; lcs_chars.multispace[multispace_pos++] = c1;
} }
@ -2779,15 +2772,15 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
break; break;
} }
if (is_listchars && strcmp(tab[i].name, "leadmultispace") == 0) { if (what == kListchars && strcmp(tab[i].name, "leadmultispace") == 0) {
const char *s = p + len + 1; const char *s = p + len + 1;
if (round == 0) { if (round == 0) {
// get length of lcs-leadmultispace string in first round // get length of lcs-leadmultispace string in first round
last_lmultispace = p; last_lmultispace = p;
lead_multispace_len = 0; lead_multispace_len = 0;
while (*s != NUL && *s != ',') { while (*s != NUL && *s != ',') {
int c1 = get_encoded_char_adv(&s); schar_T c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) { if (c1 == 0) {
return e_invarg; return e_invarg;
} }
lead_multispace_len++; lead_multispace_len++;
@ -2800,7 +2793,7 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
} else { } else {
int multispace_pos = 0; int multispace_pos = 0;
while (*s != NUL && *s != ',') { while (*s != NUL && *s != ',') {
int c1 = get_encoded_char_adv(&s); schar_T c1 = get_encoded_char_adv(&s);
if (p == last_lmultispace) { if (p == last_lmultispace) {
lcs_chars.leadmultispace[multispace_pos++] = c1; lcs_chars.leadmultispace[multispace_pos++] = c1;
} }
@ -2811,23 +2804,23 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
} }
const char *s = p + len + 1; const char *s = p + len + 1;
int c1 = get_encoded_char_adv(&s); schar_T c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) { if (c1 == 0) {
return e_invarg; return e_invarg;
} }
int c2 = 0; schar_T c2 = 0;
int c3 = 0; schar_T c3 = 0;
if (tab[i].cp == &lcs_chars.tab2) { if (tab[i].cp == &lcs_chars.tab2) {
if (*s == NUL) { if (*s == NUL) {
return e_invarg; return e_invarg;
} }
c2 = get_encoded_char_adv(&s); c2 = get_encoded_char_adv(&s);
if (c2 == 0 || char2cells(c2) > 1) { if (c2 == 0) {
return e_invarg; return e_invarg;
} }
if (!(*s == ',' || *s == NUL)) { if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s); c3 = get_encoded_char_adv(&s);
if (c3 == 0 || char2cells(c3) > 1) { if (c3 == 0) {
return e_invarg; return e_invarg;
} }
} }
@ -2859,7 +2852,7 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
} }
if (apply) { if (apply) {
if (is_listchars) { if (what == kListchars) {
xfree(wp->w_p_lcs_chars.multispace); xfree(wp->w_p_lcs_chars.multispace);
xfree(wp->w_p_lcs_chars.leadmultispace); xfree(wp->w_p_lcs_chars.leadmultispace);
wp->w_p_lcs_chars = lcs_chars; wp->w_p_lcs_chars = lcs_chars;
@ -2871,18 +2864,6 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
return NULL; // no error return NULL; // no error
} }
/// Handle the new value of 'fillchars'.
const char *set_fillchars_option(win_T *wp, char *val, bool apply)
{
return set_chars_option(wp, val, false, apply);
}
/// Handle the new value of 'listchars'.
const char *set_listchars_option(win_T *wp, char *val, bool apply)
{
return set_chars_option(wp, val, true, apply);
}
/// Function given to ExpandGeneric() to obtain possible arguments of the /// Function given to ExpandGeneric() to obtain possible arguments of the
/// 'fillchars' option. /// 'fillchars' option.
char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
@ -2911,17 +2892,17 @@ char *get_listchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
/// @return an untranslated error message if any of them is invalid, NULL otherwise. /// @return an untranslated error message if any of them is invalid, NULL otherwise.
const char *check_chars_options(void) const char *check_chars_options(void)
{ {
if (set_listchars_option(curwin, p_lcs, false) != NULL) { if (set_chars_option(curwin, p_lcs, kListchars, false) != NULL) {
return e_conflicts_with_value_of_listchars; return e_conflicts_with_value_of_listchars;
} }
if (set_fillchars_option(curwin, p_fcs, false) != NULL) { if (set_chars_option(curwin, p_fcs, kFillchars, false) != NULL) {
return e_conflicts_with_value_of_fillchars; return e_conflicts_with_value_of_fillchars;
} }
FOR_ALL_TAB_WINDOWS(tp, wp) { FOR_ALL_TAB_WINDOWS(tp, wp) {
if (set_listchars_option(wp, wp->w_p_lcs, true) != NULL) { if (set_chars_option(wp, wp->w_p_lcs, kListchars, true) != NULL) {
return e_conflicts_with_value_of_listchars; return e_conflicts_with_value_of_listchars;
} }
if (set_fillchars_option(wp, wp->w_p_fcs, true) != NULL) { if (set_chars_option(wp, wp->w_p_fcs, kFillchars, true) != NULL) {
return e_conflicts_with_value_of_fillchars; return e_conflicts_with_value_of_fillchars;
} }
} }

View File

@ -6,6 +6,11 @@
#include "nvim/option_defs.h" // IWYU pragma: keep #include "nvim/option_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep #include "nvim/types_defs.h" // IWYU pragma: keep
typedef enum {
kFillchars,
kListchars,
} CharsOption;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "optionstr.h.generated.h" # include "optionstr.h.generated.h"
#endif #endif

View File

@ -629,19 +629,19 @@ void pum_redraw(void)
} }
if (pum_rl) { if (pum_rl) {
grid_line_fill(col_off - pum_base_width - n + 1, grid_col + 1, ' ', attr); grid_line_fill(col_off - pum_base_width - n + 1, grid_col + 1, schar_from_ascii(' '), attr);
grid_col = col_off - pum_base_width - n + 1; grid_col = col_off - pum_base_width - n + 1;
} else { } else {
grid_line_fill(grid_col, col_off + pum_base_width + n, ' ', attr); grid_line_fill(grid_col, col_off + pum_base_width + n, schar_from_ascii(' '), attr);
grid_col = col_off + pum_base_width + n; grid_col = col_off + pum_base_width + n;
} }
totwidth = pum_base_width + n; totwidth = pum_base_width + n;
} }
if (pum_rl) { if (pum_rl) {
grid_line_fill(col_off - pum_width + 1, grid_col + 1, ' ', attr); grid_line_fill(col_off - pum_width + 1, grid_col + 1, schar_from_ascii(' '), attr);
} else { } else {
grid_line_fill(grid_col, col_off + pum_width, ' ', attr); grid_line_fill(grid_col, col_off + pum_width, schar_from_ascii(' '), attr);
} }
if (pum_scrollbar > 0) { if (pum_scrollbar > 0) {

View File

@ -58,7 +58,6 @@ typedef enum {
/// If inversion is possible we use it. Else '=' characters are used. /// If inversion is possible we use it. Else '=' characters are used.
void win_redr_status(win_T *wp) void win_redr_status(win_T *wp)
{ {
int fillchar;
int attr; int attr;
bool is_stl_global = global_stl_height() > 0; bool is_stl_global = global_stl_height() > 0;
static bool busy = false; static bool busy = false;
@ -84,7 +83,7 @@ void win_redr_status(win_T *wp)
// redraw custom status line // redraw custom status line
redraw_custom_statusline(wp); redraw_custom_statusline(wp);
} else { } else {
fillchar = fillchar_status(&attr, wp); schar_T fillchar = fillchar_status(&attr, wp);
const int stl_width = is_stl_global ? Columns : wp->w_width; const int stl_width = is_stl_global ? Columns : wp->w_width;
get_trans_bufname(wp->w_buffer); get_trans_bufname(wp->w_buffer);
@ -169,6 +168,7 @@ void win_redr_status(win_T *wp)
// May need to draw the character below the vertical separator. // May need to draw the character below the vertical separator.
if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) { if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) {
schar_T fillchar;
if (stl_connected(wp)) { if (stl_connected(wp)) {
fillchar = fillchar_status(&attr, wp); fillchar = fillchar_status(&attr, wp);
} else { } else {
@ -176,7 +176,7 @@ void win_redr_status(win_T *wp)
fillchar = wp->w_p_fcs_chars.vert; fillchar = wp->w_p_fcs_chars.vert;
} }
grid_line_start(&default_grid, W_ENDROW(wp)); grid_line_start(&default_grid, W_ENDROW(wp));
grid_line_put_schar(W_ENDCOL(wp), schar_from_char(fillchar), attr); grid_line_put_schar(W_ENDCOL(wp), fillchar, attr);
grid_line_flush(); grid_line_flush();
} }
busy = false; busy = false;
@ -291,7 +291,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
int row; int row;
int col = 0; int col = 0;
int maxwidth; int maxwidth;
int fillchar; schar_T fillchar;
char buf[MAXPATHL]; char buf[MAXPATHL];
char transbuf[MAXPATHL]; char transbuf[MAXPATHL];
char *stl; char *stl;
@ -316,7 +316,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
// Use 'tabline'. Always at the first line of the screen. // Use 'tabline'. Always at the first line of the screen.
stl = p_tal; stl = p_tal;
row = 0; row = 0;
fillchar = ' '; fillchar = schar_from_ascii(' ');
attr = HL_ATTR(HLF_TPF); attr = HL_ATTR(HLF_TPF);
maxwidth = Columns; maxwidth = Columns;
opt_idx = kOptTabline; opt_idx = kOptTabline;
@ -374,7 +374,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
grid = &msg_grid_adj; grid = &msg_grid_adj;
row = Rows - 1; row = Rows - 1;
maxwidth--; // writing in last column may cause scrolling maxwidth--; // writing in last column may cause scrolling
fillchar = ' '; fillchar = schar_from_ascii(' ');
attr = HL_ATTR(HLF_MSG); attr = HL_ATTR(HLF_MSG);
} }
} else { } else {
@ -513,7 +513,7 @@ void win_redr_ruler(win_T *wp)
&& *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) == NUL; && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) == NUL;
int width; int width;
int fillchar; schar_T fillchar;
int attr; int attr;
int off; int off;
bool part_of_status = false; bool part_of_status = false;
@ -529,7 +529,7 @@ void win_redr_ruler(win_T *wp)
width = Columns; width = Columns;
part_of_status = true; part_of_status = true;
} else { } else {
fillchar = ' '; fillchar = schar_from_ascii(' ');
attr = HL_ATTR(HLF_MSG); attr = HL_ATTR(HLF_MSG);
width = Columns; width = Columns;
off = 0; off = 0;
@ -577,7 +577,7 @@ void win_redr_ruler(win_T *wp)
if (this_ru_col + o < width) { if (this_ru_col + o < width) {
// Need at least 3 chars left for get_rel_pos() + NUL. // Need at least 3 chars left for get_rel_pos() + NUL.
while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) { while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) {
i += utf_char2bytes(fillchar, buffer + i); i += (int)schar_get(buffer + i, fillchar);
o++; o++;
} }
get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i); get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
@ -612,18 +612,15 @@ void win_redr_ruler(win_T *wp)
} }
/// Get the character to use in a status line. Get its attributes in "*attr". /// Get the character to use in a status line. Get its attributes in "*attr".
int fillchar_status(int *attr, win_T *wp) schar_T fillchar_status(int *attr, win_T *wp)
{ {
int fill; if (wp == curwin) {
bool is_curwin = (wp == curwin);
if (is_curwin) {
*attr = win_hl_attr(wp, HLF_S); *attr = win_hl_attr(wp, HLF_S);
fill = wp->w_p_fcs_chars.stl; return wp->w_p_fcs_chars.stl;
} else { } else {
*attr = win_hl_attr(wp, HLF_SNC); *attr = win_hl_attr(wp, HLF_SNC);
fill = wp->w_p_fcs_chars.stlnc; return wp->w_p_fcs_chars.stlnc;
} }
return fill;
} }
/// Redraw the status line according to 'statusline' and take care of any /// Redraw the status line according to 'statusline' and take care of any
@ -724,7 +721,6 @@ void draw_tabline(void)
int col = 0; int col = 0;
win_T *cwp; win_T *cwp;
int wincount; int wincount;
int c;
grid_line_start(&default_grid, 0); grid_line_start(&default_grid, 0);
FOR_ALL_TABS(tp) { FOR_ALL_TABS(tp) {
tabcount++; tabcount++;
@ -826,12 +822,8 @@ void draw_tabline(void)
} }
} }
if (use_sep_chars) { char c = use_sep_chars ? '_' : ' ';
c = '_'; grid_line_fill(col, Columns, schar_from_ascii(c), attr_fill);
} else {
c = ' ';
}
grid_line_fill(col, Columns, c, attr_fill);
// Draw the 'showcmd' information if 'showcmdloc' == "tabline". // Draw the 'showcmd' information if 'showcmdloc' == "tabline".
if (p_sc && *p_sloc == 't') { if (p_sc && *p_sloc == 't') {
@ -878,7 +870,7 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, char *buf, st
StlClickRecord *clickrec; StlClickRecord *clickrec;
char *stc = xstrdup(wp->w_p_stc); char *stc = xstrdup(wp->w_p_stc);
int width = build_stl_str_hl(wp, buf, MAXPATHL, stc, kOptStatuscolumn, OPT_LOCAL, ' ', int width = build_stl_str_hl(wp, buf, MAXPATHL, stc, kOptStatuscolumn, OPT_LOCAL, 0,
stcp->width, &stcp->hlrec, fillclick ? &clickrec : NULL, stcp); stcp->width, &stcp->hlrec, fillclick ? &clickrec : NULL, stcp);
xfree(stc); xfree(stc);
@ -920,7 +912,7 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, linenr_T relnum, char *buf, st
/// ///
/// @return The final width of the statusline /// @return The final width of the statusline
int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex opt_idx, int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex opt_idx,
int opt_scope, int fillchar, int maxwidth, stl_hlrec_t **hltab, int opt_scope, schar_T fillchar, int maxwidth, stl_hlrec_t **hltab,
StlClickRecord **tabtab, statuscol_T *stcp) StlClickRecord **tabtab, statuscol_T *stcp)
{ {
static size_t stl_items_len = 20; // Initial value, grows as needed. static size_t stl_items_len = 20; // Initial value, grows as needed.
@ -977,7 +969,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
} }
if (fillchar == 0) { if (fillchar == 0) {
fillchar = ' '; fillchar = schar_from_ascii(' ');
} }
// The cursor in windows other than the current one isn't always // The cursor in windows other than the current one isn't always
@ -1175,7 +1167,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
out_p = out_p - n + 1; out_p = out_p - n + 1;
// Fill up space left over by half a double-wide char. // Fill up space left over by half a double-wide char.
while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) { while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) {
out_p += utf_char2bytes(fillchar, out_p); schar_get_adv(&out_p, fillchar);
} }
// } // }
@ -1198,13 +1190,13 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
if (min_group_width < 0) { if (min_group_width < 0) {
min_group_width = 0 - min_group_width; min_group_width = 0 - min_group_width;
while (group_len++ < min_group_width && out_p < out_end_p) { while (group_len++ < min_group_width && out_p < out_end_p) {
out_p += utf_char2bytes(fillchar, out_p); schar_get_adv(&out_p, fillchar);
} }
// If the group is right-aligned, shift everything to the right and // If the group is right-aligned, shift everything to the right and
// prepend with filler characters. // prepend with filler characters.
} else { } else {
// { Move the group to the right // { Move the group to the right
group_len = (min_group_width - group_len) * utf_char2len(fillchar); group_len = (min_group_width - group_len) * (int)schar_len(fillchar);
memmove(t + group_len, t, (size_t)(out_p - t)); memmove(t + group_len, t, (size_t)(out_p - t));
if (out_p + group_len >= (out_end_p + 1)) { if (out_p + group_len >= (out_end_p + 1)) {
group_len = out_end_p - out_p; group_len = out_end_p - out_p;
@ -1219,7 +1211,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
// Prepend the fill characters // Prepend the fill characters
for (; group_len > 0; group_len--) { for (; group_len > 0; group_len--) {
t += utf_char2bytes(fillchar, t); schar_get_adv(&t, fillchar);
} }
} }
} }
@ -1647,8 +1639,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
// TODO(bfredl): this is very backwards. we must support schar_T // TODO(bfredl): this is very backwards. we must support schar_T
// being used directly in 'statuscolumn' // being used directly in 'statuscolumn'
for (int i = 0; i < fdc; i++) { for (int i = 0; i < fdc; i++) {
schar_get(out_p + buflen, fold_buf[i]); buflen += schar_get(out_p + buflen, fold_buf[i]);
buflen += strlen(out_p + buflen);
} }
p = out_p; p = out_p;
} }
@ -1813,7 +1804,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) { if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) {
*out_p++ = ' '; *out_p++ = ' ';
} else { } else {
out_p += utf_char2bytes(fillchar, out_p); schar_get_adv(&out_p, fillchar);
} }
} }
minwid = 0; minwid = 0;
@ -1836,7 +1827,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
// digit follows. // digit follows.
if (fillable && *t == ' ' if (fillable && *t == ' '
&& (!ascii_isdigit(*(t + 1)) || fillchar != '-')) { && (!ascii_isdigit(*(t + 1)) || fillchar != '-')) {
out_p += utf_char2bytes(fillchar, out_p); schar_get_adv(&out_p, fillchar);
} else { } else {
*out_p++ = *t; *out_p++ = *t;
} }
@ -1853,7 +1844,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
// For left-aligned items, fill any remaining space with the fillchar // For left-aligned items, fill any remaining space with the fillchar
for (; l < minwid && out_p < out_end_p; l++) { for (; l < minwid && out_p < out_end_p; l++) {
out_p += utf_char2bytes(fillchar, out_p); schar_get_adv(&out_p, fillchar);
} }
// Otherwise if the item is a number, copy that to the output buffer. // Otherwise if the item is a number, copy that to the output buffer.
@ -2070,8 +2061,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
// Fill up for half a double-wide character. // Fill up for half a double-wide character.
while (++width < maxwidth) { while (++width < maxwidth) {
trunc_p += utf_char2bytes(fillchar, trunc_p); schar_get_adv(&trunc_p, fillchar);
*trunc_p = NUL;
} }
} }
width = maxwidth; width = maxwidth;
@ -2100,12 +2090,12 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
for (int l = 0; l < num_separators; l++) { for (int l = 0; l < num_separators; l++) {
int dislocation = (l == (num_separators - 1)) ? final_spaces : standard_spaces; int dislocation = (l == (num_separators - 1)) ? final_spaces : standard_spaces;
dislocation *= utf_char2len(fillchar); dislocation *= (int)schar_len(fillchar);
char *start = stl_items[stl_separator_locations[l]].start; char *start = stl_items[stl_separator_locations[l]].start;
char *seploc = start + dislocation; char *seploc = start + dislocation;
STRMOVE(seploc, start); STRMOVE(seploc, start);
for (char *s = start; s < seploc;) { for (char *s = start; s < seploc;) {
s += utf_char2bytes(fillchar, s); schar_get_adv(&s, fillchar);
} }
for (int item_idx = stl_separator_locations[l] + 1; for (int item_idx = stl_separator_locations[l] + 1;

View File

@ -5662,7 +5662,7 @@ void win_setheight_win(int height, win_T *win)
// If there is extra space created between the last window and the command // If there is extra space created between the last window and the command
// line, clear it. // line, clear it.
if (full_screen && msg_scrolled == 0 && row < cmdline_row) { if (full_screen && msg_scrolled == 0 && row < cmdline_row) {
grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); grid_clear(&default_grid, row, cmdline_row, 0, Columns, 0);
if (msg_grid.chars) { if (msg_grid.chars) {
clear_cmdline = true; clear_cmdline = true;
} }
@ -6145,7 +6145,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
} }
} }
int row = win_comp_pos(); int row = win_comp_pos();
grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); grid_clear(&default_grid, row, cmdline_row, 0, Columns, 0);
if (msg_grid.chars) { if (msg_grid.chars) {
clear_cmdline = true; clear_cmdline = true;
} }
@ -6640,7 +6640,7 @@ void command_height(void)
// clear the lines added to cmdline // clear the lines added to cmdline
if (full_screen) { if (full_screen) {
grid_fill(&default_grid, cmdline_row, Rows, 0, Columns, ' ', ' ', 0); grid_clear(&default_grid, cmdline_row, Rows, 0, Columns, 0);
} }
msg_row = cmdline_row; msg_row = cmdline_row;
redraw_cmdline = true; redraw_cmdline = true;

View File

@ -6,6 +6,7 @@ local eq = helpers.eq
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
local insert = helpers.insert local insert = helpers.insert
local feed = helpers.feed local feed = helpers.feed
local meths = helpers.meths
describe("'fillchars'", function() describe("'fillchars'", function()
local screen local screen
@ -53,10 +54,18 @@ describe("'fillchars'", function()
]]) ]])
end) end)
it('supports composing multibyte char', function()
command('set fillchars=eob:å̲')
screen:expect([[
^ |
å̲ |*3
|
]])
end)
it('handles invalid values', function() it('handles invalid values', function()
shouldfail('eob:') -- empty string shouldfail('eob:') -- empty string
shouldfail('eob:馬') -- doublewidth char shouldfail('eob:馬') -- doublewidth char
shouldfail('eob:å̲') -- composing chars
shouldfail('eob:xy') -- two ascii chars shouldfail('eob:xy') -- two ascii chars
shouldfail('eob:\255', 'eob:<ff>') -- invalid UTF-8 shouldfail('eob:\255', 'eob:<ff>') -- invalid UTF-8
end) end)
@ -178,4 +187,28 @@ describe("'listchars'", function()
| |
]]) ]])
end) end)
it('supports composing chars', function()
screen:set_default_attr_ids {
[1] = { foreground = Screen.colors.Blue1, bold = true },
}
feed('i<tab><tab><tab>x<esc>')
command('set list laststatus=0')
-- tricky: the tab value forms three separate one-cell chars,
-- thus it should be accepted despite being a mess.
command('set listchars=tab:d̞̄̃̒̉̎ò́̌̌̂̐l̞̀̄̆̌̚,eol:å̲')
screen:expect([[
{1:d̞̄̃̒̉̎ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐l̞̀̄̆̌̚d̞̄̃̒̉̎ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐l̞̀̄̆̌̚d̞̄̃̒̉̎ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐l̞̀̄̆̌̚}^x{1:å̲} |
{1:~ }|*3
|
]])
meths._invalidate_glyph_cache()
screen:_reset()
screen:expect([[
{1:d̞̄̃̒̉̎ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐l̞̀̄̆̌̚d̞̄̃̒̉̎ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐l̞̀̄̆̌̚d̞̄̃̒̉̎ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐ò́̌̌̂̐l̞̀̄̆̌̚}^x{1:å̲} |
{1:~ }|*3
|
]])
end)
end) end)

View File

@ -9,6 +9,7 @@ local NULL = helpers.NULL
local buffer = helpers.cimport('./src/nvim/buffer.h') local buffer = helpers.cimport('./src/nvim/buffer.h')
local globals = helpers.cimport('./src/nvim/globals.h') local globals = helpers.cimport('./src/nvim/globals.h')
local stl = helpers.cimport('./src/nvim/statusline.h') local stl = helpers.cimport('./src/nvim/statusline.h')
local grid = helpers.cimport('./src/nvim/grid.h')
describe('build_stl_str_hl', function() describe('build_stl_str_hl', function()
local buffer_byte_size = 100 local buffer_byte_size = 100
@ -25,8 +26,11 @@ describe('build_stl_str_hl', function()
output_buffer = to_cstr(string.rep(' ', buffer_byte_size)) output_buffer = to_cstr(string.rep(' ', buffer_byte_size))
local pat = arg.pat or '' local pat = arg.pat or ''
local fillchar = arg.fillchar or (' '):byte() local fillchar = arg.fillchar or ' '
local maximum_cell_count = arg.maximum_cell_count or buffer_byte_size local maximum_cell_count = arg.maximum_cell_count or buffer_byte_size
if type(fillchar) == type('') then
fillchar = grid.schar_from_str(fillchar)
end
return stl.build_stl_str_hl( return stl.build_stl_str_hl(
globals.curwin, globals.curwin,
@ -61,7 +65,7 @@ describe('build_stl_str_hl', function()
-- so we either fill in option with arg or an empty dictionary -- so we either fill in option with arg or an empty dictionary
local option = arg or {} local option = arg or {}
local fillchar = option.fillchar or (' '):byte() local fillchar = option.fillchar or ' '
local expected_cell_count = option.expected_cell_count or statusline_cell_count local expected_cell_count = option.expected_cell_count or statusline_cell_count
local expected_byte_length = option.expected_byte_length or #expected_stl local expected_byte_length = option.expected_byte_length or #expected_stl
@ -110,35 +114,35 @@ describe('build_stl_str_hl', function()
10, 10,
'abcde%=', 'abcde%=',
'abcde!!!!!', 'abcde!!!!!',
{ fillchar = ('!'):byte() } { fillchar = '!' }
) )
statusline_test( statusline_test(
'should handle `~` as a fillchar', 'should handle `~` as a fillchar',
10, 10,
'%=abcde', '%=abcde',
'~~~~~abcde', '~~~~~abcde',
{ fillchar = ('~'):byte() } { fillchar = '~' }
) )
statusline_test( statusline_test(
'should put fillchar `!` in between text', 'should put fillchar `!` in between text',
10, 10,
'abc%=def', 'abc%=def',
'abc!!!!def', 'abc!!!!def',
{ fillchar = ('!'):byte() } { fillchar = '!' }
) )
statusline_test( statusline_test(
'should put fillchar `~` in between text', 'should put fillchar `~` in between text',
10, 10,
'abc%=def', 'abc%=def',
'abc~~~~def', 'abc~~~~def',
{ fillchar = ('~'):byte() } { fillchar = '~' }
) )
statusline_test( statusline_test(
'should put fillchar `━` in between text', 'should put fillchar `━` in between text',
10, 10,
'abc%=def', 'abc%=def',
'abc━━━━def', 'abc━━━━def',
{ fillchar = 0x2501 } { fillchar = '' }
) )
statusline_test( statusline_test(
'should handle zero-fillchar as a space', 'should handle zero-fillchar as a space',
@ -249,7 +253,7 @@ describe('build_stl_str_hl', function()
expected_stl:gsub('%~', ' '), expected_stl:gsub('%~', ' '),
arg arg
) )
arg.fillchar = ('!'):byte() arg.fillchar = '!'
statusline_test( statusline_test(
description .. ' with fillchar `!`', description .. ' with fillchar `!`',
statusline_cell_count, statusline_cell_count,
@ -257,7 +261,7 @@ describe('build_stl_str_hl', function()
expected_stl:gsub('%~', '!'), expected_stl:gsub('%~', '!'),
arg arg
) )
arg.fillchar = 0x2501 arg.fillchar = ''
statusline_test( statusline_test(
description .. ' with fillchar `━`', description .. ' with fillchar `━`',
statusline_cell_count, statusline_cell_count,
@ -456,7 +460,7 @@ describe('build_stl_str_hl', function()
10, 10,
'Ą%=mid%=end', 'Ą%=mid%=end',
'Ą@mid@@end', 'Ą@mid@@end',
{ fillchar = ('@'):byte() } { fillchar = '@' }
) )
-- escaping % testing -- escaping % testing