# HG changeset patch # User Bram Moolenaar # Date 1657339206 -7200 # Node ID 34e93e04652673803c6102454acaa3f057bf25b8 # Parent afeb72fa8cd9c0e9b4dd564167daf305290b2d1c patch 9.0.0048: cursor in wrong column with mouse click after concealed text Commit: https://github.com/vim/vim/commit/b90818867c089d4987f1a48ee3666674826d6f4b Author: Bram Moolenaar Date: Sat Jul 9 04:56:24 2022 +0100 patch 9.0.0048: cursor in wrong column with mouse click after concealed text Problem: Cursor in wrong column with mouse click after concealed text. Solution: Store the text column when drawing text. diff --git a/src/drawline.c b/src/drawline.c --- a/src/drawline.c +++ b/src/drawline.c @@ -779,7 +779,7 @@ win_line( trailcol = (colnr_T)STRLEN(ptr); while (trailcol > (colnr_T)0 && VIM_ISWHITE(ptr[trailcol - 1])) --trailcol; - trailcol += (colnr_T) (ptr - line); + trailcol += (colnr_T)(ptr - line); } // find end of leading whitespace if (wp->w_lcs_chars.lead || wp->w_lcs_chars.leadmultispace != NULL) @@ -792,7 +792,7 @@ win_line( leadcol = (colnr_T)0; else // keep track of the first column not filled with spaces - leadcol += (colnr_T) (ptr - line) + 1; + leadcol += (colnr_T)(ptr - line) + 1; } } @@ -1027,12 +1027,14 @@ win_line( // Repeat for the whole displayed line. for (;;) { + char_u *prev_ptr = ptr; #if defined(FEAT_CONCEAL) || defined(FEAT_SEARCH_EXTRA) - int has_match_conc = 0; // match wants to conceal + int has_match_conc = 0; // match wants to conceal #endif #ifdef FEAT_CONCEAL - int did_decrement_ptr = FALSE; + int did_decrement_ptr = FALSE; #endif + // Skip this quickly when working on the text. if (draw_state != WL_LINE) { @@ -1392,6 +1394,7 @@ win_line( &match_conc, did_line_attr, lcs_eol_one, &on_last_col); ptr = line + v; // "line" may have been changed + prev_ptr = ptr; // Do not allow a conceal over EOL otherwise EOL will be missed // and bad things happen. @@ -1553,6 +1556,7 @@ win_line( // have made it invalid. line = ml_get_buf(wp->w_buffer, lnum, FALSE); ptr = line + v; + prev_ptr = ptr; # ifdef FEAT_CONCEAL // no concealing past the end of the line, it interferes // with line highlighting @@ -1733,9 +1737,10 @@ win_line( else { #ifdef FEAT_LINEBREAK - int c0; + int c0; #endif VIM_CLEAR(p_extra_free); + prev_ptr = ptr; // Get a character from the line itself. c = *ptr; @@ -1942,17 +1947,12 @@ win_line( # endif can_spell)) { - char_u *prev_ptr, *p; + char_u *p; int len; hlf_T spell_hlf = HLF_COUNT; if (has_mbyte) - { - prev_ptr = ptr - mb_l; v -= mb_l - 1; - } - else - prev_ptr = ptr - 1; // Use nextline[] if possible, it has the start of the // next line concatenated. @@ -2771,6 +2771,7 @@ win_line( } #endif ScreenAttrs[off] = char_attr; + ScreenCols[off] = MAXCOL; #ifdef FEAT_RIGHTLEFT if (wp->w_p_rl) { @@ -2839,6 +2840,7 @@ win_line( ScreenLines[off] = ' '; if (enc_utf8) ScreenLinesUC[off] = 0; + ScreenCols[off] = MAXCOL; ++col; if (draw_color_col) draw_color_col = advance_color_col(VCOL_HLC, @@ -2992,6 +2994,8 @@ win_line( else ScreenAttrs[off] = char_attr; + ScreenCols[off] = (colnr_T)(prev_ptr - line); + if (has_mbyte && (*mb_char2cells)(mb_c) > 1) { // Need to fill two screen columns. @@ -3013,6 +3017,9 @@ win_line( // the character, otherwise highlighting won't stop. if (tocol == vcol) ++tocol; + + ScreenCols[off] = (colnr_T)(prev_ptr - line); + #ifdef FEAT_RIGHTLEFT if (wp->w_p_rl) { diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -32,15 +32,17 @@ EXTERN long Columns INIT(= 80); // nr of * The characters that are currently on the screen are kept in ScreenLines[]. * It is a single block of characters, the size of the screen plus one line. * The attributes for those characters are kept in ScreenAttrs[]. + * The byte offset in the line is kept in ScreenCols[]. * * "LineOffset[n]" is the offset from ScreenLines[] for the start of line 'n'. - * The same value is used for ScreenLinesUC[] and ScreenAttrs[]. + * The same value is used for ScreenLinesUC[], ScreenAttrs[] and ScreenCols[]. * * Note: before the screen is initialized and when out of memory these can be * NULL. */ EXTERN schar_T *ScreenLines INIT(= NULL); EXTERN sattr_T *ScreenAttrs INIT(= NULL); +EXTERN colnr_T *ScreenCols INIT(= NULL); EXTERN unsigned *LineOffset INIT(= NULL); EXTERN char_u *LineWraps INIT(= NULL); // line wraps to next line @@ -62,7 +64,7 @@ EXTERN int Screen_mco INIT(= 0); // val EXTERN schar_T *ScreenLines2 INIT(= NULL); /* - * Buffer for one screen line (characters and attributes). + * One screen line to be displayed. Points into ScreenLines. */ EXTERN schar_T *current_ScreenLine INIT(= NULL); diff --git a/src/mouse.c b/src/mouse.c --- a/src/mouse.c +++ b/src/mouse.c @@ -1543,8 +1543,9 @@ jump_to_mouse( int first; int row = mouse_row; int col = mouse_col; + colnr_T col_from_screen = -1; #ifdef FEAT_FOLDING - int mouse_char; + int mouse_char = ' '; #endif mouse_past_bottom = FALSE; @@ -1626,16 +1627,6 @@ retnomove: if (flags & MOUSE_SETPOS) goto retnomove; // ugly goto... -#ifdef FEAT_FOLDING - // Remember the character under the mouse, it might be a '-' or '+' in the - // fold column. - if (row >= 0 && row < Rows && col >= 0 && col <= Columns - && ScreenLines != NULL) - mouse_char = ScreenLines[LineOffset[row] + col]; - else - mouse_char = ' '; -#endif - old_curwin = curwin; old_cursor = curwin->w_cursor; @@ -1969,6 +1960,22 @@ retnomove: } } + if (prev_row >= 0 && prev_row < Rows && prev_col >= 0 && prev_col <= Columns + && ScreenLines != NULL) + { + int off = LineOffset[prev_row] + prev_col; + + // Only use ScreenCols[] after the window was redrawn. Mainly matters + // for tests, a user would not click before redrawing. + if (curwin->w_redr_type <= VALID_NO_UPDATE) + col_from_screen = ScreenCols[off]; +#ifdef FEAT_FOLDING + // Remember the character under the mouse, it might be a '-' or '+' in + // the fold column. + mouse_char = ScreenLines[off]; +#endif + } + #ifdef FEAT_FOLDING // Check for position outside of the fold column. if ( @@ -2001,16 +2008,40 @@ retnomove: redraw_cmdline = TRUE; // show visual mode later } - curwin->w_curswant = col; - curwin->w_set_curswant = FALSE; // May still have been TRUE - if (coladvance(col) == FAIL) // Mouse click beyond end of line + if (col_from_screen >= 0) { - if (inclusive != NULL) - *inclusive = TRUE; - mouse_past_eol = TRUE; + // Use the column from ScreenCols[], it is accurate also after + // concealed characters. + curwin->w_cursor.col = col_from_screen; + if (col_from_screen == MAXCOL) + { + curwin->w_curswant = col_from_screen; + curwin->w_set_curswant = FALSE; // May still have been TRUE + mouse_past_eol = TRUE; + if (inclusive != NULL) + *inclusive = TRUE; + } + else + { + curwin->w_set_curswant = TRUE; + if (inclusive != NULL) + *inclusive = FALSE; + } + check_cursor_col(); } - else if (inclusive != NULL) - *inclusive = FALSE; + else + { + curwin->w_curswant = col; + curwin->w_set_curswant = FALSE; // May still have been TRUE + if (coladvance(col) == FAIL) // Mouse click beyond end of line + { + if (inclusive != NULL) + *inclusive = TRUE; + mouse_past_eol = TRUE; + } + else if (inclusive != NULL) + *inclusive = FALSE; + } count = IN_BUFFER; if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -17,8 +17,12 @@ * ScreenLines[off] Contains a copy of the whole screen, as it is currently * displayed (excluding text written by external commands). * ScreenAttrs[off] Contains the associated attributes. - * LineOffset[row] Contains the offset into ScreenLines*[] and ScreenAttrs[] - * for each line. + * ScreenCols[off] Contains the byte offset in the line. -1 means not + * available (below last line), MAXCOL means after the end + * of the line. + * + * LineOffset[row] Contains the offset into ScreenLines*[], ScreenAttrs[] + * and ScreenCols[] for each line. * LineWraps[row] Flag for each line whether it wraps to the next line. * * For double-byte characters, two consecutive bytes in ScreenLines[] can form @@ -453,7 +457,6 @@ screen_line( int clear_next = FALSE; int char_cells; // 1: normal char // 2: occupies two display cells -# define CHAR_CELLS char_cells // Check for illegal row and col, just in case. if (row >= Rows) @@ -519,8 +522,8 @@ screen_line( char_cells = 1; redraw_this = redraw_next; - redraw_next = force || char_needs_redraw(off_from + CHAR_CELLS, - off_to + CHAR_CELLS, endcol - col - CHAR_CELLS); + redraw_next = force || char_needs_redraw(off_from + char_cells, + off_to + char_cells, endcol - col - char_cells); #ifdef FEAT_GUI // If the next character was bold, then redraw the current character to @@ -528,7 +531,7 @@ screen_line( // happens in the GUI. if (redraw_next && gui.in_use) { - hl = ScreenAttrs[off_to + CHAR_CELLS]; + hl = ScreenAttrs[off_to + char_cells]; if (hl > HL_ALL) hl = syn_attr2attr(hl); if (hl & HL_BOLD) @@ -666,11 +669,15 @@ screen_line( } #endif ScreenAttrs[off_to] = ScreenAttrs[off_from]; + ScreenCols[off_to] = ScreenCols[off_from]; // For simplicity set the attributes of second half of a // double-wide character equal to the first half. if (char_cells == 2) + { ScreenAttrs[off_to + 1] = ScreenAttrs[off_from]; + ScreenCols[off_to + 1] = ScreenCols[off_from + 1]; + } if (enc_dbcs != 0 && char_cells == 2) screen_char_2(off_to, row, col + coloff); @@ -695,9 +702,13 @@ screen_line( screen_stop_highlight(); } - off_to += CHAR_CELLS; - off_from += CHAR_CELLS; - col += CHAR_CELLS; + ScreenCols[off_to] = ScreenCols[off_from]; + if (char_cells == 2) + ScreenCols[off_to + 1] = ScreenCols[off_from]; + + off_to += char_cells; + off_from += char_cells; + col += char_cells; } if (clear_next) @@ -725,6 +736,7 @@ screen_line( && ScreenAttrs[off_to] == 0 && (!enc_utf8 || ScreenLinesUC[off_to] == 0)) { + ScreenCols[off_to] = MAXCOL; ++off_to; ++col; } @@ -774,8 +786,11 @@ screen_line( #endif screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ', 0); - off_to += clear_width - col; - col = clear_width; + while (col < clear_width) + { + ScreenCols[off_to++] = MAXCOL; + ++col; + } } } @@ -1679,6 +1694,7 @@ screen_puts_len( ScreenLines[off + mbyte_blen] = 0; ScreenLines[off] = c; ScreenAttrs[off] = attr; + ScreenCols[off] = -1; if (enc_utf8) { if (c < 0x80 && u8cc[0] == 0) @@ -1699,6 +1715,7 @@ screen_puts_len( { ScreenLines[off + 1] = 0; ScreenAttrs[off + 1] = attr; + ScreenCols[off + 1] = -1; } screen_char(off, row, col); } @@ -1706,6 +1723,7 @@ screen_puts_len( { ScreenLines[off + 1] = ptr[1]; ScreenAttrs[off + 1] = attr; + ScreenCols[off + 1] = -1; screen_char_2(off, row, col); } else if (enc_dbcs == DBCS_JPNU && c == 0x8e) @@ -2174,6 +2192,7 @@ screen_char(unsigned off, int row, int c ) { ScreenAttrs[off] = (sattr_T)-1; + ScreenCols[off] = -1; return; } @@ -2250,6 +2269,7 @@ screen_char_2(unsigned off, int row, int if (row == screen_Rows - 1 && col >= screen_Columns - 2) { ScreenAttrs[off] = (sattr_T)-1; + ScreenCols[off] = -1; return; } @@ -2335,6 +2355,7 @@ space_to_screenline(int off, int attr) { ScreenLines[off] = ' '; ScreenAttrs[off] = attr; + ScreenCols[off] = -1; if (enc_utf8) ScreenLinesUC[off] = 0; } @@ -2505,6 +2526,7 @@ screen_fill( if (!did_delete || c != ' ') screen_char(off, row, col); } + ScreenCols[off] = -1; ++off; if (col == start_col) { @@ -2598,6 +2620,7 @@ screenalloc(int doclear) schar_T *new_ScreenLines2 = NULL; int i; sattr_T *new_ScreenAttrs; + colnr_T *new_ScreenCols; unsigned *new_LineOffset; char_u *new_LineWraps; short *new_TabPageIdxs; @@ -2688,6 +2711,7 @@ retry: if (enc_dbcs == DBCS_JPNU) new_ScreenLines2 = LALLOC_MULT(schar_T, (Rows + 1) * Columns); new_ScreenAttrs = LALLOC_MULT(sattr_T, (Rows + 1) * Columns); + new_ScreenCols = LALLOC_MULT(colnr_T, (Rows + 1) * Columns); new_LineOffset = LALLOC_MULT(unsigned, Rows); new_LineWraps = LALLOC_MULT(char_u, Rows); new_TabPageIdxs = LALLOC_MULT(short, Columns); @@ -2735,6 +2759,7 @@ give_up: || (enc_utf8 && (new_ScreenLinesUC == NULL || i != p_mco)) || (enc_dbcs == DBCS_JPNU && new_ScreenLines2 == NULL) || new_ScreenAttrs == NULL + || new_ScreenCols == NULL || new_LineOffset == NULL || new_LineWraps == NULL || new_TabPageIdxs == NULL @@ -2760,6 +2785,7 @@ give_up: VIM_CLEAR(new_ScreenLinesC[i]); VIM_CLEAR(new_ScreenLines2); VIM_CLEAR(new_ScreenAttrs); + VIM_CLEAR(new_ScreenCols); VIM_CLEAR(new_LineOffset); VIM_CLEAR(new_LineWraps); VIM_CLEAR(new_TabPageIdxs); @@ -2802,6 +2828,8 @@ give_up: 0, (size_t)Columns * sizeof(schar_T)); (void)vim_memset(new_ScreenAttrs + new_row * Columns, 0, (size_t)Columns * sizeof(sattr_T)); + (void)vim_memset(new_ScreenCols + new_row * Columns, + 0, (size_t)Columns * sizeof(colnr_T)); old_row = new_row + (screen_Rows - Rows); if (old_row >= 0 && ScreenLines != NULL) { @@ -2835,6 +2863,9 @@ give_up: mch_memmove(new_ScreenAttrs + new_LineOffset[new_row], ScreenAttrs + LineOffset[old_row], (size_t)len * sizeof(sattr_T)); + mch_memmove(new_ScreenCols + new_LineOffset[new_row], + ScreenAttrs + LineOffset[old_row], + (size_t)len * sizeof(colnr_T)); } } } @@ -2857,6 +2888,7 @@ give_up: Screen_mco = p_mco; ScreenLines2 = new_ScreenLines2; ScreenAttrs = new_ScreenAttrs; + ScreenCols = new_ScreenCols; LineOffset = new_LineOffset; LineWraps = new_LineWraps; TabPageIdxs = new_TabPageIdxs; @@ -2929,6 +2961,7 @@ free_screenlines(void) VIM_CLEAR(ScreenLines2); VIM_CLEAR(ScreenLines); VIM_CLEAR(ScreenAttrs); + VIM_CLEAR(ScreenCols); VIM_CLEAR(LineOffset); VIM_CLEAR(LineWraps); VIM_CLEAR(TabPageIdxs); @@ -3018,6 +3051,7 @@ lineclear(unsigned off, int width, int a (void)vim_memset(ScreenLinesUC + off, 0, (size_t)width * sizeof(u8char_T)); (void)vim_memset(ScreenAttrs + off, attr, (size_t)width * sizeof(sattr_T)); + (void)vim_memset(ScreenCols + off, -1, (size_t)width * sizeof(colnr_T)); } /* @@ -3028,6 +3062,7 @@ lineclear(unsigned off, int width, int a lineinvalid(unsigned off, int width) { (void)vim_memset(ScreenAttrs + off, -1, (size_t)width * sizeof(sattr_T)); + (void)vim_memset(ScreenCols + off, -1, (size_t)width * sizeof(colnr_T)); } /* @@ -3066,6 +3101,8 @@ linecopy(int to, int from, win_T *wp) wp->w_width * sizeof(schar_T)); mch_memmove(ScreenAttrs + off_to, ScreenAttrs + off_from, wp->w_width * sizeof(sattr_T)); + mch_memmove(ScreenCols + off_to, ScreenCols + off_from, + wp->w_width * sizeof(colnr_T)); } /* diff --git a/src/testdir/test_conceal.vim b/src/testdir/test_conceal.vim --- a/src/testdir/test_conceal.vim +++ b/src/testdir/test_conceal.vim @@ -4,6 +4,7 @@ source check.vim CheckFeature conceal source screendump.vim +source view_util.vim func Test_conceal_two_windows() CheckScreendump @@ -282,4 +283,23 @@ func Test_conceal_eol() set nolist endfunc +func Test_conceal_mouse_click() + enew! + set mouse=a + setlocal conceallevel=2 concealcursor=nc + syn match Concealed "this" conceal + hi link Concealed Search + call setline(1, 'conceal this click here') + redraw + call assert_equal(['conceal click here '], ScreenLines(1, 20)) + + " click on 'h' of "here" puts cursor there + call test_setmouse(1, 16) + call feedkeys("\", "tx") + call assert_equal([0, 1, 20, 0, 20], getcurpos()) + + bwipe! + set mouse& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -736,6 +736,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 48, +/**/ 47, /**/ 46,