perf: reuse fast character size calculation algorithm from getvcol()

This commit is contained in:
VanaIgr 2023-12-18 20:57:04 -06:00 committed by zeertzjq
parent b5653984e5
commit cdf848a314
10 changed files with 497 additions and 477 deletions

View File

@ -141,17 +141,18 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
} }
} }
chartabsize_T cts; CharsizeArg arg;
init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line); CSType cstype = init_charsize_arg(&arg, curwin, pos->lnum, line);
while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL) { StrCharInfo ci = utf_ptr2StrCharInfo(line);
// Count a tab for what it's worth (if list mode not on) col = 0;
csize = win_lbr_chartabsize(&cts, &head); while (col <= wcol && *ci.ptr != NUL) {
MB_PTR_ADV(cts.cts_ptr); CharSize cs = win_charsize(cstype, col, ci.ptr, ci.chr.value, &arg);
cts.cts_vcol += csize; csize = cs.width;
head = cs.head;
col += cs.width;
ci = utfc_next(ci);
} }
col = cts.cts_vcol; idx = (int)(ci.ptr - line);
idx = (int)(cts.cts_ptr - line);
clear_chartabsize_arg(&cts);
// Handle all the special cases. The virtual_active() check // Handle all the special cases. The virtual_active() check
// is needed to ensure that a virtual position off the end of // is needed to ensure that a virtual position off the end of

View File

@ -1336,30 +1336,30 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
if (start_col > 0 && col_rows == 0) { if (start_col > 0 && col_rows == 0) {
char *prev_ptr = ptr; char *prev_ptr = ptr;
chartabsize_T cts; CharSize cs = { 0 };
int charsize = 0;
int head = 0;
init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, ptr); CharsizeArg arg;
cts.cts_max_head_vcol = start_col; CSType cstype = init_charsize_arg(&arg, wp, lnum, line);
while (cts.cts_vcol < start_col && *cts.cts_ptr != NUL) { arg.max_head_vcol = start_col;
head = 0; int vcol = wlv.vcol;
charsize = win_lbr_chartabsize(&cts, &head); StrCharInfo ci = utf_ptr2StrCharInfo(ptr);
cts.cts_vcol += charsize; while (vcol < start_col && *ci.ptr != NUL) {
prev_ptr = cts.cts_ptr; cs = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg);
MB_PTR_ADV(cts.cts_ptr); vcol += cs.width;
prev_ptr = ci.ptr;
ci = utfc_next(ci);
if (wp->w_p_list) { if (wp->w_p_list) {
in_multispace = *prev_ptr == ' ' && (*cts.cts_ptr == ' ' in_multispace = *prev_ptr == ' ' && (*ci.ptr == ' '
|| (prev_ptr > line && prev_ptr[-1] == ' ')); || (prev_ptr > line && prev_ptr[-1] == ' '));
if (!in_multispace) { if (!in_multispace) {
multispace_pos = 0; multispace_pos = 0;
} else if (cts.cts_ptr >= line + leadcol } else if (ci.ptr >= line + leadcol
&& wp->w_p_lcs_chars.multispace != NULL) { && wp->w_p_lcs_chars.multispace != NULL) {
multispace_pos++; 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 if (cts.cts_ptr < line + leadcol } else if (ci.ptr < line + leadcol
&& wp->w_p_lcs_chars.leadmultispace != NULL) { && wp->w_p_lcs_chars.leadmultispace != NULL) {
multispace_pos++; multispace_pos++;
if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) { if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
@ -1368,9 +1368,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
} }
} }
} }
wlv.vcol = cts.cts_vcol; wlv.vcol = vcol;
ptr = cts.cts_ptr; ptr = ci.ptr;
clear_chartabsize_arg(&cts); int charsize = cs.width;
int head = cs.head;
// When: // When:
// - 'cuc' is set, or // - 'cuc' is set, or
@ -2081,12 +2082,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
&& vim_isbreak(mb_c) && !vim_isbreak((uint8_t)(*ptr))) { && vim_isbreak(mb_c) && !vim_isbreak((uint8_t)(*ptr))) {
int mb_off = utf_head_off(line, ptr - 1); int mb_off = utf_head_off(line, ptr - 1);
char *p = ptr - (mb_off + 1); char *p = ptr - (mb_off + 1);
chartabsize_T cts;
CharsizeArg arg;
// lnum == 0, do not want virtual text to be counted here // lnum == 0, do not want virtual text to be counted here
init_chartabsize_arg(&cts, wp, 0, wlv.vcol, line, p); CSType cstype = init_charsize_arg(&arg, wp, 0, line);
wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1; wlv.n_extra = win_charsize(cstype, wlv.vcol, p, utf_ptr2CharInfo(p).value,
clear_chartabsize_arg(&cts); &arg).width - 1;
if (on_last_col && mb_c != TAB) { if (on_last_col && mb_c != TAB) {
// Do not continue search/match highlighting over the // Do not continue search/match highlighting over the

View File

@ -1679,33 +1679,37 @@ void change_indent(int type, int amount, int round, int replaced, bool call_chan
} else { } else {
// Compute the screen column where the cursor should be. // Compute the screen column where the cursor should be.
vcol = get_indent() - vcol; vcol = get_indent() - vcol;
curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); int const end_vcol = (colnr_T)((vcol < 0) ? 0 : vcol);
curwin->w_virtcol = end_vcol;
// Advance the cursor until we reach the right screen column. // Advance the cursor until we reach the right screen column.
int last_vcol = 0; new_cursor_col = 0;
char *ptr = get_cursor_line_ptr(); char *const line = get_cursor_line_ptr();
chartabsize_T cts; vcol = 0;
init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr); if (*line != NUL) {
while (cts.cts_vcol <= (int)curwin->w_virtcol) { CharsizeArg arg;
last_vcol = cts.cts_vcol; CSType cstype = init_charsize_arg(&arg, curwin, 0, line);
if (cts.cts_vcol > 0) { StrCharInfo ci = utf_ptr2StrCharInfo(line);
MB_PTR_ADV(cts.cts_ptr); while (true) {
int next_vcol = vcol + win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
if (next_vcol > end_vcol) {
break;
}
vcol = next_vcol;
ci = utfc_next(ci);
if (*ci.ptr == NUL) {
break;
}
} }
if (*cts.cts_ptr == NUL) { new_cursor_col = (int)(ci.ptr - line);
break;
}
cts.cts_vcol += lbr_chartabsize(&cts);
} }
vcol = last_vcol;
new_cursor_col = (int)(cts.cts_ptr - cts.cts_line);
clear_chartabsize_arg(&cts);
// May need to insert spaces to be able to position the cursor on // May need to insert spaces to be able to position the cursor on
// the right screen column. // the right screen column.
if (vcol != (int)curwin->w_virtcol) { if (vcol != (int)curwin->w_virtcol) {
curwin->w_cursor.col = (colnr_T)new_cursor_col; curwin->w_cursor.col = (colnr_T)new_cursor_col;
size_t i = (size_t)(curwin->w_virtcol - vcol); size_t i = (size_t)(curwin->w_virtcol - vcol);
ptr = xmallocz(i); char *ptr = xmallocz(i);
memset(ptr, ' ', i); memset(ptr, ' ', i);
new_cursor_col += (int)i; new_cursor_col += (int)i;
ins_str(ptr); ins_str(ptr);
@ -4347,14 +4351,16 @@ static bool ins_tab(void)
getvcol(curwin, cursor, &want_vcol, NULL, NULL); getvcol(curwin, cursor, &want_vcol, NULL, NULL);
char *tab = "\t"; char *tab = "\t";
chartabsize_T cts; int32_t tab_v = (uint8_t)(*tab);
init_chartabsize_arg(&cts, curwin, 0, vcol, tab, tab);
CharsizeArg arg;
CSType cstype = init_charsize_arg(&arg, curwin, 0, tab);
// Use as many TABs as possible. Beware of 'breakindent', 'showbreak' // Use as many TABs as possible. Beware of 'breakindent', 'showbreak'
// and 'linebreak' adding extra virtual columns. // and 'linebreak' adding extra virtual columns.
while (ascii_iswhite(*ptr)) { while (ascii_iswhite(*ptr)) {
int i = lbr_chartabsize(&cts); int i = win_charsize(cstype, vcol, tab, tab_v, &arg).width;
if (cts.cts_vcol + i > want_vcol) { if (vcol + i > want_vcol) {
break; break;
} }
if (*ptr != TAB) { if (*ptr != TAB) {
@ -4369,23 +4375,18 @@ static bool ins_tab(void)
} }
fpos.col++; fpos.col++;
ptr++; ptr++;
cts.cts_vcol += i; vcol += i;
} }
vcol = cts.cts_vcol;
clear_chartabsize_arg(&cts);
if (change_col >= 0) { if (change_col >= 0) {
int repl_off = 0; int repl_off = 0;
// Skip over the spaces we need. // Skip over the spaces we need.
init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr); cstype = init_charsize_arg(&arg, curwin, 0, ptr);
while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ') { while (vcol < want_vcol && *ptr == ' ') {
cts.cts_vcol += lbr_chartabsize(&cts); vcol += win_charsize(cstype, vcol, ptr, (uint8_t)(' '), &arg).width;
cts.cts_ptr++; ptr++;
repl_off++; repl_off++;
} }
ptr = cts.cts_ptr;
vcol = cts.cts_vcol;
clear_chartabsize_arg(&cts);
if (vcol > want_vcol) { if (vcol > want_vcol) {
// Must have a char with 'showbreak' just before it. // Must have a char with 'showbreak' just before it.
@ -4556,8 +4557,6 @@ static int ins_digraph(void)
// Returns the char to be inserted, or NUL if none found. // Returns the char to be inserted, or NUL if none found.
int ins_copychar(linenr_T lnum) int ins_copychar(linenr_T lnum)
{ {
char *ptr;
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
vim_beep(BO_COPY); vim_beep(BO_COPY);
return NUL; return NUL;
@ -4565,24 +4564,22 @@ int ins_copychar(linenr_T lnum)
// try to advance to the cursor column // try to advance to the cursor column
validate_virtcol(); validate_virtcol();
int const end_vcol = curwin->w_virtcol;
char *line = ml_get(lnum); char *line = ml_get(lnum);
char *prev_ptr = line;
chartabsize_T cts; CharsizeArg arg;
init_chartabsize_arg(&cts, curwin, lnum, 0, line, line); CSType cstype = init_charsize_arg(&arg, curwin, lnum, line);
while (cts.cts_vcol < curwin->w_virtcol && *cts.cts_ptr != NUL) { StrCharInfo ci = utf_ptr2StrCharInfo(line);
prev_ptr = cts.cts_ptr; int vcol = 0;
cts.cts_vcol += lbr_chartabsize_adv(&cts); while (vcol < end_vcol && *ci.ptr != NUL) {
vcol += win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
if (vcol > end_vcol) {
break;
}
ci = utfc_next(ci);
} }
if (cts.cts_vcol > curwin->w_virtcol) { int c = ci.chr.value < 0 ? (uint8_t)(*ci.ptr) : ci.chr.value;
ptr = prev_ptr;
} else {
ptr = cts.cts_ptr;
}
clear_chartabsize_arg(&cts);
int c = utf_ptr2char(ptr);
if (c == NUL) { if (c == NUL) {
vim_beep(BO_COPY); vim_beep(BO_COPY);
} }

View File

@ -2502,20 +2502,22 @@ static int vgetorpeek(bool advance)
// we are expecting to truncate the trailing // we are expecting to truncate the trailing
// white-space, so find the last non-white // white-space, so find the last non-white
// character -- webb // character -- webb
if (did_ai if (did_ai && *skipwhite(get_cursor_line_ptr() + curwin->w_cursor.col) == NUL) {
&& *skipwhite(get_cursor_line_ptr() + curwin->w_cursor.col) == NUL) {
curwin->w_wcol = 0; curwin->w_wcol = 0;
ptr = get_cursor_line_ptr(); ptr = get_cursor_line_ptr();
chartabsize_T cts; char *endptr = ptr + curwin->w_cursor.col;
init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, ptr, ptr);
while (cts.cts_ptr < ptr + curwin->w_cursor.col) { CharsizeArg arg;
if (!ascii_iswhite(*cts.cts_ptr)) { CSType cstype = init_charsize_arg(&arg, curwin, curwin->w_cursor.lnum, ptr);
curwin->w_wcol = cts.cts_vcol; StrCharInfo ci = utf_ptr2StrCharInfo(ptr);
int vcol = 0;
while (ci.ptr < endptr) {
if (!ascii_iswhite(ci.chr.value)) {
curwin->w_wcol = vcol;
} }
cts.cts_vcol += lbr_chartabsize(&cts); vcol += win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
cts.cts_ptr += utfc_ptr2len(cts.cts_ptr); ci = utfc_next(ci);
} }
clear_chartabsize_arg(&cts);
curwin->w_wrow = curwin->w_cline_row curwin->w_wrow = curwin->w_cline_row
+ curwin->w_wcol / curwin->w_width_inner; + curwin->w_wcol / curwin->w_width_inner;

View File

@ -1246,18 +1246,19 @@ int get_lisp_indent(void)
curwin->w_cursor.col = pos->col; curwin->w_cursor.col = pos->col;
colnr_T col = pos->col; colnr_T col = pos->col;
char *that = get_cursor_line_ptr(); char *line = get_cursor_line_ptr();
char *line = that; CharsizeArg arg;
chartabsize_T cts; CSType cstype = init_charsize_arg(&arg, curwin, pos->lnum, line);
init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
while (*cts.cts_ptr != NUL && col > 0) { StrCharInfo sci = utf_ptr2StrCharInfo(line);
cts.cts_vcol += lbr_chartabsize_adv(&cts); amount = 0;
while (*sci.ptr != NUL && col > 0) {
amount += win_charsize(cstype, amount, sci.ptr, sci.chr.value, &arg).width;
sci = utfc_next(sci);
col--; col--;
} }
amount = cts.cts_vcol; char *that = sci.ptr;
that = cts.cts_ptr;
clear_chartabsize_arg(&cts);
// Some keywords require "body" indenting rules (the // Some keywords require "body" indenting rules (the
// non-standard-lisp ones are Scheme special forms): // non-standard-lisp ones are Scheme special forms):
@ -1272,15 +1273,10 @@ int get_lisp_indent(void)
} }
colnr_T firsttry = amount; colnr_T firsttry = amount;
init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line), while (ascii_iswhite(*that)) {
amount, line, that); amount += win_charsize(cstype, amount, that, (uint8_t)(*that), &arg).width;
while (ascii_iswhite(*cts.cts_ptr)) { that++;
cts.cts_vcol += lbr_chartabsize(&cts);
cts.cts_ptr++;
} }
that = cts.cts_ptr;
amount = cts.cts_vcol;
clear_chartabsize_arg(&cts);
if (*that && (*that != ';')) { if (*that && (*that != ';')) {
// Not a comment line. // Not a comment line.
@ -1292,37 +1288,38 @@ int get_lisp_indent(void)
parencount = 0; parencount = 0;
init_chartabsize_arg(&cts, curwin, CharInfo ci = utf_ptr2CharInfo(that);
(colnr_T)(that - line), amount, line, that); if (((ci.value != '"') && (ci.value != '\'') && (ci.value != '#')
if (((*that != '"') && (*that != '\'') && (*that != '#') && ((ci.value < '0') || (ci.value > '9')))) {
&& (((uint8_t)(*that) < '0') || ((uint8_t)(*that) > '9')))) {
int quotecount = 0; int quotecount = 0;
while (*cts.cts_ptr while (*that && (!ascii_iswhite(ci.value) || quotecount || parencount)) {
&& (!ascii_iswhite(*cts.cts_ptr) || quotecount || parencount)) { if (ci.value == '"') {
if (*cts.cts_ptr == '"') {
quotecount = !quotecount; quotecount = !quotecount;
} }
if (((*cts.cts_ptr == '(') || (*cts.cts_ptr == '[')) && !quotecount) { if (((ci.value == '(') || (ci.value == '[')) && !quotecount) {
parencount++; parencount++;
} }
if (((*cts.cts_ptr == ')') || (*cts.cts_ptr == ']')) && !quotecount) { if (((ci.value == ')') || (ci.value == ']')) && !quotecount) {
parencount--; parencount--;
} }
if ((*cts.cts_ptr == '\\') && (*(cts.cts_ptr + 1) != NUL)) { if ((ci.value == '\\') && (*(that + 1) != NUL)) {
cts.cts_vcol += lbr_chartabsize_adv(&cts); amount += win_charsize(cstype, amount, that, ci.value, &arg).width;
StrCharInfo next_sci = utfc_next((StrCharInfo){ that, ci });
that = next_sci.ptr;
ci = next_sci.chr;
} }
cts.cts_vcol += lbr_chartabsize_adv(&cts); amount += win_charsize(cstype, amount, that, ci.value, &arg).width;
StrCharInfo next_sci = utfc_next((StrCharInfo){ that, ci });
that = next_sci.ptr;
ci = next_sci.chr;
} }
} }
while (ascii_iswhite(*cts.cts_ptr)) { while (ascii_iswhite(*that)) {
cts.cts_vcol += lbr_chartabsize(&cts); amount += win_charsize(cstype, amount, that, (uint8_t)(*that), &arg).width;
cts.cts_ptr++; that++;
} }
that = cts.cts_ptr;
amount = cts.cts_vcol;
clear_chartabsize_arg(&cts);
if (!*that || (*that == ';')) { if (!*that || (*that == ';')) {
amount = firsttry; amount = firsttry;

View File

@ -110,3 +110,11 @@ static inline StrCharInfo utfc_next(StrCharInfo cur)
next += next_len; next += next_len;
} }
} }
static inline StrCharInfo utf_ptr2StrCharInfo(char *ptr)
REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE;
static inline StrCharInfo utf_ptr2StrCharInfo(char *ptr)
{
return (StrCharInfo){ .ptr = ptr, .chr = utf_ptr2CharInfo(ptr) };
}

View File

@ -1755,22 +1755,23 @@ colnr_T vcol2col(win_T *wp, linenr_T lnum, colnr_T vcol, colnr_T *coladdp)
{ {
// try to advance to the specified column // try to advance to the specified column
char *line = ml_get_buf(wp->w_buffer, lnum); char *line = ml_get_buf(wp->w_buffer, lnum);
chartabsize_T cts; CharsizeArg arg;
init_chartabsize_arg(&cts, wp, lnum, 0, line, line); CSType cstype = init_charsize_arg(&arg, wp, lnum, line);
while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) { StrCharInfo ci = utf_ptr2StrCharInfo(line);
int size = win_lbr_chartabsize(&cts, NULL); int cur_vcol = 0;
if (cts.cts_vcol + size > vcol) { while (cur_vcol < vcol && *ci.ptr != NUL) {
int next_vcol = cur_vcol + win_charsize(cstype, cur_vcol, ci.ptr, ci.chr.value, &arg).width;
if (next_vcol > vcol) {
break; break;
} }
cts.cts_vcol += size; cur_vcol = next_vcol;
MB_PTR_ADV(cts.cts_ptr); ci = utfc_next(ci);
} }
clear_chartabsize_arg(&cts);
if (coladdp != NULL) { if (coladdp != NULL) {
*coladdp = vcol - cts.cts_vcol; *coladdp = vcol - cur_vcol;
} }
return (colnr_T)(cts.cts_ptr - line); return (colnr_T)(ci.ptr - line);
} }
/// Set UI mouse depending on current mode and 'mouse'. /// Set UI mouse depending on current mode and 'mouse'.

View File

@ -387,17 +387,18 @@ static void shift_block(oparg_T *oap, int amount)
} }
// TODO(vim): is passing bd.textstart for start of the line OK? // TODO(vim): is passing bd.textstart for start of the line OK?
chartabsize_T cts; CharsizeArg arg;
init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, CSType cstype = init_charsize_arg(&arg, curwin, curwin->w_cursor.lnum, bd.textstart);
bd.start_vcol, bd.textstart, bd.textstart); StrCharInfo ci = utf_ptr2StrCharInfo(bd.textstart);
while (ascii_iswhite(*cts.cts_ptr)) { int vcol = bd.start_vcol;
incr = lbr_chartabsize_adv(&cts); while (ascii_iswhite(ci.chr.value)) {
incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
ci = utfc_next(ci);
total += incr; total += incr;
cts.cts_vcol += incr; vcol += incr;
} }
bd.textstart = cts.cts_ptr; bd.textstart = ci.ptr;
bd.start_vcol = cts.cts_vcol; bd.start_vcol = vcol;
clear_chartabsize_arg(&cts);
int tabs = 0; int tabs = 0;
int spaces = 0; int spaces = 0;
@ -448,16 +449,13 @@ static void shift_block(oparg_T *oap, int amount)
// The character's column is in "bd.start_vcol". // The character's column is in "bd.start_vcol".
colnr_T non_white_col = bd.start_vcol; colnr_T non_white_col = bd.start_vcol;
chartabsize_T cts; CharsizeArg arg;
init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, CSType cstype = init_charsize_arg(&arg, curwin, curwin->w_cursor.lnum, bd.textstart);
non_white_col, bd.textstart, non_white); while (ascii_iswhite(*non_white)) {
while (ascii_iswhite(*cts.cts_ptr)) { incr = win_charsize(cstype, non_white_col, non_white, (uint8_t)(*non_white), &arg).width;
incr = lbr_chartabsize_adv(&cts); non_white_col += incr;
cts.cts_vcol += incr; non_white++;
} }
non_white_col = cts.cts_vcol;
non_white = cts.cts_ptr;
clear_chartabsize_arg(&cts);
const colnr_T block_space_width = non_white_col - oap->start_vcol; const colnr_T block_space_width = non_white_col - oap->start_vcol;
// We will shift by "total" or "block_space_width", whichever is less. // We will shift by "total" or "block_space_width", whichever is less.
@ -478,19 +476,17 @@ static void shift_block(oparg_T *oap, int amount)
if (bd.startspaces) { if (bd.startspaces) {
verbatim_copy_width -= bd.start_char_vcols; verbatim_copy_width -= bd.start_char_vcols;
} }
init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width, cstype = init_charsize_arg(&arg, curwin, 0, bd.textstart);
bd.textstart, verbatim_copy_end); StrCharInfo ci = utf_ptr2StrCharInfo(verbatim_copy_end);
while (cts.cts_vcol < destination_col) { while (verbatim_copy_width < destination_col) {
incr = lbr_chartabsize(&cts); incr = win_charsize(cstype, verbatim_copy_width, ci.ptr, ci.chr.value, &arg).width;
if (cts.cts_vcol + incr > destination_col) { if (verbatim_copy_width + incr > destination_col) {
break; break;
} }
cts.cts_vcol += incr; verbatim_copy_width += incr;
MB_PTR_ADV(cts.cts_ptr); ci = utfc_next(ci);
} }
verbatim_copy_width = cts.cts_vcol; verbatim_copy_end = ci.ptr;
verbatim_copy_end = cts.cts_ptr;
clear_chartabsize_arg(&cts);
// If "destination_col" is different from the width of the initial // If "destination_col" is different from the width of the initial
// part of the line that will be copied, it means we encountered a tab // part of the line that will be copied, it means we encountered a tab
@ -3250,19 +3246,19 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
} }
// get the old line and advance to the position to insert at // get the old line and advance to the position to insert at
char *oldp = get_cursor_line_ptr(); char *oldp = get_cursor_line_ptr();
size_t oldlen = strlen(oldp);
chartabsize_T cts;
init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0, oldp, oldp);
while (cts.cts_vcol < col && *cts.cts_ptr != NUL) { CharsizeArg arg;
// Count a tab for what it's worth (if list mode not on) CSType cstype = init_charsize_arg(&arg, curwin, curwin->w_cursor.lnum, oldp);
incr = lbr_chartabsize_adv(&cts); StrCharInfo ci = utf_ptr2StrCharInfo(oldp);
cts.cts_vcol += incr; vcol = 0;
while (vcol < col && *ci.ptr != NUL) {
incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
vcol += incr;
ci = utfc_next(ci);
} }
vcol = cts.cts_vcol; size_t oldlen = (size_t)(ci.ptr - oldp) + strlen(ci.ptr);
char *ptr = cts.cts_ptr; char *ptr = ci.ptr;
bd.textcol = (colnr_T)(ptr - oldp); bd.textcol = (colnr_T)(ptr - oldp);
clear_chartabsize_arg(&cts);
shortline = (vcol < col) || (vcol == col && !*ptr); shortline = (vcol < col) || (vcol == col && !*ptr);
@ -3286,16 +3282,15 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
yanklen = (int)strlen(y_array[i]); yanklen = (int)strlen(y_array[i]);
if ((flags & PUT_BLOCK_INNER) == 0) { if ((flags & PUT_BLOCK_INNER) == 0) {
// calculate number of spaces required to fill right side of // calculate number of spaces required to fill right side of block
// block
spaces = y_width + 1; spaces = y_width + 1;
init_chartabsize_arg(&cts, curwin, 0, 0, y_array[i], y_array[i]);
for (int j = 0; j < yanklen; j++) { cstype = init_charsize_arg(&arg, curwin, 0, y_array[i]);
spaces -= lbr_chartabsize(&cts); ci = utf_ptr2StrCharInfo(y_array[i]);
cts.cts_ptr++; while (*ci.ptr != NUL) {
cts.cts_vcol = 0; spaces -= win_charsize(cstype, 0, ci.ptr, ci.chr.value, &arg).width;
ci = utfc_next(ci);
} }
clear_chartabsize_arg(&cts);
if (spaces < 0) { if (spaces < 0) {
spaces = 0; spaces = 0;
} }
@ -4228,25 +4223,25 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
char *line = ml_get(lnum); char *line = ml_get(lnum);
char *prev_pstart = line; char *prev_pstart = line;
chartabsize_T cts; CharsizeArg arg;
init_chartabsize_arg(&cts, curwin, lnum, bdp->start_vcol, line, line); CSType cstype = init_charsize_arg(&arg, curwin, lnum, line);
while (cts.cts_vcol < oap->start_vcol && *cts.cts_ptr != NUL) { StrCharInfo ci = utf_ptr2StrCharInfo(line);
// Count a tab for what it's worth (if list mode not on) int vcol = bdp->start_vcol;
incr = lbr_chartabsize(&cts); while (vcol < oap->start_vcol && *ci.ptr != NUL) {
cts.cts_vcol += incr; incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
if (ascii_iswhite(*cts.cts_ptr)) { vcol += incr;
if (ascii_iswhite(ci.chr.value)) {
bdp->pre_whitesp += incr; bdp->pre_whitesp += incr;
bdp->pre_whitesp_c++; bdp->pre_whitesp_c++;
} else { } else {
bdp->pre_whitesp = 0; bdp->pre_whitesp = 0;
bdp->pre_whitesp_c = 0; bdp->pre_whitesp_c = 0;
} }
prev_pstart = cts.cts_ptr; prev_pstart = ci.ptr;
MB_PTR_ADV(cts.cts_ptr); ci = utfc_next(ci);
} }
bdp->start_vcol = cts.cts_vcol; bdp->start_vcol = vcol;
char *pstart = cts.cts_ptr; char *pstart = ci.ptr;
clear_chartabsize_arg(&cts);
bdp->start_char_vcols = incr; bdp->start_char_vcols = incr;
if (bdp->start_vcol < oap->start_vcol) { // line too short if (bdp->start_vcol < oap->start_vcol) { // line too short
@ -4283,17 +4278,18 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
} }
} }
} else { } else {
init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol, line, pend); cstype = init_charsize_arg(&arg, curwin, lnum, line);
ci = utf_ptr2StrCharInfo(pend);
vcol = bdp->end_vcol;
char *prev_pend = pend; char *prev_pend = pend;
while (cts.cts_vcol <= oap->end_vcol && *cts.cts_ptr != NUL) { while (vcol <= oap->end_vcol && *ci.ptr != NUL) {
// Count a tab for what it's worth (if list mode not on) prev_pend = ci.ptr;
prev_pend = cts.cts_ptr; incr = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &arg).width;
incr = lbr_chartabsize_adv(&cts); vcol += incr;
cts.cts_vcol += incr; ci = utfc_next(ci);
} }
bdp->end_vcol = cts.cts_vcol; bdp->end_vcol = vcol;
pend = cts.cts_ptr; pend = ci.ptr;
clear_chartabsize_arg(&cts);
if (bdp->end_vcol <= oap->end_vcol if (bdp->end_vcol <= oap->end_vcol
&& (!is_del && (!is_del

View File

@ -51,48 +51,21 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col)
return ptr2cells(p); return ptr2cells(p);
} }
/// Return the number of characters the string 's' will take on the screen, /// Like linetabsize_str(), but "s" starts at virtual column "startvcol".
/// taking into account the size of a tab.
///
/// @param s
///
/// @return Number of characters the string will take on the screen.
int linetabsize_str(char *s)
{
return linetabsize_col(0, s);
}
/// Like linetabsize_str(), but "s" starts at column "startcol".
/// ///
/// @param startcol /// @param startcol
/// @param s /// @param s
/// ///
/// @return Number of characters the string will take on the screen. /// @return Number of characters the string will take on the screen.
int linetabsize_col(int startcol, char *s) int linetabsize_col(int startvcol, char *s)
{ {
chartabsize_T cts; CharsizeArg arg;
init_chartabsize_arg(&cts, curwin, 0, startcol, s, s); CSType const cstype = init_charsize_arg(&arg, curwin, 0, s);
while (*cts.cts_ptr != NUL) { if (cstype == kCharsizeFast) {
cts.cts_vcol += lbr_chartabsize_adv(&cts); return linesize_fast(&arg, startvcol, MAXCOL);
} else {
return linesize_regular(&arg, startvcol, MAXCOL);
} }
clear_chartabsize_arg(&cts);
return cts.cts_vcol;
}
/// Like linetabsize_str(), but for a given window instead of the current one.
///
/// @param wp
/// @param line
/// @param len
///
/// @return Number of characters the string will take on the screen.
int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
{
chartabsize_T cts;
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
win_linetabsize_cts(&cts, len);
clear_chartabsize_arg(&cts);
return cts.cts_vcol;
} }
/// Return the number of cells line "lnum" of window "wp" will take on the /// Return the number of cells line "lnum" of window "wp" will take on the
@ -102,128 +75,79 @@ int linetabsize(win_T *wp, linenr_T lnum)
return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL); return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL);
} }
void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) /// Prepare the structure passed to charsize functions.
{
for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len);
MB_PTR_ADV(cts->cts_ptr)) {
cts->cts_vcol += win_lbr_chartabsize(cts, NULL);
}
// check for inline virtual text after the end of the line
if (len == MAXCOL && cts->virt_row >= 0 && *cts->cts_ptr == NUL) {
(void)win_lbr_chartabsize(cts, NULL);
cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right;
}
}
/// Prepare the structure passed to chartabsize functions.
/// ///
/// "line" is the start of the line, "ptr" is the first relevant character. /// "line" is the start of the line.
/// When "lnum" is zero do not use inline virtual text. /// When "lnum" is zero do not use inline virtual text.
void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char *line, CSType init_charsize_arg(CharsizeArg *cts, win_T *wp, linenr_T lnum, char *line)
char *ptr)
{ {
cts->cts_win = wp; cts->win = wp;
cts->cts_vcol = col; cts->line = line;
cts->cts_line = line; cts->max_head_vcol = 0;
cts->cts_ptr = ptr; cts->cur_text_width_left = 0;
cts->cts_max_head_vcol = 0; cts->cur_text_width_right = 0;
cts->cts_cur_text_width_left = 0;
cts->cts_cur_text_width_right = 0;
cts->virt_row = -1; cts->virt_row = -1;
cts->indent_width = INT_MIN; cts->indent_width = INT_MIN;
cts->use_tabstop = !wp->w_p_list || wp->w_p_lcs_chars.tab1;
if (lnum > 0 && wp->w_buffer->b_virt_text_inline > 0) { if (lnum > 0 && wp->w_buffer->b_virt_text_inline > 0) {
marktree_itr_get(wp->w_buffer->b_marktree, lnum - 1, 0, cts->cts_iter); marktree_itr_get(wp->w_buffer->b_marktree, lnum - 1, 0, cts->iter);
MTKey mark = marktree_itr_current(cts->cts_iter); MTKey mark = marktree_itr_current(cts->iter);
if (mark.pos.row == lnum - 1) { if (mark.pos.row == lnum - 1) {
cts->virt_row = lnum - 1; cts->virt_row = lnum - 1;
} }
} }
}
/// Free any allocated item in "cts". if (cts->virt_row >= 0
void clear_chartabsize_arg(chartabsize_T *cts) || (wp->w_p_wrap && (wp->w_p_lbr || wp->w_p_bri || *get_showbreak_value(wp) != NUL))) {
{ return kCharsizeRegular;
} } else {
return kCharsizeFast;
/// like win_chartabsize(), but also check for line breaks on the screen
///
/// @param cts
///
/// @return The number of characters taken up on the screen.
int lbr_chartabsize(chartabsize_T *cts)
{
if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL
&& !curwin->w_p_bri && cts->virt_row < 0) {
if (curwin->w_p_wrap) {
return win_nolbr_chartabsize(cts, NULL);
}
return win_chartabsize(curwin, cts->cts_ptr, cts->cts_vcol);
} }
return win_lbr_chartabsize(cts, NULL);
} }
/// Call lbr_chartabsize() and advance the pointer. /// Get the number of characters taken up on the screen for the given cts and position.
/// /// "cts->cur_text_width_left" and "cts->cur_text_width_right" are set
/// @param cts
///
/// @return The number of characters take up on the screen.
int lbr_chartabsize_adv(chartabsize_T *cts)
{
int retval = lbr_chartabsize(cts);
MB_PTR_ADV(cts->cts_ptr);
return retval;
}
/// Get the number of characters taken up on the screen indicated by "cts".
/// "cts->cts_cur_text_width_left" and "cts->cts_cur_text_width_right" are set
/// to the extra size for inline virtual text. /// to the extra size for inline virtual text.
/// This function is used very often, keep it fast!!!! /// This function is used very often, keep it fast!!!!
/// ///
/// If "headp" not NULL, set "*headp" to the size of 'showbreak'/'breakindent' /// When "cts->max_head_vcol" is positive, only count in "head" the size
/// included in the return value. /// of 'showbreak'/'breakindent' before "cts->max_head_vcol".
/// When "cts->cts_max_head_vcol" is positive, only count in "*headp" the size /// When "cts->max_head_vcol" is negative, only count in "head" the size
/// of 'showbreak'/'breakindent' before "cts->cts_max_head_vcol".
/// When "cts->cts_max_head_vcol" is negative, only count in "*headp" the size
/// of 'showbreak'/'breakindent' before where cursor should be placed. /// of 'showbreak'/'breakindent' before where cursor should be placed.
/// CharSize charsize_regular(CharsizeArg *cts, char *const cur, colnr_T const vcol,
/// Warning: "*headp" may not be set if it's 0, init to 0 before calling. int32_t const cur_char)
int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
{ {
win_T *wp = cts->cts_win; cts->cur_text_width_left = 0;
char *line = cts->cts_line; // start of the line cts->cur_text_width_right = 0;
char *s = cts->cts_ptr;
colnr_T vcol = cts->cts_vcol; win_T *wp = cts->win;
buf_T *buf = wp->w_buffer;
char *line = cts->line;
bool const use_tabstop = cur_char == TAB && cts->use_tabstop;
int mb_added = 0; int mb_added = 0;
cts->cts_cur_text_width_left = 0;
cts->cts_cur_text_width_right = 0;
char *const sbr = get_showbreak_value(wp);
// No 'linebreak', 'showbreak' and 'breakindent': return quickly.
if (!wp->w_p_lbr && !wp->w_p_bri && *sbr == NUL
&& cts->virt_row < 0) {
if (wp->w_p_wrap) {
return win_nolbr_chartabsize(cts, headp);
}
return win_chartabsize(wp, s, vcol);
}
bool has_lcs_eol = wp->w_p_list && wp->w_p_lcs_chars.eol != NUL; bool has_lcs_eol = wp->w_p_list && wp->w_p_lcs_chars.eol != NUL;
// First get normal size, without 'linebreak' or inline virtual text // First get normal size, without 'linebreak' or inline virtual text
int size = win_chartabsize(wp, s, vcol); int size;
if (*s == NUL && !has_lcs_eol) { int is_doublewidth = false;
size = 0; // NUL is not displayed if (use_tabstop) {
size = tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array);
} else if (*cur == NUL && !has_lcs_eol) {
size = 0;
} else if (cur_char < 0) {
size = kInvalidByteCells;
} else {
size = char2cells(cur_char);
is_doublewidth = size == 2 && cur_char > 0x80;
} }
bool is_doublewidth = size == 2 && MB_BYTE2LEN((uint8_t)(*s)) > 1;
if (cts->virt_row >= 0) { if (cts->virt_row >= 0) {
int tab_size = size; int tab_size = size;
int col = (int)(s - line); int col = (int)(cur - line);
while (true) { while (true) {
MTKey mark = marktree_itr_current(cts->cts_iter); MTKey mark = marktree_itr_current(cts->iter);
if (mark.pos.row != cts->virt_row || mark.pos.col > col) { if (mark.pos.row != cts->virt_row || mark.pos.col > col) {
break; break;
} else if (mark.pos.col == col) { } else if (mark.pos.col == col) {
@ -233,15 +157,15 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
while (vt) { while (vt) {
if (!(vt->flags & kVTIsLines) && vt->pos == kVPosInline) { if (!(vt->flags & kVTIsLines) && vt->pos == kVPosInline) {
if (mt_right(mark)) { if (mt_right(mark)) {
cts->cts_cur_text_width_right += vt->width; cts->cur_text_width_right += vt->width;
} else { } else {
cts->cts_cur_text_width_left += vt->width; cts->cur_text_width_left += vt->width;
} }
size += vt->width; size += vt->width;
if (*s == TAB) { if (use_tabstop) {
// tab size changes because of the inserted text // tab size changes because of the inserted text
size -= tab_size; size -= tab_size;
tab_size = win_chartabsize(wp, s, vcol + size); tab_size = tabstop_padding(vcol + size, buf->b_p_ts, buf->b_p_vts_array);
size += tab_size; size += tab_size;
} }
} }
@ -249,7 +173,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
} }
} }
} }
marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter); marktree_itr_next(wp->w_buffer->b_marktree, cts->iter);
} }
} }
@ -259,6 +183,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
mb_added = 1; mb_added = 1;
} }
char *const sbr = get_showbreak_value(wp);
// May have to add something for 'breakindent' and/or 'showbreak' // May have to add something for 'breakindent' and/or 'showbreak'
// string at the start of a screen line. // string at the start of a screen line.
int head = mb_added; int head = mb_added;
@ -267,7 +193,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
int col_off_prev = win_col_off(wp); int col_off_prev = win_col_off(wp);
int width2 = wp->w_width_inner - col_off_prev + win_col_off2(wp); int width2 = wp->w_width_inner - col_off_prev + win_col_off2(wp);
colnr_T wcol = vcol + col_off_prev; colnr_T wcol = vcol + col_off_prev;
colnr_T max_head_vcol = cts->cts_max_head_vcol; colnr_T max_head_vcol = cts->max_head_vcol;
int added = 0; int added = 0;
// cells taken by 'showbreak'/'breakindent' before current char // cells taken by 'showbreak'/'breakindent' before current char
@ -333,7 +259,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
head += (max_head_vcol - (vcol + head_prev + prev_rem) head += (max_head_vcol - (vcol + head_prev + prev_rem)
+ width2 - 1) / width2 * head_mid; + width2 - 1) / width2 * head_mid;
} else if (max_head_vcol < 0) { } else if (max_head_vcol < 0) {
int off = virt_text_cursor_off(cts, *s == NUL); int off = virt_text_cursor_off(cts, *cur == NUL);
if (off >= prev_rem) { if (off >= prev_rem) {
if (size > off) { if (size > off) {
head += (1 + (off - prev_rem) / width) * head_mid; head += (1 + (off - prev_rem) / width) * head_mid;
@ -348,19 +274,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
size += added; size += added;
} }
if (headp != NULL) { char *s = cur;
*headp = head;
}
colnr_T vcol_start = 0; // start from where to consider linebreak colnr_T vcol_start = 0; // start from where to consider linebreak
// If 'linebreak' set check at a blank before a non-blank if the line // If 'linebreak' set check at a blank before a non-blank if the line
// needs a break here // needs a break here
if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0) { if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0) {
char *t = cts->cts_line; char *t = cts->line;
while (vim_isbreak((uint8_t)t[0])) { while (vim_isbreak((uint8_t)t[0])) {
t++; t++;
} }
vcol_start = (colnr_T)(t - cts->cts_line); vcol_start = (colnr_T)(t - cts->line);
} }
if (wp->w_p_lbr && vcol_start <= vcol if (wp->w_p_lbr && vcol_start <= vcol
&& vim_isbreak((uint8_t)s[0]) && vim_isbreak((uint8_t)s[0])
@ -398,39 +321,50 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
} }
} }
return size; return (CharSize){ .width = size, .head = head };
} }
/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and /// Like charsize_regular(), except it doesn't handle virtual text,
/// 'wrap' is on. This means we need to check for a double-byte character that /// linebreak, breakindent and showbreak. Handles normal characters, tabs and wrapping.
/// doesn't fit at the end of the screen line. /// This function is always inlined.
/// ///
/// @param cts /// @see charsize_regular
/// @param headp /// @see charsize_fast
/// static inline CharSize charsize_fast_impl(win_T *const wp, bool use_tabstop, colnr_T const vcol,
/// @return The number of characters take up on the screen. int32_t const cur_char)
static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) FUNC_ATTR_PURE FUNC_ATTR_ALWAYS_INLINE
{ {
win_T *wp = cts->cts_win; // A tab gets expanded, depending on the current column
char *s = cts->cts_ptr; if (cur_char == TAB && use_tabstop) {
colnr_T col = cts->cts_vcol; return (CharSize){
.width = tabstop_padding(vcol, wp->w_buffer->b_p_ts,
if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { wp->w_buffer->b_p_vts_array)
return tabstop_padding(col, };
wp->w_buffer->b_p_ts, } else {
wp->w_buffer->b_p_vts_array); int width;
} if (cur_char < 0) {
int n = ptr2cells(s); width = kInvalidByteCells;
} else {
// Add one cell for a double-width character in the last column of the width = char2cells(cur_char);
// window, displayed with a ">". }
if ((n == 2) && (MB_BYTE2LEN((uint8_t)(*s)) > 1) && in_win_border(wp, col)) {
if (headp != NULL) { // If a double-width char doesn't fit at the end of a line, it wraps to the next line,
*headp = 1; // and the last column displays a '>'.
if (width == 2 && cur_char >= 0x80 && wp->w_p_wrap && in_win_border(wp, vcol)) {
return (CharSize){ .width = 3, .head = 1 };
} else {
return (CharSize){ .width = width };
} }
return 3;
} }
return n; }
/// Like charsize_regular(), except it doesn't handle virtual text,
/// linebreak, breakindent and showbreak. Handles normal characters, tabs and wrapping.
/// Can be used if CSType is kCharsizeFast.
CharSize charsize_fast(CharsizeArg *cts, colnr_T const vcol, int32_t const cur_char)
FUNC_ATTR_PURE
{
return charsize_fast_impl(cts->win, cts->use_tabstop, vcol, cur_char);
} }
/// Check that virtual column "vcol" is in the rightmost column of window "wp". /// Check that virtual column "vcol" is in the rightmost column of window "wp".
@ -461,19 +395,63 @@ static bool in_win_border(win_T *wp, colnr_T vcol)
return (vcol - width1) % width2 == width2 - 1; return (vcol - width1) % width2 == width2 - 1;
} }
/// Calculate virtual column until the given 'len'.
///
/// @param arg Argument to charsize functions.
/// @param vcol Starting virtual column.
/// @param len First byte of the end character, or MAXCOL.
///
/// @return virtual column before the character at 'len',
/// or full size of the line if 'len' is MAXCOL.
int linesize_regular(CharsizeArg *const arg, int vcol, colnr_T const len)
{
char *const line = arg->line;
StrCharInfo ci = utf_ptr2StrCharInfo(line);
while (ci.ptr - line < len && *ci.ptr != NUL) {
vcol += charsize_regular(arg, ci.ptr, vcol, ci.chr.value).width;
ci = utfc_next(ci);
}
// Check for inline virtual text after the end of the line.
if (len == MAXCOL && arg->virt_row >= 0) {
(void)charsize_regular(arg, ci.ptr, vcol, ci.chr.value);
vcol += arg->cur_text_width_left + arg->cur_text_width_right;
}
return vcol;
}
/// Like win_linesize_regular, but can be used when CStype is kCharsizeFast.
///
/// @see win_linesize_regular
int linesize_fast(CharsizeArg const *const arg, int vcol, colnr_T const len)
{
win_T *const wp = arg->win;
bool const use_tabstop = arg->use_tabstop;
char *const line = arg->line;
StrCharInfo ci = utf_ptr2StrCharInfo(line);
while (ci.ptr - line < len && *ci.ptr != NUL) {
vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width;
ci = utfc_next(ci);
}
return vcol;
}
/// Get how many virtual columns inline virtual text should offset the cursor. /// Get how many virtual columns inline virtual text should offset the cursor.
/// ///
/// @param cts should contain information stored by win_lbr_chartabsize()
/// about widths of left and right gravity virtual text
/// @param on_NUL whether this is the end of the line /// @param on_NUL whether this is the end of the line
static int virt_text_cursor_off(chartabsize_T *cts, bool on_NUL) static int virt_text_cursor_off(CharsizeArg *cts, bool on_NUL)
{ {
int off = 0; int off = 0;
if (!on_NUL || !(State & MODE_NORMAL)) { if (!on_NUL || !(State & MODE_NORMAL)) {
off += cts->cts_cur_text_width_left; off += cts->cur_text_width_left;
} }
if (!on_NUL && (State & MODE_NORMAL)) { if (!on_NUL && (State & MODE_NORMAL)) {
off += cts->cts_cur_text_width_right; off += cts->cur_text_width_right;
} }
return off; return off;
} }
@ -492,95 +470,53 @@ static int virt_text_cursor_off(chartabsize_T *cts, bool on_NUL)
/// @param end /// @param end
void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end) void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end)
{ {
char *ptr; // points to current char char *const line = ml_get_buf(wp->w_buffer, pos->lnum); // start of the line
int incr; int const end_col = pos->col;
int head;
colnr_T *vts = wp->w_buffer->b_p_vts_array; CharsizeArg arg;
int ts = (int)wp->w_buffer->b_p_ts; bool on_NUL = false;
CSType const cstype = init_charsize_arg(&arg, wp, pos->lnum, line);
arg.max_head_vcol = -1;
colnr_T vcol = 0; colnr_T vcol = 0;
char *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum); // start of the line CharSize char_size;
StrCharInfo ci = utf_ptr2StrCharInfo(line);
uintptr_t last_pos = (uintptr_t)(ptr + pos->col); if (cstype == kCharsizeFast) {
if (last_pos < (uintptr_t)ptr) { bool const use_tabstop = arg.use_tabstop;
last_pos = UINTPTR_MAX; // unsigned overflow
}
chartabsize_T cts;
bool on_NUL = false;
init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line);
cts.cts_max_head_vcol = -1;
// This function is used very often, do some speed optimizations.
// When 'linebreak', 'showbreak' and 'breakindent' are not set
// and there are no virtual text use a simple loop.
if (!wp->w_p_lbr && !wp->w_p_bri && cts.virt_row < 0 && *get_showbreak_value(wp) == NUL) {
bool const special_tab = !wp->w_p_list || wp->w_p_lcs_chars.tab1 != NUL;
CharInfo cur_char = utf_ptr2CharInfo(ptr);
while (true) { while (true) {
head = 0; if (*ci.ptr == NUL) {
// make sure we don't go past the end of the line // if cursor is at NUL, it is treated like 1 cell char
if (cur_char.value == 0 && cur_char.len == 1) { char_size = (CharSize){ .width = 1 };
// NUL at end of line only takes one column
incr = 1;
break; break;
} }
char_size = charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value);
// A tab gets expanded, depending on the current column StrCharInfo const next = utfc_next(ci);
if (cur_char.value == TAB && special_tab) { if (next.ptr - line > end_col) {
incr = tabstop_padding(vcol, ts, vts);
} else {
if (cur_char.value < 0) {
incr = kInvalidByteCells;
} else {
incr = char2cells(cur_char.value);
}
// If a double-cell char doesn't fit at the end of a line
// it wraps to the next line, it's like this char is three
// cells wide.
if (incr == 2 && cur_char.value >= 0x80
&& wp->w_p_wrap && in_win_border(wp, vcol)) {
incr++;
head = 1;
}
}
StrCharInfo const next_char = utfc_next((StrCharInfo){ ptr, cur_char });
if ((uintptr_t)next_char.ptr > last_pos) {
break; break;
} }
ci = next;
cur_char = next_char.chr; vcol += char_size.width;
ptr = next_char.ptr;
vcol += incr;
} }
} else { } else {
while (true) { while (true) {
// A tab gets expanded, depending on the current column char_size = charsize_regular(&arg, ci.ptr, vcol, ci.chr.value);
// Other things also take up space. if (*ci.ptr == NUL) {
head = 0; // if cursor is at NUL, it is treated like 1 cell char unless there is virtual text
incr = win_lbr_chartabsize(&cts, &head); char_size.width = MAX(1, arg.cur_text_width_left + arg.cur_text_width_right);
// make sure we don't go past the end of the line
if (*cts.cts_ptr == NUL) {
// NUL at end of line only takes one column, unless there is virtual text
incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right);
on_NUL = true; on_NUL = true;
break; break;
} }
StrCharInfo const next = utfc_next(ci);
char *const next = cts.cts_ptr + utfc_ptr2len(cts.cts_ptr); if (next.ptr - line > end_col) {
if ((uintptr_t)next > last_pos) {
break; break;
} }
ci = next;
cts.cts_ptr = next; vcol += char_size.width;
cts.cts_vcol += incr;
} }
vcol = cts.cts_vcol;
ptr = cts.cts_ptr;
} }
clear_chartabsize_arg(&cts);
int head = char_size.head;
int incr = char_size.width;
if (start != NULL) { if (start != NULL) {
*start = vcol + head; *start = vcol + head;
@ -591,7 +527,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
} }
if (cursor != NULL) { if (cursor != NULL) {
if ((*ptr == TAB) if (ci.chr.value == TAB
&& (State & MODE_NORMAL) && (State & MODE_NORMAL)
&& !wp->w_p_list && !wp->w_p_list
&& !virtual_active() && !virtual_active()
@ -599,7 +535,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
// cursor at end // cursor at end
*cursor = vcol + incr - 1; *cursor = vcol + incr - 1;
} else { } else {
vcol += virt_text_cursor_off(&cts, on_NUL); vcol += virt_text_cursor_off(&arg, on_NUL);
// cursor at start // cursor at start
*cursor = vcol + head; *cursor = vcol + head;
} }
@ -788,14 +724,18 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool limit_winheight)
int plines_win_nofold(win_T *wp, linenr_T lnum) int plines_win_nofold(win_T *wp, linenr_T lnum)
{ {
char *s = ml_get_buf(wp->w_buffer, lnum); char *s = ml_get_buf(wp->w_buffer, lnum);
chartabsize_T cts; CharsizeArg arg;
init_chartabsize_arg(&cts, wp, lnum, 0, s, s); CSType const cstype = init_charsize_arg(&arg, wp, lnum, s);
if (*s == NUL && cts.virt_row < 0) { if (*s == NUL && arg.virt_row < 0) {
return 1; // be quick for an empty line return 1; // be quick for an empty line
} }
win_linetabsize_cts(&cts, (colnr_T)MAXCOL);
clear_chartabsize_arg(&cts); int64_t col;
int64_t col = cts.cts_vcol; if (cstype == kCharsizeFast) {
col = linesize_fast(&arg, 0, MAXCOL);
} else {
col = linesize_regular(&arg, 0, MAXCOL);
}
// If list mode is on, then the '$' at the end of the line may take up one // If list mode is on, then the '$' at the end of the line may take up one
// extra column. // extra column.
@ -834,26 +774,33 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
char *line = ml_get_buf(wp->w_buffer, lnum); char *line = ml_get_buf(wp->w_buffer, lnum);
colnr_T col = 0; CharsizeArg cts;
chartabsize_T cts; CSType const cstype = init_charsize_arg(&cts, wp, lnum, line);
init_chartabsize_arg(&cts, wp, lnum, 0, line, line); colnr_T vcol = 0;
while (*cts.cts_ptr != NUL && --column >= 0) { StrCharInfo ci = utf_ptr2StrCharInfo(line);
cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); if (cstype == kCharsizeFast) {
MB_PTR_ADV(cts.cts_ptr); bool const use_tabstop = cts.use_tabstop;
while (*ci.ptr != NUL && --column >= 0) {
vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width;
ci = utfc_next(ci);
}
} else {
while (*ci.ptr != NUL && --column >= 0) {
vcol += charsize_regular(&cts, ci.ptr, vcol, ci.chr.value).width;
ci = utfc_next(ci);
}
} }
// If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not // If current char is a TAB, and the TAB is not displayed as ^I, and we're not
// in MODE_INSERT state, then col must be adjusted so that it represents the // in MODE_INSERT state, then col must be adjusted so that it represents the
// last screen position of the TAB. This only fixes an error when the TAB // last screen position of the TAB. This only fixes an error when the TAB
// wraps from one screen line to the next (when 'columns' is not a multiple // wraps from one screen line to the next (when 'columns' is not a multiple
// of 'ts') -- webb. // of 'ts') -- webb.
col = cts.cts_vcol; colnr_T col = vcol;
if (*cts.cts_ptr == TAB && (State & MODE_NORMAL) if (ci.chr.value == TAB && (State & MODE_NORMAL) && cts.use_tabstop) {
&& (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { col += win_charsize(cstype, col, ci.ptr, ci.chr.value, &cts).width - 1;
col += win_lbr_chartabsize(&cts, NULL) - 1;
} }
clear_chartabsize_arg(&cts);
// Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
int width = wp->w_width_inner - win_col_off(wp); int width = wp->w_width_inner - win_col_off(wp);

View File

@ -4,26 +4,96 @@
#include <stdint.h> // IWYU pragma: keep #include <stdint.h> // IWYU pragma: keep
#include "nvim/marktree_defs.h" #include "nvim/marktree_defs.h"
#include "nvim/mbyte_defs.h"
#include "nvim/pos_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" #include "nvim/types_defs.h"
/// Argument for lbr_chartabsize(). typedef bool CSType;
enum {
kCharsizeRegular,
kCharsizeFast,
};
/// Argument for char size functions.
typedef struct { typedef struct {
win_T *cts_win; win_T *win;
char *cts_line; ///< start of the line char *line; ///< start of the line
char *cts_ptr; ///< current position in line
int cts_vcol; ///< virtual column at current position
int indent_width; ///< width of showbreak and breakindent on wrapped lines
/// INT_MIN if not yet calculated
int virt_row; ///< line number, -1 if no virtual text bool use_tabstop; ///< use tabstop for tab insted of counting it as ^I
int cts_cur_text_width_left; ///< width of virtual text left of cursor int indent_width; ///< width of showbreak and breakindent on wrapped lines
int cts_cur_text_width_right; ///< width of virtual text right of cursor /// INT_MIN if not yet calculated
int cts_max_head_vcol; ///< see win_lbr_chartabsize() int virt_row; ///< line number, -1 if no virtual text
MarkTreeIter cts_iter[1]; int cur_text_width_left; ///< width of virtual text left of cursor
} chartabsize_T; int cur_text_width_right; ///< width of virtual text right of cursor
int max_head_vcol; ///< see charsize_regular()
MarkTreeIter iter[1];
} CharsizeArg;
typedef struct {
int width;
int head; // size of breakindent etc. before the character (included in width)
} CharSize;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "plines.h.generated.h" # include "plines.h.generated.h"
#endif #endif
static inline CharSize win_charsize(CSType cstype, int vcol, char *ptr, int32_t chr,
CharsizeArg *arg)
REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE;
/// Get the number of cells taken up on the screen by the given character at vcol.
/// "arg->cur_text_width_left" and "arg->cur_text_width_right" are set
/// to the extra size for inline virtual text.
///
/// When "arg->max_head_vcol" is positive, only count in "head" the size
/// of 'showbreak'/'breakindent' before "arg->max_head_vcol".
/// When "arg->max_head_vcol" is negative, only count in "head" the size
/// of 'showbreak'/'breakindent' before where cursor should be placed.
static inline CharSize win_charsize(CSType cstype, int vcol, char *ptr, int32_t chr,
CharsizeArg *arg)
{
if (cstype == kCharsizeFast) {
return charsize_fast(arg, vcol, chr);
} else {
return charsize_regular(arg, ptr, vcol, chr);
}
}
static inline int linetabsize_str(char *s)
REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE;
/// Return the number of characters the string 's' will take on the screen,
/// taking into account the size of a tab.
///
/// @param s
///
/// @return Number of characters the string will take on the screen.
static inline int linetabsize_str(char *s)
{
return linetabsize_col(0, s);
}
static inline int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE;
/// Like linetabsize_str(), but for a given window instead of the current one.
///
/// @param wp
/// @param line
/// @param len
///
/// @return Number of characters the string will take on the screen.
static inline int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
{
CharsizeArg arg;
CSType const cstype = init_charsize_arg(&arg, wp, lnum, line);
if (cstype == kCharsizeFast) {
return linesize_fast(&arg, 0, len);
} else {
return linesize_regular(&arg, 0, len);
}
}