/// /// Return length of line "lnum" for horizontal scrolling. /// static colnr_T scroll_line_len(linenr_T lnum) { colnr_T col = 0; char_u *line = ml_get(lnum); if (*line != NUL) { for (;;) { int numchar = chartabsize(line, col); mb_ptr_adv(line); if (*line == NUL) { // don't count the last character break; } col += numchar; } } return col; }
/// Adjusts the clicked column position when 'conceallevel' > 0 static int mouse_adjust_click(win_T *wp, int row, int col) { if (!(wp->w_p_cole > 0 && curbuf->b_p_smc > 0 && wp->w_leftcol < curbuf->b_p_smc && conceal_cursor_line(wp))) { return col; } // `col` is the position within the current line that is highlighted by the // cursor without consideration for concealed characters. The current line is // scanned *up to* `col`, nudging it left or right when concealed characters // are encountered. // // chartabsize() is used to keep track of the virtual column position relative // to the line's bytes. For example: if col == 9 and the line starts with a // tab that's 8 columns wide, we would want the cursor to be highlighting the // second byte, not the ninth. linenr_T lnum = wp->w_cursor.lnum; char_u *line = ml_get(lnum); char_u *ptr = line; char_u *ptr_end = line; char_u *ptr_row_offset = line; // Where we begin adjusting `ptr_end` // Find the offset where scanning should begin. int offset = wp->w_leftcol; if (row > 0) { offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) - wp->w_leftcol + wp->w_skipcol); } int vcol; if (offset) { // Skip everything up to an offset since nvim takes care of displaying the // correct portion of the line when horizontally scrolling. // When 'wrap' is enabled, only the row (of the wrapped line) needs to be // checked for concealed characters. vcol = 0; while (vcol < offset && *ptr != NUL) { vcol += chartabsize(ptr, vcol); ptr += utfc_ptr2len(ptr); } ptr_row_offset = ptr; } // Align `ptr_end` with `col` vcol = offset; ptr_end = ptr_row_offset; while (vcol < col && *ptr_end != NUL) { vcol += chartabsize(ptr_end, vcol); ptr_end += utfc_ptr2len(ptr_end); } int matchid; int prev_matchid; int nudge = 0; int cwidth = 0; vcol = offset; #define incr() nudge++; ptr_end += utfc_ptr2len(ptr_end) #define decr() nudge--; ptr_end -= utfc_ptr2len(ptr_end) while (ptr < ptr_end && *ptr != NUL) { cwidth = chartabsize(ptr, vcol); vcol += cwidth; if (cwidth > 1 && *ptr == '\t' && nudge > 0) { // A tab will "absorb" any previous adjustments. cwidth = MIN(cwidth, nudge); while (cwidth > 0) { decr(); cwidth--; } } matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line)); if (matchid != 0) { if (wp->w_p_cole == 3) { incr(); } else { if (!(row > 0 && ptr == ptr_row_offset) && (wp->w_p_cole == 1 || (wp->w_p_cole == 2 && (lcs_conceal != NUL || syn_get_sub_char() != NUL)))) { // At least one placeholder character will be displayed. decr(); } prev_matchid = matchid; while (prev_matchid == matchid && *ptr != NUL) { incr(); ptr += utfc_ptr2len(ptr); matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line)); } continue; } } ptr += utfc_ptr2len(ptr); } return col + nudge; }