Commit: patch 9.1.0433: Wrong yanking with exclusive selection and ve=all

4 views
Skip to first unread message

Christian Brabandt

unread,
May 23, 2024, 2:00:12 AMMay 23
to vim...@googlegroups.com
patch 9.1.0433: Wrong yanking with exclusive selection and ve=all

Commit: https://github.com/vim/vim/commit/701ad50a9efcf0adfe6d787b606c4e4dbd31f26d
Author: zeertzjq <zeer...@outlook.com>
Date: Thu May 23 07:47:55 2024 +0200

patch 9.1.0433: Wrong yanking with exclusive selection and ve=all

Problem: Wrong yanking with exclusive selection and virtualedit=all,
and integer overflow when using getregion() on it.
Solution: Set coladd when decreasing column and 'virtualedit' is active.
Add more tests for getregion() with 'virtualedit' (zeertzjq).

closes: #14830

Signed-off-by: zeertzjq <zeer...@outlook.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/evalfunc.c b/src/evalfunc.c
index b65df5dd9..f70b032ad 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -5594,31 +5594,12 @@ getregionpos(

if (*region_type == MCHAR)
{
- // handle 'selection' == "exclusive"
+ // Handle 'selection' == "exclusive".
if (is_select_exclusive && !EQUAL_POS(*p1, *p2))
- {
- if (p2->coladd > 0)
- p2->coladd--;
- else if (p2->col > 0)
- {
- p2->col--;
-
- mb_adjustpos(curbuf, p2);
- }
- else if (p2->lnum > 1)
- {
- p2->lnum--;
- p2->col = ml_get_len(p2->lnum);
- if (p2->col > 0)
- {
- p2->col--;
-
- mb_adjustpos(curbuf, p2);
- }
- }
- }
- // if fp2 is on NUL (empty line) inclusive becomes false
- if (*ml_get_pos(p2) == NUL && !virtual_op)
+ // When backing up to previous line, inclusive becomes false.
+ *inclusive = !unadjust_for_sel_inner(p2);
+ // If p2 is on NUL (end of line), inclusive becomes false.
+ if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL)
*inclusive = FALSE;
}
else if (*region_type == MBLOCK)
diff --git a/src/normal.c b/src/normal.c
index 38c6bad80..b55d941fc 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -6696,29 +6696,40 @@ adjust_for_sel(cmdarg_T *cap)
int
unadjust_for_sel(void)
{
- pos_T *pp;
-
if (*p_sel == 'e' && !EQUAL_POS(VIsual, curwin->w_cursor))
+ return unadjust_for_sel_inner(LT_POS(VIsual, curwin->w_cursor)
+ ? &curwin->w_cursor : &VIsual);
+ return FALSE;
+}
+
+/*
+ * Move position "*pp" back one character for 'selection' == "exclusive".
+ * Returns TRUE when backed up to the previous line.
+ */
+ int
+unadjust_for_sel_inner(pos_T *pp)
+{
+ colnr_T cs, ce;
+
+ if (pp->coladd > 0)
+ --pp->coladd;
+ else if (pp->col > 0)
{
- if (LT_POS(VIsual, curwin->w_cursor))
- pp = &curwin->w_cursor;
- else
- pp = &VIsual;
- if (pp->coladd > 0)
- --pp->coladd;
- else
- if (pp->col > 0)
- {
- --pp->col;
- mb_adjustpos(curbuf, pp);
- }
- else if (pp->lnum > 1)
+ --pp->col;
+ mb_adjustpos(curbuf, pp);
+ if (virtual_active())
{
- --pp->lnum;
- pp->col = ml_get_len(pp->lnum);
- return TRUE;
+ getvcol(curwin, pp, &cs, NULL, &ce);
+ pp->coladd = ce - cs;
}
}
+ else if (pp->lnum > 1)
+ {
+ --pp->lnum;
+ pp->col = ml_get_len(pp->lnum);
+ return TRUE;
+ }
+
return FALSE;
}

diff --git a/src/proto/normal.pro b/src/proto/normal.pro
index 6dcbe414f..36a26ec48 100644
--- a/src/proto/normal.pro
+++ b/src/proto/normal.pro
@@ -31,5 +31,6 @@ int get_visual_text(cmdarg_T *cap, char_u **pp, int *lenp);
void start_selection(void);
void may_start_select(int c);
int unadjust_for_sel(void);
+int unadjust_for_sel_inner(pos_T *pp);
void set_cursor_for_append_to_line(void);
/* vim: set ft=c : */
diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim
index 621be298d..febf67855 100644
--- a/src/testdir/test_visual.vim
+++ b/src/testdir/test_visual.vim
@@ -1631,6 +1631,22 @@ func Test_visual_substitute_visual()
bwipe!
endfunc

+func Test_virtualedit_exclusive_selection()
+ new
+ set virtualedit=all selection=exclusive
+
+ call setline(1, "a b")
+ normal! 0v8ly
+ call assert_equal("a ", getreg('"'))
+ normal! 0v6ly
+ call assert_equal('a ', getreg('"'))
+ normal! 06lv2ly
+ call assert_equal(' ', getreg('"'))
+
+ set virtualedit& selection&
+ bwipe!
+endfunc
+
func Test_visual_getregion()
let lines =<< trim END
new
@@ -2012,37 +2028,113 @@ func Test_visual_getregion()
#" Exclusive selection 2
new
call setline(1, ["a c", "x z", '', ''])
+
call cursor(1, 1)
call feedkeys("\<Esc>v2l", 'xt')
call assert_equal(["a "],
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
call cursor(1, 1)
call feedkeys("\<Esc>v$G", 'xt')
call assert_equal(["a c", "x z", ''],
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
call cursor(1, 1)
call feedkeys("\<Esc>v$j", 'xt')
call assert_equal(["a c", "x z"],
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
call cursor(1, 1)
call feedkeys("\<Esc>\<C-v>$j", 'xt')
call assert_equal(["a c", "x z"],
\ getregion(getpos('v'), getpos('.'),
\ {'exclusive': v:true, 'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'exclusive': v:true, 'type': "\<C-v>" }))
+
call cursor(1, 1)
call feedkeys("\<Esc>\<C-v>$G", 'xt')
call assert_equal(["a", "x", '', ''],
\ getregion(getpos('v'), getpos('.'),
\ {'exclusive': v:true, 'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ [[bufnr('%'), 4, 0, 0], [bufnr('%'), 4, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'exclusive': v:true, 'type': "\<C-v>" }))
+
call cursor(1, 1)
call feedkeys("\<Esc>wv2j", 'xt')
call assert_equal(["c", "x z"],
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))

- #" virtualedit
+ #" 'virtualedit' with exclusive selection
set selection=exclusive
set virtualedit=all

+ call cursor(1, 1)
+ call feedkeys("\<Esc>vj", 'xt')
+ call assert_equal(["a c"],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v8l", 'xt')
+ call assert_equal(["a "],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v6l", 'xt')
+ call assert_equal(['a '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 5]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>6lv2l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 2, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
call cursor(1, 1)
call feedkeys("\<Esc>lv2l", 'xt')
call assert_equal([' '],
@@ -2102,9 +2194,106 @@ func Test_visual_getregion()
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))

- set virtualedit&
+ #" 'virtualedit' with inclusive selection
set selection&

+ call cursor(1, 1)
+ call feedkeys("\<Esc>vj", 'xt')
+ call assert_equal(["a c", 'x'],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v8l", 'xt')
+ call assert_equal(["a c"],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v6l", 'xt')
+ call assert_equal(['a '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 6]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>6lv2l", 'xt')
+ call assert_equal([' c'],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>lv2l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>2lv2l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call feedkeys('j', 'xt')
+ call assert_equal([' c', 'x '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 4]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>6l\<C-v>2lj", 'xt')
+ call assert_equal([' c', ' z'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>l\<C-v>2l2j", 'xt')
+ call assert_equal([' ', ' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]],
+ \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 3]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 3]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt')
+ call assert_equal([' ', ' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]],
+ \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 4]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 3]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ set virtualedit&
bwipe!
END
call v9.CheckLegacyAndVim9Success(lines)
diff --git a/src/version.c b/src/version.c
index b37dbcf85..c4bccb447 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 433,
/**/
432,
/**/
Reply all
Reply to author
Forward
0 new messages