# HG changeset patch # User Christian Brabandt # Date 1697276704 -7200 # Node ID bfe07ef45143c6a1f84a554cc2ec405de3e6e1aa # Parent c8e41b6fe0025b9e90d5eb8ef0dac65cc4b9b127 patch 9.0.2022: getmousepos() returns wrong index for TAB char Commit: https://github.com/vim/vim/commit/b583eda7031b1f6a3469a2537d0c10ca5fa5568e Author: zeertzjq Date: Sat Oct 14 11:32:28 2023 +0200 patch 9.0.2022: getmousepos() returns wrong index for TAB char Problem: When clicking in the middle of a TAB, getmousepos() returns the column of the next char instead of the TAB. Solution: Break out of the loop when the vcol to find is inside current char. Fix invalid memory access when calling virtcol2col() on an empty line. closes: #13321 Signed-off-by: Christian Brabandt Co-authored-by: zeertzjq diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -10376,6 +10376,8 @@ virtcol2col({winid}, {lnum}, {col}) *v character in window {winid} at buffer line {lnum} and virtual column {col}. + If buffer line {lnum} is an empty line, 0 is returned. + If {col} is greater than the last virtual column in line {lnum}, then the byte index of the character at the last virtual column is returned. diff --git a/src/mouse.c b/src/mouse.c --- a/src/mouse.c +++ b/src/mouse.c @@ -3201,7 +3201,7 @@ mouse_find_win(int *rowp, int *colp, mou || defined(FEAT_EVAL) || defined(PROTO) /* * Convert a virtual (screen) column to a character column. - * The first column is one. + * The first column is zero. */ int vcol2col(win_T *wp, linenr_T lnum, int vcol) @@ -3214,7 +3214,10 @@ vcol2col(win_T *wp, linenr_T lnum, int v init_chartabsize_arg(&cts, wp, lnum, 0, line, line); while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); + int size = win_lbr_chartabsize(&cts, NULL); + if (cts.cts_vcol + size > vcol) + break; + cts.cts_vcol += size; MB_PTR_ADV(cts.cts_ptr); } clear_chartabsize_arg(&cts); diff --git a/src/move.c b/src/move.c --- a/src/move.c +++ b/src/move.c @@ -1547,14 +1547,17 @@ f_screenpos(typval_T *argvars UNUSED, ty static int virtcol2col(win_T *wp, linenr_T lnum, int vcol) { - int offset = vcol2col(wp, lnum, vcol); + int offset = vcol2col(wp, lnum, vcol - 1); char_u *line = ml_get_buf(wp->w_buffer, lnum, FALSE); char_u *p = line + offset; - // For a multibyte character, need to return the column number of the first - // byte. - MB_PTR_BACK(line, p); - + if (*p == NUL) + { + if (p == line) // empty line + return 0; + // Move to the first byte of the last char. + MB_PTR_BACK(line, p); + } return (int)(p - line + 1); } diff --git a/src/testdir/test_cursor_func.vim b/src/testdir/test_cursor_func.vim --- a/src/testdir/test_cursor_func.vim +++ b/src/testdir/test_cursor_func.vim @@ -573,6 +573,11 @@ func Test_virtcol2col() call assert_equal(8, virtcol2col(0, 1, 7)) call assert_equal(8, virtcol2col(0, 1, 8)) + " These used to cause invalid memory access + call setline(1, '') + call assert_equal(0, virtcol2col(0, 1, 1)) + call assert_equal(0, virtcol2col(0, 1, 2)) + let w = winwidth(0) call setline(2, repeat('a', w + 2)) let win_nosbr = win_getid() diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -3366,6 +3366,46 @@ func Test_getmousepos() \ line: 1, \ column: 1, \ }, getmousepos()) + call test_setmouse(1, 2) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 2, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 2, + \ line: 1, + \ column: 1, + \ }, getmousepos()) + call test_setmouse(1, 8) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 8, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 8, + \ line: 1, + \ column: 1, + \ }, getmousepos()) + call test_setmouse(1, 9) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 9, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 9, + \ line: 1, + \ column: 2, + \ }, getmousepos()) + call test_setmouse(1, 12) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 12, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 12, + \ line: 1, + \ column: 2, + \ }, getmousepos()) call test_setmouse(1, 25) call assert_equal(#{ \ screenrow: 1, 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 */ /**/ + 2022, +/**/ 2021, /**/ 2020,