static int coladvance2( pos_T *pos, bool addspaces, /* change the text to achieve our goal? */ bool finetune, /* change char offset for the exact column */ colnr_T wcol /* column to move to */ ) { int idx; char_u *ptr; char_u *line; colnr_T col = 0; int csize = 0; int one_more; int head = 0; one_more = (State & INSERT) || restart_edit != NUL || (VIsual_active && *p_sel != 'o') || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL); line = ml_get_buf(curbuf, pos->lnum, false); if (wcol >= MAXCOL) { idx = (int)STRLEN(line) - 1 + one_more; col = wcol; if ((addspaces || finetune) && !VIsual_active) { curwin->w_curswant = linetabsize(line) + one_more; if (curwin->w_curswant > 0) --curwin->w_curswant; } } else { int width = curwin->w_width - win_col_off(curwin); if (finetune && curwin->w_p_wrap && curwin->w_width != 0 && wcol >= (colnr_T)width) { csize = linetabsize(line); if (csize > 0) csize--; if (wcol / width > (colnr_T)csize / width && ((State & INSERT) == 0 || (int)wcol > csize + 1)) { /* In case of line wrapping don't move the cursor beyond the * right screen edge. In Insert mode allow going just beyond * the last character (like what happens when typing and * reaching the right window edge). */ wcol = (csize / width + 1) * width - 1; } } ptr = line; while (col <= wcol && *ptr != NUL) { /* Count a tab for what it's worth (if list mode not on) */ csize = win_lbr_chartabsize(curwin, line, ptr, col, &head); MB_PTR_ADV(ptr); col += csize; } idx = (int)(ptr - line); /* * Handle all the special cases. The virtual_active() check * is needed to ensure that a virtual position off the end of * a line has the correct indexing. The one_more comparison * replaces an explicit add of one_more later on. */ if (col > wcol || (!virtual_active() && one_more == 0)) { idx -= 1; /* Don't count the chars from 'showbreak'. */ csize -= head; col -= csize; } if (virtual_active() && addspaces && ((col != wcol && col != wcol + 1) || csize > 1)) { /* 'virtualedit' is set: The difference between wcol and col is * filled with spaces. */ if (line[idx] == NUL) { /* Append spaces */ int correct = wcol - col; char_u *newline = xmallocz((size_t)(idx + correct)); memcpy(newline, line, (size_t)idx); memset(newline + idx, ' ', (size_t)correct); ml_replace(pos->lnum, newline, false); changed_bytes(pos->lnum, (colnr_T)idx); idx += correct; col = wcol; } else { /* Break a tab */ int linelen = (int)STRLEN(line); int correct = wcol - col - csize + 1; /* negative!! */ char_u *newline; if (-correct > csize) return FAIL; newline = xmallocz((size_t)(linelen - 1 + csize)); // Copy first idx chars memcpy(newline, line, (size_t)idx); // Replace idx'th char with csize spaces memset(newline + idx, ' ', (size_t)csize); // Copy the rest of the line memcpy(newline + idx + csize, line + idx + 1, (size_t)(linelen - idx - 1)); ml_replace(pos->lnum, newline, false); changed_bytes(pos->lnum, idx); idx += (csize - 1 + correct); col += correct; } } } if (idx < 0) pos->col = 0; else pos->col = idx; pos->coladd = 0; if (finetune) { if (wcol == MAXCOL) { /* The width of the last character is used to set coladd. */ if (!one_more) { colnr_T scol, ecol; getvcol(curwin, pos, &scol, NULL, &ecol); pos->coladd = ecol - scol; } } else { int b = (int)wcol - (int)col; /* The difference between wcol and col is used to set coladd. */ if (b > 0 && b < (MAXCOL - 2 * curwin->w_width)) pos->coladd = b; col += b; } } // Prevent from moving onto a trail byte. if (has_mbyte) { mark_mb_adjustpos(curbuf, pos); } if (col < wcol) return FAIL; return OK; }
// Compute the position in the buffer line from the posn on the screen in // window "win". // Returns true if the position is below the last line. bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) { int col = *colp; int row = *rowp; linenr_T lnum; bool retval = false; int off; int count; if (win->w_p_rl) col = win->w_width - 1 - col; lnum = win->w_topline; while (row > 0) { // Don't include filler lines in "count" if (win->w_p_diff && !hasFoldingWin(win, lnum, NULL, NULL, true, NULL)) { if (lnum == win->w_topline) { row -= win->w_topfill; } else { row -= diff_check_fill(win, lnum); } count = plines_win_nofill(win, lnum, true); } else { count = plines_win(win, lnum, true); } if (count > row) { break; // Position is in this buffer line. } (void)hasFoldingWin(win, lnum, NULL, &lnum, true, NULL); if (lnum == win->w_buffer->b_ml.ml_line_count) { retval = true; break; // past end of file } row -= count; ++lnum; } if (!retval) { // Compute the column without wrapping. off = win_col_off(win) - win_col_off2(win); if (col < off) col = off; col += row * (win->w_width - off); // add skip column (for long wrapping line) col += win->w_skipcol; } if (!win->w_p_wrap) { col += win->w_leftcol; } // skip line number and fold column in front of the line col -= win_col_off(win); if (col < 0) { col = 0; } *colp = col; *rowp = row; *lnump = lnum; return retval; }
// Adjust the clicked column position if there are concealed characters // before the current column. But only when it's absolutely necessary. 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; } int end = (colnr_T)STRLEN(ml_get(wp->w_cursor.lnum)); int vend = getviscol2(end, 0); if (col >= vend) { return col; } int i = wp->w_leftcol; if (row > 0) { i += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) - wp->w_leftcol) + wp->w_skipcol; } int start_col = i; int matchid; int last_matchid; int bcol = end - (vend - col); while (i < bcol) { matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i); if (matchid != 0) { if (wp->w_p_cole == 3) { bcol++; } else { if (row > 0 && i == start_col) { // Check if the current concealed character is actually part of // the previous wrapped row's conceal group. last_matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i - 1); if (last_matchid == matchid) { bcol++; } } else if (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. bcol--; } last_matchid = matchid; // Adjust for concealed text that spans more than one character. do { i++; bcol++; matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i); } while (last_matchid == matchid); continue; } } i++; } return getviscol2(bcol, 0); }
/// 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; }