]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.0184: Cursor pos wrong when clicking with conceal and wrap v9.1.0184
authorzeertzjq <zeertzjq@outlook.com>
Sat, 16 Mar 2024 14:03:33 +0000 (15:03 +0100)
committerChristian Brabandt <cb@256bit.org>
Sat, 16 Mar 2024 14:03:33 +0000 (15:03 +0100)
Problem:  Cursor position wrong when clicking with conceal and wrap.
Solution: Use the virtual column of the last char for ScreenCols[] in
          boguscols.  Remove use of MAXCOL in ScreenCols[].  Rename
          third argument of wlv_screen_line() to "clear_end" as that's
          clearer what it does (zeertzjq).

related: 14192
closes: #14200

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/drawline.c
src/drawscreen.c
src/mouse.c
src/proto/screen.pro
src/screen.c
src/terminal.c
src/testdir/test_conceal.vim
src/version.c
src/vim.h

index 9368095485bc4bc65eeaf4d990de42b0b1bc96c0..e73d5b1b63fd6fe3fff01b64e8a1c99652530d30 100644 (file)
@@ -832,9 +832,10 @@ text_prop_position(
  * Call screen_line() using values from "wlv".
  * Also takes care of putting "<<<" on the first line for 'smoothscroll'
  * when 'showbreak' is not set.
+ * When "clear_end" is TRUE clear until the end of the screen line.
  */
     static void
-wlv_screen_line(win_T *wp, winlinevars_T *wlv, int negative_width)
+wlv_screen_line(win_T *wp, winlinevars_T *wlv, int clear_end)
 {
     if (wlv->row == 0 && wp->w_skipcol > 0
 #if defined(FEAT_LINEBREAK)
@@ -872,8 +873,8 @@ wlv_screen_line(win_T *wp, winlinevars_T *wlv, int negative_width)
     }
 
     screen_line(wp, wlv->screen_row, wp->w_wincol, wlv->col,
-                   negative_width ? -wp->w_width : wp->w_width,
-                   wlv->screen_line_flags);
+                   clear_end ? wp->w_width : -wp->w_width,
+                   wlv->vcol - 1, wlv->screen_line_flags);
 }
 
 /*
@@ -939,7 +940,7 @@ draw_screen_line(win_T *wp, winlinevars_T *wlv)
            ScreenLines[wlv->off] = ' ';
            if (enc_utf8)
                ScreenLinesUC[wlv->off] = 0;
-           ScreenCols[wlv->off] = MAXCOL;
+           ScreenCols[wlv->off] = wlv->vcol;
            ++wlv->col;
            if (wlv->draw_color_col)
                wlv->draw_color_col = advance_color_col(
@@ -969,7 +970,11 @@ draw_screen_line(win_T *wp, winlinevars_T *wlv)
     }
 #endif
 
-    wlv_screen_line(wp, wlv, FALSE);
+    // Set increasing virtual columns in ScreenCols[] to set correct curswant
+    // (or "coladd" for 'virtualedit') when clicking after end of line.
+    wlv->screen_line_flags |= SLF_INC_VCOL;
+    wlv_screen_line(wp, wlv, TRUE);
+    wlv->screen_line_flags &= ~SLF_INC_VCOL;
     ++wlv->row;
     ++wlv->screen_row;
 }
@@ -1928,7 +1933,7 @@ win_line(
            // stop here.
            if (number_only > 0 && wlv.draw_state == WL_NR && wlv.n_extra == 0)
            {
-               wlv_screen_line(wp, &wlv, TRUE);
+               wlv_screen_line(wp, &wlv, FALSE);
                // Need to update more screen lines if:
                // - LineNrAbove or LineNrBelow is used, or
                // - still drawing filler lines.
@@ -1987,7 +1992,7 @@ win_line(
                && lnum == wp->w_cursor.lnum
                && wlv.vcol >= (long)wp->w_virtcol)
        {
-           wlv_screen_line(wp, &wlv, TRUE);
+           wlv_screen_line(wp, &wlv, FALSE);
            // Pretend we have finished updating the window.  Except when
            // 'cursorcolumn' is set.
 #ifdef FEAT_SYN_HL
@@ -3764,7 +3769,7 @@ win_line(
                }
 #endif
                ScreenAttrs[wlv.off] = wlv.char_attr;
-               ScreenCols[wlv.off] = MAXCOL;
+               ScreenCols[wlv.off] = wlv.vcol;
 #ifdef FEAT_RIGHTLEFT
                if (wp->w_p_rl)
                {
@@ -4167,7 +4172,7 @@ win_line(
                    if (enc_utf8)
                        ScreenLinesUC[wlv.off] = 0;
                    ScreenAttrs[wlv.off] = attr;
-                   ScreenCols[wlv.off] = MAXCOL;  // TODO: this is wrong
+                   ScreenCols[wlv.off] = wlv.vcol - 1;
 # ifdef FEAT_RIGHTLEFT
                    if (wp->w_p_rl)
                    {
@@ -4184,12 +4189,12 @@ win_line(
                    }
                }
            }
-           wlv_screen_line(wp, &wlv, FALSE);
+           wlv_screen_line(wp, &wlv, TRUE);
            wlv.col += wlv.boguscols;
            wlv.boguscols = 0;
            wlv.vcol_off_co = 0;
 #else
-           wlv_screen_line(wp, &wlv, FALSE);
+           wlv_screen_line(wp, &wlv, TRUE);
 #endif
            ++wlv.row;
            ++wlv.screen_row;
index 3d6c230c194f5e818f6162f54a586b3f32a61081..f8818ff1aa86cf25d0966215cd11ee88122a1f68 100644 (file)
@@ -1028,7 +1028,8 @@ redraw_win_toolbar(win_T *wp)
     }
     wp->w_winbar_items[item_idx].wb_menu = NULL; // end marker
 
-    screen_line(wp, wp->w_winrow, wp->w_wincol, wp->w_width, wp->w_width, 0);
+    screen_line(wp, wp->w_winrow, wp->w_wincol, wp->w_width, wp->w_width, -1,
+                                                                           0);
 }
 #endif
 
@@ -1363,7 +1364,7 @@ fold_line(
 #endif
 
     screen_line(wp, row + W_WINROW(wp), wp->w_wincol,
-                                                 wp->w_width, wp->w_width, 0);
+                                             wp->w_width, wp->w_width, -1, 0);
 
     // Update w_cline_height and w_cline_folded if the cursor line was
     // updated (saves a call to plines() later).
@@ -3049,7 +3050,7 @@ redraw_asap(int type)
                    mch_memmove(ScreenLines2 + off,
                                screenline2 + r * cols,
                                (size_t)cols * sizeof(schar_T));
-               screen_line(curwin, cmdline_row + r, 0, cols, cols, 0);
+               screen_line(curwin, cmdline_row + r, 0, cols, cols, -1, 0);
            }
            ret = 4;
        }
index e5f06874f8deec2235fd81c35c7906492ea187c5..af14af050176c2fd02def2c0c2f39e0590a07eab 100644 (file)
@@ -2098,35 +2098,7 @@ retnomove:
            redraw_cmdline = TRUE;      // show visual mode later
     }
 
-    if (col_from_screen == MAXCOL)
-    {
-       // When clicking after end of line, still need to set correct curswant
-       int off_l = LineOffset[prev_row] + curwin->w_wincol;
-       if (ScreenCols[off_l] < MAXCOL)
-       {
-           // Binary search to find last char in line
-           int off_r = LineOffset[prev_row] + 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;
-           }
-           colnr_T eol_vcol = ScreenCols[off_r];
-           if (eol_vcol < 0)
-               // Empty line or whole line before w_leftcol,
-               // with columns before buffer text
-               eol_vcol = curwin->w_leftcol - 1;
-           col = eol_vcol + (off_click - off_r);
-       }
-       else
-           // Empty line or whole line before w_leftcol
-           col = prev_col - curwin->w_wincol + curwin->w_leftcol;
-    }
-    else if (col_from_screen >= 0)
+    if (col_from_screen >= 0)
     {
        // Use the virtual column from ScreenCols[], it is accurate also after
        // concealed characters.
index 53937d3c7f990cfb0dfe22e44b4802047f3babd8..cf7cd95f46210b64b1c580191ac6bd1233764b8f 100644 (file)
@@ -7,7 +7,7 @@ int compute_foldcolumn(win_T *wp, int col);
 size_t fill_foldcolumn(char_u *p, win_T *wp, int closed, linenr_T lnum);
 int screen_get_current_line_off(void);
 void reset_screen_attr(void);
-void screen_line(win_T *wp, int row, int coloff, int endcol, int clear_width, int flags);
+void screen_line(win_T *wp, int row, int coloff, int endcol, int clear_width, colnr_T last_vcol, int flags);
 void rl_mirror(char_u *str);
 void draw_vsep_win(win_T *wp, int row);
 int stl_connected(win_T *wp);
index 208b1825411337af18bf315e6f0d04454dab4b81..08b1c018f219d2610b80e4981b6678e8cfb0039c 100644 (file)
@@ -452,6 +452,10 @@ skip_for_popup(int row, int col)
  * SLF_RIGHTLEFT    rightleft window:
  *    When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
  *    When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
+ * SLF_INC_VCOL:
+ *    When FALSE, use "last_vcol" for ScreenCols[] of the columns to clear.
+ *    When TRUE, use an increasing sequence starting from "last_vcol + 1" for
+ *    ScreenCols[] of the columns to clear.
  */
     void
 screen_line(
@@ -460,6 +464,7 @@ screen_line(
        int     coloff,
        int     endcol,
        int     clear_width,
+       colnr_T last_vcol,
        int     flags UNUSED)
 {
     unsigned       off_from;
@@ -775,7 +780,8 @@ screen_line(
                                                  && ScreenAttrs[off_to] == 0
                                  && (!enc_utf8 || ScreenLinesUC[off_to] == 0))
        {
-           ScreenCols[off_to] = MAXCOL;
+           ScreenCols[off_to] =
+                             (flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol;
            ++off_to;
            ++col;
        }
@@ -830,7 +836,8 @@ screen_line(
                                                                 ' ', ' ', 0);
            while (col < clear_width)
            {
-               ScreenCols[off_to++] = MAXCOL;
+               ScreenCols[off_to++]
+                           = (flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol;
                ++col;
            }
        }
index a641a850b0c943b963a22ba70fcdb22ad181dcb3..25a6a5dd7618a016a8113b2fcbe429a4aec18edc 100644 (file)
@@ -3965,7 +3965,8 @@ update_system_term(term_T *term)
        else
            pos.col = 0;
 
-       screen_line(curwin, term->tl_toprow + pos.row, 0, pos.col, Columns, 0);
+       screen_line(curwin, term->tl_toprow + pos.row, 0, pos.col, Columns, -1,
+                                                                           0);
     }
 
     term->tl_dirty_row_start = MAX_ROW;
@@ -4088,7 +4089,7 @@ term_update_window(win_T *wp)
 #ifdef FEAT_MENU
                                + winbar_height(wp)
 #endif
-                               , wp->w_wincol, pos.col, wp->w_width,
+                               , wp->w_wincol, pos.col, wp->w_width, -1,
 #ifdef FEAT_PROP_POPUP
                                popup_is_popup(wp) ? SLF_POPUP :
 #endif
index 1cc08fcf84b9580332d706e24976a3f3771df96e..9696cdeae8088aab59f9f7b111b2703679e7c32d 100644 (file)
@@ -389,77 +389,123 @@ func Test_conceal_eol()
 endfunc
 
 func Test_conceal_mouse_click()
-  enew!
+  call NewWindow(10, 40)
   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 the space between "this" and "click" puts cursor there
-  call test_setmouse(1, 9)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 13, 0, 13], getcurpos())
-  " 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())
-  " click on 'e' of "here" puts cursor there
-  call test_setmouse(1, 19)
-  call feedkeys("\<LeftMouse>", "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("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 23, 0, 24], getcurpos())
-  call test_setmouse(1, 21)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 23, 0, 25], getcurpos())
-  call test_setmouse(1, 22)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 23, 0, 26], getcurpos())
-  call test_setmouse(1, 31)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 23, 0, 35], getcurpos())
-  call test_setmouse(1, 32)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 23, 0, 36], getcurpos())
-
-  set virtualedit=all
-  redraw
-  " click on the space between "this" and "click" puts cursor there
-  call test_setmouse(1, 9)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 13, 0, 13], getcurpos())
-  " 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())
-  " click on 'e' of "here" puts cursor there
-  call test_setmouse(1, 19)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 23, 0, 23], getcurpos())
-  " click after end of line puts cursor there with 'virtualedit'
-  call test_setmouse(1, 20)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 24, 0, 24], getcurpos())
-  call test_setmouse(1, 21)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 24, 1, 25], getcurpos())
-  call test_setmouse(1, 22)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 24, 2, 26], getcurpos())
-  call test_setmouse(1, 31)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 24, 11, 35], getcurpos())
-  call test_setmouse(1, 32)
-  call feedkeys("\<LeftMouse>", "tx")
-  call assert_equal([0, 1, 24, 12, 36], getcurpos())
-
-  bwipe!
-  set mouse& virtualedit&
+
+  " Test with both 'nocursorline' and 'cursorline', as they use two different
+  " code paths to set virtual columns for the cells to clear.
+  for cul in [v:false, v:true]
+    let &l:cursorline = cul
+
+    call setline(1, 'conceal this click here')
+    call assert_equal([
+          \ 'conceal  click here                     ',
+          \ ], ScreenLines(1, 40))
+
+    " Click on the space between "this" and "click" puts cursor there.
+    call test_setmouse(1, 9)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 13, 0, 13], getcurpos())
+    " 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())
+    " Click on 'e' of "here" puts cursor there.
+    call test_setmouse(1, 19)
+    call feedkeys("\<LeftMouse>", "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("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 23, 0, 24], getcurpos())
+    call test_setmouse(1, 21)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 23, 0, 25], getcurpos())
+    call test_setmouse(1, 22)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 23, 0, 26], getcurpos())
+    call test_setmouse(1, 31)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 23, 0, 35], getcurpos())
+    call test_setmouse(1, 32)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 23, 0, 36], getcurpos())
+
+    set virtualedit=all
+    redraw
+    " Click on the space between "this" and "click" puts cursor there.
+    call test_setmouse(1, 9)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 13, 0, 13], getcurpos())
+    " 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())
+    " Click on 'e' of "here" puts cursor there.
+    call test_setmouse(1, 19)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 23, 0, 23], getcurpos())
+    " Click after end of line puts cursor there with 'virtualedit'.
+    call test_setmouse(1, 20)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 24, 0, 24], getcurpos())
+    call test_setmouse(1, 21)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 24, 1, 25], getcurpos())
+    call test_setmouse(1, 22)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 24, 2, 26], getcurpos())
+    call test_setmouse(1, 31)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 24, 11, 35], getcurpos())
+    call test_setmouse(1, 32)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 24, 12, 36], getcurpos())
+    set virtualedit&
+
+    " Test with a wrapped line.
+    call setline(1, ['conceal this click here']->repeat(3)->join())
+    call assert_equal([
+          \ 'conceal  click here conceal  cli        ',
+          \ 'ck here conceal  click here             ',
+          \ ], ScreenLines([1, 2], 40))
+    " Click on boguscols puts cursor on the last char of a screen line.
+    for col in range(33, 40)
+      call test_setmouse(1, col)
+      call feedkeys("\<LeftMouse>", "tx")
+      call assert_equal([0, 1, 40, 0, 40], getcurpos())
+    endfor
+
+    " Also test with the last char of a screen line concealed.
+    setlocal number signcolumn=yes
+    call assert_equal([
+          \ '    1 conceal  click here conceal       ',
+          \ '       click here conceal  click h      ',
+          \ '      ere                               ',
+          \ ], ScreenLines([1, 3], 40))
+    call test_setmouse(1, 34)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 32, 0, 32], getcurpos())
+    call test_setmouse(2, 7)
+    call feedkeys("\<LeftMouse>", "tx")
+    call assert_equal([0, 1, 37, 0, 37], getcurpos())
+    " Click on boguscols puts cursor on the last char of a screen line.
+    for col in range(35, 40)
+      call test_setmouse(1, col)
+      call feedkeys("\<LeftMouse>", "tx")
+      call assert_equal([0, 1, 34, 0, 34], getcurpos())
+      call test_setmouse(2, col)
+      call feedkeys("\<LeftMouse>", "tx")
+      call assert_equal([0, 1, 68, 0, 68], getcurpos())
+    endfor
+    setlocal number& signcolumn&
+  endfor
+
+  call CloseWindow()
+  set mouse&
 endfunc
 
 " Test that cursor is drawn at the correct column when it is after end of the
index c8037f7928d3e9489406f847559be887d5141ecf..5d768c178b9b244a77020c7ae88e7cb038602c62 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    184,
 /**/
     183,
 /**/
index 7ef9b00336d3dcdd5701964f2457772668d9c0c1..85d92d5a9afa85179cf1bb730f3e7e1d367ee776 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -627,6 +627,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
 // flags for screen_line()
 #define SLF_RIGHTLEFT  1
 #define SLF_POPUP      2
+#define SLF_INC_VCOL   4
 
 #define MB_FILLER_CHAR '<'  // character used when a double-width character
                            // doesn't fit.