diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-05-23 14:23:09 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-23 14:23:09 +0800 |
commit | bdf15dbe6909b39e5d3cf22ae844ebd37862a1a8 (patch) | |
tree | 4b885d031e34866f6f401683b33d9e04241e0d56 | |
parent | cd48b72b60af0a8b5a7770ab599e4062e974183d (diff) | |
download | rneovim-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.c | 21 | ||||
-rw-r--r-- | src/nvim/normal.c | 41 | ||||
-rw-r--r-- | test/old/testdir/test_visual.vim | 193 |
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) |