From 701ad50a9efcf0adfe6d787b606c4e4dbd31f26d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 May 2024 07:47:55 +0200 Subject: 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 Signed-off-by: Christian Brabandt --- src/evalfunc.c | 29 ++----- src/normal.c | 47 ++++++----- src/proto/normal.pro | 1 + src/testdir/test_visual.vim | 193 +++++++++++++++++++++++++++++++++++++++++++- src/version.c | 2 + 5 files changed, 228 insertions(+), 44 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index b65df5dd90..f70b032ad2 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 38c6bad80f..b55d941fcc 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 6dcbe414fc..36a26ec480 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 621be298d6..febf67855c 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\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 @@ -2012,37 +2028,113 @@ func Test_visual_getregion() #" Exclusive selection 2 new call setline(1, ["a\tc", "x\tz", '', '']) + call cursor(1, 1) call feedkeys("\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("\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("\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("\\$j", 'xt') call assert_equal(["a\tc", "x\tz"], \ getregion(getpos('v'), getpos('.'), \ {'exclusive': v:true, 'type': "\" })) + 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': "\" })) + call cursor(1, 1) call feedkeys("\\$G", 'xt') call assert_equal(["a", "x", '', ''], \ getregion(getpos('v'), getpos('.'), \ {'exclusive': v:true, 'type': "\" })) + 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': "\" })) + call cursor(1, 1) call feedkeys("\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("\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("\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("\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("\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("\lv2l", 'xt') call assert_equal([' '], @@ -2102,9 +2194,106 @@ func Test_visual_getregion() \ ], \ getregionpos(getpos('v'), getpos('.'), {'type': "\" })) - set virtualedit& + #" 'virtualedit' with inclusive selection set selection& + call cursor(1, 1) + call feedkeys("\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("\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("\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("\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("\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("\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("\6l\2lj", 'xt') + call assert_equal([' c', ' z'], + \ getregion(getpos('v'), getpos('.'), {'type': "\" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\" })) + + call cursor(1, 1) + call feedkeys("\l\2l2j", 'xt') + call assert_equal([' ', ' ', ' '], + \ getregion(getpos('v'), getpos('.'), {'type': "\" })) + 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': "\" })) + + call cursor(1, 1) + call feedkeys("\2l\2l2j", 'xt') + call assert_equal([' ', ' ', ' '], + \ getregion(getpos('v'), getpos('.'), {'type': "\" })) + 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': "\" })) + + set virtualedit& bwipe! END call v9.CheckLegacyAndVim9Success(lines) diff --git a/src/version.c b/src/version.c index b37dbcf857..c4bccb447e 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, /**/ -- cgit v1.2.3