patch 9.1.0142: getregion() can be improved
Problem: getregion() can be improved (after v9.1.120)
Solution: change getregion() implementation to use pos as lists and
one optional {opt} dictionary (Shougo Matsushita)
Note: The following is a breaking change!
Currently, the getregion() function (included as of patch v9.1.120) takes
3 arguments: the first 2 arguments are strings, describing a position,
arg3 is the type string.
However, that is slightly inflexible, there is no way to specify
additional arguments. So let's instead change the function signature to:
getregion(pos1, pos2 [, {Dict}]) where both pos1 and pos2 are lists.
This is slightly cleaner, and gives us the flexibility to specify
additional arguments as key/value pairs to the optional Dict arg.
Now it supports the "type" key to specify the selection type
(characterwise, blockwise or linewise) and now in addition one can also
define the selection type, independently of what the 'selection' option
actually is.
Technically, this is a breaking change, but since the getregion()
Vimscript function is still quite new, this should be fine.
closes: #14090
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Shougo Matsushita <Shougo.Matsu@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 37010d7..b5d8c87 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1151,6 +1151,7 @@
static argcheck_T arg3_dict_number_number[] = {arg_dict_any, arg_number, arg_number};
static argcheck_T arg3_diff[] = {arg_list_string, arg_list_string, arg_dict_any};
static argcheck_T arg3_list_string_dict[] = {arg_list_any, arg_string, arg_dict_any};
+static argcheck_T arg3_list_list_dict[] = {arg_list_any, arg_list_any, arg_dict_any};
static argcheck_T arg3_lnum_number_bool[] = {arg_lnum, arg_number, arg_bool};
static argcheck_T arg3_number[] = {arg_number, arg_number, arg_number};
static argcheck_T arg3_number_any_dict[] = {arg_number, arg_any, arg_dict_any};
@@ -2132,7 +2133,7 @@
ret_getreg, f_getreg},
{"getreginfo", 0, 1, FEARG_1, arg1_string,
ret_dict_any, f_getreginfo},
- {"getregion", 3, 3, FEARG_1, arg3_string,
+ {"getregion", 2, 3, FEARG_1, arg3_list_list_dict,
ret_list_string, f_getregion},
{"getregtype", 0, 1, FEARG_1, arg1_string,
ret_string, f_getregtype},
@@ -5491,41 +5492,43 @@
int inclusive = TRUE;
int fnum = -1;
pos_T p1, p2;
- pos_T *fp = NULL;
- char_u *pos1, *pos2, *type;
+ char_u *type;
+ char_u default_type[] = "v";
int save_virtual = -1;
int l;
int region_type = -1;
- int is_visual;
+ int is_select_exclusive;
if (rettv_list_alloc(rettv) == FAIL)
return;
- if (check_for_string_arg(argvars, 0) == FAIL
- || check_for_string_arg(argvars, 1) == FAIL
- || check_for_string_arg(argvars, 2) == FAIL)
+ if (check_for_list_arg(argvars, 0) == FAIL
+ || check_for_list_arg(argvars, 1) == FAIL
+ || check_for_opt_dict_arg(argvars, 2) == FAIL)
return;
- // NOTE: var2fpos() returns static pointer.
- fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
- if (fp == NULL || (fnum >= 0 && fnum != curbuf->b_fnum))
+ if (list2fpos(&argvars[0], &p1, &fnum, NULL, FALSE) != OK
+ || (fnum >= 0 && fnum != curbuf->b_fnum))
return;
- p1 = *fp;
- fp = var2fpos(&argvars[1], TRUE, &fnum, FALSE);
- if (fp == NULL || (fnum >= 0 && fnum != curbuf->b_fnum))
+ if (list2fpos(&argvars[1], &p2, &fnum, NULL, FALSE) != OK
+ || (fnum >= 0 && fnum != curbuf->b_fnum))
return;
- p2 = *fp;
- pos1 = tv_get_string(&argvars[0]);
- pos2 = tv_get_string(&argvars[1]);
- type = tv_get_string(&argvars[2]);
-
- is_visual = (pos1[0] == 'v' && pos1[1] == NUL)
- || (pos2[0] == 'v' && pos2[1] == NUL);
-
- if (is_visual && !VIsual_active)
- return;
+ if (argvars[2].v_type == VAR_DICT)
+ {
+ is_select_exclusive = dict_get_bool(
+ argvars[2].vval.v_dict, "exclusive", *p_sel == 'e');
+ type = dict_get_string(
+ argvars[2].vval.v_dict, "type", FALSE);
+ if (type == NULL)
+ type = default_type;
+ }
+ else
+ {
+ is_select_exclusive = *p_sel == 'e';
+ type = default_type;
+ }
if (type[0] == 'v' && type[1] == NUL)
region_type = MCHAR;
@@ -5539,6 +5542,10 @@
save_virtual = virtual_op;
virtual_op = virtual_active();
+ // NOTE: Adjust is needed.
+ p1.col--;
+ p2.col--;
+
if (!LT_POS(p1, p2))
{
// swap position
@@ -5552,7 +5559,7 @@
if (region_type == MCHAR)
{
// handle 'selection' == "exclusive"
- if (*p_sel == 'e' && !EQUAL_POS(p1, p2))
+ if (is_select_exclusive && !EQUAL_POS(p1, p2))
{
if (p2.coladd > 0)
p2.coladd--;
@@ -5590,7 +5597,7 @@
oa.start = p1;
oa.end = p2;
oa.start_vcol = MIN(sc1, sc2);
- if (*p_sel == 'e' && ec1 < sc2 && 0 < sc2 && ec2 > ec1)
+ if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1)
oa.end_vcol = sc2 - 1;
else
oa.end_vcol = MAX(ec1, ec2);
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index e2585d4..afb8cb9 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -5198,12 +5198,11 @@
enddef
def Test_getregion()
- assert_equal(['x'], getregion('.', '.', 'v')->map((_, _) => 'x'))
+ assert_equal(['x'], getregion(getpos('.'), getpos('.'))->map((_, _) => 'x'))
- v9.CheckDefAndScriptFailure(['getregion(10, ".", "v")'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1'])
- assert_equal([''], getregion('.', '.', 'v'))
- v9.CheckDefExecFailure(['getregion("a", ".", "v")'], 'E1209:')
- v9.CheckDefExecAndScriptFailure(['getregion("", ".", "v")'], 'E1209: Invalid value for a line number')
+ v9.CheckDefAndScriptFailure(['getregion(10, getpos("."))'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1211: List required for argument 1'])
+ assert_equal([''], getregion(getpos('.'), getpos('.')))
+ v9.CheckDefExecFailure(['getregion(getpos("a"), getpos("."))'], 'E1209:')
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim
index 8b43423..fdc1982 100644
--- a/src/testdir/test_visual.vim
+++ b/src/testdir/test_visual.vim
@@ -1638,80 +1638,113 @@
" Visual mode
call cursor(1, 1)
call feedkeys("\<ESC>vjl", 'tx')
- call assert_equal(['one', 'tw'], 'v'->getregion('.', 'v'))
- call assert_equal(['one', 'tw'], '.'->getregion('v', 'v'))
- call assert_equal(['o'], 'v'->getregion('v', 'v'))
- call assert_equal(['w'], '.'->getregion('.', 'v'))
- call assert_equal(['one', 'two'], '.'->getregion('v', 'V'))
- call assert_equal(['on', 'tw'], '.'->getregion('v', "\<C-v>"))
+ call assert_equal(['one', 'tw'],
+ \ 'v'->getpos()->getregion(getpos('.')))
+ call assert_equal(['one', 'tw'],
+ \ '.'->getpos()->getregion(getpos('v')))
+ call assert_equal(['o'],
+ \ 'v'->getpos()->getregion(getpos('v')))
+ call assert_equal(['w'],
+ \ '.'->getpos()->getregion(getpos('.'), #{ type: 'v' }))
+ call assert_equal(['one', 'two'],
+ \ getpos('.')->getregion(getpos('v'), #{ type: 'V' }))
+ call assert_equal(['on', 'tw'],
+ \ getpos('.')->getregion(getpos('v'), #{ type: "\<C-v>" }))
" Line visual mode
call cursor(1, 1)
call feedkeys("\<ESC>Vl", 'tx')
- call assert_equal(['one'], getregion('v', '.', 'V'))
- call assert_equal(['one'], getregion('.', 'v', 'V'))
- call assert_equal(['one'], getregion('v', 'v', 'V'))
- call assert_equal(['one'], getregion('.', '.', 'V'))
- call assert_equal(['on'], '.'->getregion('v', 'v'))
- call assert_equal(['on'], '.'->getregion('v', "\<C-v>"))
+ call assert_equal(['one'],
+ \ getregion(getpos('v'), getpos('.'), #{ type: 'V' }))
+ call assert_equal(['one'],
+ \ getregion(getpos('.'), getpos('v'), #{ type: 'V' }))
+ call assert_equal(['one'],
+ \ getregion(getpos('v'), getpos('v'), #{ type: 'V' }))
+ call assert_equal(['one'],
+ \ getregion(getpos('.'), getpos('.'), #{ type: 'V' }))
+ call assert_equal(['on'],
+ \ getpos('.')->getregion(getpos('v'), #{ type: 'v' }))
+ call assert_equal(['on'],
+ \ getpos('.')->getregion(getpos('v'), #{ type: "\<C-v>" }))
" Block visual mode
call cursor(1, 1)
call feedkeys("\<ESC>\<C-v>ll", 'tx')
- call assert_equal(['one'], getregion('v', '.', "\<C-v>"))
- call assert_equal(['one'], getregion('.', 'v', "\<C-v>"))
- call assert_equal(['o'], getregion('v', 'v', "\<C-v>"))
- call assert_equal(['e'], getregion('.', '.', "\<C-v>"))
- call assert_equal(['one'], '.'->getregion('v', 'V'))
- call assert_equal(['one'], '.'->getregion('v', 'v'))
+ call assert_equal(['one'],
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
+ call assert_equal(['one'],
+ \ getregion(getpos('.'), getpos('v'), #{ type: "\<C-v>" }))
+ call assert_equal(['o'],
+ \ getregion(getpos('v'), getpos('v'), #{ type: "\<C-v>" }))
+ call assert_equal(['e'],
+ \ getregion(getpos('.'), getpos('.'), #{ type: "\<C-v>" }))
+ call assert_equal(['one'],
+ \ '.'->getpos()->getregion(getpos('v'), #{ type: 'V' }))
+ call assert_equal(['one'],
+ \ '.'->getpos()->getregion(getpos('v'), #{ type: 'v' }))
" Using Marks
call setpos("'a", [0, 2, 3, 0])
call cursor(1, 1)
- call assert_equal(['one', 'two'], "'a"->getregion('.', 'v'))
- call assert_equal(['one', 'two'], "."->getregion("'a", 'v'))
- call assert_equal(['one', 'two'], "."->getregion("'a", 'V'))
- call assert_equal(['two'], "'a"->getregion("'a", 'V'))
- call assert_equal(['one', 'two'], "."->getregion("'a", "\<c-v>"))
+ call assert_equal(['one', 'two'],
+ \ "'a"->getpos()->getregion(getpos('.'), #{ type: 'v' }))
+ call assert_equal(['one', 'two'],
+ \ "."->getpos()->getregion(getpos("'a"), #{ type: 'v' }))
+ call assert_equal(['one', 'two'],
+ \ "."->getpos()->getregion(getpos("'a"), #{ type: 'V' }))
+ call assert_equal(['two'],
+ \ "'a"->getpos()->getregion(getpos("'a"), #{ type: 'V' }))
+ call assert_equal(['one', 'two'],
+ \ "."->getpos()->getregion(getpos("'a"), #{ type: "\<c-v>" }))
+
+ " Using List
+ call cursor(1, 1)
+ call assert_equal(['one', 'two'],
+ \ [0, 2, 3, 0]->getregion(getpos('.'), #{ type: 'v' }))
+ call assert_equal(['one', 'two'],
+ \ '.'->getpos()->getregion([0, 2, 3, 0], #{ type: 'v' }))
+ call assert_equal(['one', 'two'],
+ \ '.'->getpos()->getregion([0, 2, 3, 0], #{ type: 'V' }))
+ call assert_equal(['two'],
+ \ [0, 2, 3, 0]->getregion([0, 2, 3, 0], #{ type: 'V' }))
+ call assert_equal(['one', 'two'],
+ \ '.'->getpos()->getregion([0, 2, 3, 0], #{ type: "\<c-v>" }))
" Multiline with line visual mode
call cursor(1, 1)
call feedkeys("\<ESC>Vjj", 'tx')
- call assert_equal(['one', 'two', 'three'], getregion('v', '.', 'V'))
+ call assert_equal(['one', 'two', 'three'],
+ \ getregion(getpos('v'), getpos('.'), #{ type: 'V' }))
" Multiline with block visual mode
call cursor(1, 1)
call feedkeys("\<ESC>\<C-v>jj", 'tx')
- call assert_equal(['o', 't', 't'], getregion('v', '.', "\<C-v>"))
+ call assert_equal(['o', 't', 't'],
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
call cursor(1, 1)
call feedkeys("\<ESC>\<C-v>jj$", 'tx')
- call assert_equal(['one', 'two', 'three'], getregion('v', '.', "\<C-v>"))
+ call assert_equal(['one', 'two', 'three'],
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
" 'virtualedit'
set virtualedit=all
call cursor(1, 1)
call feedkeys("\<ESC>\<C-v>10ljj$", 'tx')
call assert_equal(['one ', 'two ', 'three '],
- \ getregion('v', '.', "\<C-v>"))
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
set virtualedit&
" Invalid position
call cursor(1, 1)
call feedkeys("\<ESC>vjj$", 'tx')
- call assert_fails("call getregion(1, 2, 'v')", 'E1174:')
- call assert_fails("call getregion('.', {}, 'v')", 'E1174:')
- call assert_equal([], getregion('', '.', 'v'))
- call assert_equal([], getregion('.', '.', ''))
- call feedkeys("\<ESC>", 'tx')
- call assert_equal([], getregion('v', '.', 'v'))
+ call assert_fails("call getregion(1, 2)", 'E1211:')
+ call assert_fails("call getregion(getpos('.'), {})", 'E1211:')
+ call assert_equal([], getregion(getpos('.'), getpos('.'), #{ type: '' }))
- " using an unset mark
- call assert_equal([], "'z"->getregion(".", 'V'))
" using the wrong type
- call assert_fails(':echo "."->getregion([],"V")', 'E1174:')
- call assert_fails(':echo "."->getregion("$", {})', 'E1174:')
- call assert_fails(':echo [0, 1, 1, 0]->getregion("$", "v")', 'E1174:')
+ call assert_fails(':echo "."->getpos()->getregion("$", [])', 'E1211:')
+
" using a mark in another buffer
new
let newbuf = bufnr()
@@ -1719,8 +1752,8 @@
normal! GmA
wincmd p
call assert_equal([newbuf, 10, 1, 0], getpos("'A"))
- call assert_equal([], getregion(".", "'A", 'v'))
- call assert_equal([], getregion("'A", ".", 'v'))
+ call assert_equal([], getregion(getpos('.'), getpos("'A"), #{ type: 'v' }))
+ call assert_equal([], getregion(getpos("'A"), getpos('.'), #{ type: 'v' }))
exe newbuf .. 'bwipe!'
bwipe!
@@ -1734,26 +1767,47 @@
call cursor(1, 3)
call feedkeys("\<Esc>\<C-v>ljj", 'xt')
call assert_equal(['cd', "\u00ab ", '34'],
- \ getregion('v', '.', "\<C-v>"))
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
call cursor(1, 4)
call feedkeys("\<Esc>\<C-v>ljj", 'xt')
call assert_equal(['de', "\U0001f1e7", '45'],
- \ getregion('v', '.', "\<C-v>"))
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
call cursor(1, 5)
call feedkeys("\<Esc>\<C-v>jj", 'xt')
- call assert_equal(['e', ' ', '5'], getregion('v', '.', "\<C-v>"))
+ call assert_equal(['e', ' ', '5'],
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
call cursor(1, 1)
call feedkeys("\<Esc>vj", 'xt')
- call assert_equal(['abcdefghijk«', "\U0001f1e6"], getregion('v', '.', "v"))
+ call assert_equal(['abcdefghijk«', "\U0001f1e6"],
+ \ getregion(getpos('v'), getpos('.'), #{ type: 'v' }))
" marks on multibyte chars
set selection=exclusive
call setpos("'a", [0, 1, 11, 0])
call setpos("'b", [0, 2, 16, 0])
call setpos("'c", [0, 2, 0, 0])
call cursor(1, 1)
- call assert_equal(['ghijk', '🇨«ðŸ‡©'], getregion("'a", "'b", "\<c-v>"))
- call assert_equal(['k«', '🇦«ðŸ‡§«ðŸ‡¨'], getregion("'a", "'b", "v"))
- call assert_equal(['k«'], getregion("'a", "'c", "v"))
+ call assert_equal(['ghijk', '🇨«ðŸ‡©'],
+ \ getregion(getpos("'a"), getpos("'b"), #{ type: "\<c-v>" }))
+ call assert_equal(['k«', '🇦«ðŸ‡§«ðŸ‡¨'],
+ \ getregion(getpos("'a"), getpos("'b"), #{ type: 'v' }))
+ call assert_equal(['k«'],
+ \ getregion(getpos("'a"), getpos("'c"), #{ type: 'v' }))
+
+ " use inclusive selection, although 'selection' is exclusive
+ call setpos("'a", [0, 1, 11, 0])
+ call setpos("'b", [0, 1, 1, 0])
+ call assert_equal(['abcdefghijk'],
+ \ getregion(getpos("'a"), getpos("'b"), #{ type: "\<c-v>", exclusive: v:false }))
+ call assert_equal(['abcdefghij'],
+ \ getregion(getpos("'a"), getpos("'b"), #{ type: "\<c-v>", exclusive: v:true }))
+ call assert_equal(['abcdefghijk'],
+ \ getregion(getpos("'a"), getpos("'b"), #{ type: 'v', exclusive: 0 }))
+ call assert_equal(['abcdefghij'],
+ \ getregion(getpos("'a"), getpos("'b"), #{ type: 'v', exclusive: 1 }))
+ call assert_equal(['abcdefghijk«'],
+ \ getregion(getpos("'a"), getpos("'b"), #{ type: 'V', exclusive: 0 }))
+ call assert_equal(['abcdefghijk«'],
+ \ getregion(getpos("'a"), getpos("'b"), #{ type: 'V', exclusive: 1 }))
bwipe!
@@ -1763,31 +1817,71 @@
call setline(1, ["a\tc", "x\tz", '', ''])
call cursor(1, 1)
call feedkeys("\<Esc>v2l", 'xt')
- call assert_equal(["a\t"], getregion('v', '.', 'v'))
+ call assert_equal(["a\t"],
+ \ getregion(getpos('v'), getpos('.'), #{ type: 'v' }))
call cursor(1, 1)
call feedkeys("\<Esc>v$G", 'xt')
- call assert_equal(["a\tc", "x\tz", ''], getregion('v', '.', 'v'))
+ call assert_equal(["a\tc", "x\tz", ''],
+ \ getregion(getpos('v'), getpos('.'), #{ type: 'v' }))
call cursor(1, 1)
call feedkeys("\<Esc>v$j", 'xt')
- call assert_equal(["a\tc", "x\tz"], getregion('v', '.', 'v'))
+ call assert_equal(["a\tc", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), #{ type: 'v' }))
call cursor(1, 1)
call feedkeys("\<Esc>\<C-v>$j", 'xt')
- call assert_equal(["a\tc", "x\tz"], getregion('v', '.', "\<C-v>"))
+ call assert_equal(["a\tc", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
call cursor(1, 1)
call feedkeys("\<Esc>\<C-v>$G", 'xt')
- call assert_equal(["a", "x", '', ''], getregion('v', '.', "\<C-v>"))
+ call assert_equal(["a", "x", '', ''],
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
call cursor(1, 1)
call feedkeys("\<Esc>wv2j", 'xt')
- call assert_equal(["c", "x\tz"], getregion('v', '.', 'v'))
+ call assert_equal(["c", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), #{ type: 'v' }))
+ set selection&
+
+ " 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 cursor(1, 1)
+ call feedkeys("\<Esc>v$G", 'xt')
+ call assert_equal(["a\tc", "x\tz", ''],
+ \ getregion(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 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 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 cursor(1, 1)
+ call feedkeys("\<Esc>wv2j", 'xt')
+ call assert_equal(["c", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), #{ exclusive: v:true }))
" virtualedit
+ set selection=exclusive
set virtualedit=all
call cursor(1, 1)
call feedkeys("\<Esc>2lv2lj", 'xt')
- call assert_equal([' c', 'x '], getregion('v', '.', 'v'))
+ call assert_equal([' c', 'x '],
+ \ getregion(getpos('v'), getpos('.'), #{ type: 'v' }))
call cursor(1, 1)
call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt')
- call assert_equal([' ', ' ', ' '], getregion('v', '.', "\<C-v>"))
+ call assert_equal([' ', ' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), #{ type: "\<C-v>" }))
set virtualedit&
set selection&
diff --git a/src/version.c b/src/version.c
index 5a6f4c1..c6c536a 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 142,
+/**/
141,
/**/
140,