mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
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 <zeertzjq@outlook.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
f2d74e3b63
commit
701ad50a9e
@@ -5594,31 +5594,12 @@ getregionpos(
|
|||||||
|
|
||||||
if (*region_type == MCHAR)
|
if (*region_type == MCHAR)
|
||||||
{
|
{
|
||||||
// handle 'selection' == "exclusive"
|
// Handle 'selection' == "exclusive".
|
||||||
if (is_select_exclusive && !EQUAL_POS(*p1, *p2))
|
if (is_select_exclusive && !EQUAL_POS(*p1, *p2))
|
||||||
{
|
// When backing up to previous line, inclusive becomes false.
|
||||||
if (p2->coladd > 0)
|
*inclusive = !unadjust_for_sel_inner(p2);
|
||||||
p2->coladd--;
|
// If p2 is on NUL (end of line), inclusive becomes false.
|
||||||
else if (p2->col > 0)
|
if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL)
|
||||||
{
|
|
||||||
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)
|
|
||||||
*inclusive = FALSE;
|
*inclusive = FALSE;
|
||||||
}
|
}
|
||||||
else if (*region_type == MBLOCK)
|
else if (*region_type == MBLOCK)
|
||||||
|
47
src/normal.c
47
src/normal.c
@@ -6696,29 +6696,40 @@ adjust_for_sel(cmdarg_T *cap)
|
|||||||
int
|
int
|
||||||
unadjust_for_sel(void)
|
unadjust_for_sel(void)
|
||||||
{
|
{
|
||||||
pos_T *pp;
|
|
||||||
|
|
||||||
if (*p_sel == 'e' && !EQUAL_POS(VIsual, curwin->w_cursor))
|
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->col;
|
||||||
pp = &curwin->w_cursor;
|
mb_adjustpos(curbuf, pp);
|
||||||
else
|
if (virtual_active())
|
||||||
pp = &VIsual;
|
|
||||||
if (pp->coladd > 0)
|
|
||||||
--pp->coladd;
|
|
||||||
else
|
|
||||||
if (pp->col > 0)
|
|
||||||
{
|
{
|
||||||
--pp->col;
|
getvcol(curwin, pp, &cs, NULL, &ce);
|
||||||
mb_adjustpos(curbuf, pp);
|
pp->coladd = ce - cs;
|
||||||
}
|
|
||||||
else if (pp->lnum > 1)
|
|
||||||
{
|
|
||||||
--pp->lnum;
|
|
||||||
pp->col = ml_get_len(pp->lnum);
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (pp->lnum > 1)
|
||||||
|
{
|
||||||
|
--pp->lnum;
|
||||||
|
pp->col = ml_get_len(pp->lnum);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,5 +31,6 @@ int get_visual_text(cmdarg_T *cap, char_u **pp, int *lenp);
|
|||||||
void start_selection(void);
|
void start_selection(void);
|
||||||
void may_start_select(int c);
|
void may_start_select(int c);
|
||||||
int unadjust_for_sel(void);
|
int unadjust_for_sel(void);
|
||||||
|
int unadjust_for_sel_inner(pos_T *pp);
|
||||||
void set_cursor_for_append_to_line(void);
|
void set_cursor_for_append_to_line(void);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -1631,6 +1631,22 @@ func Test_visual_substitute_visual()
|
|||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
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()
|
func Test_visual_getregion()
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
new
|
new
|
||||||
@@ -2012,37 +2028,113 @@ func Test_visual_getregion()
|
|||||||
#" Exclusive selection 2
|
#" Exclusive selection 2
|
||||||
new
|
new
|
||||||
call setline(1, ["a\tc", "x\tz", '', ''])
|
call setline(1, ["a\tc", "x\tz", '', ''])
|
||||||
|
|
||||||
call cursor(1, 1)
|
call cursor(1, 1)
|
||||||
call feedkeys("\<Esc>v2l", 'xt')
|
call feedkeys("\<Esc>v2l", 'xt')
|
||||||
call assert_equal(["a\t"],
|
call assert_equal(["a\t"],
|
||||||
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
|
\ 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 cursor(1, 1)
|
||||||
call feedkeys("\<Esc>v$G", 'xt')
|
call feedkeys("\<Esc>v$G", 'xt')
|
||||||
call assert_equal(["a\tc", "x\tz", ''],
|
call assert_equal(["a\tc", "x\tz", ''],
|
||||||
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
|
\ 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 cursor(1, 1)
|
||||||
call feedkeys("\<Esc>v$j", 'xt')
|
call feedkeys("\<Esc>v$j", 'xt')
|
||||||
call assert_equal(["a\tc", "x\tz"],
|
call assert_equal(["a\tc", "x\tz"],
|
||||||
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
|
\ 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 cursor(1, 1)
|
||||||
call feedkeys("\<Esc>\<C-v>$j", 'xt')
|
call feedkeys("\<Esc>\<C-v>$j", 'xt')
|
||||||
call assert_equal(["a\tc", "x\tz"],
|
call assert_equal(["a\tc", "x\tz"],
|
||||||
\ getregion(getpos('v'), getpos('.'),
|
\ getregion(getpos('v'), getpos('.'),
|
||||||
\ {'exclusive': v:true, 'type': "\<C-v>" }))
|
\ {'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 cursor(1, 1)
|
||||||
call feedkeys("\<Esc>\<C-v>$G", 'xt')
|
call feedkeys("\<Esc>\<C-v>$G", 'xt')
|
||||||
call assert_equal(["a", "x", '', ''],
|
call assert_equal(["a", "x", '', ''],
|
||||||
\ getregion(getpos('v'), getpos('.'),
|
\ getregion(getpos('v'), getpos('.'),
|
||||||
\ {'exclusive': v:true, 'type': "\<C-v>" }))
|
\ {'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 cursor(1, 1)
|
||||||
call feedkeys("\<Esc>wv2j", 'xt')
|
call feedkeys("\<Esc>wv2j", 'xt')
|
||||||
call assert_equal(["c", "x\tz"],
|
call assert_equal(["c", "x\tz"],
|
||||||
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
|
\ 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 selection=exclusive
|
||||||
set virtualedit=all
|
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 cursor(1, 1)
|
||||||
call feedkeys("\<Esc>lv2l", 'xt')
|
call feedkeys("\<Esc>lv2l", 'xt')
|
||||||
call assert_equal([' '],
|
call assert_equal([' '],
|
||||||
@@ -2102,9 +2194,106 @@ func Test_visual_getregion()
|
|||||||
\ ],
|
\ ],
|
||||||
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
|
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
|
||||||
|
|
||||||
set virtualedit&
|
#" 'virtualedit' with inclusive selection
|
||||||
set 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!
|
bwipe!
|
||||||
END
|
END
|
||||||
call v9.CheckLegacyAndVim9Success(lines)
|
call v9.CheckLegacyAndVim9Success(lines)
|
||||||
|
@@ -704,6 +704,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
433,
|
||||||
/**/
|
/**/
|
||||||
432,
|
432,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user