vim-patch:9.0.2022: getmousepos() returns wrong index for TAB char (#25636)

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: vim/vim#13321

b583eda703
This commit is contained in:
zeertzjq 2023-10-14 19:18:25 +08:00 committed by GitHub
parent 99b1163b5a
commit bcda800933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 6 deletions

View File

@ -8509,6 +8509,8 @@ virtcol2col({winid}, {lnum}, {col}) *virtcol2col()*
character in window {winid} at buffer line {lnum} and virtual character in window {winid} at buffer line {lnum} and virtual
column {col}. column {col}.
If buffer line {lnum} is an empty line, 0 is returned.
If {col} is greater than the last virtual column in line If {col} is greater than the last virtual column in line
{lnum}, then the byte index of the character at the last {lnum}, then the byte index of the character at the last
virtual column is returned. virtual column is returned.

View File

@ -10099,6 +10099,8 @@ function vim.fn.virtcol(expr, list, winid) end
--- character in window {winid} at buffer line {lnum} and virtual --- character in window {winid} at buffer line {lnum} and virtual
--- column {col}. --- column {col}.
--- ---
--- If buffer line {lnum} is an empty line, 0 is returned.
---
--- If {col} is greater than the last virtual column in line --- If {col} is greater than the last virtual column in line
--- {lnum}, then the byte index of the character at the last --- {lnum}, then the byte index of the character at the last
--- virtual column is returned. --- virtual column is returned.

View File

@ -12107,6 +12107,8 @@ M.funcs = {
character in window {winid} at buffer line {lnum} and virtual character in window {winid} at buffer line {lnum} and virtual
column {col}. column {col}.
If buffer line {lnum} is an empty line, 0 is returned.
If {col} is greater than the last virtual column in line If {col} is greater than the last virtual column in line
{lnum}, then the byte index of the character at the last {lnum}, then the byte index of the character at the last
virtual column is returned. virtual column is returned.

View File

@ -1754,7 +1754,7 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
} }
/// Convert a virtual (screen) column to a character column. /// Convert a virtual (screen) column to a character column.
/// The first column is one. /// The first column is zero.
colnr_T vcol2col(win_T *const wp, const linenr_T lnum, const colnr_T vcol) colnr_T vcol2col(win_T *const wp, const linenr_T lnum, const colnr_T vcol)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{ {
@ -1763,7 +1763,11 @@ colnr_T vcol2col(win_T *const wp, const linenr_T lnum, const colnr_T vcol)
chartabsize_T cts; chartabsize_T cts;
init_chartabsize_arg(&cts, wp, lnum, 0, line, line); init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) { 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); MB_PTR_ADV(cts.cts_ptr);
} }
clear_chartabsize_arg(&cts); clear_chartabsize_arg(&cts);

View File

@ -1142,13 +1142,17 @@ void f_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// returned. /// returned.
static int virtcol2col(win_T *wp, linenr_T lnum, int vcol) 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 *line = ml_get_buf(wp->w_buffer, lnum); char *line = ml_get_buf(wp->w_buffer, lnum);
char *p = line + offset; char *p = line + offset;
// For a multibyte character, need to return the column number of the first byte. if (*p == NUL) {
MB_PTR_BACK(line, p); 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); return (int)(p - line + 1);
} }

View File

@ -573,6 +573,11 @@ func Test_virtcol2col()
call assert_equal(8, virtcol2col(0, 1, 7)) call assert_equal(8, virtcol2col(0, 1, 7))
call assert_equal(8, virtcol2col(0, 1, 8)) 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) let w = winwidth(0)
call setline(2, repeat('a', w + 2)) call setline(2, repeat('a', w + 2))
let win_nosbr = win_getid() let win_nosbr = win_getid()

View File

@ -3069,6 +3069,46 @@ func Test_getmousepos()
\ line: 1, \ line: 1,
\ column: 1, \ column: 1,
\ }, getmousepos()) \ }, getmousepos())
call Ntest_setmouse(1, 2)
call assert_equal(#{
\ screenrow: 1,
\ screencol: 2,
\ winid: win_getid(),
\ winrow: 1,
\ wincol: 2,
\ line: 1,
\ column: 1,
\ }, getmousepos())
call Ntest_setmouse(1, 8)
call assert_equal(#{
\ screenrow: 1,
\ screencol: 8,
\ winid: win_getid(),
\ winrow: 1,
\ wincol: 8,
\ line: 1,
\ column: 1,
\ }, getmousepos())
call Ntest_setmouse(1, 9)
call assert_equal(#{
\ screenrow: 1,
\ screencol: 9,
\ winid: win_getid(),
\ winrow: 1,
\ wincol: 9,
\ line: 1,
\ column: 2,
\ }, getmousepos())
call Ntest_setmouse(1, 12)
call assert_equal(#{
\ screenrow: 1,
\ screencol: 12,
\ winid: win_getid(),
\ winrow: 1,
\ wincol: 12,
\ line: 1,
\ column: 2,
\ }, getmousepos())
call Ntest_setmouse(1, 25) call Ntest_setmouse(1, 25)
call assert_equal(#{ call assert_equal(#{
\ screenrow: 1, \ screenrow: 1,