aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/mouse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/mouse.c')
-rw-r--r--src/nvim/mouse.c188
1 files changed, 57 insertions, 131 deletions
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index d8341d2b10..0f3405bf0d 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1329,15 +1329,15 @@ retnomove:
}
}
+ colnr_T col_from_screen = -1;
+ int mouse_fold_flags = 0;
+ mouse_check_grid(&col_from_screen, &mouse_fold_flags);
+
// compute the position in the buffer line from the posn on the screen
if (mouse_comp_pos(curwin, &row, &col, &curwin->w_cursor.lnum)) {
mouse_past_bottom = true;
}
- if (!(flags & MOUSE_RELEASED) && which_button == MOUSE_LEFT) {
- col = mouse_adjust_click(curwin, row, col);
- }
-
// Start Visual mode before coladvance(), for when 'sel' != "old"
if ((flags & MOUSE_MAY_VIS) && !VIsual_active) {
VIsual = old_cursor;
@@ -1352,6 +1352,10 @@ retnomove:
}
}
+ if (col_from_screen >= 0) {
+ col = col_from_screen;
+ }
+
curwin->w_curswant = col;
curwin->w_set_curswant = false; // May still have been true
if (coladvance(col) == FAIL) { // Mouse click beyond end of line
@@ -1369,14 +1373,14 @@ retnomove:
count |= CURSOR_MOVED; // Cursor has moved
}
- count |= mouse_check_fold();
+ count |= mouse_fold_flags;
return count;
}
-// 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.
+/// 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;
@@ -1573,9 +1577,7 @@ static void set_mouse_topline(win_T *wp)
orig_topfill = wp->w_topfill;
}
-///
/// Return length of line "lnum" for horizontal scrolling.
-///
static colnr_T scroll_line_len(linenr_T lnum)
{
colnr_T col = 0;
@@ -1663,116 +1665,9 @@ bool mouse_scroll_horiz(int dir)
return set_leftcol(leftcol);
}
-/// 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.
- //
- // win_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;
- // Make a copy of the line, because syntax matching may free it.
- char *line = xstrdup(ml_get(lnum));
- char *ptr = line;
- char *ptr_end;
- char *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_inner - 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 += win_chartabsize(curwin, 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 += win_chartabsize(curwin, ptr_end, vcol);
- ptr_end += utfc_ptr2len(ptr_end);
- }
-
- int prev_matchid;
- int nudge = 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) {
- int cwidth = win_chartabsize(curwin, 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--;
- }
- }
-
- int 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
- && (wp->w_p_lcs_chars.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);
- }
-
- xfree(line);
- return col + nudge;
-}
-
-// Check clicked cell is foldcolumn
-int mouse_check_fold(void)
+/// Check clicked cell on its grid
+static void mouse_check_grid(colnr_T *vcolp, int *flagsp)
+ FUNC_ATTR_NONNULL_ALL
{
int click_grid = mouse_grid;
int click_row = mouse_row;
@@ -1780,7 +1675,8 @@ int mouse_check_fold(void)
int mouse_char = ' ';
int max_row = Rows;
int max_col = Columns;
- int multigrid = ui_has(kUIMultigrid);
+ bool multigrid = ui_has(kUIMultigrid);
+ colnr_T col_from_screen = -1;
win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
if (wp && multigrid) {
@@ -1792,14 +1688,46 @@ int mouse_check_fold(void)
&& mouse_col >= 0 && mouse_col < max_col) {
ScreenGrid *gp = multigrid ? &wp->w_grid_alloc : &default_grid;
int fdc = win_fdccol_count(wp);
- int row = multigrid && mouse_grid == 0 ? click_row : mouse_row;
- int col = multigrid && mouse_grid == 0 ? click_col : mouse_col;
+ int use_row = multigrid && mouse_grid == 0 ? click_row : mouse_row;
+ int use_col = multigrid && mouse_grid == 0 ? click_col : mouse_col;
- // Remember the character under the mouse, might be one of foldclose or
- // foldopen fillchars in the fold column.
if (gp->chars != NULL) {
- mouse_char = utf_ptr2char((char *)gp->chars[gp->line_offset[row]
- + (unsigned)col]);
+ const size_t off = gp->line_offset[use_row] + (size_t)use_col;
+
+ // Only use vcols[] after the window was redrawn. Mainly matters
+ // for tests, a user would not click before redrawing.
+ if (wp->w_redr_type == 0) {
+ col_from_screen = gp->vcols[off];
+ }
+
+ if (col_from_screen == MAXCOL) {
+ // When clicking after end of line, still need to set correct curswant
+ size_t off_l = gp->line_offset[use_row];
+ if (gp->vcols[off_l] < MAXCOL) {
+ // Binary search to find last char in line
+ size_t off_r = off;
+ while (off_l < off_r) {
+ size_t off_m = (off_l + off_r + 1) / 2;
+ if (gp->vcols[off_m] < MAXCOL) {
+ off_l = off_m;
+ } else {
+ off_r = off_m - 1;
+ }
+ }
+ *vcolp = gp->vcols[off_r] + (int)(off - off_r);
+ } else {
+ // Shouldn't normally happen
+ *vcolp = MAXCOL;
+ }
+ } else if (col_from_screen >= 0) {
+ // Use the virtual column from vcols[], it is accurate also after
+ // concealed characters.
+ *vcolp = col_from_screen;
+ }
+
+ // Remember the character under the mouse, might be one of foldclose or
+ // foldopen fillchars in the fold column.
+ mouse_char = utf_ptr2char((char *)gp->chars[off]);
}
// Check for position outside of the fold column.
@@ -1810,10 +1738,8 @@ int mouse_check_fold(void)
}
if (wp && mouse_char == wp->w_p_fcs_chars.foldclosed) {
- return MOUSE_FOLD_OPEN;
+ *flagsp |= MOUSE_FOLD_OPEN;
} else if (mouse_char != ' ') {
- return MOUSE_FOLD_CLOSE;
+ *flagsp |= MOUSE_FOLD_CLOSE;
}
-
- return 0;
}