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;
#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)
{
*(*b)++ = (char)((v >> 8) & 0xff);
@ -98,10 +98,9 @@ static char *mpack_array_dyn16(char **buf)
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);
size_t len = strlen(str);
mpack_w(buf, 0xa0 | len);
memcpy(*buf, str, len);
*buf += len;
@ -566,7 +565,7 @@ static void flush_event(UIData *data)
// [2, "redraw", [...]]
mpack_array(buf, 3);
mpack_uint(buf, 2);
mpack_str(buf, "redraw");
mpack_str(buf, S_LEN("redraw"));
data->nevents_pos = mpack_array_dyn16(buf);
}
}
@ -607,7 +606,7 @@ static bool prepare_call(UI *ui, const char *name)
data->cur_event = name;
char **buf = &data->buf_wptr;
data->ncalls_pos = mpack_array_dyn16(buf);
mpack_str(buf, name);
mpack_str(buf, name, strlen(name));
data->nevents++;
data->ncalls = 1;
return true;
@ -640,17 +639,18 @@ static void push_call(UI *ui, const char *name, Array args)
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)
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;
char **buf = &data->buf_wptr;
mpack_array(buf, 3);
mpack_uint(buf, 2);
mpack_str(buf, "redraw");
mpack_str(buf, S_LEN("redraw"));
mpack_array(buf, 1);
mpack_array(buf, 2);
mpack_str(buf, name);
mpack_str(buf, name, name_len);
} else {
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);
nelem++;
mpack_array(buf, csize);
char sc_buf[MAX_SCHAR_SIZE];
schar_get(sc_buf, chunk[i]);
mpack_str(buf, sc_buf);
char *size_byte = (*buf)++;
size_t len = schar_get_adv(buf, chunk[i]);
*size_byte = (char)(0xa0 | len);
if (csize >= 2) {
mpack_uint(buf, (uint32_t)attrs[i]);
if (csize >= 3) {
@ -916,7 +916,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
nelem++;
data->ncells_pending += 1;
mpack_array(buf, 3);
mpack_str(buf, " ");
mpack_str(buf, S_LEN(" "));
mpack_uint(buf, (uint32_t)clearattr);
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;
int maxwidth;
int fillchar = 0;
schar_T fillchar = 0;
int statuscol_lnum = 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)) {
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, {
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;
@ -2181,7 +2183,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 };
if (opts->use_tabline) {
fillchar = ' ';
fillchar = schar_from_ascii(' ');
} else {
if (fillchar == 0) {
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;
wp->w_p_crb = false;
int width = build_stl_str_hl(wp,
buf,
sizeof(buf),
str.data,
-1,
0,
fillchar,
maxwidth,
opts->highlights ? &hltab : NULL,
NULL,
int width = build_stl_str_hl(wp, buf, sizeof(buf), str.data, -1, 0, fillchar, maxwidth,
opts->highlights ? &hltab : NULL, NULL,
statuscol_lnum ? &statuscol : NULL);
PUT(result, "width", INTEGER_OBJ(width));

View File

@ -971,41 +971,41 @@ typedef struct {
/// Characters from the 'listchars' option.
typedef struct {
int eol;
int ext;
int prec;
int nbsp;
int space;
int tab1; ///< first tab character
int tab2; ///< second tab character
int tab3; ///< third tab character
int lead;
int trail;
int *multispace;
int *leadmultispace;
int conceal;
schar_T eol;
schar_T ext;
schar_T prec;
schar_T nbsp;
schar_T space;
schar_T tab1; ///< first tab character
schar_T tab2; ///< second tab character
schar_T tab3; ///< third tab character
schar_T lead;
schar_T trail;
schar_T *multispace;
schar_T *leadmultispace;
schar_T conceal;
} lcs_chars_T;
/// Characters from the 'fillchars' option.
typedef struct {
int stl;
int stlnc;
int wbr;
int horiz;
int horizup;
int horizdown;
int vert;
int vertleft;
int vertright;
int verthoriz;
int fold;
int foldopen; ///< when fold is open
int foldclosed; ///< when fold is closed
int foldsep; ///< continuous fold marker
int diff;
int msgsep;
int eob;
int lastline;
schar_T stl;
schar_T stlnc;
schar_T wbr;
schar_T horiz;
schar_T horizup;
schar_T horizdown;
schar_T vert;
schar_T vertleft;
schar_T vertright;
schar_T verthoriz;
schar_T fold;
schar_T foldopen; ///< when fold is open
schar_T foldclosed; ///< when fold is closed
schar_T foldsep; ///< continuous fold marker
schar_T diff;
schar_T msgsep;
schar_T eob;
schar_T lastline;
} fcs_chars_T;
/// 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) {
*buf = NUL;

View File

@ -64,6 +64,7 @@ typedef struct {
colnr_T vcol; ///< virtual column, before wrapping
int col; ///< visual column on screen, after wrapping
int boguscols; ///< nonexistent columns added to "col" to force wrapping
int old_boguscols; ///< bogus boguscols
int vcol_off; ///< offset for concealed characters
int off; ///< offset relative start of line
@ -83,10 +84,10 @@ typedef struct {
int n_extra; ///< number of extra bytes
int n_attr; ///< chars with special attr
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 c_extra; ///< extra chars, all the same
int c_final; ///< final char, mandatory if set
schar_T sc_extra; ///< extra chars, all the same
schar_T sc_final; ///< final char, mandatory if set
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);
for (int i = 0; i < fdc; i++) {
int symbol = 0;
schar_T symbol = 0;
if (i >= level) {
symbol = ' ';
symbol = schar_from_ascii(' ');
} else if (i == closedcol - 1 && closed) {
symbol = wp->w_p_fcs_chars.foldclosed;
} 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) {
symbol = wp->w_p_fcs_chars.foldsep;
} else if (first_level + i <= 9) {
symbol = '0' + first_level + i;
symbol = schar_from_ascii('0' + first_level + i);
} else {
symbol = '>';
symbol = schar_from_ascii('>');
}
if (out_buffer) {
out_buffer[i] = schar_from_char(symbol);
out_buffer[i] = symbol;
} else {
linebuf_vcol[*wlv_off] = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3;
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)
{
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) {
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];
} else {
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);
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);
} else if (wlv->filler_todo > 0) {
// Draw "deleted" diff line(s)
int c = (char2cells(wp->w_p_fcs_chars.diff) > 1) ? '-' : wp->w_p_fcs_chars.diff;
draw_col_fill(wlv, schar_from_char(c), remaining, win_hl_attr(wp, HLF_DED));
schar_T c = wp->w_p_fcs_chars.diff;
draw_col_fill(wlv, c, remaining, win_hl_attr(wp, HLF_DED));
}
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) {
continue;
}
wlv->c_extra = NUL;
wlv->c_final = NUL;
wlv->sc_extra = NUL;
wlv->sc_final = NUL;
wlv->extra_attr = attr;
wlv->n_attr = mb_charlen(text);
// 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));
}
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.
/// 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 mb_l = 1; // multi-byte byte length
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_end = -1; // last col of changed area
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);
bool is_concealing = false;
bool did_wcol = false;
int old_boguscols = 0;
#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);
@ -994,6 +993,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.fromcol = -10;
wlv.tocol = MAXCOL;
wlv.vcol_sbr = -1;
wlv.old_boguscols = 0;
buf_T *buf = wp->w_buffer;
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 leadcol = 0; // start of leading spaces
int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
bool lcs_eol_todo = true; // need to keep track of this even if lcs_eol is NUL
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_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.
v = ptr - line;
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);
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) {
foldtext_free = wlv.p_extra;
}
wlv.c_extra = NUL;
wlv.c_final = NUL;
wlv.sc_extra = NUL;
wlv.sc_final = NUL;
wlv.p_extra[wlv.n_extra] = NUL;
// 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) {
// Fill rest of line with 'fold'.
wlv.c_extra = wp->w_p_fcs_chars.fold;
wlv.c_final = NUL;
wlv.sc_extra = wp->w_p_fcs_chars.fold;
wlv.sc_final = NUL;
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
// represent special characters (non-printable stuff) and other
// things. When all characters are the same, c_extra is used.
// If c_final is set, it will compulsorily be used at the end.
// things. When all characters are the same, sc_extra is used.
// 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[n_extra]".
// For the '$' of the 'list' option, n_extra == 1, p_extra == "".
if (wlv.n_extra > 0) {
if (wlv.c_extra != NUL || (wlv.n_extra == 1 && wlv.c_final != NUL)) {
mb_c = (wlv.n_extra == 1 && wlv.c_final != NUL) ? wlv.c_final : wlv.c_extra;
mb_schar = schar_from_char(mb_c);
if (wlv.sc_extra != NUL || (wlv.n_extra == 1 && wlv.sc_final != NUL)) {
mb_schar = (wlv.n_extra == 1 && wlv.sc_final != NUL) ? wlv.sc_final : wlv.sc_extra;
mb_c = schar_get_first_codepoint(mb_schar);
wlv.n_extra--;
} else {
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(' ');
} else if (has_fold) {
// skip writing the buffer line itself
mb_c = NUL;
mb_schar = NUL;
} else {
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_schar = schar_from_char(mb_c);
wlv.n_extra = (int)strlen(wlv.p_extra);
wlv.c_extra = NUL;
wlv.c_final = NUL;
wlv.sc_extra = NUL;
wlv.sc_final = NUL;
if (area_attr == 0 && search_attr == 0) {
wlv.n_attr = wlv.n_extra + 1;
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
// next line.
if (wlv.col >= grid->cols - 1 && utf_char2cells(mb_c) == 2) {
mb_schar = schar_from_ascii('>');
mb_c = '>';
mb_l = 1;
mb_schar = schar_from_ascii(mb_c);
multi_attr = win_hl_attr(wp, HLF_AT);
// Put pointer back so that the character will be
// 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.
if (wlv.skip_cells > 0 && mb_l > 1 && wlv.n_extra == 0) {
wlv.n_extra = 1;
wlv.c_extra = MB_FILLER_CHAR;
wlv.c_final = NUL;
wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR);
wlv.sc_final = NUL;
mb_schar = schar_from_ascii(' ');
mb_c = ' ';
mb_l = 1;
mb_schar = schar_from_ascii(mb_c);
if (area_attr == 0 && search_attr == 0) {
wlv.n_attr = wlv.n_extra + 1;
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
// 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) {
@ -1957,7 +1958,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
spell_attr = 0;
// do not calculate cap_col at the end of the line or when
// 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;
hlf_T spell_hlf = HLF_COUNT;
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
// '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))) {
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,
wp->w_buffer->b_p_vts_array) - 1;
}
wlv.c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
wlv.c_final = NUL;
wlv.sc_extra = schar_from_ascii(mb_off > 0 ? MB_FILLER_CHAR : ' ');
wlv.sc_final = NUL;
if (mb_c < 128 && ascii_iswhite(mb_c)) {
if (mb_c == TAB) {
// See "Tab alignment" below.
FIX_FOR_BOGUSCOLS;
fix_for_boguscols(&wlv);
}
if (!wp->w_p_list) {
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 <= trailcol))) {
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) {
multispace_pos = 0;
}
} 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.extra_attr = win_hl_attr(wp, HLF_0);
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)
|| (leadcol != 0 && ptr < line + leadcol))) {
if (leadcol != 0 && in_multispace && ptr < line + leadcol
&& 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) {
multispace_pos = 0;
}
} 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) {
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) {
mb_c = wp->w_p_lcs_chars.space;
mb_schar = wp->w_p_lcs_chars.space;
}
wlv.n_attr = 1;
wlv.extra_attr = win_hl_attr(wp, HLF_0);
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
tab_len += wlv.vcol_off;
}
// boguscols before FIX_FOR_BOGUSCOLS macro from above.
if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0
// boguscols before fix_for_boguscols() from above.
if (wp->w_p_lcs_chars.tab1 && wlv.old_boguscols > 0
&& 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
// to use for a tab, else we need to calculate the
// width for a tab.
int tab2_len = utf_char2len(wp->w_p_lcs_chars.tab2);
int len = tab_len * tab2_len;
size_t tab2_len = schar_len(wp->w_p_lcs_chars.tab2);
size_t len = (size_t)tab_len * tab2_len;
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) {
len += wlv.n_extra - tab_len;
len += (size_t)(wlv.n_extra - tab_len);
}
mb_c = wp->w_p_lcs_chars.tab1;
char *p = get_extra_buf((size_t)len + 1);
memset(p, ' ', (size_t)len);
p[len] = NUL;
mb_schar = wp->w_p_lcs_chars.tab1;
mb_c = schar_get_first_codepoint(mb_schar);
char *p = get_extra_buf(len + 1);
memset(p, ' ', len);
wlv.p_extra = p;
for (int i = 0; i < tab_len; i++) {
if (*p == NUL) {
tab_len = i;
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 (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
lcs = wp->w_p_lcs_chars.tab3;
}
p += utf_char2bytes(lcs, p);
wlv.n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
size_t slen = schar_get_adv(&p, lcs);
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
if (wlv.vcol_off > 0) {
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
// line. Note that the tab can be longer than
// 'tabstop' when there are concealed characters.
FIX_FOR_BOGUSCOLS;
fix_for_boguscols(&wlv);
// Make sure, the highlighting for the tab char will be
// 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) {
mb_c = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3)
? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1;
mb_schar = (wlv.n_extra == 0 && wp->w_p_lcs_chars.tab3)
? 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) {
wlv.c_extra = NUL; // using p_extra from above
wlv.sc_extra = NUL; // using p_extra from above
} 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.extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = wlv.char_attr; // save current attr
} else {
wlv.c_final = NUL;
wlv.c_extra = ' ';
mb_c = ' ';
wlv.sc_final = NUL;
wlv.sc_extra = schar_from_ascii(' ');
mb_schar = schar_from_ascii(' ');
}
mb_schar = schar_from_char(mb_c);
} else if (mb_c == NUL
mb_c = schar_get_first_codepoint(mb_schar);
} else if (mb_schar == NUL
&& (wp->w_p_list
|| ((wlv.fromcol >= 0 || fromcol_prev >= 0)
&& 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
&& lnum == wp->w_cursor.lnum
&& wlv.vcol == wp->w_virtcol)))
&& lcs_eol_one > 0) {
&& lcs_eol_todo && lcs_eol != NUL) {
// Display a '$' after the line or highlight an extra
// character if the line break is included.
// 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;
}
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 {
mb_c = ' ';
mb_schar = schar_from_ascii(' ');
}
lcs_eol_one = -1;
lcs_eol_todo = false;
ptr--; // put it back at the NUL
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
wlv.n_attr = 1;
mb_schar = schar_from_char(mb_c);
} else if (mb_c != NUL) {
mb_c = schar_get_first_codepoint(mb_schar);
} else if (mb_schar != NUL) {
wlv.p_extra = transchar_buf(wp->w_buffer, mb_c);
if (wlv.n_extra == 0) {
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) {
rl_mirror_ascii(wlv.p_extra, NULL); // reverse "<12>"
}
wlv.c_extra = NUL;
wlv.c_final = NUL;
wlv.sc_extra = NUL;
wlv.sc_final = NUL;
if (wp->w_p_lbr) {
mb_c = (uint8_t)(*wlv.p_extra);
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)
&& !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
wlv.char_attr = conceal_attr;
bool is_conceal_char = false;
if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)
|| has_match_conc > 1 || decor_conceal > 1)
&& (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
// character.
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) {
mb_schar = decor_state.conceal_char;
mb_c = schar_get_first_codepoint(mb_schar);
is_conceal_char = true;
if (decor_state.conceal_attr) {
wlv.char_attr = decor_state.conceal_attr;
}
} 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) {
mb_c = wp->w_p_lcs_chars.conceal;
mb_schar = wp->w_p_lcs_chars.conceal;
} else {
mb_c = ' ';
mb_schar = schar_from_ascii(' ');
}
mb_c = schar_get_first_codepoint(mb_schar);
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;
wlv.skip_cells = 1;
}
if (!is_conceal_char) {
mb_schar = schar_from_char(mb_c);
}
} else {
prev_syntax_id = 0;
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_wrap ? (wp->w_skipcol > 0 && wlv.row == 0) : wp->w_leftcol > 0)
&& wlv.filler_todo <= 0
&& mb_c != NUL) {
mb_c = wp->w_p_lcs_chars.prec;
&& mb_schar != NUL) {
mb_schar = wp->w_p_lcs_chars.prec;
lcs_prec_todo = NUL;
if (utf_char2cells(mb_c) > 1) {
// Double-width character being overwritten by the "precedes"
// character, need to fill up half the character.
wlv.c_extra = MB_FILLER_CHAR;
wlv.c_final = NUL;
wlv.sc_extra = schar_from_ascii(MB_FILLER_CHAR);
wlv.sc_final = NUL;
wlv.n_extra = 1;
wlv.n_attr = 2;
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
wlv.char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr
n_attr3 = 1;
}
// 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
// one of the matches
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
// char on the screen, just overwrite that one (tricky!) Not
// 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
&& (VIsual_mode != Ctrl_V
|| 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.
if (mb_c == NUL) {
if (mb_schar == NUL) {
// Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
if (wp->w_p_wrap) {
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;
// Make sure alignment is the same regardless
// if listchars=eol:X is used or not.
int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0
? 1 : 0);
int eol_skip = (lcs_eol_todo && eol_hl_off == 0 ? 1 : 0);
if (has_decor) {
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.
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);
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.col == grid->cols - 1
&& !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
decor_redraw_col(wp, (colnr_T)(ptr - line), wlv.off, false, &decor_state);
}
if (*ptr != NUL
|| lcs_eol_one > 0
|| (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL))
|| (lcs_eol > 0 && lcs_eol_todo)
|| (wlv.n_extra > 0 && (wlv.sc_extra != NUL || *wlv.p_extra != NUL))
|| 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);
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
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
&& 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))) {
bool wrap = wp->w_p_wrap // Wrapping enabled.
&& 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.
&& (grid->cols == Columns // Window spans the width of the screen,
|| 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
// '$' 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;
}
// When the window is too narrow draw all "@" lines.
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);
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.
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);
@ -1297,9 +1297,8 @@ static void draw_vsep_win(win_T *wp)
// draw the vertical separator right of this window
int hl = win_hl_attr(wp, HLF_C);
int c = wp->w_p_fcs_chars.vert;
grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
schar_T c = wp->w_p_fcs_chars.vert;
grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, c, hl);
}
/// 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
int hl = win_hl_attr(wp, HLF_C);
int c = wp->w_p_fcs_chars.horiz;
grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
wp->w_wincol, W_ENDCOL(wp), c, c, hl);
schar_T c = wp->w_p_fcs_chars.horiz;
grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, wp->w_wincol, W_ENDCOL(wp), c, c, hl);
}
/// 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
// So if they're not vertically connected, assume they're horizontally connected
int c;
if (vsep_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) {
c = wp->w_p_fcs_chars.vertright;
return wp->w_p_fcs_chars.vertright;
} 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) {
c = wp->w_p_fcs_chars.horizdown;
return wp->w_p_fcs_chars.horizdown;
} 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"
@ -2384,7 +2380,7 @@ static void win_update(win_T *wp)
// Last line isn't finished: Display "@@@" in the last screen line.
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(3, wp->w_grid.cols, ' ', at_attr);
grid_line_fill(3, wp->w_grid.cols, schar_from_ascii(' '), at_attr);
grid_line_flush();
set_empty_rows(wp, srow);
wp->w_botline = lnum;
@ -2399,7 +2395,8 @@ static void win_update(win_T *wp)
set_empty_rows(wp, srow);
wp->w_botline = lnum;
} 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);
wp->w_botline = lnum;
}
@ -2432,7 +2429,8 @@ static void win_update(win_T *wp)
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);
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.
static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, int endrow,
int attr)
static int win_clear_end(win_T *wp, int off, int width, int row, int endrow, int attr)
{
int nn = off + width;
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) {
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 {
grid_fill(&wp->w_grid, row, endrow, off, nn, c1, c2, attr);
grid_clear(&wp->w_grid, row, endrow, off, nn, attr);
}
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".
/// Use "c2" as filler character.
/// 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);
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
int fdc = compute_foldcolumn(wp, 0);
if (fdc > 0) {
n = win_fill_end(wp, ' ', ' ', n, fdc, row, endrow,
win_hl_attr(wp, HLF_FC));
n = win_clear_end(wp, n, fdc, row, endrow, win_hl_attr(wp, HLF_FC));
}
// draw the sign column
int count = wp->w_scwidth;
if (count > 0) {
n = win_fill_end(wp, ' ', ' ', n, SIGN_WIDTH * count, row,
endrow, win_hl_attr(wp, HLF_SC));
n = win_clear_end(wp, n, SIGN_WIDTH * count, row, endrow, win_hl_attr(wp, HLF_SC));
}
// draw the number column
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,
win_hl_attr(wp, HLF_N));
n = win_clear_end(wp, n, number_width(wp) + 1, row, endrow, 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;
screenchar_adjust(&grid, &row, &col);
int c;
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
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;
rettv->vval.v_number = (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols)
? -1 : schar_get_first_codepoint(grid_getchar(grid, row, col, NULL));
}
/// "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)
{
int syntax_flags = 0;
int cchar;
int matchid = 0;
char str[NUMBUFLEN];
@ -8402,14 +8394,13 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
// get the conceal character
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) {
cchar = (curwin->w_p_lcs_chars.conceal == NUL)
? ' '
: curwin->w_p_lcs_chars.conceal;
? schar_from_ascii(' ') : curwin->w_p_lcs_chars.conceal;
}
if (cchar != NUL) {
utf_char2bytes(cchar, str);
schar_get(str, cchar);
}
}
}

View File

@ -27,6 +27,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/types_defs.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) {
return 0;
@ -120,6 +121,13 @@ void schar_cache_clear(void)
{
decor_check_invalid_glyphs();
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)
@ -137,15 +145,39 @@ bool schar_high(schar_T sc)
# define schar_idx(sc) (sc >> 8)
#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)) {
uint32_t idx = schar_idx(sc);
assert(idx < glyph_cache.h.n_keys);
xstrlcpy(buf_out, &glyph_cache.keys[idx], 32);
return strlen(&glyph_cache.keys[idx]);
} else {
memcpy(buf_out, (char *)&sc, 4);
buf_out[4] = NUL;
return strnlen((char *)&sc, 4);
}
}
@ -433,14 +465,13 @@ int grid_line_puts(int col, const char *text, int textlen, int attr)
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);
if (start_col >= end_col) {
return;
}
schar_T sc = schar_from_char(c);
for (int col = start_col; col < end_col; col++) {
linebuf_char[col] = sc;
linebuf_attr[col] = attr;
@ -532,11 +563,17 @@ void grid_line_flush_if_valid_row(void)
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"
/// to "end_col" (exclusive) with character "c1" in first column followed by
/// "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,
int c2, int attr)
void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, schar_T c1,
schar_T c2, int attr)
{
int row_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;
schar_T sc = schar_from_char(c1);
schar_T sc = c1;
for (col = start_col; col < end_col; col++) {
size_t off = lineoff + (size_t)col;
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;
if (col == start_col) {
sc = schar_from_char(c2);
sc = c2;
}
}
if (dirty_last > dirty_first) {
@ -683,7 +720,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol
col++;
}
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;

View File

@ -666,7 +666,7 @@ bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char **lin
/// is endcol.
/// 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 *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)
{
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.
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;
}
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)
{
char buf[MB_MAXCHAR + 1];
size_t size = (size_t)utf_char2bytes(curwin->w_p_fcs_chars.msgsep, buf);
buf[size] = '\0';
char buf[MAX_SCHAR_SIZE];
size_t size = schar_get(buf, curwin->w_p_fcs_chars.msgsep);
ui_call_msg_set_pos(msg_grid.handle, row, scrolled,
(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
void msg_prt_line(const char *s, bool list)
{
int c;
schar_T sc;
int col = 0;
int n_extra = 0;
int c_extra = 0;
int c_final = 0;
const char *p_extra = NULL; // init to make SASC shut up
schar_T sc_extra = 0;
schar_T sc_final = 0;
const char *p_extra = NULL; // init to make SASC shut up. ASCII only!
int n;
int attr = 0;
const char *lead = NULL;
@ -1845,13 +1844,13 @@ void msg_prt_line(const char *s, bool list)
while (!got_int) {
if (n_extra > 0) {
n_extra--;
if (n_extra == 0 && c_final) {
c = c_final;
} else if (c_extra) {
c = c_extra;
if (n_extra == 0 && sc_final) {
sc = sc_final;
} else if (sc_extra) {
sc = sc_extra;
} else {
assert(p_extra != NULL);
c = (unsigned char)(*p_extra++);
sc = schar_from_ascii((unsigned char)(*p_extra++));
}
} else if ((l = utfc_ptr2len(s)) > 1) {
col += utf_ptr2cells(s);
@ -1859,10 +1858,8 @@ void msg_prt_line(const char *s, bool list)
if (l >= MB_MAXBYTES) {
xstrlcpy(buf, "?", sizeof(buf));
} else if (curwin->w_p_lcs_chars.nbsp != NUL && list
&& (utf_ptr2char(s) == 160
|| utf_ptr2char(s) == 0x202f)) {
int len = utf_char2bytes(curwin->w_p_lcs_chars.nbsp, buf);
buf[len] = NUL;
&& (utf_ptr2char(s) == 160 || utf_ptr2char(s) == 0x202f)) {
schar_get(buf, curwin->w_p_lcs_chars.nbsp);
} else {
memmove(buf, s, (size_t)l);
buf[l] = NUL;
@ -1872,7 +1869,9 @@ void msg_prt_line(const char *s, bool list)
continue;
} else {
attr = 0;
c = (uint8_t)(*s++);
int c = (uint8_t)(*s++);
sc_extra = NUL;
sc_final = NUL;
if (list) {
in_multispace = c == ' ' && (*s == ' '
|| (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)) {
// tab amount depends on current column
n_extra = tabstop_padding(col,
curbuf->b_p_ts,
n_extra = tabstop_padding(col, curbuf->b_p_ts,
curbuf->b_p_vts_array) - 1;
if (!list) {
c = ' ';
c_extra = ' ';
c_final = NUL;
sc = schar_from_ascii(' ');
sc_extra = schar_from_ascii(' ');
} else {
c = (n_extra == 0 && curwin->w_p_lcs_chars.tab3)
? curwin->w_p_lcs_chars.tab3
: curwin->w_p_lcs_chars.tab1;
c_extra = curwin->w_p_lcs_chars.tab2;
c_final = 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.tab1;
sc_extra = curwin->w_p_lcs_chars.tab2;
sc_final = curwin->w_p_lcs_chars.tab3;
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) {
p_extra = "";
c_extra = NUL;
c_final = NUL;
n_extra = 1;
c = curwin->w_p_lcs_chars.eol;
sc = curwin->w_p_lcs_chars.eol;
attr = HL_ATTR(HLF_AT);
s--;
} else if (c != NUL && (n = byte2cells(c)) > 1) {
n_extra = n - 1;
p_extra = transchar_byte_buf(NULL, c);
c_extra = NUL;
c_final = NUL;
c = (unsigned char)(*p_extra++);
sc = schar_from_ascii(*p_extra++);
// Use special coloring to be able to distinguish <hex> from
// the same in plain text.
attr = HL_ATTR(HLF_0);
} else if (c == ' ') {
if (lead != NULL && s <= lead && in_multispace
&& 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) {
multispace_pos = 0;
}
attr = HL_ATTR(HLF_0);
} 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);
} 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);
} else if (in_multispace
&& 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) {
multispace_pos = 0;
}
attr = HL_ATTR(HLF_0);
} 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);
} else {
sc = schar_from_ascii(' '); // SPACE!
}
} else {
sc = schar_from_ascii(c);
}
}
if (c == NUL) {
if (sc == NUL) {
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++;
}
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;
}
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
@ -2823,16 +2820,14 @@ static bool do_more_prompt(int typed_char)
if (toscroll == -1 && !to_redraw) {
grid_ins_lines(&msg_grid_adj, 0, 1, Rows, 0, Columns);
grid_fill(&msg_grid_adj, 0, 1, 0, Columns, ' ', ' ',
HL_ATTR(HLF_MSG));
grid_clear(&msg_grid_adj, 0, 1, 0, Columns, HL_ATTR(HLF_MSG));
// display line at top
disp_sb_line(0, mp);
} else {
// redisplay all lines
// TODO(bfredl): this case is not optimized (though only concerns
// event fragmentation, not unnecessary scroll events).
grid_fill(&msg_grid_adj, 0, Rows, 0, Columns, ' ', ' ',
HL_ATTR(HLF_MSG));
grid_clear(&msg_grid_adj, 0, Rows, 0, Columns, HL_ATTR(HLF_MSG));
for (int i = 0; mp != NULL && i < Rows - 1; i++) {
mp = disp_sb_line(i, mp);
msg_scrolled++;
@ -2858,8 +2853,7 @@ static bool do_more_prompt(int typed_char)
// scroll up, display line at bottom
msg_scroll_up(true, false);
inc_msg_scrolled();
grid_fill(&msg_grid_adj, Rows - 2, Rows - 1, 0, Columns, ' ', ' ',
HL_ATTR(HLF_MSG));
grid_clear(&msg_grid_adj, Rows - 2, Rows - 1, 0, Columns, HL_ATTR(HLF_MSG));
mp_last = disp_sb_line(Rows - 2, mp_last);
toscroll--;
}
@ -2867,8 +2861,7 @@ static bool do_more_prompt(int typed_char)
if (toscroll <= 0) {
// displayed the requested text, more prompt again
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));
msg_moremsg(false);
continue;
}
@ -2881,8 +2874,7 @@ static bool do_more_prompt(int typed_char)
}
// clear the --more-- message
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));
redraw_cmdline = true;
clear_cmdline = 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,
' ', ' ', HL_ATTR(HLF_MSG));
grid_fill(&msg_grid_adj, msg_row + 1, Rows, 0, Columns,
' ', ' ', HL_ATTR(HLF_MSG));
grid_clear(&msg_grid_adj, msg_row, msg_row + 1, msg_startcol, msg_endcol, HL_ATTR(HLF_MSG));
grid_clear(&msg_grid_adj, msg_row + 1, Rows, 0, Columns, HL_ATTR(HLF_MSG));
redraw_cmdline = true; // overwritten the command line
if (msg_row < Rows - 1 || msg_col == 0) {

View File

@ -1696,10 +1696,10 @@ static void didset_options2(void)
highlight_changed();
// 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'.
set_listchars_option(curwin, curwin->w_p_lcs, true);
set_chars_option(curwin, curwin->w_p_lcs, kListchars, true);
// Parse default for 'wildmode'.
check_opt_wim();
@ -4991,8 +4991,8 @@ void didset_window_options(win_T *wp, bool valid_cursor)
check_colorcolumn(wp);
briopt_check(wp);
fill_culopt_flags(NULL, wp);
set_fillchars_option(wp, wp->w_p_fcs, true);
set_listchars_option(wp, wp->w_p_lcs, true);
set_chars_option(wp, wp->w_p_fcs, kFillchars, 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
check_blending(wp);
set_winbar_win(wp, false, valid_cursor);

View File

@ -21,6 +21,7 @@
#include "nvim/fold.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.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.
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;
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
// local value
if (opt_lcs) {
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));
}
errmsg = set_chars_option(win, val, what, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL));
if (errmsg != NULL) {
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.
// If no error was returned above, we don't expect an error
// here, so ignore the return value.
if (opt_lcs) {
if (*wp->w_p_lcs == NUL) {
set_listchars_option(wp, wp->w_p_lcs, true);
}
} else {
if (*wp->w_p_fcs == NUL) {
set_fillchars_option(wp, wp->w_p_fcs, true);
}
char *opt = (what == kListchars) ? wp->w_p_lcs : wp->w_p_fcs;
if (*opt == NUL) {
set_chars_option(wp, opt, what, true);
}
}
@ -926,13 +919,14 @@ const char *did_set_chars_option(optset_T *args)
char **varp = (char **)args->os_varp;
const char *errmsg = NULL;
if (varp == &p_lcs // global 'listchars'
|| varp == &p_fcs) { // global 'fillchars'
errmsg = did_set_global_listfillchars(win, *varp, varp == &p_lcs, args->os_flags);
if (varp == &p_lcs) { // global 'listchars'
errmsg = did_set_global_chars_option(win, *varp, kListchars, 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'
errmsg = set_listchars_option(win, *varp, true);
errmsg = set_chars_option(win, *varp, kListchars, true);
} 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;
@ -2603,7 +2597,7 @@ static const char e_conflicts_with_value_of_fillchars[]
/// Calls mb_cptr2char_adv(p) and returns the character.
/// 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.
static int get_encoded_char_adv(const char **p)
static schar_T get_encoded_char_adv(const char **p)
{
const char *s = *p;
@ -2618,71 +2612,69 @@ static int get_encoded_char_adv(const char **p)
num = num * 256 + n;
}
*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 = utf_ptr2len(s);
int c = mb_cptr2char_adv(p);
if (clen == 1 && c > 127) { // Invalid UTF-8 byte
return 0;
}
return c;
int clen = utfc_ptr2len(s);
int firstc;
schar_T c = utfc_ptr2schar(s, &firstc);
*p += clen;
// Invalid UTF-8 byte or doublewidth not allowed
return ((clen == 1 && firstc > 127) || char2cells(firstc) > 1) ? 0 : c;
}
struct chars_tab {
int *cp; ///< char value
schar_T *cp; ///< char value
const char *name; ///< char id
int def; ///< default value
int fallback; ///< default value when "def" isn't single-width
const char *def; ///< default value
const char *fallback; ///< default value when "def" isn't single-width
};
static fcs_chars_T fcs_chars;
static const struct chars_tab fcs_tab[] = {
{ &fcs_chars.stl, "stl", ' ', NUL },
{ &fcs_chars.stlnc, "stlnc", ' ', NUL },
{ &fcs_chars.wbr, "wbr", ' ', NUL },
{ &fcs_chars.horiz, "horiz", 0x2500, '-' }, // ─
{ &fcs_chars.horizup, "horizup", 0x2534, '-' }, // ┴
{ &fcs_chars.horizdown, "horizdown", 0x252c, '-' }, // ┬
{ &fcs_chars.vert, "vert", 0x2502, '|' }, // │
{ &fcs_chars.vertleft, "vertleft", 0x2524, '|' }, // ┤
{ &fcs_chars.vertright, "vertright", 0x251c, '|' }, // ├
{ &fcs_chars.verthoriz, "verthoriz", 0x253c, '+' }, // ┼
{ &fcs_chars.fold, "fold", 0x00b7, '-' }, // ·
{ &fcs_chars.foldopen, "foldopen", '-', NUL },
{ &fcs_chars.foldclosed, "foldclose", '+', NUL },
{ &fcs_chars.foldsep, "foldsep", 0x2502, '|' }, // │
{ &fcs_chars.diff, "diff", '-', NUL },
{ &fcs_chars.msgsep, "msgsep", ' ', NUL },
{ &fcs_chars.eob, "eob", '~', NUL },
{ &fcs_chars.lastline, "lastline", '@', NUL },
{ &fcs_chars.stl, "stl", " ", NULL },
{ &fcs_chars.stlnc, "stlnc", " ", NULL },
{ &fcs_chars.wbr, "wbr", " ", NULL },
{ &fcs_chars.horiz, "horiz", "", "-" },
{ &fcs_chars.horizup, "horizup", "", "-" },
{ &fcs_chars.horizdown, "horizdown", "", "-" },
{ &fcs_chars.vert, "vert", "", "|" },
{ &fcs_chars.vertleft, "vertleft", "", "|" },
{ &fcs_chars.vertright, "vertright", "", "|" },
{ &fcs_chars.verthoriz, "verthoriz", "", "+" },
{ &fcs_chars.fold, "fold", "·", "-" },
{ &fcs_chars.foldopen, "foldopen", "-", NULL },
{ &fcs_chars.foldclosed, "foldclose", "+", NULL },
{ &fcs_chars.foldsep, "foldsep", "", "|" },
{ &fcs_chars.diff, "diff", "-", NULL },
{ &fcs_chars.msgsep, "msgsep", " ", NULL },
{ &fcs_chars.eob, "eob", "~", NULL },
{ &fcs_chars.lastline, "lastline", "@", NULL },
};
static lcs_chars_T lcs_chars;
static const struct chars_tab lcs_tab[] = {
{ &lcs_chars.eol, "eol", NUL, NUL },
{ &lcs_chars.ext, "extends", NUL, NUL },
{ &lcs_chars.nbsp, "nbsp", NUL, NUL },
{ &lcs_chars.prec, "precedes", NUL, NUL },
{ &lcs_chars.space, "space", NUL, NUL },
{ &lcs_chars.tab2, "tab", NUL, NUL },
{ &lcs_chars.lead, "lead", NUL, NUL },
{ &lcs_chars.trail, "trail", NUL, NUL },
{ &lcs_chars.conceal, "conceal", NUL, NUL },
{ NULL, "multispace", NUL, NUL },
{ NULL, "leadmultispace", NUL, NUL },
{ &lcs_chars.eol, "eol", NULL, NULL },
{ &lcs_chars.ext, "extends", NULL, NULL },
{ &lcs_chars.nbsp, "nbsp", NULL, NULL },
{ &lcs_chars.prec, "precedes", NULL, NULL },
{ &lcs_chars.space, "space", NULL, NULL },
{ &lcs_chars.tab2, "tab", NULL, NULL },
{ &lcs_chars.lead, "lead", NULL, NULL },
{ &lcs_chars.trail, "trail", NULL, NULL },
{ &lcs_chars.conceal, "conceal", NULL, NULL },
{ NULL, "multispace", NULL, NULL },
{ NULL, "leadmultispace", NULL, NULL },
};
/// Handle setting 'listchars' or 'fillchars'.
/// Assume monocell characters
///
/// @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.
/// @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 bool apply)
const char *set_chars_option(win_T *wp, const char *value, CharsOption what, bool apply)
{
const char *last_multispace = NULL; // Last occurrence of "multispace:"
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;
int entries;
if (is_listchars) {
if (what == kListchars) {
tab = lcs_tab;
entries = ARRAY_SIZE(lcs_tab);
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) {
// XXX: Characters taking 2 columns is forbidden (TUI limitation?).
// 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.tab3 = NUL;
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;
} else {
lcs_chars.multispace = NULL;
}
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;
} else {
lcs_chars.leadmultispace = NULL;
@ -2748,15 +2741,15 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
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;
if (round == 0) {
// Get length of lcs-multispace string in the first round
last_multispace = p;
multispace_len = 0;
while (*s != NUL && *s != ',') {
int c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
schar_T c1 = get_encoded_char_adv(&s);
if (c1 == 0) {
return e_invarg;
}
multispace_len++;
@ -2769,7 +2762,7 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
int c1 = get_encoded_char_adv(&s);
schar_T c1 = get_encoded_char_adv(&s);
if (p == last_multispace) {
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;
}
if (is_listchars && strcmp(tab[i].name, "leadmultispace") == 0) {
if (what == kListchars && strcmp(tab[i].name, "leadmultispace") == 0) {
const char *s = p + len + 1;
if (round == 0) {
// get length of lcs-leadmultispace string in first round
last_lmultispace = p;
lead_multispace_len = 0;
while (*s != NUL && *s != ',') {
int c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
schar_T c1 = get_encoded_char_adv(&s);
if (c1 == 0) {
return e_invarg;
}
lead_multispace_len++;
@ -2800,7 +2793,7 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
int c1 = get_encoded_char_adv(&s);
schar_T c1 = get_encoded_char_adv(&s);
if (p == last_lmultispace) {
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;
int c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
schar_T c1 = get_encoded_char_adv(&s);
if (c1 == 0) {
return e_invarg;
}
int c2 = 0;
int c3 = 0;
schar_T c2 = 0;
schar_T c3 = 0;
if (tab[i].cp == &lcs_chars.tab2) {
if (*s == NUL) {
return e_invarg;
}
c2 = get_encoded_char_adv(&s);
if (c2 == 0 || char2cells(c2) > 1) {
if (c2 == 0) {
return e_invarg;
}
if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s);
if (c3 == 0 || char2cells(c3) > 1) {
if (c3 == 0) {
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 (is_listchars) {
if (what == kListchars) {
xfree(wp->w_p_lcs_chars.multispace);
xfree(wp->w_p_lcs_chars.leadmultispace);
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
}
/// 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
/// 'fillchars' option.
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.
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;
}
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;
}
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;
}
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;
}
}

View File

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

View File

@ -629,19 +629,19 @@ void pum_redraw(void)
}
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;
} 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;
}
totwidth = pum_base_width + n;
}
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 {
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) {

View File

@ -58,7 +58,6 @@ typedef enum {
/// If inversion is possible we use it. Else '=' characters are used.
void win_redr_status(win_T *wp)
{
int fillchar;
int attr;
bool is_stl_global = global_stl_height() > 0;
static bool busy = false;
@ -84,7 +83,7 @@ void win_redr_status(win_T *wp)
// redraw custom status line
redraw_custom_statusline(wp);
} else {
fillchar = fillchar_status(&attr, wp);
schar_T fillchar = fillchar_status(&attr, wp);
const int stl_width = is_stl_global ? Columns : wp->w_width;
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.
if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) {
schar_T fillchar;
if (stl_connected(wp)) {
fillchar = fillchar_status(&attr, wp);
} else {
@ -176,7 +176,7 @@ void win_redr_status(win_T *wp)
fillchar = wp->w_p_fcs_chars.vert;
}
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();
}
busy = false;
@ -291,7 +291,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
int row;
int col = 0;
int maxwidth;
int fillchar;
schar_T fillchar;
char buf[MAXPATHL];
char transbuf[MAXPATHL];
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.
stl = p_tal;
row = 0;
fillchar = ' ';
fillchar = schar_from_ascii(' ');
attr = HL_ATTR(HLF_TPF);
maxwidth = Columns;
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;
row = Rows - 1;
maxwidth--; // writing in last column may cause scrolling
fillchar = ' ';
fillchar = schar_from_ascii(' ');
attr = HL_ATTR(HLF_MSG);
}
} else {
@ -513,7 +513,7 @@ void win_redr_ruler(win_T *wp)
&& *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) == NUL;
int width;
int fillchar;
schar_T fillchar;
int attr;
int off;
bool part_of_status = false;
@ -529,7 +529,7 @@ void win_redr_ruler(win_T *wp)
width = Columns;
part_of_status = true;
} else {
fillchar = ' ';
fillchar = schar_from_ascii(' ');
attr = HL_ATTR(HLF_MSG);
width = Columns;
off = 0;
@ -577,7 +577,7 @@ void win_redr_ruler(win_T *wp)
if (this_ru_col + o < width) {
// Need at least 3 chars left for get_rel_pos() + NUL.
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++;
}
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".
int fillchar_status(int *attr, win_T *wp)
schar_T fillchar_status(int *attr, win_T *wp)
{
int fill;
bool is_curwin = (wp == curwin);
if (is_curwin) {
if (wp == curwin) {
*attr = win_hl_attr(wp, HLF_S);
fill = wp->w_p_fcs_chars.stl;
return wp->w_p_fcs_chars.stl;
} else {
*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
@ -724,7 +721,6 @@ void draw_tabline(void)
int col = 0;
win_T *cwp;
int wincount;
int c;
grid_line_start(&default_grid, 0);
FOR_ALL_TABS(tp) {
tabcount++;
@ -826,12 +822,8 @@ void draw_tabline(void)
}
}
if (use_sep_chars) {
c = '_';
} else {
c = ' ';
}
grid_line_fill(col, Columns, c, attr_fill);
char c = use_sep_chars ? '_' : ' ';
grid_line_fill(col, Columns, schar_from_ascii(c), attr_fill);
// Draw the 'showcmd' information if 'showcmdloc' == "tabline".
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;
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);
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
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)
{
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) {
fillchar = ' ';
fillchar = schar_from_ascii(' ');
}
// 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;
// Fill up space left over by half a double-wide char.
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) {
min_group_width = 0 - min_group_width;
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
// prepend with filler characters.
} else {
// { 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));
if (out_p + group_len >= (out_end_p + 1)) {
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
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
// being used directly in 'statuscolumn'
for (int i = 0; i < fdc; i++) {
schar_get(out_p + buflen, fold_buf[i]);
buflen += strlen(out_p + buflen);
buflen += schar_get(out_p + buflen, fold_buf[i]);
}
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)) {
*out_p++ = ' ';
} else {
out_p += utf_char2bytes(fillchar, out_p);
schar_get_adv(&out_p, fillchar);
}
}
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.
if (fillable && *t == ' '
&& (!ascii_isdigit(*(t + 1)) || fillchar != '-')) {
out_p += utf_char2bytes(fillchar, out_p);
schar_get_adv(&out_p, fillchar);
} else {
*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 (; 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.
@ -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.
while (++width < maxwidth) {
trunc_p += utf_char2bytes(fillchar, trunc_p);
*trunc_p = NUL;
schar_get_adv(&trunc_p, fillchar);
}
}
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++) {
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 *seploc = start + dislocation;
STRMOVE(seploc, start);
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;

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
// line, clear it.
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) {
clear_cmdline = true;
}
@ -6145,7 +6145,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
}
}
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) {
clear_cmdline = true;
}
@ -6640,7 +6640,7 @@ void command_height(void)
// clear the lines added to cmdline
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;
redraw_cmdline = true;

View File

@ -6,6 +6,7 @@ local eq = helpers.eq
local exc_exec = helpers.exc_exec
local insert = helpers.insert
local feed = helpers.feed
local meths = helpers.meths
describe("'fillchars'", function()
local screen
@ -53,10 +54,18 @@ describe("'fillchars'", function()
]])
end)
it('supports composing multibyte char', function()
command('set fillchars=eob:å̲')
screen:expect([[
^ |
å̲ |*3
|
]])
end)
it('handles invalid values', function()
shouldfail('eob:') -- empty string
shouldfail('eob:馬') -- doublewidth char
shouldfail('eob:å̲') -- composing chars
shouldfail('eob:xy') -- two ascii chars
shouldfail('eob:\255', 'eob:<ff>') -- invalid UTF-8
end)
@ -178,4 +187,28 @@ describe("'listchars'", function()
|
]])
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)

View File

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