diff --git a/src/nvim/change.c b/src/nvim/change.c index 8d905c368d..b5533f273f 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -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); diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 32032b5fb9..2d5383887e 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -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); diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 0e571bece0..8778c7f954 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -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; diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index c871d73ca7..e6d93ac456 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -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++; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 5744f86cf6..4a87cd0a50 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -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); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 07361d820c..984b31643c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -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++; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9da682d6f4..3757d5be95 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -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; diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 10c7755b3c..806526b734 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -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; } diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index e989d01073..a3da750411 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -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; diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 12e5f7fe26..d3f4b09d96 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -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 { diff --git a/src/nvim/move.c b/src/nvim/move.c index 6be8ba4d6d..dda59db82c 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -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; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 3f464fb6ce..a4b053313a 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -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) { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c32935f49e..4e436c09f6 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -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 { diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 389af69849..1fafdf2206 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -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) { diff --git a/src/nvim/plines.h b/src/nvim/plines.h index 26e1e6ac63..5ed5ef32e0 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -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, +}; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index ba3d9cb5d6..0d3732279a 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -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 diff --git a/src/nvim/register.c b/src/nvim/register.c index e6e8401f26..c45332556a 100644 --- a/src/nvim/register.c +++ b/src/nvim/register.c @@ -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); } } diff --git a/src/nvim/search.c b/src/nvim/search.c index 8a3a09b09e..cbebfccf6e 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -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 diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 82febeb6c5..34be77ba50 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -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; } diff --git a/test/functional/legacy/listlbr_utf8_spec.lua b/test/functional/legacy/listlbr_utf8_spec.lua index 3a0aa7b2b3..1cf984bbda 100644 --- a/test/functional/legacy/listlbr_utf8_spec.lua +++ b/test/functional/legacy/listlbr_utf8_spec.lua @@ -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\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\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(':setlocal linebreakgv') + 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\2j2h" + ]]) + screen:expect([[ + f{17:oo}bar | + f{17:oo123456}bar | + f^o{17:o}bar | + {1:~ }|*2 + {5:-- VISUAL BLOCK --} 3x8 3,2 All | + ]]) + feed(':setlocal linebreakgv') + 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\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(':setlocal linebreakgv') + screen:expect_unchanged(true) + end) end) diff --git a/test/old/testdir/test_listlbr_utf8.vim b/test/old/testdir/test_listlbr_utf8.vim index 693f2015fc..90339e4fc2 100644 --- a/test/old/testdir/test_listlbr_utf8.vim +++ b/test/old/testdir/test_listlbr_utf8.vim @@ -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\3lj" + endfunc + + func Case2() + setlocal nolinebreak + call setline(1, ["foo\tbar", 'foo12345bar', "foo\tbar"]) + exe "normal! gg03l\2j2h" + endfunc + + func Case3() + setlocal nolinebreak + call setline(1, ["foo\uffffbar", 'foo123456bar', "foo\uffffbar"]) + exe "normal! gg03l\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\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, "\:bwipe! | call Case2()\r") + call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_2', {}) + call term_sendkeys(buf, "\: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, "\:bwipe! | call Case3()\r") + call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_3', {}) + call term_sendkeys(buf, "\: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, "\:bwipe! | call Case4()\r") + call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_4', {}) + call term_sendkeys(buf, "\:setlocal linebreak\rgv") + call term_wait(buf, 50) + call VerifyScreenDump(buf, 'Test_visual_block_hl_with_linebreak_4', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab