aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2024-05-23 14:23:09 +0800
committerGitHub <noreply@github.com>2024-05-23 14:23:09 +0800
commitbdf15dbe6909b39e5d3cf22ae844ebd37862a1a8 (patch)
tree4b885d031e34866f6f401683b33d9e04241e0d56
parentcd48b72b60af0a8b5a7770ab599e4062e974183d (diff)
downloadrneovim-bdf15dbe6909b39e5d3cf22ae844ebd37862a1a8.tar.gz
rneovim-bdf15dbe6909b39e5d3cf22ae844ebd37862a1a8.tar.bz2
rneovim-bdf15dbe6909b39e5d3cf22ae844ebd37862a1a8.zip
vim-patch:9.1.0433: Wrong yanking with exclusive selection and ve=all (#28933)
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: vim/vim#14830 https://github.com/vim/vim/commit/701ad50a9efcf0adfe6d787b606c4e4dbd31f26d
-rw-r--r--src/nvim/eval/funcs.c21
-rw-r--r--src/nvim/normal.c41
-rw-r--r--test/old/testdir/test_visual.vim193
3 files changed, 222 insertions, 33 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 7453bbb601..fcc86bb708 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -2913,24 +2913,13 @@ static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2
}
if (*region_type == kMTCharWise) {
- // handle 'selection' == "exclusive"
+ // Handle 'selection' == "exclusive".
if (is_select_exclusive && !equalpos(*p1, *p2)) {
- if (p2->coladd > 0) {
- p2->coladd--;
- } else if (p2->col > 0) {
- p2->col--;
- mark_mb_adjustpos(curbuf, p2);
- } else if (p2->lnum > 1) {
- p2->lnum--;
- p2->col = ml_get_len(p2->lnum);
- if (p2->col > 0) {
- p2->col--;
- mark_mb_adjustpos(curbuf, p2);
- }
- }
+ // When backing up to previous line, inclusive becomes false.
+ *inclusive = !unadjust_for_sel_inner(p2);
}
- // if fp2 is on NUL (empty line) inclusive becomes false
- if (*ml_get_pos(p2) == NUL && !virtual_op) {
+ // 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 == kMTBlockWise) {
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 3b1f0bf73c..8ba375f29d 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -6014,23 +6014,34 @@ static void adjust_for_sel(cmdarg_T *cap)
bool unadjust_for_sel(void)
{
if (*p_sel == 'e' && !equalpos(VIsual, curwin->w_cursor)) {
- pos_T *pp;
- if (lt(VIsual, curwin->w_cursor)) {
- pp = &curwin->w_cursor;
- } else {
- pp = &VIsual;
- }
- if (pp->coladd > 0) {
- pp->coladd--;
- } else if (pp->col > 0) {
- pp->col--;
- mark_mb_adjustpos(curbuf, pp);
- } else if (pp->lnum > 1) {
- pp->lnum--;
- pp->col = ml_get_len(pp->lnum);
- return true;
+ return unadjust_for_sel_inner(lt(VIsual, curwin->w_cursor)
+ ? &curwin->w_cursor : &VIsual);
+ }
+ return false;
+}
+
+/// Move position "*pp" back one character for 'selection' == "exclusive".
+///
+/// @return true when backed up to the previous line.
+bool unadjust_for_sel_inner(pos_T *pp)
+{
+ colnr_T cs, ce;
+
+ if (pp->coladd > 0) {
+ pp->coladd--;
+ } else if (pp->col > 0) {
+ pp->col--;
+ mark_mb_adjustpos(curbuf, pp);
+ if (virtual_active(curwin)) {
+ 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/test/old/testdir/test_visual.vim b/test/old/testdir/test_visual.vim
index a682455227..72c58acdc9 100644
--- a/test/old/testdir/test_visual.vim
+++ b/test/old/testdir/test_visual.vim
@@ -1635,6 +1635,22 @@ func Test_visual_substitute_visual()
bwipe!
endfunc
+func Test_virtualedit_exclusive_selection()
+ new
+ set virtualedit=all selection=exclusive
+
+ call setline(1, "a\tb")
+ normal! 0v8ly
+ call assert_equal("a\t", 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
@@ -2016,38 +2032,114 @@ func Test_visual_getregion()
#" Exclusive selection 2
new
call setline(1, ["a\tc", "x\tz", '', ''])
+
call cursor(1, 1)
call feedkeys("\<Esc>v2l", 'xt')
call assert_equal(["a\t"],
\ 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\tc", "x\tz", ''],
\ 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\tc", "x\tz"],
\ 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\tc", "x\tz"],
\ 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\tz"],
\ 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\tc"],
+ \ 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\t"],
+ \ 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([' '],
\ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
@@ -2106,9 +2198,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\tc", '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\tc"],
+ \ 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 CheckLegacyAndVim9Success(lines)