vim-patch:9.2.0289: 'linebreak' may lead to wrong Visual block highlighting (#38749)

Problem:  'linebreak' may lead to wrong Visual block highlighting when
          end char occupies multiple cells (after 7.4.467).
Solution: Exclude 'linebreak' from the ending column instead of setting
          'virtualedit' temporarily (zeertzjq).

fixes:  vim/vim#19898
closes: vim/vim#19900

23be1889d1
This commit is contained in:
zeertzjq
2026-04-04 08:58:17 +08:00
committed by GitHub
parent d769fde8c9
commit 164dfa1d5f
21 changed files with 249 additions and 101 deletions

View File

@@ -741,7 +741,7 @@ void ins_char_bytes(char *buf, size_t charlen)
// be deleted to make room for the new character, counting screen
// cells. May result in adding spaces to fill a gap.
colnr_T vcol;
getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL);
getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL, 0);
colnr_T new_vcol = vcol + win_chartabsize(curwin, buf, vcol);
while (oldp[col + oldlen] != NUL && vcol < new_vcol) {
vcol += win_chartabsize(curwin, oldp + col + oldlen, vcol);

View File

@@ -34,7 +34,7 @@ int getviscol(void)
{
colnr_T x;
getvvcol(curwin, &curwin->w_cursor, &x, NULL, NULL);
getvvcol(curwin, &curwin->w_cursor, &x, NULL, NULL, 0);
return (int)x;
}
@@ -47,7 +47,7 @@ int getviscol2(colnr_T col, colnr_T coladd)
pos.lnum = curwin->w_cursor.lnum;
pos.col = col;
pos.coladd = coladd;
getvvcol(curwin, &pos, &x, NULL, NULL);
getvvcol(curwin, &pos, &x, NULL, NULL, 0);
return (int)x;
}
@@ -222,7 +222,7 @@ static int coladvance2(win_T *wp, pos_T *pos, bool addspaces, bool finetune, col
if (!one_more) {
colnr_T scol, ecol;
getvcol(wp, pos, &scol, NULL, &ecol);
getvcol(wp, pos, &scol, NULL, &ecol, 0);
pos->coladd = ecol - scol;
}
} else {
@@ -372,7 +372,7 @@ void check_cursor_col(win_T *win)
assert(win->w_cursor.coladd > 0);
int cs, ce;
getvcol(win, &win->w_cursor, &cs, NULL, &ce);
getvcol(win, &win->w_cursor, &cs, NULL, &ce, 0);
win->w_cursor.coladd = MIN(win->w_cursor.coladd, ce - cs);
}
} else {
@@ -452,7 +452,7 @@ bool set_leftcol(colnr_T leftcol)
// advance the cursor one more char. If this fails (last char of the
// line) adjust the scrolling.
colnr_T s, e;
getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e, 0);
if (e > (colnr_T)lastcol) {
retval = true;
coladvance(curwin, s - 1);

View File

@@ -1217,7 +1217,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
if (VIsual_mode == 'V') { // linewise
wlv.fromcol = 0;
} else {
getvvcol(wp, top, (colnr_T *)&wlv.fromcol, NULL, NULL);
getvvcol(wp, top, (colnr_T *)&wlv.fromcol, NULL, NULL, 0);
if (gchar_pos(top) == NUL) {
wlv.tocol = wlv.fromcol + 1;
}
@@ -1233,9 +1233,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
} else {
pos_T pos = *bot;
if (*p_sel == 'e') {
getvvcol(wp, &pos, (colnr_T *)&wlv.tocol, NULL, NULL);
getvvcol(wp, &pos, (colnr_T *)&wlv.tocol, NULL, NULL, 0);
} else {
getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&wlv.tocol);
getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&wlv.tocol, 0);
wlv.tocol++;
}
}
@@ -1260,8 +1260,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
&& lnum >= curwin->w_cursor.lnum
&& lnum <= curwin->w_cursor.lnum + search_match_lines) {
if (lnum == curwin->w_cursor.lnum) {
getvcol(curwin, &(curwin->w_cursor),
(colnr_T *)&wlv.fromcol, NULL, NULL);
getvcol(curwin, &(curwin->w_cursor), (colnr_T *)&wlv.fromcol, NULL, NULL, 0);
} else {
wlv.fromcol = 0;
}
@@ -1270,7 +1269,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
.lnum = lnum,
.col = search_match_endcol,
};
getvcol(curwin, &pos, (colnr_T *)&wlv.tocol, NULL, NULL);
getvcol(curwin, &pos, (colnr_T *)&wlv.tocol, NULL, NULL, 0);
}
// do at least one character; happens when past end of line
if (wlv.fromcol == wlv.tocol && search_match_endcol) {
@@ -2331,6 +2330,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
CharsizeArg csarg;
// lnum == 0, do not want virtual text to be counted here
CSType cstype = init_charsize_arg(&csarg, wp, 0, line);
// TODO(zeertzjq): consider using CharSize.tail here
wlv.n_extra = win_charsize(cstype, wlv.vcol, p, utf_ptr2CharInfo(p).value,
&csarg).width - 1;

View File

@@ -1850,15 +1850,8 @@ static void win_update(win_T *wp)
// First compute the actual start and end column.
if (VIsual_mode == Ctrl_V) {
colnr_T fromc, toc;
unsigned save_ve_flags = curwin->w_ve_flags;
if (curwin->w_p_lbr) {
curwin->w_ve_flags = kOptVeFlagAll;
}
getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc, GETVCOL_END_EXCL_LBR);
toc++;
curwin->w_ve_flags = save_ve_flags;
// Highlight to the end of the line, unless 'virtualedit' has
// "block".
if (curwin->w_curswant == MAXCOL) {
@@ -1875,7 +1868,7 @@ static void win_update(win_T *wp)
colnr_T t;
pos.col = ml_get_buf_len(wp->w_buffer, pos.lnum);
getvvcol(wp, &pos, NULL, NULL, &t);
getvvcol(wp, &pos, NULL, NULL, &t, 0);
toc = MAX(toc, t);
}
toc++;

View File

@@ -2820,7 +2820,7 @@ static void replace_do_bs(int limit_col)
if (l_State & VREPLACE_FLAG) {
// Get the number of screen cells used by the character we are
// going to delete.
getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL);
getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL, 0);
orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol);
}
del_char_after_col(limit_col);
@@ -4009,8 +4009,8 @@ static bool ins_tab(void)
}
// compute virtual column numbers of first white and cursor
getvcol(curwin, &fpos, &vcol, NULL, NULL);
getvcol(curwin, cursor, &want_vcol, NULL, NULL);
getvcol(curwin, &fpos, &vcol, NULL, NULL, 0);
getvcol(curwin, cursor, &want_vcol, NULL, NULL, 0);
char *tab = "\t";
int32_t tab_v = (uint8_t)(*tab);

View File

@@ -2285,8 +2285,8 @@ static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2
} else if (*region_type == kMTBlockWise) {
colnr_T sc1, ec1, sc2, ec2;
const bool lbr_saved = reset_lbr();
getvvcol(curwin, p1, &sc1, NULL, &ec1);
getvvcol(curwin, p2, &sc2, NULL, &ec2);
getvvcol(curwin, p1, &sc1, NULL, &ec1, 0);
getvvcol(curwin, p2, &sc2, NULL, &ec2, 0);
restore_lbr(lbr_saved);
oap->motion_type = kMTBlockWise;
oap->inclusive = true;
@@ -7763,7 +7763,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
fp->col = len;
}
}
getvvcol(wp, fp, &vcol_start, NULL, &vcol_end);
getvvcol(wp, fp, &vcol_start, NULL, &vcol_end, 0);
vcol_start++;
vcol_end++;
}

View File

@@ -3959,10 +3959,10 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
print_line_no_prefix(lnum, subflags.do_number, subflags.do_list);
colnr_T sc, ec;
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL, 0);
curwin->w_cursor.col = MAX(regmatch.endpos[0].col - 1, 0);
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec, 0);
curwin->w_cursor.col = regmatch.startpos[0].col;
if (subflags.do_number || curwin->w_p_nu) {
int numw = number_width(curwin) + 1;

View File

@@ -766,7 +766,7 @@ int get_number_indent(linenr_T lnum)
if ((pos.lnum == 0) || (*ml_get_pos(&pos) == NUL)) {
return -1;
}
getvcol(curwin, &pos, &col, NULL, NULL);
getvcol(curwin, &pos, &col, NULL, NULL, 0);
return (int)col;
}

View File

@@ -679,7 +679,7 @@ static int get_indent_nolabel(linenr_T lnum) // XXX
fp.col = (colnr_T)(p - l);
fp.lnum = lnum;
getvcol(curwin, &fp, &col, NULL, NULL);
getvcol(curwin, &fp, &col, NULL, NULL, 0);
return (int)col;
}
@@ -754,7 +754,7 @@ static int cin_first_id_amount(void)
p = skipwhite(p + len);
fp.lnum = curwin->w_cursor.lnum;
fp.col = (colnr_T)(p - line);
getvcol(curwin, &fp, &col, NULL, NULL);
getvcol(curwin, &fp, &col, NULL, NULL, 0);
return (int)col;
}
@@ -803,7 +803,7 @@ static int cin_get_equal_amount(linenr_T lnum)
fp.lnum = lnum;
fp.col = (colnr_T)(s - line);
getvcol(curwin, &fp, &col, NULL, NULL);
getvcol(curwin, &fp, &col, NULL, NULL, 0);
return (int)col;
}
@@ -1367,7 +1367,7 @@ static int get_baseclass_amount(int col)
}
} else {
curwin->w_cursor.col = col;
getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL);
getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL, 0);
amount = (int)vcol;
}
if (amount < curbuf->b_ind_cpp_baseclass) {
@@ -2044,7 +2044,7 @@ int get_c_indent(void)
}
if (trypos != NULL) {
// find how indented the line beginning the comment is
getvcol(curwin, trypos, &col, NULL, NULL);
getvcol(curwin, trypos, &col, NULL, NULL, 0);
amount = col;
goto theend;
}
@@ -2064,7 +2064,7 @@ int get_c_indent(void)
int done = false;
// find how indented the line beginning the comment is
getvcol(curwin, comment_pos, &col, NULL, NULL);
getvcol(curwin, comment_pos, &col, NULL, NULL, 0);
amount = col;
*lead_start = NUL;
*lead_middle = NUL;
@@ -2177,7 +2177,7 @@ int get_c_indent(void)
comment_pos->col = (colnr_T)(skipwhite(look) - start);
}
}
getvcol(curwin, comment_pos, &col, NULL, NULL);
getvcol(curwin, comment_pos, &col, NULL, NULL, 0);
amount = col;
if (curbuf->b_ind_in_comment2 || *look == NUL) {
amount += curbuf->b_ind_in_comment;
@@ -2364,7 +2364,7 @@ int get_c_indent(void)
// Find how indented the paren is, or the character after it
// if we did the above "if".
if (our_paren_pos.col > 0) {
getvcol(curwin, &our_paren_pos, &col, NULL, NULL);
getvcol(curwin, &our_paren_pos, &col, NULL, NULL, 0);
if (cur_amount > (int)col) {
cur_amount = col;
}
@@ -2447,7 +2447,7 @@ int get_c_indent(void)
// a whole and then add the "imaginary indent" to that.
look = skipwhite(start);
if (*look == '{') {
getvcol(curwin, trypos, &col, NULL, NULL);
getvcol(curwin, trypos, &col, NULL, NULL, 0);
amount = col;
if (*start == '{') {
start_brace = BRACE_IN_COL0;

View File

@@ -305,8 +305,8 @@ static int do_popup(int which_button, int m_pos_flag, pos_T m_pos)
jump_flags = MOUSE_MAY_STOP_VIS;
} else if (VIsual_mode == Ctrl_V) {
colnr_T leftcol, rightcol;
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol, 0);
getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL, 0);
if (m_pos.col < leftcol || m_pos.col > rightcol) {
jump_flags = MOUSE_MAY_STOP_VIS;
}
@@ -769,7 +769,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
// that is in the quarter that the cursor is in.
if (VIsual_mode == Ctrl_V) {
colnr_T leftcol, rightcol;
getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol, 0);
if (curwin->w_curswant > (leftcol + rightcol) / 2) {
end_visual.col = leftcol;
} else {

View File

@@ -308,7 +308,7 @@ void update_topline(win_T *wp)
// Check that the cursor position is visible. Add columns for
// the marker displayed in the top-left if needed.
getvvcol(wp, &wp->w_cursor, &vcol, NULL, NULL);
getvvcol(wp, &wp->w_cursor, &vcol, NULL, NULL, 0);
int overlap = sms_marker_overlap(wp, -1);
if (wp->w_skipcol + overlap > vcol) {
check_topline = true;
@@ -730,7 +730,7 @@ void validate_virtcol(win_T *wp)
return;
}
getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL, 0);
redraw_for_cursorcolumn(wp);
wp->w_valid |= VALID_VIRTCOL;
}
@@ -823,7 +823,7 @@ void curs_columns(win_T *wp, int may_scroll)
// In a folded line the cursor is always in the first column
startcol = wp->w_virtcol = endcol = wp->w_leftcol;
} else {
getvvcol(wp, &wp->w_cursor, &startcol, &(wp->w_virtcol), &endcol);
getvvcol(wp, &wp->w_cursor, &startcol, &(wp->w_virtcol), &endcol, 0);
}
// remove '$' from change command when cursor moves onto it
@@ -1083,7 +1083,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp,
coloff = (local ? 0 : wp->w_wincol + wp->w_wincol_off) + 1 + off;
} else {
assert(lnum == pos->lnum);
getvcol(wp, pos, &scol, &ccol, &ecol);
getvcol(wp, pos, &scol, &ccol, &ecol, 0);
// similar to what is done in validate_cursor_col()
colnr_T col = scol;

View File

@@ -1910,7 +1910,7 @@ void clear_showcmd(void)
// Make 'sbr' empty for a moment to get the correct size.
p_sbr = empty_string_option;
curwin->w_p_sbr = empty_string_option;
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol, 0);
p_sbr = saved_sbr;
curwin->w_p_sbr = saved_w_sbr;
snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64,
@@ -2888,7 +2888,7 @@ static void nv_zet(cmdarg_T *cap)
if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) {
col = 0; // like the cursor is in col 0
} else {
getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL, 0);
}
if (col > siso) {
col -= siso;
@@ -2908,7 +2908,7 @@ static void nv_zet(cmdarg_T *cap)
if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) {
col = 0; // like the cursor is in col 0
} else {
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col, 0);
}
int n = curwin->w_view_width - win_col_off(curwin);
if (col + siso < n) {
@@ -4086,7 +4086,7 @@ static void nv_csearch(cmdarg_T *cap)
&& (t_cmd || cap->oap->op_type != OP_NOP)) {
colnr_T scol, ecol;
getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol, 0);
curwin->w_cursor.coladd = ecol - scol;
} else {
curwin->w_cursor.coladd = 0;
@@ -4639,7 +4639,7 @@ static void v_swap_corners(int cmdchar)
if (cmdchar == 'O' && VIsual_mode == Ctrl_V) {
pos_T old_cursor = curwin->w_cursor;
getvcols(curwin, &old_cursor, &VIsual, &left, &right);
getvcols(curwin, &old_cursor, &VIsual, &left, &right, 0);
curwin->w_cursor.lnum = VIsual.lnum;
coladvance(curwin, left);
VIsual = curwin->w_cursor;
@@ -5356,7 +5356,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap)
if (curwin->w_cursor.col > 0 && utf_ptr2cells(get_cursor_pos_ptr()) > 1) {
colnr_T vcol;
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol, 0);
if (vcol >= curwin->w_leftcol + curwin->w_view_width - col_off) {
curwin->w_cursor.col--;
}
@@ -6077,7 +6077,7 @@ bool unadjust_for_sel_inner(pos_T *pp)
mark_mb_adjustpos(curbuf, pp);
if (virtual_active(curwin)) {
colnr_T cs, ce;
getvcol(curwin, pp, &cs, NULL, &ce);
getvcol(curwin, pp, &cs, NULL, &ce, 0);
pp->coladd = ce - cs;
}
} else if (pp->lnum > 1) {

View File

@@ -1806,7 +1806,7 @@ void adjust_cursor_eol(void)
colnr_T scol, ecol;
// Coladd is set to the width of the last character.
getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol, 0);
curwin->w_cursor.coladd = ecol - scol + 1;
}
}
@@ -2076,6 +2076,9 @@ theend:
return ret;
}
/// TODO(zeertzjq): consider using CharSize.tail instead of temporarily
/// resetting 'linebreak'.
///
/// Reset 'linebreak' and take care of side effects.
/// @return the previous value, to be passed to restore_lbr().
bool reset_lbr(void)
@@ -2253,7 +2256,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
if (lnum == start.lnum) {
startcol = start.col;
if (virtual_op) {
getvcol(curwin, &start, &cs, NULL, &ce);
getvcol(curwin, &start, &cs, NULL, &ce, 0);
if (ce != cs && start.coladd > 0) {
// Part of a tab selected -- but don't double-count it.
bdp->start_char_vcols = ce - cs + 1;
@@ -2266,7 +2269,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
if (lnum == end.lnum) {
endcol = end.col;
if (virtual_op) {
getvcol(curwin, &end, &cs, NULL, &ce);
getvcol(curwin, &end, &cs, NULL, &ce, 0);
if (p[endcol] == NUL || (cs + end.coladd < ce
// Don't add space for double-wide
// char; endcol will be on last byte
@@ -2871,7 +2874,7 @@ void cursor_pos_info(dict_T *dict)
oparg.is_VIsual = true;
oparg.motion_type = kMTBlockWise;
oparg.op_type = OP_NOP;
getvcols(curwin, &min_pos, &max_pos, &oparg.start_vcol, &oparg.end_vcol);
getvcols(curwin, &min_pos, &max_pos, &oparg.start_vcol, &oparg.end_vcol, 0);
p_sbr = saved_sbr;
curwin->w_p_sbr = saved_w_sbr;
if (curwin->w_curswant == MAXCOL) {
@@ -2961,7 +2964,7 @@ void cursor_pos_info(dict_T *dict)
if (dict == NULL) {
if (l_VIsual_active) {
if (l_VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL) {
getvcols(curwin, &min_pos, &max_pos, &min_pos.col, &max_pos.col);
getvcols(curwin, &min_pos, &max_pos, &min_pos.col, &max_pos.col, 0);
int64_t cols;
STRICT_SUB(oparg.end_vcol + 1, oparg.start_vcol, &cols, int64_t);
vim_snprintf(buf1, sizeof(buf1), _("%" PRId64 " Cols; "),
@@ -3213,9 +3216,9 @@ static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial)
// prevent from moving onto a trail byte
mark_mb_adjustpos(curwin->w_buffer, &oap->end);
getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol);
getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol, 0);
if (!redo_VIsual_busy) {
getvvcol(curwin, &(oap->end), &start, NULL, &end);
getvvcol(curwin, &(oap->end), &start, NULL, &end, 0);
oap->start_vcol = MIN(oap->start_vcol, start);
if (end > oap->end_vcol) {
@@ -3235,7 +3238,7 @@ static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial)
oap->end_vcol = 0;
for (curwin->w_cursor.lnum = oap->start.lnum;
curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) {
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end, 0);
oap->end_vcol = MAX(oap->end_vcol, end);
}
} else if (redo_VIsual_busy) {
@@ -3480,13 +3483,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
resel_VIsual_vcol = MAXCOL;
} else {
if (VIsual_mode != Ctrl_V) {
getvvcol(curwin, &(oap->end),
NULL, NULL, &oap->end_vcol);
getvvcol(curwin, &(oap->end), NULL, NULL, &oap->end_vcol, 0);
}
if (VIsual_mode == Ctrl_V || oap->line_count <= 1) {
if (VIsual_mode != Ctrl_V) {
getvvcol(curwin, &(oap->start),
&oap->start_vcol, NULL, NULL);
getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, NULL, 0);
}
resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1;
} else {

View File

@@ -288,6 +288,7 @@ CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vco
size += added;
}
int size_before_lbr = size;
bool need_lbr = false;
// If 'linebreak' set check at a blank before a non-blank if the line
// needs a break here.
@@ -333,7 +334,9 @@ CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vco
}
}
return (CharSize){ .width = size, .head = head };
int tail = size - size_before_lbr;
return (CharSize){ .width = size, .head = head, .tail = tail };
}
/// Like charsize_regular(), except it doesn't handle inline virtual text,
@@ -512,6 +515,10 @@ static int virt_text_cursor_off(const CharsizeArg *csarg, bool on_NUL)
/// cursor: where the cursor is on this character (first char, except for TAB)
/// end: on the last position of this character (TAB, ctrl)
///
/// When 'linebreak' follows this character, "end" is set to the position before
/// 'linebreak' if "flags" contains GETVCOL_END_EXCL_LBR, otherwise it's set to
/// the end of 'linebreak'.
///
/// This is used very often, keep it fast!
///
/// @param wp
@@ -519,7 +526,8 @@ static int virt_text_cursor_off(const CharsizeArg *csarg, bool on_NUL)
/// @param start
/// @param cursor
/// @param end
void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end)
/// @param flags
void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end, int flags)
{
char *const line = ml_get_buf(wp->w_buffer, pos->lnum); // start of the line
colnr_T const end_col = pos->col;
@@ -571,29 +579,27 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
pos->col = (colnr_T)(ci.ptr - line);
}
int head = char_size.head;
int incr = char_size.width;
int head = char_size.head;
int tail = char_size.tail;
if (start != NULL) {
*start = vcol + head;
}
if (end != NULL) {
*end = vcol + incr - 1;
*end = vcol + incr - (flags & GETVCOL_END_EXCL_LBR ? tail : 0) - 1;
}
if (cursor != NULL) {
if (ci.chr.value == TAB
&& (State & MODE_NORMAL)
&& !wp->w_p_list
&& !virtual_active(wp)
&& !(VIsual_active && ((*p_sel == 'e') || ltoreq(*pos, VIsual)))) {
// cursor at end
*cursor = vcol + incr - 1;
// TODO(zeertzjq): subtracting "tail" may lead to better cursor position
*cursor = vcol + incr - 1; // cursor at end
} else {
vcol += virt_text_cursor_off(&csarg, on_NUL);
// cursor at start
*cursor = vcol + head;
*cursor = vcol + head; // cursor at start
}
}
}
@@ -610,9 +616,9 @@ colnr_T getvcol_nolist(pos_T *posp)
curwin->w_p_list = false;
if (posp->coladd) {
getvvcol(curwin, posp, NULL, &vcol, NULL);
getvvcol(curwin, posp, NULL, &vcol, NULL, 0);
} else {
getvcol(curwin, posp, NULL, &vcol, NULL);
getvcol(curwin, posp, NULL, &vcol, NULL, 0);
}
curwin->w_p_list = list_save;
return vcol;
@@ -625,13 +631,14 @@ colnr_T getvcol_nolist(pos_T *posp)
/// @param start
/// @param cursor
/// @param end
void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end)
/// @param flags
void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end, int flags)
{
colnr_T col;
if (virtual_active(wp)) {
// For virtual mode, only want one value
getvcol(wp, pos, &col, NULL, NULL);
getvcol(wp, pos, &col, NULL, NULL, flags);
colnr_T coladd = pos->coladd;
colnr_T endadd = 0;
@@ -644,8 +651,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *e
if ((c != TAB) && vim_isprintc(c)) {
endadd = (colnr_T)(ptr2cells(ptr + pos->col) - 1);
if (coladd > endadd) {
// past end of line
endadd = 0;
endadd = 0; // past end of line
} else {
coladd = 0;
}
@@ -656,16 +662,14 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *e
if (start != NULL) {
*start = col;
}
if (cursor != NULL) {
*cursor = col;
}
if (end != NULL) {
*end = col + endadd;
}
} else {
getvcol(wp, pos, start, cursor, end);
getvcol(wp, pos, start, cursor, end, flags);
}
}
@@ -677,7 +681,8 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *e
/// @param pos2
/// @param left
/// @param right
void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right)
/// @param flags
void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right, int flags)
{
colnr_T from1;
colnr_T from2;
@@ -685,11 +690,11 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right
colnr_T to2;
if (lt(*pos1, *pos2)) {
getvvcol(wp, pos1, &from1, NULL, &to1);
getvvcol(wp, pos2, &from2, NULL, &to2);
getvvcol(wp, pos1, &from1, NULL, &to1, flags);
getvvcol(wp, pos2, &from2, NULL, &to2, flags);
} else {
getvvcol(wp, pos2, &from1, NULL, &to1);
getvvcol(wp, pos1, &from2, NULL, &to2);
getvvcol(wp, pos2, &from1, NULL, &to1, flags);
getvvcol(wp, pos1, &from2, NULL, &to2, flags);
}
if (from2 < from1) {

View File

@@ -34,6 +34,7 @@ typedef struct {
typedef struct {
int width;
int head; ///< Size of 'breakindent' etc. before the character (included in width).
int tail; ///< Size of 'linebreak' after the character (included in width).
} CharSize;
#include "plines.h.generated.h"
@@ -89,3 +90,8 @@ static inline int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T
return linesize_regular(&csarg, 0, len);
}
}
/// Flags used by getvcol()
enum {
GETVCOL_END_EXCL_LBR = 1,
};

View File

@@ -1493,8 +1493,8 @@ static bool reg_match_visual(void)
return false;
}
} else if (mode == Ctrl_V) {
getvvcol(wp, &top, &start, NULL, &end);
getvvcol(wp, &bot, &start2, NULL, &end2);
getvvcol(wp, &top, &start, NULL, &end, 0);
getvvcol(wp, &bot, &start2, NULL, &end2, 0);
if (start2 < start) {
start = start2;
}
@@ -4681,7 +4681,7 @@ static uint8_t *regatom(int *flagp)
} else {
if (cur) {
colnr_T vcol = 0;
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol, 0);
n = (uint32_t)(++vcol);
}
ret = regnode(RE_VCOL);
@@ -10389,7 +10389,7 @@ static int nfa_regatom(void)
} else {
if (cur) {
colnr_T vcol = 0;
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol, 0);
n = ++vcol;
}
// \%{n}v \%{n}<v \%{n}>v

View File

@@ -1585,16 +1585,16 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
if (dir == FORWARD && c != NUL) {
if (cur_ve_flags == kOptVeFlagAll) {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2, 0);
} else {
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col, 0);
}
// move to start of next multi-byte character
curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
col++;
} else {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2, 0);
}
col += curwin->w_cursor.coladd;
@@ -1802,7 +1802,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
.col = col,
.coladd = 0,
};
getvcol(curwin, &pos, NULL, &vcol, NULL);
getvcol(curwin, &pos, NULL, &vcol, NULL, 0);
}
}

View File

@@ -2352,7 +2352,7 @@ void showmatch(int c)
}
if (!curwin->w_p_wrap) {
getvcol(curwin, lpos, NULL, &vcol, NULL);
getvcol(curwin, lpos, NULL, &vcol, NULL, 0);
}
bool col_visible = curwin->w_p_wrap

View File

@@ -485,7 +485,7 @@ void redraw_ruler(void)
colnr_T virtcol = wp->w_virtcol;
if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
wp->w_p_list = false;
getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL, 0);
wp->w_p_list = true;
}

View File

@@ -214,7 +214,7 @@ describe('linebreak', function()
-- oldtest: Test_visual_ends_before_showbreak()
it("Visual area is correct when it ends before multibyte 'showbreak'", function()
local screen = Screen.new(60, 8)
local screen = Screen.new(60, 6)
exec([[
let &wrap = v:true
let &linebreak = v:true
@@ -226,8 +226,84 @@ describe('linebreak', function()
xxxxx |
{1:↪ }{17:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy}^ {17: }|
{1:↪ }zzzz |
{1:~ }|*4
{1:~ }|*2
{5:-- VISUAL --} |
]])
end)
it("Visual block highlight is correct with 'linebreak'", function()
local screen = Screen.new(60, 6)
exec('set laststatus=0 showcmd ruler')
-- 'linebreak' after end char (initially fixed by patch 7.4.467)
exec([[
20vnew
setlocal linebreak
call setline(1, ['foo ' .. repeat('x', 10), 'foo ' .. repeat('x', 20)])
exe "normal! gg0\<C-V>3lj"
]])
screen:expect([[
{17:foo }xxxxxxxxxx │ |
{17:foo}^ │{1:~ }|
xxxxxxxxxxxxxxxxxxxx│{1:~ }|
{1:~ }│{1:~ }|*2
{5:-- VISUAL BLOCK --} 2x20 2,4 All |
]])
-- TAB as end char: 'linebreak' shouldn't break Visual block hl
exec([[
bwipe!
setlocal nolinebreak
call setline(1, ["foo\tbar", 'foo12345bar', "foo\tbar"])
exe "normal! gg03l\<C-V>2j2h"
]])
screen:expect([[
f{17:oo }bar |
f{17:oo12345}bar |
f^o{17:o }bar |
{1:~ }|*2
{5:-- VISUAL BLOCK --} 3x7 3,2 All |
]])
feed('<Esc>:setlocal linebreak<CR>gv')
screen:expect_unchanged(true)
-- Unprintable end char: 'linebreak' shouldn't break Visual block hl
exec([[
bwipe!
setlocal nolinebreak
call setline(1, ["foo\uffffbar", 'foo123456bar', "foo\uffffbar"])
exe "normal! gg03l\<C-V>2j2h"
]])
screen:expect([[
f{17:oo<ffff>}bar |
f{17:oo123456}bar |
f^o{17:o<ffff>}bar |
{1:~ }|*2
{5:-- VISUAL BLOCK --} 3x8 3,2 All |
]])
feed('<Esc>:setlocal linebreak<CR>gv')
screen:expect_unchanged(true)
-- Virtual text before end char: 'linebreak' shouldn't break Visual block hl
exec([=[
bwipe!
setlocal nolinebreak
call setline(1, [repeat('x', 15), repeat('x', 10), repeat('x', 10)])
let s:ns = nvim_create_namespace('test')
call nvim_buf_set_extmark(0, s:ns, 1, 4, #{virt_text: [['foo: ']],
\ virt_text_pos: 'inline'})
call nvim_buf_set_extmark(0, s:ns, 2, 4, #{virt_text: [['bar: ']],
\ virt_text_pos: 'inline'})
exe "normal! gg02l\<C-V>2j2l"
]=])
screen:expect([[
xx{17:xxxxxxxx}xxxxx |
xx{17:xx}foo: {17:x}xxxxx |
xx{17:xx}bar: ^xxxxxx |
{1:~ }|*2
{5:-- VISUAL BLOCK --} 3x8 3,5-10 All |
]])
feed('<Esc>:setlocal linebreak<CR>gv')
screen:expect_unchanged(true)
end)
end)

View File

@@ -385,4 +385,71 @@ func Test_visual_ends_before_showbreak()
call StopVimInTerminal(buf)
endfunc
func Test_visual_block_hl_with_linebreak()
CheckScreendump
let lines =<< trim END
func Case1()
20vnew
setlocal linebreak
call setline(1, ['foo ' .. repeat('x', 10), 'foo ' .. repeat('x', 20)])
exe "normal! gg0\<C-V>3lj"
endfunc
func Case2()
setlocal nolinebreak
call setline(1, ["foo\tbar", 'foo12345bar', "foo\tbar"])
exe "normal! gg03l\<C-V>2j2h"
endfunc
func Case3()
setlocal nolinebreak
call setline(1, ["foo\uffffbar", 'foo123456bar', "foo\uffffbar"])
exe "normal! gg03l\<C-V>2j2h"
endfunc
func Case4()
setlocal nolinebreak
call setline(1, [repeat('x', 15), repeat('x', 10), repeat('x', 10)])
call prop_type_add('test', {})
call prop_add(2, 5, #{text: "foo: ",type: "test"})
call prop_add(3, 5, #{text: "bar: ",type: "test"})
exe "normal! gg02l\<C-V>2j2l"
endfunc
" FIXME: clipboard=autoselect sometimes changes Visual highlight
set clipboard=
END
call writefile(lines, 'XvisualBlockHlWithLinebreak', 'D')
let buf = RunVimInTerminal('-S XvisualBlockHlWithLinebreak', #{rows: 6})
" 'linebreak' after end char (initially fixed by patch 7.4.467)
call term_sendkeys(buf, ":call Case1()\r")
call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_1', {})
" TAB as end char: 'linebreak' shouldn't break Visual block hl
call term_sendkeys(buf, "\<Esc>:bwipe! | call Case2()\r")
call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_2', {})
call term_sendkeys(buf, "\<Esc>:setlocal linebreak\rgv")
call term_wait(buf, 50)
call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_2', {})
" Unprintable end char: 'linebreak' shouldn't break Visual block hl
call term_sendkeys(buf, "\<Esc>:bwipe! | call Case3()\r")
call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_3', {})
call term_sendkeys(buf, "\<Esc>:setlocal linebreak\rgv")
call term_wait(buf, 50)
call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_3', {})
" Virtual text before end char: 'linebreak' shouldn't break Visual block hl
call term_sendkeys(buf, "\<Esc>:bwipe! | call Case4()\r")
call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_4', {})
call term_sendkeys(buf, "\<Esc>:setlocal linebreak\rgv")
call term_wait(buf, 50)
call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_4', {})
call term_sendkeys(buf, "\<Esc>")
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab