aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-10-14 19:18:25 +0800
committerGitHub <noreply@github.com>2023-10-14 19:18:25 +0800
commitbcda800933f6de09392c3c91e290077952989722 (patch)
treeb4bababb6bb9aa3f80375db4feab2ccffd32c594
parent99b1163b5ae7a2f199803541c09f3da80547b40c (diff)
downloadrneovim-bcda800933f6de09392c3c91e290077952989722.tar.gz
rneovim-bcda800933f6de09392c3c91e290077952989722.tar.bz2
rneovim-bcda800933f6de09392c3c91e290077952989722.zip
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 https://github.com/vim/vim/commit/b583eda7031b1f6a3469a2537d0c10ca5fa5568e
-rw-r--r--runtime/doc/builtin.txt2
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua2
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/mouse.c8
-rw-r--r--src/nvim/move.c12
-rw-r--r--test/old/testdir/test_cursor_func.vim5
-rw-r--r--test/old/testdir/test_functions.vim40
7 files changed, 65 insertions, 6 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 48fa953954..c09c0d552e 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -8509,6 +8509,8 @@ virtcol2col({winid}, {lnum}, {col}) *virtcol2col()*
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/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index 481ffd1831..a2d9ce5bc4 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -10099,6 +10099,8 @@ function vim.fn.virtcol(expr, list, winid) end
--- 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/nvim/eval.lua b/src/nvim/eval.lua
index 14476e29d4..6ba5a171f9 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -12107,6 +12107,8 @@ M.funcs = {
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/nvim/mouse.c b/src/nvim/mouse.c
index 0433031393..75c399bcad 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -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.
-/// 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)
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;
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/nvim/move.c b/src/nvim/move.c
index 25dee0a114..dfd2bf795d 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -1142,13 +1142,17 @@ void f_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// returned.
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 *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/test/old/testdir/test_cursor_func.vim b/test/old/testdir/test_cursor_func.vim
index 65abbf7c85..cf131e8dad 100644
--- a/test/old/testdir/test_cursor_func.vim
+++ b/test/old/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/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
index b27b0be802..6a4d80d94d 100644
--- a/test/old/testdir/test_functions.vim
+++ b/test/old/testdir/test_functions.vim
@@ -3069,6 +3069,46 @@ func Test_getmousepos()
\ line: 1,
\ column: 1,
\ }, 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 assert_equal(#{
\ screenrow: 1,