# HG changeset patch # User Christian Brabandt # Date 1692305104 -7200 # Node ID 22a08166a8762b1951b1b8c5cbfe5e84a1912b25 # Parent d18b744d834b369e0176e6e6239a3a10427b2a33 patch 9.0.1725: cursor pos wrong after concealed text with 'virtualedit' Commit: https://github.com/vim/vim/commit/e500ae8e29ad921378085f5d70ee5c0c537be1ba Author: zeertzjq Date: Thu Aug 17 22:35:26 2023 +0200 patch 9.0.1725: cursor pos wrong after concealed text with 'virtualedit' Problem: Wrong cursor position when clicking after concealed text with 'virtualedit'. Solution: Store virtual columns in ScreenCols[] instead of text columns, and always use coladvance() when clicking. This also fixes incorrect curswant when clicking on a TAB, so now Test_normal_click_on_ctrl_char() asserts the same results as the ones before patch 9.0.0048. closes: #12808 Signed-off-by: Christian Brabandt Co-authored-by: zeertzjq diff --git a/src/drawline.c b/src/drawline.c --- a/src/drawline.c +++ b/src/drawline.c @@ -1829,7 +1829,6 @@ win_line( win_line_start(wp, &wlv, FALSE); - char_u *prev_ptr = ptr; // Repeat for the whole displayed line. for (;;) { @@ -2261,9 +2260,9 @@ win_line( } #endif +#ifdef FEAT_SEARCH_EXTRA if (wlv.n_extra == 0) { -#ifdef FEAT_SEARCH_EXTRA // Check for start/end of 'hlsearch' and other matches. // After end, check for start/end of next match. // When another match, have to check for start again. @@ -2278,11 +2277,9 @@ win_line( // and bad things happen. if (*ptr == NUL) has_match_conc = 0; + } #endif - prev_ptr = ptr; - } - #ifdef FEAT_DIFF if (wlv.diff_hlf != (hlf_T)0) { @@ -2356,7 +2353,6 @@ 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 @@ -2567,7 +2563,9 @@ win_line( #ifdef FEAT_LINEBREAK int c0; #endif - prev_ptr = ptr; +#ifdef FEAT_SPELL + char_u *prev_ptr = ptr; +#endif // Get a character from the line itself. c = *ptr; @@ -3809,7 +3807,7 @@ win_line( else ScreenAttrs[wlv.off] = wlv.char_attr; - ScreenCols[wlv.off] = (colnr_T)(prev_ptr - line); + ScreenCols[wlv.off] = wlv.vcol; if (has_mbyte && (*mb_char2cells)(mb_c) > 1) { @@ -3833,7 +3831,7 @@ win_line( if (wlv.tocol == wlv.vcol) ++wlv.tocol; - ScreenCols[wlv.off] = (colnr_T)(prev_ptr - line); + ScreenCols[wlv.off] = wlv.vcol; #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,7 +32,7 @@ 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[]. + * The virtual column 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[], ScreenAttrs[] and ScreenCols[]. diff --git a/src/mouse.c b/src/mouse.c --- a/src/mouse.c +++ b/src/mouse.c @@ -2060,7 +2060,7 @@ retnomove: // Only use ScreenCols[] after the window was redrawn. Mainly matters // for tests, a user would not click before redrawing. // Do not use when 'virtualedit' is active. - if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE && !virtual_active()) + if (curwin->w_redr_type <= UPD_VALID_NO_UPDATE) col_from_screen = ScreenCols[off]; #ifdef FEAT_FOLDING // Remember the character under the mouse, it might be a '-' or '+' in @@ -2098,41 +2098,47 @@ retnomove: redraw_cmdline = TRUE; // show visual mode later } - if (col_from_screen >= 0) + if (col_from_screen == MAXCOL) { - // Use the column from ScreenCols[], it is accurate also after - // concealed characters. - curwin->w_cursor.col = col_from_screen; - if (col_from_screen == MAXCOL) + // When clicking after end of line, still need to set correct curswant + int off_l = LineOffset[prev_row]; + if (ScreenCols[off_l] < 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; + // Binary search to find last char in line + int off_r = off_l + prev_col; + int off_click = off_r; + while (off_l < off_r) + { + int off_m = (off_l + off_r + 1) / 2; + if (ScreenCols[off_m] < MAXCOL) + off_l = off_m; + else + off_r = off_m - 1; + } + col = ScreenCols[off_r] + (off_click - off_r); } else - { - curwin->w_set_curswant = TRUE; - if (inclusive != NULL) - *inclusive = FALSE; - } - check_cursor_col(); + // Shouldn't normally happen + col = MAXCOL; } - else + else if (col_from_screen >= 0) { - 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; + // Use the virtual column from ScreenCols[], it is accurate also after + // concealed characters. + 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 + { + 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 || curwin->w_cursor.col != old_cursor.col) 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 @@ -351,9 +351,55 @@ func Test_conceal_mouse_click() call test_setmouse(1, 16) call feedkeys("\", "tx") call assert_equal([0, 1, 20, 0, 20], getcurpos()) + " click on 'e' of "here" puts cursor there + call test_setmouse(1, 19) + call feedkeys("\", "tx") + call assert_equal([0, 1, 23, 0, 23], getcurpos()) + " click after end of line puts cursor on 'e' without 'virtualedit' + call test_setmouse(1, 20) + call feedkeys("\", "tx") + call assert_equal([0, 1, 23, 0, 24], getcurpos()) + call test_setmouse(1, 21) + call feedkeys("\", "tx") + call assert_equal([0, 1, 23, 0, 25], getcurpos()) + call test_setmouse(1, 22) + call feedkeys("\", "tx") + call assert_equal([0, 1, 23, 0, 26], getcurpos()) + call test_setmouse(1, 31) + call feedkeys("\", "tx") + call assert_equal([0, 1, 23, 0, 35], getcurpos()) + call test_setmouse(1, 32) + call feedkeys("\", "tx") + call assert_equal([0, 1, 23, 0, 36], getcurpos()) + + set virtualedit=all + " 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()) + " click on 'e' of "here" puts cursor there + call test_setmouse(1, 19) + call feedkeys("\", "tx") + call assert_equal([0, 1, 23, 0, 23], getcurpos()) + " click after end of line puts cursor there without 'virtualedit' + call test_setmouse(1, 20) + call feedkeys("\", "tx") + call assert_equal([0, 1, 24, 0, 24], getcurpos()) + call test_setmouse(1, 21) + call feedkeys("\", "tx") + call assert_equal([0, 1, 24, 1, 25], getcurpos()) + call test_setmouse(1, 22) + call feedkeys("\", "tx") + call assert_equal([0, 1, 24, 2, 26], getcurpos()) + call test_setmouse(1, 31) + call feedkeys("\", "tx") + call assert_equal([0, 1, 24, 11, 35], getcurpos()) + call test_setmouse(1, 32) + call feedkeys("\", "tx") + call assert_equal([0, 1, 24, 12, 36], getcurpos()) bwipe! - set mouse& + set mouse& virtualedit& endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -4049,13 +4049,13 @@ func Test_normal_click_on_ctrl_char() call assert_equal([0, 1, 1, 0, 1], getcurpos()) call test_setmouse(1, 2) call feedkeys("\", 'xt') - call assert_equal([0, 1, 2, 0, 8], getcurpos()) + call assert_equal([0, 1, 2, 0, 2], getcurpos()) call test_setmouse(1, 3) call feedkeys("\", 'xt') - call assert_equal([0, 1, 2, 0, 8], getcurpos()) + call assert_equal([0, 1, 2, 0, 3], getcurpos()) call test_setmouse(1, 7) call feedkeys("\", 'xt') - call assert_equal([0, 1, 2, 0, 8], getcurpos()) + call assert_equal([0, 1, 2, 0, 7], getcurpos()) call test_setmouse(1, 8) call feedkeys("\", 'xt') call assert_equal([0, 1, 2, 0, 8], getcurpos()) @@ -4067,13 +4067,13 @@ func Test_normal_click_on_ctrl_char() call assert_equal([0, 1, 4, 0, 10], getcurpos()) call test_setmouse(1, 11) call feedkeys("\", 'xt') - call assert_equal([0, 1, 4, 0, 10], getcurpos()) + call assert_equal([0, 1, 4, 0, 11], getcurpos()) call test_setmouse(1, 12) call feedkeys("\", 'xt') call assert_equal([0, 1, 5, 0, 12], getcurpos()) call test_setmouse(1, 13) call feedkeys("\", 'xt') - call assert_equal([0, 1, 5, 0, v:maxcol], getcurpos()) + call assert_equal([0, 1, 5, 0, 13], getcurpos()) bwipe! let &mouse = save_mouse diff --git a/src/testdir/test_virtualedit.vim b/src/testdir/test_virtualedit.vim --- a/src/testdir/test_virtualedit.vim +++ b/src/testdir/test_virtualedit.vim @@ -586,6 +586,12 @@ func Test_virtualedit_mouse() call test_setmouse(1, 9) call feedkeys("\", "xt") call assert_equal([0, 1, 6, 0, 9], getcurpos()) + call test_setmouse(1, 12) + call feedkeys("\", "xt") + call assert_equal([0, 1, 9, 0, 12], getcurpos()) + call test_setmouse(1, 13) + call feedkeys("\", "xt") + call assert_equal([0, 1, 10, 0, 13], getcurpos()) call test_setmouse(1, 15) call feedkeys("\", "xt") call assert_equal([0, 1, 10, 2, 15], getcurpos()) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1725, +/**/ 1724, /**/ 1723,