# HG changeset patch # User Christian Brabandt # Date 1710598504 -3600 # Node ID fdd232ab72ea2af51a9e2f194eb60118aaca7b81 # Parent 68553f9444071e76bb3a9ea488aa9f1e491d2b34 patch 9.1.0184: Cursor pos wrong when clicking with conceal and wrap Commit: https://github.com/vim/vim/commit/d0c1b7723f7e73763597af2f97a53d94ab7ed020 Author: zeertzjq Date: Sat Mar 16 15:03:33 2024 +0100 patch 9.1.0184: Cursor pos wrong when clicking with conceal and wrap 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 Signed-off-by: Christian Brabandt diff --git a/src/drawline.c b/src/drawline.c --- a/src/drawline.c +++ b/src/drawline.c @@ -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 } 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_ 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_ } #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; diff --git a/src/drawscreen.c b/src/drawscreen.c --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -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; } diff --git a/src/mouse.c b/src/mouse.c --- a/src/mouse.c +++ b/src/mouse.c @@ -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. diff --git a/src/proto/screen.pro b/src/proto/screen.pro --- a/src/proto/screen.pro +++ b/src/proto/screen.pro @@ -7,7 +7,7 @@ int compute_foldcolumn(win_T *wp, int co 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); diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -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; } } diff --git a/src/terminal.c b/src/terminal.c --- a/src/terminal.c +++ b/src/terminal.c @@ -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 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 @@ -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)) + + " 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("\", "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("\", "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()) + " Click on the space between "this" and "click" puts cursor there. + call test_setmouse(1, 9) + call feedkeys("\", "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("\", "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 - redraw - " click on the space between "this" and "click" puts cursor there - call test_setmouse(1, 9) - call feedkeys("\", "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("\", "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 with '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()) + set virtualedit=all + redraw + " Click on the space between "this" and "click" puts cursor there. + call test_setmouse(1, 9) + call feedkeys("\", "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("\", "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 with '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()) + set virtualedit& - bwipe! - set mouse& 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("\", "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("\", "tx") + call assert_equal([0, 1, 32, 0, 32], getcurpos()) + call test_setmouse(2, 7) + call feedkeys("\", "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("\", "tx") + call assert_equal([0, 1, 34, 0, 34], getcurpos()) + call test_setmouse(2, col) + call feedkeys("\", "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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 184, +/**/ 183, /**/ 182, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -627,6 +627,7 @@ extern int (*dyn_libintl_wputenv)(const // 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.