diff options
Diffstat (limited to 'src/nvim/mouse.c')
-rw-r--r-- | src/nvim/mouse.c | 148 |
1 files changed, 139 insertions, 9 deletions
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 2f499e477c..887cbde921 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -1,11 +1,16 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #include <stdbool.h> #include "nvim/mouse.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/window.h" +#include "nvim/state.h" #include "nvim/strings.h" #include "nvim/screen.h" +#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/os_unix.h" #include "nvim/fold.h" @@ -103,12 +108,14 @@ retnomove: goto retnomove; // ugly goto... // Remember the character under the mouse, it might be a '-' or '+' in the - // fold column. + // fold column. NB: only works for ASCII chars! if (row >= 0 && row < Rows && col >= 0 && col <= Columns - && ScreenLines != NULL) - mouse_char = ScreenLines[LineOffset[row] + (unsigned)col]; - else + && default_grid.chars != NULL) { + mouse_char = default_grid.chars[default_grid.line_offset[row] + + (unsigned)col][0]; + } else { mouse_char = ' '; + } old_curwin = curwin; old_cursor = curwin->w_cursor; @@ -119,6 +126,9 @@ retnomove: // find the window where the row is in wp = mouse_find_win(&row, &col); + if (wp == NULL) { + return IN_UNKNOWN; + } dragwin = NULL; // winpos and height may change in win_enter()! if (row >= wp->w_height) { // In (or below) status line @@ -303,9 +313,12 @@ retnomove: 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) { - check_visual_highlight(); VIsual = old_cursor; VIsual_active = true; VIsual_reselect = true; @@ -417,6 +430,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) // Find the window at screen position "*rowp" and "*colp". The positions are // updated to become relative to the top-left of the window. +// Returns NULL when something is wrong. win_T *mouse_find_win(int *rowp, int *colp) { frame_T *fp; @@ -440,7 +454,14 @@ win_T *mouse_find_win(int *rowp, int *colp) } } } - return fp->fr_win; + // When using a timer that closes a window the window might not actually + // exist. + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp == fp->fr_win) { + return wp; + } + } + return NULL; } /* @@ -450,6 +471,7 @@ void setmouse(void) { int checkfor; + ui_cursor_shape(); /* be quick when mouse is off */ if (*p_mouse == NUL) @@ -469,9 +491,9 @@ void setmouse(void) checkfor = MOUSE_NORMAL; /* assume normal mode */ if (mouse_has(checkfor)) { - ui_mouse_on(); + ui_call_mouse_on(); } else { - ui_mouse_off(); + ui_call_mouse_off(); } } @@ -515,7 +537,7 @@ static colnr_T scroll_line_len(linenr_T lnum) if (*line != NUL) { for (;;) { int numchar = chartabsize(line, col); - mb_ptr_adv(line); + MB_PTR_ADV(line); if (*line == NUL) { // don't count the last character break; } @@ -597,3 +619,111 @@ bool mouse_scroll_horiz(int dir) return leftcol_changed(); } + +/// 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; + 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; +} |