Merge pull request #19975 from bfredl/chartabsize

refactor(plines): use a struct for chartabsize state
This commit is contained in:
bfredl 2022-08-29 12:48:51 +02:00 committed by GitHub
commit f05cc672e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 354 additions and 205 deletions

View File

@ -653,9 +653,9 @@ void ins_char_bytes(char_u *buf, size_t charlen)
// cells. May result in adding spaces to fill a gap. // cells. May result in adding spaces to fill a gap.
colnr_T vcol; colnr_T vcol;
getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL); getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL);
colnr_T new_vcol = vcol + win_chartabsize(curwin, buf, vcol); colnr_T new_vcol = vcol + win_chartabsize(curwin, (char *)buf, vcol);
while (oldp[col + oldlen] != NUL && vcol < new_vcol) { while (oldp[col + oldlen] != NUL && vcol < new_vcol) {
vcol += win_chartabsize(curwin, oldp + col + oldlen, vcol); vcol += win_chartabsize(curwin, (char *)oldp + col + oldlen, vcol);
// Don't need to remove a TAB that takes us to the right // Don't need to remove a TAB that takes us to the right
// position. // position.
if (vcol > new_vcol && oldp[col + oldlen] == TAB) { if (vcol > new_vcol && oldp[col + oldlen] == TAB) {

View File

@ -930,14 +930,18 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
posptr -= utf_head_off(line, posptr); posptr -= utf_head_off(line, posptr);
} }
chartabsize_T cts;
init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line);
// This function is used very often, do some speed optimizations. // This function is used very often, do some speed optimizations.
// When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set // When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set
// use a simple loop. // and there are no virtual text use a simple loop.
// Also use this when 'list' is set but tabs take their normal size. // Also use this when 'list' is set but tabs take their normal size.
if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL)) if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL))
&& !wp->w_p_lbr && !wp->w_p_lbr
&& *get_showbreak_value(wp) == NUL && *get_showbreak_value(wp) == NUL
&& !wp->w_p_bri) { && !wp->w_p_bri
&& !cts.cts_has_virt_text) {
for (;;) { for (;;) {
head = 0; head = 0;
int c = *ptr; int c = *ptr;
@ -984,25 +988,29 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
} else { } else {
for (;;) { for (;;) {
// A tab gets expanded, depending on the current column // A tab gets expanded, depending on the current column
// Other things also take up space.
head = 0; head = 0;
incr = win_lbr_chartabsize(wp, line, ptr, vcol, &head); incr = win_lbr_chartabsize(&cts, &head);
// make sure we don't go past the end of the line // make sure we don't go past the end of the line
if (*ptr == NUL) { if (*cts.cts_ptr == NUL) {
// NUL at end of line only takes one column // NUL at end of line only takes one column
incr = 1; incr = 1;
break; break;
} }
if ((posptr != NULL) && (ptr >= posptr)) { if ((posptr != NULL) && ((char_u *)cts.cts_ptr >= posptr)) {
// character at pos->col // character at pos->col
break; break;
} }
vcol += incr; cts.cts_vcol += incr;
MB_PTR_ADV(ptr); MB_PTR_ADV(cts.cts_ptr);
} }
vcol = cts.cts_vcol;
ptr = (char_u *)cts.cts_ptr;
} }
clear_chartabsize_arg(&cts);
if (start != NULL) { if (start != NULL) {
*start = vcol + head; *start = vcol + head;
@ -1013,6 +1021,8 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
} }
if (cursor != NULL) { if (cursor != NULL) {
// cursor is after inserted text
vcol += cts.cts_cur_text_width;
if ((*ptr == TAB) if ((*ptr == TAB)
&& (State & MODE_NORMAL) && (State & MODE_NORMAL)
&& !wp->w_p_list && !wp->w_p_list

View File

@ -137,14 +137,18 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
} }
} }
char_u *ptr = line; chartabsize_T cts;
while (col <= wcol && *ptr != NUL) { init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on) // Count a tab for what it's worth (if list mode not on)
csize = win_lbr_chartabsize(curwin, line, ptr, col, &head); csize = win_lbr_chartabsize(&cts, &head);
MB_PTR_ADV(ptr); MB_PTR_ADV(cts.cts_ptr);
col += csize; cts.cts_vcol += csize;
} }
idx = (int)(ptr - line); col = cts.cts_vcol;
idx = (int)(cts.cts_ptr - (char *)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
// a line has the correct indexing. The one_more comparison // a line has the correct indexing. The one_more comparison

View File

@ -940,12 +940,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
} }
if (v > 0 && !number_only) { if (v > 0 && !number_only) {
char_u *prev_ptr = ptr; char_u *prev_ptr = ptr;
while (vcol < v && *ptr != NUL) { chartabsize_T cts;
c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL); int charsize;
vcol += c;
prev_ptr = ptr; init_chartabsize_arg(&cts, wp, lnum, (colnr_T)vcol, line, ptr);
MB_PTR_ADV(ptr); while (cts.cts_vcol < v && *cts.cts_ptr != NUL) {
charsize = win_lbr_chartabsize(&cts, NULL);
cts.cts_vcol += charsize;
prev_ptr = (char_u *)cts.cts_ptr;
MB_PTR_ADV(cts.cts_ptr);
} }
vcol = cts.cts_vcol;
ptr = (char_u *)cts.cts_ptr;
clear_chartabsize_arg(&cts);
// When: // When:
// - 'cuc' is set, or // - 'cuc' is set, or
@ -963,11 +970,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// Handle a character that's not completely on the screen: Put ptr at // Handle a character that's not completely on the screen: Put ptr at
// that character but skip the first few screen characters. // that character but skip the first few screen characters.
if (vcol > v) { if (vcol > v) {
vcol -= c; vcol -= charsize;
ptr = prev_ptr; ptr = prev_ptr;
// If the character fits on the screen, don't need to skip it. // If the character fits on the screen, don't need to skip it.
// Except for a TAB. // Except for a TAB.
if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) { if (utf_ptr2cells((char *)ptr) >= charsize || *ptr == TAB) {
n_skip = (int)(v - vcol); n_skip = (int)(v - vcol);
} }
} }
@ -1798,8 +1805,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& !vim_isbreak((int)(*ptr))) { && !vim_isbreak((int)(*ptr))) {
int mb_off = utf_head_off(line, ptr - 1); int mb_off = utf_head_off(line, ptr - 1);
char_u *p = ptr - (mb_off + 1); char_u *p = ptr - (mb_off + 1);
// TODO(neovim): is passing p for start of the line OK? chartabsize_T cts;
n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1;
init_chartabsize_arg(&cts, wp, lnum, (colnr_T)vcol, line, p);
n_extra = win_lbr_chartabsize(&cts, NULL) - 1;
// We have just drawn the showbreak value, no need to add // We have just drawn the showbreak value, no need to add
// space for it again. // space for it again.
@ -1825,6 +1834,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
c = ' '; c = ' ';
} }
} }
clear_chartabsize_arg(&cts);
} }
in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' '); in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' ');

View File

@ -1665,28 +1665,28 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
} else if (!(State & MODE_INSERT)) { } else if (!(State & MODE_INSERT)) {
new_cursor_col = curwin->w_cursor.col; new_cursor_col = curwin->w_cursor.col;
} 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); curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol);
/* // Advance the cursor until we reach the right screen column.
* Advance the cursor until we reach the right screen column. last_vcol = 0;
*/
vcol = last_vcol = 0;
new_cursor_col = -1;
ptr = get_cursor_line_ptr(); ptr = get_cursor_line_ptr();
while (vcol <= (int)curwin->w_virtcol) { chartabsize_T cts;
last_vcol = vcol; init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr);
if (new_cursor_col >= 0) { while (cts.cts_vcol <= (int)curwin->w_virtcol) {
new_cursor_col += utfc_ptr2len((char *)ptr + new_cursor_col); last_vcol = cts.cts_vcol;
} else { if (cts.cts_vcol > 0) {
new_cursor_col++; MB_PTR_ADV(cts.cts_ptr);
} }
vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol); if (*cts.cts_ptr == NUL) {
break;
}
cts.cts_vcol += lbr_chartabsize(&cts);
} }
vcol = last_vcol; 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
@ -2991,7 +2991,7 @@ static void replace_do_bs(int limit_col)
// Get the number of screen cells used by the character we are // Get the number of screen cells used by the character we are
// going to delete. // going to delete.
getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL); getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL);
orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol); orig_vcols = win_chartabsize(curwin, (char *)get_cursor_pos_ptr(), start_vcol);
} }
(void)del_char_after_col(limit_col); (void)del_char_after_col(limit_col);
if (l_State & VREPLACE_FLAG) { if (l_State & VREPLACE_FLAG) {
@ -3006,7 +3006,7 @@ static void replace_do_bs(int limit_col)
ins_len = (int)STRLEN(p) - orig_len; ins_len = (int)STRLEN(p) - orig_len;
vcol = start_vcol; vcol = start_vcol;
for (i = 0; i < ins_len; i++) { for (i = 0; i < ins_len; i++) {
vcol += win_chartabsize(curwin, p + i, vcol); vcol += win_chartabsize(curwin, (char *)p + i, vcol);
i += utfc_ptr2len((char *)p) - 1; i += utfc_ptr2len((char *)p) - 1;
} }
vcol -= start_vcol; vcol -= start_vcol;
@ -4644,11 +4644,15 @@ static bool ins_tab(void)
getvcol(curwin, &fpos, &vcol, NULL, NULL); getvcol(curwin, &fpos, &vcol, NULL, NULL);
getvcol(curwin, cursor, &want_vcol, NULL, NULL); getvcol(curwin, cursor, &want_vcol, NULL, NULL);
char_u *tab = (char_u *)"\t";
chartabsize_T cts;
init_chartabsize_arg(&cts, curwin, 0, vcol, tab, 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)) {
i = lbr_chartabsize(NULL, (char_u *)"\t", vcol); i = lbr_chartabsize(&cts);
if (vcol + i > want_vcol) { if (cts.cts_vcol + i > want_vcol) {
break; break;
} }
if (*ptr != TAB) { if (*ptr != TAB) {
@ -4663,19 +4667,24 @@ static bool ins_tab(void)
} }
fpos.col++; fpos.col++;
ptr++; ptr++;
vcol += i; cts.cts_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;
char_u *line = ptr;
// Skip over the spaces we need. // Skip over the spaces we need.
while (vcol < want_vcol && *ptr == ' ') { init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr);
vcol += lbr_chartabsize(line, ptr, vcol); while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ') {
ptr++; cts.cts_vcol += lbr_chartabsize(&cts);
cts.cts_ptr++;
repl_off++; repl_off++;
} }
ptr = (char_u *)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.
ptr--; ptr--;
@ -4855,7 +4864,6 @@ static int ins_digraph(void)
int ins_copychar(linenr_T lnum) int ins_copychar(linenr_T lnum)
{ {
int c; int c;
int temp;
char_u *ptr, *prev_ptr; char_u *ptr, *prev_ptr;
char_u *line; char_u *line;
@ -4865,17 +4873,23 @@ int ins_copychar(linenr_T lnum)
} }
// try to advance to the cursor column // try to advance to the cursor column
temp = 0; line = ml_get(lnum);
line = ptr = ml_get(lnum); prev_ptr = line;
prev_ptr = ptr;
validate_virtcol(); validate_virtcol();
while ((colnr_T)temp < curwin->w_virtcol && *ptr != NUL) {
prev_ptr = ptr; chartabsize_T cts;
temp += lbr_chartabsize_adv(line, &ptr, (colnr_T)temp); init_chartabsize_arg(&cts, curwin, lnum, 0, line, line);
while (cts.cts_vcol < curwin->w_virtcol && *cts.cts_ptr != NUL) {
prev_ptr = (char_u *)cts.cts_ptr;
cts.cts_vcol += lbr_chartabsize_adv(&cts);
} }
if ((colnr_T)temp > curwin->w_virtcol) {
if (cts.cts_vcol > curwin->w_virtcol) {
ptr = prev_ptr; ptr = prev_ptr;
} else {
ptr = (char_u *)cts.cts_ptr;
} }
clear_chartabsize_arg(&cts);
c = utf_ptr2char((char *)ptr); c = utf_ptr2char((char *)ptr);
if (c == NUL) { if (c == NUL) {

View File

@ -849,11 +849,11 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
// col(".") when the cursor is on the NUL at the end of the line // col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column. // because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor) { if (virtual_active() && fp == &curwin->w_cursor) {
char_u *p = get_cursor_pos_ptr(); char *p = (char *)get_cursor_pos_ptr();
if (curwin->w_cursor.coladd >= if (curwin->w_cursor.coladd >=
(colnr_T)win_chartabsize(curwin, p, curwin->w_virtcol - curwin->w_cursor.coladd)) { (colnr_T)win_chartabsize(curwin, p, curwin->w_virtcol - curwin->w_cursor.coladd)) {
int l; int l;
if (*p != NUL && p[(l = utfc_ptr2len((char *)p))] == NUL) { if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) {
col += l; col += l;
} }
} }
@ -8537,7 +8537,7 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData f
col = (int)tv_get_number(&argvars[1]); col = (int)tv_get_number(&argvars[1]);
} }
rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char *)s) - col);
} }
/// "strwidth()" function /// "strwidth()" function

View File

@ -841,7 +841,7 @@ void ex_retab(exarg_T *eap)
if (ptr[col] == NUL) { if (ptr[col] == NUL) {
break; break;
} }
vcol += win_chartabsize(curwin, (char_u *)ptr + col, (colnr_T)vcol); vcol += win_chartabsize(curwin, ptr + col, (colnr_T)vcol);
if (vcol >= MAXCOL) { if (vcol >= MAXCOL) {
emsg(_(e_resulting_text_too_long)); emsg(_(e_resulting_text_too_long));
break; break;

View File

@ -2547,7 +2547,7 @@ static int vgetorpeek(bool advance)
&& (State & MODE_INSERT) && (State & MODE_INSERT)
&& (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout)) && (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout))
&& (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) { && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) {
colnr_T col = 0, vcol; colnr_T col = 0;
char_u *ptr; char_u *ptr;
if (mode_displayed) { if (mode_displayed) {
@ -2565,15 +2565,20 @@ 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
col = vcol = curwin->w_wcol = 0; curwin->w_wcol = 0;
ptr = get_cursor_line_ptr(); ptr = get_cursor_line_ptr();
while (col < curwin->w_cursor.col) { chartabsize_T cts;
if (!ascii_iswhite(ptr[col])) { init_chartabsize_arg(&cts, curwin,
curwin->w_wcol = vcol; curwin->w_cursor.lnum, 0, ptr, ptr);
while ((char_u *)cts.cts_ptr < ptr + curwin->w_cursor.col) {
if (!ascii_iswhite(*cts.cts_ptr)) {
curwin->w_wcol = cts.cts_vcol;
} }
vcol += lbr_chartabsize(ptr, ptr + col, vcol); cts.cts_vcol += lbr_chartabsize(&cts);
col += utfc_ptr2len((char *)ptr + col); cts.cts_ptr += utfc_ptr2len(cts.cts_ptr);
} }
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;
curwin->w_wcol %= curwin->w_width_inner; curwin->w_wcol %= curwin->w_width_inner;

View File

@ -1034,13 +1034,15 @@ int get_lisp_indent(void)
amount = 2; amount = 2;
} else { } else {
char_u *line = that; char_u *line = that;
chartabsize_T cts;
amount = 0; init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
while (*cts.cts_ptr != NUL && col > 0) {
while (*that && col) { cts.cts_vcol += lbr_chartabsize_adv(&cts);
amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
col--; col--;
} }
amount = cts.cts_vcol;
that = (char_u *)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):
@ -1056,10 +1058,15 @@ int get_lisp_indent(void)
} }
firsttry = amount; firsttry = amount;
while (ascii_iswhite(*that)) { init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line),
amount += lbr_chartabsize(line, that, (colnr_T)amount); amount, line, that);
that++; while (ascii_iswhite(*cts.cts_ptr)) {
cts.cts_vcol += lbr_chartabsize(&cts);
cts.cts_ptr++;
} }
that = (char_u *)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.
@ -1072,33 +1079,38 @@ int get_lisp_indent(void)
parencount = 0; parencount = 0;
quotecount = 0; quotecount = 0;
init_chartabsize_arg(&cts, curwin,
(colnr_T)(that - line), amount, line, that);
if (vi_lisp || ((*that != '"') && (*that != '\'') if (vi_lisp || ((*that != '"') && (*that != '\'')
&& (*that != '#') && ((*that < '0') || (*that > '9')))) { && (*that != '#') && ((*that < '0') || (*that > '9')))) {
while (*that while (*cts.cts_ptr
&& (!ascii_iswhite(*that) || quotecount || parencount) && (!ascii_iswhite(*cts.cts_ptr) || quotecount || parencount)
&& (!((*that == '(' || *that == '[') && (!((*cts.cts_ptr == '(' || *cts.cts_ptr == '[')
&& !quotecount && !parencount && vi_lisp))) { && !quotecount && !parencount && vi_lisp))) {
if (*that == '"') { if (*cts.cts_ptr == '"') {
quotecount = !quotecount; quotecount = !quotecount;
} }
if (((*that == '(') || (*that == '[')) && !quotecount) { if (((*cts.cts_ptr == '(') || (*cts.cts_ptr == '[')) && !quotecount) {
parencount++; parencount++;
} }
if (((*that == ')') || (*that == ']')) && !quotecount) { if (((*cts.cts_ptr == ')') || (*cts.cts_ptr == ']')) && !quotecount) {
parencount--; parencount--;
} }
if ((*that == '\\') && (*(that + 1) != NUL)) { if ((*cts.cts_ptr == '\\') && (*(cts.cts_ptr + 1) != NUL)) {
amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount); cts.cts_vcol += lbr_chartabsize_adv(&cts);
} }
amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount); cts.cts_vcol += lbr_chartabsize_adv(&cts);
} }
} }
while (ascii_iswhite(*that)) { while (ascii_iswhite(*cts.cts_ptr)) {
amount += lbr_chartabsize(line, that, (colnr_T)amount); cts.cts_vcol += lbr_chartabsize(&cts);
that++; cts.cts_ptr++;
} }
that = (char_u *)cts.cts_ptr;
amount = cts.cts_vcol;
clear_chartabsize_arg(&cts);
if (!*that || (*that == ';')) { if (!*that || (*that == ';')) {
amount = firsttry; amount = firsttry;

View File

@ -631,14 +631,16 @@ colnr_T vcol2col(win_T *const wp, const linenr_T lnum, const colnr_T vcol)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{ {
// try to advance to the specified column // try to advance to the specified column
char_u *ptr = ml_get_buf(wp->w_buffer, lnum, false); char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
char_u *const line = ptr; chartabsize_T cts;
colnr_T count = 0; init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
while (count < vcol && *ptr != NUL) { while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) {
count += win_lbr_chartabsize(wp, line, ptr, count, NULL); cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
MB_PTR_ADV(ptr); MB_PTR_ADV(cts.cts_ptr);
} }
return (colnr_T)(ptr - line); clear_chartabsize_arg(&cts);
return (colnr_T)((char_u *)cts.cts_ptr - line);
} }
/// Set UI mouse depending on current mode and 'mouse'. /// Set UI mouse depending on current mode and 'mouse'.
@ -667,7 +669,7 @@ static colnr_T scroll_line_len(linenr_T lnum)
char_u *line = ml_get(lnum); char_u *line = ml_get(lnum);
if (*line != NUL) { if (*line != NUL) {
for (;;) { for (;;) {
int numchar = win_chartabsize(curwin, line, col); int numchar = win_chartabsize(curwin, (char *)line, col);
MB_PTR_ADV(line); MB_PTR_ADV(line);
if (*line == NUL) { // don't count the last character if (*line == NUL) { // don't count the last character
break; break;
@ -790,7 +792,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// checked for concealed characters. // checked for concealed characters.
vcol = 0; vcol = 0;
while (vcol < offset && *ptr != NUL) { while (vcol < offset && *ptr != NUL) {
vcol += win_chartabsize(curwin, ptr, vcol); vcol += win_chartabsize(curwin, (char *)ptr, vcol);
ptr += utfc_ptr2len((char *)ptr); ptr += utfc_ptr2len((char *)ptr);
} }
@ -801,7 +803,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
vcol = offset; vcol = offset;
ptr_end = ptr_row_offset; ptr_end = ptr_row_offset;
while (vcol < col && *ptr_end != NUL) { while (vcol < col && *ptr_end != NUL) {
vcol += win_chartabsize(curwin, ptr_end, vcol); vcol += win_chartabsize(curwin, (char *)ptr_end, vcol);
ptr_end += utfc_ptr2len((char *)ptr_end); ptr_end += utfc_ptr2len((char *)ptr_end);
} }
@ -816,7 +818,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
#define DECR() nudge--; ptr_end -= utfc_ptr2len((char *)ptr_end) #define DECR() nudge--; ptr_end -= utfc_ptr2len((char *)ptr_end)
while (ptr < ptr_end && *ptr != NUL) { while (ptr < ptr_end && *ptr != NUL) {
cwidth = win_chartabsize(curwin, ptr, vcol); cwidth = win_chartabsize(curwin, (char *)ptr, vcol);
vcol += cwidth; vcol += cwidth;
if (cwidth > 1 && *ptr == '\t' && nudge > 0) { if (cwidth > 1 && *ptr == '\t' && nudge > 0) {
// A tab will "absorb" any previous adjustments. // A tab will "absorb" any previous adjustments.

View File

@ -378,12 +378,20 @@ static void shift_block(oparg_T *oap, int amount)
bd.startspaces = 0; bd.startspaces = 0;
} }
} }
for (; ascii_iswhite(*bd.textstart);) {
// TODO(fmoralesc): is passing bd.textstart for start of the line OK? // TODO(vim): is passing bd.textstart for start of the line OK?
incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, bd.start_vcol); chartabsize_T cts;
init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum,
bd.start_vcol, bd.textstart, bd.textstart);
while (ascii_iswhite(*cts.cts_ptr)) {
incr = lbr_chartabsize_adv(&cts);
total += incr; total += incr;
bd.start_vcol += incr; cts.cts_vcol += incr;
} }
bd.textstart = (char_u *)cts.cts_ptr;
bd.start_vcol = cts.cts_vcol;
clear_chartabsize_arg(&cts);
// OK, now total=all the VWS reqd, and textstart points at the 1st // OK, now total=all the VWS reqd, and textstart points at the 1st
// non-ws char in the block. // non-ws char in the block.
if (!curbuf->b_p_et) { if (!curbuf->b_p_et) {
@ -438,10 +446,16 @@ 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;
while (ascii_iswhite(*non_white)) { chartabsize_T cts;
incr = lbr_chartabsize_adv(bd.textstart, &non_white, non_white_col); init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum,
non_white_col += incr; non_white_col, bd.textstart, non_white);
while (ascii_iswhite(*cts.cts_ptr)) {
incr = lbr_chartabsize_adv(&cts);
cts.cts_vcol += incr;
} }
non_white_col = cts.cts_vcol;
non_white = (char_u *)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.
@ -462,17 +476,19 @@ 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;
} }
while (verbatim_copy_width < destination_col) { init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width,
char_u *line = verbatim_copy_end; bd.textstart, verbatim_copy_end);
while (cts.cts_vcol < destination_col) {
// TODO: is passing verbatim_copy_end for start of the line OK? incr = lbr_chartabsize(&cts);
incr = lbr_chartabsize(line, verbatim_copy_end, verbatim_copy_width); if (cts.cts_vcol + incr > destination_col) {
if (verbatim_copy_width + incr > destination_col) {
break; break;
} }
verbatim_copy_width += incr; cts.cts_vcol += incr;
MB_PTR_ADV(verbatim_copy_end); MB_PTR_ADV(cts.cts_ptr);
} }
verbatim_copy_width = cts.cts_vcol;
verbatim_copy_end = (char_u *)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
@ -1550,6 +1566,7 @@ int op_delete(oparg_T *oap)
// Put deleted text into register 1 and shift number registers if the // Put deleted text into register 1 and shift number registers if the
// delete contains a line break, or when using a specific operator (Vi // delete contains a line break, or when using a specific operator (Vi
// compatible) // compatible)
if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) {
shift_delete_registers(is_append_register(oap->regname)); shift_delete_registers(is_append_register(oap->regname));
reg = &y_regs[1]; reg = &y_regs[1];
@ -3280,12 +3297,19 @@ void do_put(int regname, yankreg_T *reg, int dir, long 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
oldp = get_cursor_line_ptr(); oldp = get_cursor_line_ptr();
oldlen = STRLEN(oldp); oldlen = STRLEN(oldp);
for (ptr = oldp; vcol < col && *ptr;) { chartabsize_T cts;
init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0,
oldp, oldp);
while (cts.cts_vcol < col && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on) // Count a tab for what it's worth (if list mode not on)
incr = lbr_chartabsize_adv(oldp, &ptr, vcol); incr = lbr_chartabsize_adv(&cts);
vcol += incr; cts.cts_vcol += incr;
} }
vcol = cts.cts_vcol;
ptr = (char_u *)cts.cts_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);
@ -3312,9 +3336,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// 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,
(char_u *)y_array[i], (char_u *)y_array[i]);
for (int j = 0; j < yanklen; j++) { for (int j = 0; j < yanklen; j++) {
spaces -= lbr_chartabsize(NULL, (char_u *)(&y_array[i][j]), 0); spaces -= lbr_chartabsize(&cts);
cts.cts_ptr++;
cts.cts_vcol = 0;
} }
clear_chartabsize_arg(&cts);
if (spaces < 0) { if (spaces < 0) {
spaces = 0; spaces = 0;
} }
@ -4227,22 +4256,28 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
bdp->start_char_vcols = 0; bdp->start_char_vcols = 0;
line = ml_get(lnum); line = ml_get(lnum);
pstart = line;
prev_pstart = line; prev_pstart = line;
while (bdp->start_vcol < oap->start_vcol && *pstart) {
chartabsize_T cts;
init_chartabsize_arg(&cts, curwin, lnum, bdp->start_vcol, line, line);
while (cts.cts_vcol < oap->start_vcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on) // Count a tab for what it's worth (if list mode not on)
incr = lbr_chartabsize(line, pstart, bdp->start_vcol); incr = lbr_chartabsize(&cts);
bdp->start_vcol += incr; cts.cts_vcol += incr;
if (ascii_iswhite(*pstart)) { if (ascii_iswhite(*cts.cts_ptr)) {
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 = pstart; prev_pstart = (char_u *)cts.cts_ptr;
MB_PTR_ADV(pstart); MB_PTR_ADV(cts.cts_ptr);
} }
bdp->start_vcol = cts.cts_vcol;
pstart = (char_u *)cts.cts_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
bdp->end_vcol = bdp->start_vcol; bdp->end_vcol = bdp->start_vcol;
@ -4278,13 +4313,19 @@ 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);
prev_pend = pend; prev_pend = pend;
while (bdp->end_vcol <= oap->end_vcol && *pend != NUL) { while (cts.cts_vcol <= oap->end_vcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on) // Count a tab for what it's worth (if list mode not on)
prev_pend = pend; prev_pend = (char_u *)cts.cts_ptr;
incr = lbr_chartabsize_adv(line, &pend, bdp->end_vcol); incr = lbr_chartabsize_adv(&cts);
bdp->end_vcol += incr; cts.cts_vcol += incr;
} }
bdp->end_vcol = cts.cts_vcol;
pend = (char_u *)cts.cts_ptr;
clear_chartabsize_arg(&cts);
if (bdp->end_vcol <= oap->end_vcol if (bdp->end_vcol <= oap->end_vcol
&& (!is_del && (!is_del
|| oap->op_type == OP_APPEND || oap->op_type == OP_APPEND

View File

@ -98,15 +98,15 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight)
/// "wp". Does not care about folding, 'wrap' or 'diff'. /// "wp". Does not care about folding, 'wrap' or 'diff'.
int plines_win_nofold(win_T *wp, linenr_T lnum) int plines_win_nofold(win_T *wp, linenr_T lnum)
{ {
char_u *s; char *s;
unsigned int col; unsigned int col;
int width; int width;
s = ml_get_buf(wp->w_buffer, lnum, false); s = (char *)ml_get_buf(wp->w_buffer, lnum, false);
if (*s == NUL) { // empty line if (*s == NUL) { // empty line
return 1; return 1;
} }
col = win_linetabsize(wp, s, MAXCOL); col = win_linetabsize(wp, lnum, (char_u *)s, 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.
@ -145,23 +145,27 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
} }
char_u *line = ml_get_buf(wp->w_buffer, lnum, false); char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
char_u *s = line;
colnr_T col = 0; colnr_T col = 0;
while (*s != NUL && --column >= 0) { chartabsize_T cts;
col += win_lbr_chartabsize(wp, line, s, col, NULL);
MB_PTR_ADV(s); init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
while (*cts.cts_ptr != NUL && --column >= 0) {
cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
MB_PTR_ADV(cts.cts_ptr);
} }
// If *s is a TAB, and the TAB is not displayed as ^I, and we're not in // If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not
// 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.
if (*s == TAB && (State & MODE_NORMAL) col = cts.cts_vcol;
if (*cts.cts_ptr == TAB && (State & MODE_NORMAL)
&& (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
col += win_lbr_chartabsize(wp, line, s, col, NULL) - 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);
@ -223,7 +227,7 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
/// @param col /// @param col
/// ///
/// @return Number of characters. /// @return Number of characters.
int win_chartabsize(win_T *wp, char_u *p, colnr_T col) int win_chartabsize(win_T *wp, char *p, colnr_T col)
{ {
buf_T *buf = wp->w_buffer; buf_T *buf = wp->w_buffer;
if (*p == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { if (*p == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
@ -241,24 +245,24 @@ int win_chartabsize(win_T *wp, char_u *p, colnr_T col)
/// @return Number of characters the string will take on the screen. /// @return Number of characters the string will take on the screen.
int linetabsize(char_u *s) int linetabsize(char_u *s)
{ {
return linetabsize_col(0, s); return linetabsize_col(0, (char *)s);
} }
/// Like linetabsize(), but starting at column "startcol". /// Like linetabsize(), 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_u *s) int linetabsize_col(int startcol, char *s)
{ {
colnr_T col = startcol; chartabsize_T cts;
char_u *line = s; // pointer to start of line, for breakindent init_chartabsize_arg(&cts, curwin, 0, startcol, (char_u *)s, (char_u *)s);
while (*cts.cts_ptr != NUL) {
while (*s != NUL) { cts.cts_vcol += lbr_chartabsize_adv(&cts);
col += lbr_chartabsize_adv(line, &s, col);
} }
return (int)col; clear_chartabsize_arg(&cts);
return (int)cts.cts_vcol;
} }
/// Like linetabsize(), but for a given window instead of the current one. /// Like linetabsize(), but for a given window instead of the current one.
@ -268,19 +272,39 @@ int linetabsize_col(int startcol, char_u *s)
/// @param len /// @param len
/// ///
/// @return Number of characters the string will take on the screen. /// @return Number of characters the string will take on the screen.
unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len) unsigned int win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len)
{ {
colnr_T col = 0; chartabsize_T cts;
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
for (char_u *s = line; for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < (char *)line + len);
*s != NUL && (len == MAXCOL || s < line + len); MB_PTR_ADV(cts.cts_ptr)) {
MB_PTR_ADV(s)) { cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
col += win_lbr_chartabsize(wp, line, s, col, NULL);
} }
clear_chartabsize_arg(&cts);
return (unsigned int)col; return (unsigned int)cts.cts_vcol;
} }
/// Prepare the structure passed to chartabsize functions.
///
/// "line" is the start of the line, "ptr" is the first relevant character.
/// When "lnum" is zero do not use text properties that insert text.
void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char_u *line,
char_u *ptr)
{
cts->cts_win = wp;
cts->cts_lnum = lnum;
cts->cts_vcol = col;
cts->cts_line = (char *)line;
cts->cts_ptr = (char *)ptr;
cts->cts_cur_text_width = 0;
// TODO(bfredl): actually lookup inline virtual text here
cts->cts_has_virt_text = false;
}
/// Free any allocated item in "cts".
void clear_chartabsize_arg(chartabsize_T *cts)
{}
/// like win_chartabsize(), but also check for line breaks on the screen /// like win_chartabsize(), but also check for line breaks on the screen
/// ///
/// @param line /// @param line
@ -288,16 +312,16 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
/// @param col /// @param col
/// ///
/// @return The number of characters taken up on the screen. /// @return The number of characters taken up on the screen.
int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col) int lbr_chartabsize(chartabsize_T *cts)
{ {
if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL
&& !curwin->w_p_bri) { && !curwin->w_p_bri && !cts->cts_has_virt_text) {
if (curwin->w_p_wrap) { if (curwin->w_p_wrap) {
return win_nolbr_chartabsize(curwin, s, col, NULL); return win_nolbr_chartabsize(cts, NULL);
} }
return win_chartabsize(curwin, s, col); return win_chartabsize(curwin, cts->cts_ptr, cts->cts_vcol);
} }
return win_lbr_chartabsize(curwin, line == NULL ? s: line, s, col, NULL); return win_lbr_chartabsize(cts, NULL);
} }
/// Call lbr_chartabsize() and advance the pointer. /// Call lbr_chartabsize() and advance the pointer.
@ -307,12 +331,12 @@ int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col)
/// @param col /// @param col
/// ///
/// @return The number of characters take up on the screen. /// @return The number of characters take up on the screen.
int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col) int lbr_chartabsize_adv(chartabsize_T *cts)
{ {
int retval; int retval;
retval = lbr_chartabsize(line, *s, col); retval = lbr_chartabsize(cts);
MB_PTR_ADV(*s); MB_PTR_ADV(cts->cts_ptr);
return retval; return retval;
} }
@ -322,17 +346,19 @@ int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col)
/// string at start of line. Warning: *headp is only set if it's a non-zero /// string at start of line. Warning: *headp is only set if it's a non-zero
/// value, init to 0 before calling. /// value, init to 0 before calling.
/// ///
/// @param wp /// @param cts
/// @param line
/// @param s
/// @param col
/// @param headp /// @param headp
/// ///
/// @return The number of characters taken up on the screen. /// @return The number of characters taken up on the screen.
int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *headp) int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
{ {
win_T *wp = cts->cts_win;
char *line = cts->cts_line; // start of the line
char_u *s = (char_u *)cts->cts_ptr;
colnr_T vcol = cts->cts_vcol;
colnr_T col2; colnr_T col2;
colnr_T col_adj = 0; // col + screen size of tab colnr_T col_adj = 0; // vcol + screen size of tab
colnr_T colmax; colnr_T colmax;
int added; int added;
int mb_added = 0; int mb_added = 0;
@ -340,16 +366,23 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
char_u *ps; char_u *ps;
int n; int n;
cts->cts_cur_text_width = 0;
// No 'linebreak', 'showbreak' and 'breakindent': return quickly. // No 'linebreak', 'showbreak' and 'breakindent': return quickly.
if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL) { if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL
&& !cts->cts_has_virt_text) {
if (wp->w_p_wrap) { if (wp->w_p_wrap) {
return win_nolbr_chartabsize(wp, s, col, headp); return win_nolbr_chartabsize(cts, headp);
} }
return win_chartabsize(wp, s, col); return win_chartabsize(wp, (char *)s, vcol);
}
// First get normal size, without 'linebreak' or virtual text
int size = win_chartabsize(wp, (char *)s, vcol);
if (cts->cts_has_virt_text) {
// TODO(bfredl): inline virtual text
} }
// First get normal size, without 'linebreak'
int size = win_chartabsize(wp, s, col);
int c = *s; int c = *s;
if (*s == TAB) { if (*s == TAB) {
col_adj = size - 1; col_adj = size - 1;
@ -365,15 +398,15 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
// Count all characters from first non-blank after a blank up to next // Count all characters from first non-blank after a blank up to next
// non-blank after a blank. // non-blank after a blank.
numberextra = win_col_off(wp); numberextra = win_col_off(wp);
col2 = col; col2 = vcol;
colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj); colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj);
if (col >= colmax) { if (vcol >= colmax) {
colmax += col_adj; colmax += col_adj;
n = colmax + win_col_off2(wp); n = colmax + win_col_off2(wp);
if (n > 0) { if (n > 0) {
colmax += (((col - colmax) / n) + 1) * n - col_adj; colmax += (((vcol - colmax) / n) + 1) * n - col_adj;
} }
} }
@ -383,21 +416,21 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
c = *s; c = *s;
if (!(c != NUL if (!(c != NUL
&& (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) { && (vim_isbreak(c) || col2 == vcol || !vim_isbreak((int)(*ps))))) {
break; break;
} }
col2 += win_chartabsize(wp, s, col2); col2 += win_chartabsize(wp, (char *)s, col2);
if (col2 >= colmax) { // doesn't fit if (col2 >= colmax) { // doesn't fit
size = colmax - col + col_adj; size = colmax - vcol + col_adj;
break; break;
} }
} }
} else if ((size == 2) } else if ((size == 2)
&& (MB_BYTE2LEN(*s) > 1) && (MB_BYTE2LEN(*s) > 1)
&& wp->w_p_wrap && wp->w_p_wrap
&& in_win_border(wp, col)) { && in_win_border(wp, vcol)) {
// Count the ">" in the last column. // Count the ">" in the last column.
size++; size++;
mb_added = 1; mb_added = 1;
@ -409,40 +442,40 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
added = 0; added = 0;
char *const sbr = (char *)get_showbreak_value(wp); char *const sbr = (char *)get_showbreak_value(wp);
if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && col != 0) { if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) {
colnr_T sbrlen = 0; colnr_T sbrlen = 0;
int numberwidth = win_col_off(wp); int numberwidth = win_col_off(wp);
numberextra = numberwidth; numberextra = numberwidth;
col += numberextra + mb_added; vcol += numberextra + mb_added;
if (col >= (colnr_T)wp->w_width_inner) { if (vcol >= (colnr_T)wp->w_width_inner) {
col -= wp->w_width_inner; vcol -= wp->w_width_inner;
numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp)); numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp));
if (col >= numberextra && numberextra > 0) { if (vcol >= numberextra && numberextra > 0) {
col %= numberextra; vcol %= numberextra;
} }
if (*sbr != NUL) { if (*sbr != NUL) {
sbrlen = (colnr_T)mb_charlen((char_u *)sbr); sbrlen = (colnr_T)mb_charlen((char_u *)sbr);
if (col >= sbrlen) { if (vcol >= sbrlen) {
col -= sbrlen; vcol -= sbrlen;
} }
} }
if (col >= numberextra && numberextra > 0) { if (vcol >= numberextra && numberextra > 0) {
col %= numberextra; vcol %= numberextra;
} else if (col > 0 && numberextra > 0) { } else if (vcol > 0 && numberextra > 0) {
col += numberwidth - win_col_off2(wp); vcol += numberwidth - win_col_off2(wp);
} }
numberwidth -= win_col_off2(wp); numberwidth -= win_col_off2(wp);
} }
if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) { if (vcol == 0 || (vcol + size + sbrlen > (colnr_T)wp->w_width_inner)) {
if (*sbr != NUL) { if (*sbr != NUL) {
if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) { if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) {
// Calculate effective window width. // Calculate effective window width.
int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth; int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth;
int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col)) int prev_width = vcol ? ((colnr_T)wp->w_width_inner - (sbrlen + vcol))
: 0; : 0;
if (width <= 0) { if (width <= 0) {
@ -459,11 +492,11 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
} }
if (wp->w_p_bri) { if (wp->w_p_bri) {
added += get_breakindent_win(wp, line); added += get_breakindent_win(wp, (char_u *)line);
} }
size += added; size += added;
if (col != 0) { if (vcol != 0) {
added = 0; added = 0;
} }
} }
@ -485,8 +518,11 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
/// @param headp /// @param headp
/// ///
/// @return The number of characters take up on the screen. /// @return The number of characters take up on the screen.
static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp)
{ {
win_T *wp = cts->cts_win;
char *s = cts->cts_ptr;
colnr_T col = cts->cts_vcol;
int n; int n;
if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
@ -498,7 +534,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
// Add one cell for a double-width character in the last column of the // Add one cell for a double-width character in the last column of the
// window, displayed with a ">". // window, displayed with a ">".
if ((n == 2) && (MB_BYTE2LEN(*s) > 1) && in_win_border(wp, col)) { if ((n == 2) && (MB_BYTE2LEN((uint8_t)(*s)) > 1) && in_win_border(wp, col)) {
if (headp != NULL) { if (headp != NULL) {
*headp = 1; *headp = 1;
} }

View File

@ -3,6 +3,20 @@
#include "nvim/vim.h" #include "nvim/vim.h"
// Argument for lbr_chartabsize().
typedef struct {
win_T *cts_win;
linenr_T cts_lnum; // zero when not using text properties
char *cts_line; // start of the line
char *cts_ptr; // current position in line
bool cts_has_virt_text; // true if if a property inserts text
int cts_cur_text_width; // width of current inserted text
// TODO(bfredl): iterator in to the marktree for scanning virt text
int cts_vcol; // virtual column at current position
} chartabsize_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "plines.h.generated.h" # include "plines.h.generated.h"
#endif #endif

View File

@ -1165,7 +1165,7 @@ static bool reg_match_visual(void)
rex.line = reg_getline(rex.lnum); rex.line = reg_getline(rex.lnum);
rex.input = rex.line + col; rex.input = rex.line + col;
unsigned int cols_u = win_linetabsize(wp, rex.line, col); unsigned int cols_u = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, rex.line, col);
assert(cols_u <= MAXCOL); assert(cols_u <= MAXCOL);
colnr_T cols = (colnr_T)cols_u; colnr_T cols = (colnr_T)cols_u;
if (cols < start || cols > end - (*p_sel == 'e')) { if (cols < start || cols > end - (*p_sel == 'e')) {

View File

@ -3764,6 +3764,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case RE_VCOL: case RE_VCOL:
if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
? curwin : rex.reg_win, ? curwin : rex.reg_win,
rex.reg_firstlnum + rex.lnum,
rex.line, rex.line,
(colnr_T)(rex.input - rex.line)) + 1, (colnr_T)(rex.input - rex.line)) + 1,
scan)) { scan)) {

View File

@ -6910,7 +6910,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
result = col > t->state->val * ts; result = col > t->state->val * ts;
} }
if (!result) { if (!result) {
uintmax_t lts = win_linetabsize(wp, rex.line, col); uintmax_t lts = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, rex.line, col);
assert(t->state->val >= 0); assert(t->state->val >= 0);
result = nfa_re_num_cmp((uintmax_t)t->state->val, op, lts + 1); result = nfa_re_num_cmp((uintmax_t)t->state->val, op, lts + 1);
} }