diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/drawline.c | 12 | ||||
-rw-r--r-- | src/nvim/grid.c | 24 | ||||
-rw-r--r-- | src/nvim/grid.h | 1 | ||||
-rw-r--r-- | src/nvim/grid_defs.h | 15 | ||||
-rw-r--r-- | src/nvim/mouse.c | 188 |
5 files changed, 102 insertions, 138 deletions
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index d0142c6efa..4c90a562ce 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -2022,6 +2022,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl c = NUL; } else { int c0; + char *prev_ptr = ptr; // Get a character from the line itself. c0 = c = (uint8_t)(*ptr); @@ -2209,7 +2210,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl v = (ptr - line); if (spv->spv_has_spell && v >= word_end && v > cur_checked_col) { spell_attr = 0; - char *prev_ptr = ptr - mb_l; // do not calculate cap_col at the end of the line or when // only white space is following if (c != 0 && (*skipwhite(prev_ptr) != NUL) && can_spell) { @@ -2746,6 +2746,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl eol_attr = hl_combine_attr(wlv.cul_attr, eol_attr); } linebuf_attr[wlv.off] = eol_attr; + linebuf_vcol[wlv.off] = MAXCOL; if (wp->w_p_rl) { wlv.col--; wlv.off--; @@ -2832,6 +2833,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl while (wp->w_p_rl ? wlv.col >= 0 : wlv.col < grid->cols) { schar_from_ascii(linebuf_char[wlv.off], ' '); + linebuf_vcol[wlv.off] = MAXCOL; wlv.col += col_stride; if (draw_color_col) { draw_color_col = advance_color_col(VCOL_HLC, &color_cols); @@ -2866,6 +2868,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl while (wlv.col >= 0 && wlv.col < grid->cols) { schar_from_ascii(linebuf_char[wlv.off], ' '); linebuf_attr[wlv.off] = wlv.vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[wlv.vcol]; + linebuf_vcol[wlv.off] = wlv.vcol; wlv.off += n; wlv.vcol += n; wlv.col += n; @@ -2945,9 +2948,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // Skip characters that are left of the screen for 'nowrap'. vcol_prev = wlv.vcol; if (wlv.draw_state < WL_LINE || n_skip <= 0) { - // // Store the character. - // if (wp->w_p_rl && utf_char2cells(mb_c) > 1) { // A double-wide character is: put first half in left cell. wlv.off--; @@ -2965,6 +2966,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl linebuf_attr[wlv.off] = wlv.char_attr; } + linebuf_vcol[wlv.off] = wlv.vcol; + if (utf_char2cells(mb_c) > 1) { // Need to fill two screen columns. wlv.off++; @@ -2980,6 +2983,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl if (wlv.tocol == wlv.vcol) { wlv.tocol++; } + + linebuf_vcol[wlv.off] = wlv.vcol; + if (wp->w_p_rl) { // now it's time to backup one cell wlv.off--; diff --git a/src/nvim/grid.c b/src/nvim/grid.c index aef10db414..e25b1b1a73 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -78,6 +78,7 @@ void grid_clear_line(ScreenGrid *grid, size_t off, int width, bool valid) } int fill = valid ? 0 : -1; (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T)); + (void)memset(grid->vcols + off, -1, (size_t)width * sizeof(colnr_T)); } void grid_invalidate(ScreenGrid *grid) @@ -196,6 +197,7 @@ void grid_put_schar(ScreenGrid *grid, int row, int col, char *schar, int attr) // TODO(bfredl): Y U NO DOUBLEWIDTH? put_dirty_last = MAX(put_dirty_last, col + 1); } + grid->vcols[off] = -1; } /// like grid_puts(), but output "text[len]". When "len" is -1 output up to @@ -324,9 +326,11 @@ int grid_puts_len(ScreenGrid *grid, const char *text, int textlen, int row, int schar_copy(grid->chars[off], buf); grid->attrs[off] = attr; + grid->vcols[off] = -1; if (mbyte_cells == 2) { grid->chars[off + 1][0] = 0; grid->attrs[off + 1] = attr; + grid->vcols[off + 1] = -1; } put_dirty_first = MIN(put_dirty_first, col); put_dirty_last = MAX(put_dirty_last, col + mbyte_cells); @@ -437,6 +441,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int } dirty_last = col + 1; } + grid->vcols[off] = -1; if (col == start_col) { schar_from_char(sc, c2); } @@ -620,13 +625,20 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle } grid->attrs[off_to] = linebuf_attr[off_from]; + grid->vcols[off_to] = linebuf_vcol[off_from]; // For simplicity set the attributes of second half of a // double-wide character equal to the first half. if (char_cells == 2) { grid->attrs[off_to + 1] = linebuf_attr[off_from]; + grid->vcols[off_to + 1] = linebuf_vcol[off_from + 1]; } } + grid->vcols[off_to] = linebuf_vcol[off_from]; + if (char_cells == 2) { + grid->vcols[off_to + 1] = linebuf_vcol[off_from]; + } + off_to += (size_t)char_cells; off_from += (size_t)char_cells; col += char_cells; @@ -659,6 +671,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle } clear_end = col + 1; } + grid->vcols[off_to] = MAXCOL; col++; off_to++; } @@ -690,6 +703,8 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid) size_t ncells = (size_t)rows * (size_t)columns; ngrid.chars = xmalloc(ncells * sizeof(schar_T)); ngrid.attrs = xmalloc(ncells * sizeof(sattr_T)); + ngrid.vcols = xmalloc(ncells * sizeof(colnr_T)); + memset(ngrid.vcols, -1, ncells * sizeof(colnr_T)); ngrid.line_offset = xmalloc((size_t)rows * sizeof(*ngrid.line_offset)); ngrid.line_wraps = xmalloc((size_t)rows * sizeof(*ngrid.line_wraps)); @@ -715,6 +730,9 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid) memmove(ngrid.attrs + ngrid.line_offset[new_row], grid->attrs + grid->line_offset[new_row], (size_t)len * sizeof(sattr_T)); + memmove(ngrid.vcols + ngrid.line_offset[new_row], + grid->vcols + grid->line_offset[new_row], + (size_t)len * sizeof(colnr_T)); } } } @@ -726,8 +744,10 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid) if (linebuf_size < (size_t)columns) { xfree(linebuf_char); xfree(linebuf_attr); + xfree(linebuf_vcol); linebuf_char = xmalloc((size_t)columns * sizeof(schar_T)); linebuf_attr = xmalloc((size_t)columns * sizeof(sattr_T)); + linebuf_vcol = xmalloc((size_t)columns * sizeof(colnr_T)); linebuf_size = (size_t)columns; } } @@ -736,11 +756,13 @@ void grid_free(ScreenGrid *grid) { xfree(grid->chars); xfree(grid->attrs); + xfree(grid->vcols); xfree(grid->line_offset); xfree(grid->line_wraps); grid->chars = NULL; grid->attrs = NULL; + grid->vcols = NULL; grid->line_offset = NULL; grid->line_wraps = NULL; } @@ -751,6 +773,7 @@ void grid_free_all_mem(void) grid_free(&default_grid); xfree(linebuf_char); xfree(linebuf_attr); + xfree(linebuf_vcol); } /// (Re)allocates a window grid if size changed while in ext_multigrid mode. @@ -939,6 +962,7 @@ static void linecopy(ScreenGrid *grid, int to, int from, int col, int width) memmove(grid->chars + off_to, grid->chars + off_from, (size_t)width * sizeof(schar_T)); memmove(grid->attrs + off_to, grid->attrs + off_from, (size_t)width * sizeof(sattr_T)); + memmove(grid->vcols + off_to, grid->vcols + off_from, (size_t)width * sizeof(colnr_T)); } win_T *get_win_by_grid_handle(handle_T handle) diff --git a/src/nvim/grid.h b/src/nvim/grid.h index deb3d3785d..0db97345d1 100644 --- a/src/nvim/grid.h +++ b/src/nvim/grid.h @@ -27,6 +27,7 @@ EXTERN bool resizing_screen INIT(= 0); EXTERN schar_T *linebuf_char INIT(= NULL); EXTERN sattr_T *linebuf_attr INIT(= NULL); +EXTERN colnr_T *linebuf_vcol INIT(= NULL); // Low-level functions to manipulate individual character cells on the // screen grid. diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index aae61cc719..aeaadea73c 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -5,6 +5,7 @@ #include <stddef.h> #include <stdint.h> +#include "nvim/pos.h" #include "nvim/types.h" #define MAX_MCO 6 // fixed value for 'maxcombine' @@ -37,9 +38,14 @@ enum { /// screen is cleared, the cells should be filled with a single whitespace char. /// /// attrs[] contains the highlighting attribute for each cell. -/// line_offset[n] is the offset from chars[] and attrs[] for the -/// start of line 'n'. These offsets are in general not linear, as full screen -/// scrolling is implemented by rotating the offsets in the line_offset array. +/// +/// vcols[] countain the virtual columns in the line. -1 means not available +/// (below last line), MAXCOL means after the end of the line. +/// +/// line_offset[n] is the offset from chars[], attrs[] and vcols[] for the start +/// of line 'n'. These offsets are in general not linear, as full screen scrolling +/// is implemented by rotating the offsets in the line_offset array. +/// /// line_wraps[] is an array of boolean flags indicating if the screen line /// wraps to the next line. It can only be true if a window occupies the entire /// screen width. @@ -49,6 +55,7 @@ struct ScreenGrid { schar_T *chars; sattr_T *attrs; + colnr_T *vcols; size_t *line_offset; char *line_wraps; @@ -106,7 +113,7 @@ struct ScreenGrid { bool comp_disabled; }; -#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \ +#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \ false, 0, 0, NULL, false, true, 0, \ 0, 0, 0, 0, 0, false } 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; } |