changeset 29413:34e93e046526 v9.0.0048

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 <Bram@vim.org> 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.
author Bram Moolenaar <Bram@vim.org>
date Sat, 09 Jul 2022 06:00:06 +0200
parents afeb72fa8cd9
children c33c19e5a541
files src/drawline.c src/globals.h src/mouse.c src/screen.c src/testdir/test_conceal.vim src/version.c
diffstat 6 files changed, 142 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- 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)
 		{
--- 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);
 
--- 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
--- 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));
 }
 
 /*
--- 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("\<LeftMouse>", "tx")
+  call assert_equal([0, 1, 20, 0, 20], getcurpos())
+
+  bwipe!
+  set mouse&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- 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,