Merge pull request #28854 from zeertzjq/vim-9.1.0394

vim-patch:9.1.{0394,0395,0423}: getregionpos()
This commit is contained in:
zeertzjq 2024-05-20 21:17:21 +08:00 committed by GitHub
commit 36a9da6547
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 383 additions and 72 deletions

View File

@ -2969,6 +2969,25 @@ getregion({pos1}, {pos2} [, {opts}]) *getregion()*
\ getpos('v'), getpos('.'), #{ type: mode() })<CR>
<
getregionpos({pos1}, {pos2} [, {opts}]) *getregionpos()*
Same as |getregion()|, but returns a list of positions
describing the buffer text segments bound by {pos1} and
{pos2}.
The segments are a pair of positions for every line: >
[[{start_pos}, {end_pos}], ...]
<
The position is a |List| with four numbers:
[bufnum, lnum, col, off]
"bufnum" is the buffer number.
"lnum" and "col" are the position in the buffer. The first
column is 1.
If the "off" number of a starting position is non-zero, it is
the offset in screen columns from the start of the character.
E.g., a position within a <Tab> or after the last character.
If the "off" number of an ending position is non-zero, it is
the character's number of cells included in the selection,
otherwise the whole character is included.
getregtype([{regname}]) *getregtype()*
The result is a String, which is type of register {regname}.
The value will be one of:

View File

@ -794,6 +794,7 @@ Cursor and mark position: *cursor-functions* *mark-functions*
Working with text in the current buffer: *text-functions*
getline() get a line or list of lines from the buffer
getregion() get a region of text from the buffer
getregionpos() get a list of positions for a region
setline() replace a line in the buffer
append() append line or list of lines in the buffer
indent() indent of a specific line

View File

@ -3581,6 +3581,30 @@ function vim.fn.getreginfo(regname) end
--- @return string[]
function vim.fn.getregion(pos1, pos2, opts) end
--- Same as |getregion()|, but returns a list of positions
--- describing the buffer text segments bound by {pos1} and
--- {pos2}.
--- The segments are a pair of positions for every line: >
--- [[{start_pos}, {end_pos}], ...]
--- <
--- The position is a |List| with four numbers:
--- [bufnum, lnum, col, off]
--- "bufnum" is the buffer number.
--- "lnum" and "col" are the position in the buffer. The first
--- column is 1.
--- If the "off" number of a starting position is non-zero, it is
--- the offset in screen columns from the start of the character.
--- E.g., a position within a <Tab> or after the last character.
--- If the "off" number of an ending position is non-zero, it is
--- the character's number of cells included in the selection,
--- otherwise the whole character is included.
---
--- @param pos1 table
--- @param pos2 table
--- @param opts? table
--- @return integer[][][]
function vim.fn.getregionpos(pos1, pos2, opts) end
--- The result is a String, which is type of register {regname}.
--- The value will be one of:
--- "v" for |charwise| text

View File

@ -4414,6 +4414,33 @@ M.funcs = {
returns = 'string[]',
signature = 'getregion({pos1}, {pos2} [, {opts}])',
},
getregionpos = {
args = { 2, 3 },
base = 1,
desc = [=[
Same as |getregion()|, but returns a list of positions
describing the buffer text segments bound by {pos1} and
{pos2}.
The segments are a pair of positions for every line: >
[[{start_pos}, {end_pos}], ...]
<
The position is a |List| with four numbers:
[bufnum, lnum, col, off]
"bufnum" is the buffer number.
"lnum" and "col" are the position in the buffer. The first
column is 1.
If the "off" number of a starting position is non-zero, it is
the offset in screen columns from the start of the character.
E.g., a position within a <Tab> or after the last character.
If the "off" number of an ending position is non-zero, it is
the character's number of cells included in the selection,
otherwise the whole character is included.
]=],
name = 'getregionpos',
params = { { 'pos1', 'table' }, { 'pos2', 'table' }, { 'opts', 'table' } },
returns = 'integer[][][]',
signature = 'getregionpos({pos1}, {pos2} [, {opts}])',
},
getregtype = {
args = { 0, 1 },
base = 1,

View File

@ -2823,24 +2823,24 @@ static char *block_def2str(struct block_def *bd)
return ret;
}
/// "getregion()" function
static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2,
bool *const inclusive, MotionType *region_type, oparg_T *oa)
FUNC_ATTR_NONNULL_ALL
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (tv_check_for_list_arg(argvars, 0) == FAIL
|| tv_check_for_list_arg(argvars, 1) == FAIL
|| tv_check_for_opt_dict_arg(argvars, 2) == FAIL) {
return;
return FAIL;
}
int fnum1 = -1;
int fnum2 = -1;
pos_T p1, p2;
if (list2fpos(&argvars[0], &p1, &fnum1, NULL, false) != OK
|| list2fpos(&argvars[1], &p2, &fnum2, NULL, false) != OK
if (list2fpos(&argvars[0], p1, &fnum1, NULL, false) != OK
|| list2fpos(&argvars[1], p2, &fnum2, NULL, false) != OK
|| fnum1 != fnum2) {
return;
return FAIL;
}
bool is_select_exclusive;
@ -2858,108 +2858,120 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
type = default_type;
}
MotionType region_type = kMTUnknown;
if (type[0] == 'v' && type[1] == NUL) {
region_type = kMTCharWise;
*region_type = kMTCharWise;
} else if (type[0] == 'V' && type[1] == NUL) {
region_type = kMTLineWise;
*region_type = kMTLineWise;
} else if (type[0] == Ctrl_V && type[1] == NUL) {
region_type = kMTBlockWise;
*region_type = kMTBlockWise;
} else {
semsg(_(e_invargNval), "type", type);
return;
return FAIL;
}
buf_T *findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf;
if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) {
emsg(_(e_buffer_is_not_loaded));
return;
return FAIL;
}
if (p1.lnum < 1 || p1.lnum > findbuf->b_ml.ml_line_count) {
semsg(_(e_invalid_line_number_nr), p1.lnum);
return;
if (p1->lnum < 1 || p1->lnum > findbuf->b_ml.ml_line_count) {
semsg(_(e_invalid_line_number_nr), p1->lnum);
return FAIL;
}
if (p1.col == MAXCOL) {
p1.col = ml_get_buf_len(findbuf, p1.lnum) + 1;
} else if (p1.col < 1 || p1.col > ml_get_buf_len(findbuf, p1.lnum) + 1) {
semsg(_(e_invalid_column_number_nr), p1.col);
return;
if (p1->col == MAXCOL) {
p1->col = ml_get_buf_len(findbuf, p1->lnum) + 1;
} else if (p1->col < 1 || p1->col > ml_get_buf_len(findbuf, p1->lnum) + 1) {
semsg(_(e_invalid_column_number_nr), p1->col);
return FAIL;
}
if (p2.lnum < 1 || p2.lnum > findbuf->b_ml.ml_line_count) {
semsg(_(e_invalid_line_number_nr), p2.lnum);
return;
if (p2->lnum < 1 || p2->lnum > findbuf->b_ml.ml_line_count) {
semsg(_(e_invalid_line_number_nr), p2->lnum);
return FAIL;
}
if (p2.col == MAXCOL) {
p2.col = ml_get_buf_len(findbuf, p2.lnum) + 1;
} else if (p2.col < 1 || p2.col > ml_get_buf_len(findbuf, p2.lnum) + 1) {
semsg(_(e_invalid_column_number_nr), p2.col);
return;
if (p2->col == MAXCOL) {
p2->col = ml_get_buf_len(findbuf, p2->lnum) + 1;
} else if (p2->col < 1 || p2->col > ml_get_buf_len(findbuf, p2->lnum) + 1) {
semsg(_(e_invalid_column_number_nr), p2->col);
return FAIL;
}
buf_T *const save_curbuf = curbuf;
curbuf = findbuf;
curwin->w_buffer = curbuf;
const TriState save_virtual = virtual_op;
virtual_op = virtual_active(curwin);
// NOTE: Adjust is needed.
p1.col--;
p2.col--;
// NOTE: Adjustment is needed.
p1->col--;
p2->col--;
if (!lt(p1, p2)) {
if (!lt(*p1, *p2)) {
// swap position
pos_T p = p1;
p1 = p2;
p2 = p;
pos_T p = *p1;
*p1 = *p2;
*p2 = p;
}
oparg_T oa;
bool inclusive = true;
if (region_type == kMTCharWise) {
if (*region_type == kMTCharWise) {
// 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);
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);
}
}
}
// if fp2 is on NUL (empty line) inclusive becomes false
if (*ml_get_pos(&p2) == NUL && !virtual_op) {
inclusive = false;
if (*ml_get_pos(p2) == NUL && !virtual_op) {
*inclusive = false;
}
} else if (region_type == kMTBlockWise) {
} else if (*region_type == kMTBlockWise) {
colnr_T sc1, ec1, sc2, ec2;
getvvcol(curwin, &p1, &sc1, NULL, &ec1);
getvvcol(curwin, &p2, &sc2, NULL, &ec2);
oa.motion_type = kMTBlockWise;
oa.inclusive = true;
oa.op_type = OP_NOP;
oa.start = p1;
oa.end = p2;
oa.start_vcol = MIN(sc1, sc2);
getvvcol(curwin, p1, &sc1, NULL, &ec1);
getvvcol(curwin, p2, &sc2, NULL, &ec2);
oa->motion_type = kMTBlockWise;
oa->inclusive = true;
oa->op_type = OP_NOP;
oa->start = *p1;
oa->end = *p2;
oa->start_vcol = MIN(sc1, sc2);
if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1) {
oa.end_vcol = sc2 - 1;
oa->end_vcol = sc2 - 1;
} else {
oa.end_vcol = MAX(ec1, ec2);
oa->end_vcol = MAX(ec1, ec2);
}
}
// Include the trailing byte of a multi-byte char.
int l = utfc_ptr2len(ml_get_pos(&p2));
int l = utfc_ptr2len(ml_get_pos(p2));
if (l > 1) {
p2.col += l - 1;
p2->col += l - 1;
}
return OK;
}
/// "getregion()" function
static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *const save_curbuf = curbuf;
const TriState save_virtual = virtual_op;
pos_T p1, p2;
bool inclusive = true;
MotionType region_type = kMTUnknown;
oparg_T oa;
if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, &region_type, &oa) == FAIL) {
return;
}
for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) {
@ -2983,6 +2995,88 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
tv_list_append_allocated_string(rettv->vval.v_list, akt);
}
// getregionpos() may change curbuf and virtual_op
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
virtual_op = save_virtual;
}
static void add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2)
{
list_T *l1 = tv_list_alloc(2);
tv_list_append_list(rettv->vval.v_list, l1);
list_T *l2 = tv_list_alloc(4);
tv_list_append_list(l1, l2);
list_T *l3 = tv_list_alloc(4);
tv_list_append_list(l1, l3);
int max_col1 = ml_get_len(p1.lnum);
tv_list_append_number(l2, curbuf->b_fnum);
tv_list_append_number(l2, p1.lnum);
tv_list_append_number(l2, p1.col > max_col1 ? max_col1 : p1.col);
tv_list_append_number(l2, p1.coladd);
int max_col2 = ml_get_len(p2.lnum);
tv_list_append_number(l3, curbuf->b_fnum);
tv_list_append_number(l3, p2.lnum);
tv_list_append_number(l3, p2.col > max_col2 ? max_col2 : p2.col);
tv_list_append_number(l3, p2.coladd);
}
/// "getregionpos()" function
static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *const save_curbuf = curbuf;
const TriState save_virtual = virtual_op;
pos_T p1, p2;
bool inclusive = true;
MotionType region_type = kMTUnknown;
oparg_T oa;
if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, &region_type, &oa) == FAIL) {
return;
}
for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) {
struct block_def bd;
pos_T ret_p1, ret_p2;
if (region_type == kMTLineWise) {
ret_p1.col = 1;
ret_p1.coladd = 0;
ret_p2.col = MAXCOL;
ret_p2.coladd = 0;
} else {
if (region_type == kMTBlockWise) {
block_prep(&oa, &bd, lnum, false);
} else {
charwise_block_prep(p1, p2, &bd, lnum, inclusive);
}
if (bd.startspaces > 0) {
ret_p1.col = bd.textcol;
ret_p1.coladd = bd.start_char_vcols - bd.startspaces;
} else {
ret_p1.col = bd.textcol + 1;
ret_p1.coladd = 0;
}
if (bd.endspaces > 0) {
ret_p2.col = bd.textcol + bd.textlen + 1;
ret_p2.coladd = bd.endspaces;
} else {
ret_p2.col = bd.textcol + bd.textlen;
ret_p2.coladd = 0;
}
}
ret_p1.lnum = lnum;
ret_p2.lnum = lnum;
add_regionpos_range(rettv, ret_p1, ret_p2);
}
// getregionpos() may change curbuf and virtual_op
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
virtual_op = save_virtual;

View File

@ -4258,6 +4258,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
char *p = ml_get(lnum);
bdp->startspaces = 0;
bdp->endspaces = 0;
bdp->start_char_vcols = 0;
if (lnum == start.lnum) {
startcol = start.col;
@ -4265,7 +4266,8 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
getvcol(curwin, &start, &cs, NULL, &ce);
if (ce != cs && start.coladd > 0) {
// Part of a tab selected -- but don't double-count it.
bdp->startspaces = (ce - cs + 1) - start.coladd;
bdp->start_char_vcols = ce - cs + 1;
bdp->startspaces = bdp->start_char_vcols - start.coladd;
if (bdp->startspaces < 0) {
bdp->startspaces = 0;
}
@ -4303,6 +4305,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
} else {
bdp->textlen = endcol - startcol + inclusive;
}
bdp->textcol = startcol;
bdp->textstart = p + startcol;
}

View File

@ -1644,18 +1644,52 @@ func Test_visual_getregion()
#" Visual mode
call cursor(1, 1)
call feedkeys("\<ESC>vjl", 'tx')
call assert_equal(['one', 'tw'],
\ 'v'->getpos()->getregion(getpos('.')))
call assert_equal([
\ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
\ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]]
\ ],
\ 'v'->getpos()->getregionpos(getpos('.')))
call assert_equal(['one', 'tw'],
\ '.'->getpos()->getregion(getpos('v')))
call assert_equal([
\ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
\ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]]
\ ],
\ '.'->getpos()->getregionpos(getpos('v')))
call assert_equal(['o'],
\ 'v'->getpos()->getregion(getpos('v')))
call assert_equal([
\ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]],
\ ],
\ 'v'->getpos()->getregionpos(getpos('v')))
call assert_equal(['w'],
\ '.'->getpos()->getregion(getpos('.'), {'type': 'v' }))
call assert_equal([
\ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 0]],
\ ],
\ '.'->getpos()->getregionpos(getpos('.'), {'type': 'v' }))
call assert_equal(['one', 'two'],
\ getpos('.')->getregion(getpos('v'), {'type': 'V' }))
call assert_equal([
\ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
\ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
\ ],
\ getpos('.')->getregionpos(getpos('v'), {'type': 'V' }))
call assert_equal(['on', 'tw'],
\ getpos('.')->getregion(getpos('v'), {'type': "\<C-v>" }))
call assert_equal([
\ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]],
\ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]],
\ ],
\ getpos('.')->getregionpos(getpos('v'), {'type': "\<C-v>" }))
#" Line visual mode
call cursor(1, 1)
@ -1750,9 +1784,13 @@ func Test_visual_getregion()
call assert_fails("call getregion(1, 2)", 'E1211:')
call assert_fails("call getregion(getpos('.'), {})", 'E1211:')
call assert_fails(':echo "."->getpos()->getregion("$", [])', 'E1211:')
call assert_fails("call getregionpos(1, 2)", 'E1211:')
call assert_fails("call getregionpos(getpos('.'), {})", 'E1211:')
call assert_fails(':echo "."->getpos()->getregionpos("$", [])', 'E1211:')
#" using invalid value for "type"
call assert_fails("call getregion(getpos('.'), getpos('.'), {'type': '' })", 'E475:')
call assert_fails("call getregionpos(getpos('.'), getpos('.'), {'type': '' })", 'E475:')
#" using a mark from another buffer to current buffer
new
@ -1763,13 +1801,20 @@ func Test_visual_getregion()
call assert_equal([g:buf, 10, 1, 0], getpos("'A"))
call assert_equal([], getregion(getpos('.'), getpos("'A"), {'type': 'v' }))
call assert_equal([], getregion(getpos("'A"), getpos('.'), {'type': 'v' }))
call assert_equal([], getregionpos(getpos('.'), getpos("'A"), {'type': 'v' }))
call assert_equal([], getregionpos(getpos("'A"), getpos('.'), {'type': 'v' }))
#" using two marks from another buffer
wincmd p
normal! GmB
wincmd p
call assert_equal([g:buf, 10, 1, 0], getpos("'B"))
call assert_equal(['9'], getregion(getpos("'B"), getpos("'A"), {'type': 'v' }))
call assert_equal(['9'],
\ getregion(getpos("'B"), getpos("'A"), {'type': 'v' }))
call assert_equal([
\ [[g:buf, 10, 1, 0], [g:buf, 10, 1, 0]],
\ ],
\ getregionpos(getpos("'B"), getpos("'A"), {'type': 'v' }))
#" using two positions from another buffer
for type in ['v', 'V', "\<C-V>"]
@ -1780,6 +1825,12 @@ func Test_visual_getregion()
call assert_equal(range(10)->mapnew('string(v:val)'),
\ getregion([g:buf, 10, 2, 0], [g:buf, 1, 1, 0],
\ {'type': type, 'exclusive': exclusive }))
call assert_equal(range(1, 10)->mapnew('repeat([[g:buf, v:val, 1, 0]], 2)'),
\ getregionpos([g:buf, 1, 1, 0], [g:buf, 10, 2, 0],
\ {'type': type, 'exclusive': exclusive }))
call assert_equal(range(1, 10)->mapnew('repeat([[g:buf, v:val, 1, 0]], 2)'),
\ getregionpos([g:buf, 10, 2, 0], [g:buf, 1, 1, 0],
\ {'type': type, 'exclusive': exclusive }))
endfor
endfor
@ -1792,6 +1843,8 @@ func Test_visual_getregion()
call assert_fails('call getregion([g:buf, 10, 0, 0], [g:buf, 1, 1, 0])', 'E964:')
call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 10, 3, 0])', 'E964:')
call assert_fails('call getregion([g:buf, 10, 3, 0], [g:buf, 1, 1, 0])', 'E964:')
call assert_fails('call getregion([g:buf, 1, 0, 0], [g:buf, 1, 1, 0])', 'E964:')
call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 1, 0, 0])', 'E964:')
#" using invalid buffer
call assert_fails('call getregion([10000, 10, 1, 0], [10000, 10, 1, 0])', 'E681:')
@ -1811,22 +1864,55 @@ func Test_visual_getregion()
\ "\U0001f1e6\u00ab\U0001f1e7\u00ab\U0001f1e8\u00ab\U0001f1e9",
\ "1234567890"
\ ])
call cursor(1, 3)
call feedkeys("\<Esc>\<C-v>ljj", 'xt')
call assert_equal(['cd', "\u00ab ", '34'],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
call assert_equal([
\ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 4, 0]],
\ [[bufnr('%'), 2, 5, 0], [bufnr('%'), 2, 7, 1]],
\ [[bufnr('%'), 3, 3, 0], [bufnr('%'), 3, 4, 0]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
call cursor(1, 4)
call feedkeys("\<Esc>\<C-v>ljj", 'xt')
call assert_equal(['de', "\U0001f1e7", '45'],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
call assert_equal([
\ [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 5, 0]],
\ [[bufnr('%'), 2, 7, 0], [bufnr('%'), 2, 10, 0]],
\ [[bufnr('%'), 3, 4, 0], [bufnr('%'), 3, 5, 0]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
call cursor(1, 5)
call feedkeys("\<Esc>\<C-v>jj", 'xt')
call assert_equal(['e', ' ', '5'],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
call assert_equal([
\ [[bufnr('%'), 1, 5, 0], [bufnr('%'), 1, 5, 0]],
\ [[bufnr('%'), 2, 10, 1], [bufnr('%'), 2, 10, 0]],
\ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
call assert_equal([
\ [[bufnr('%'), 1, 5, 0], [bufnr('%'), 1, 13, 0]],
\ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 22, 0]],
\ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
call cursor(1, 1)
call feedkeys("\<Esc>vj", 'xt')
call assert_equal(['abcdefghijk«', "\U0001f1e6"],
\ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
call assert_equal([
\ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 13, 0]],
\ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
#" marks on multibyte chars
:set selection=exclusive
@ -1834,12 +1920,29 @@ func Test_visual_getregion()
call setpos("'b", [0, 2, 16, 0])
call setpos("'c", [0, 2, 0, 0])
call cursor(1, 1)
call assert_equal(['ghijk', '🇨«🇩'],
\ getregion(getpos("'a"), getpos("'b"), {'type': "\<c-v>" }))
\ getregion(getpos("'a"), getpos("'b"), {'type': "\<C-v>" }))
call assert_equal([
\ [[bufnr('%'), 1, 7, 0], [bufnr('%'), 1, 11, 0]],
\ [[bufnr('%'), 2, 13, 0], [bufnr('%'), 2, 22, 0]],
\ ],
\ getregionpos(getpos("'a"), getpos("'b"), {'type': "\<C-v>" }))
call assert_equal(['k«', '🇦«🇧«🇨'],
\ getregion(getpos("'a"), getpos("'b"), {'type': 'v' }))
call assert_equal([
\ [[bufnr('%'), 1, 11, 0], [bufnr('%'), 1, 13, 0]],
\ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 16, 0]],
\ ],
\ getregionpos(getpos("'a"), getpos("'b"), {'type': 'v' }))
call assert_equal(['k«'],
\ getregion(getpos("'a"), getpos("'c"), {'type': 'v' }))
call assert_equal([
\ [[bufnr('%'), 1, 11, 0], [bufnr('%'), 1, 13, 0]],
\ ],
\ getregionpos(getpos("'a"), getpos("'c"), {'type': 'v' }))
#" use inclusive selection, although 'selection' is exclusive
call setpos("'a", [0, 1, 11, 0])
@ -1932,14 +2035,28 @@ func Test_visual_getregion()
#" virtualedit
set selection=exclusive
set virtualedit=all
call cursor(1, 1)
call feedkeys("\<Esc>2lv2lj", '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, 3]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': '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, 5], [bufnr('%'), 1, 2, 0]],
\ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 2, 0]],
\ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 2]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
set virtualedit&
set selection&
@ -1962,4 +2079,30 @@ func Test_getregion_invalid_buf()
bwipe!
endfunc
func Test_getregion_maxcol()
new
autocmd TextYankPost *
\ : if v:event.operator ==? 'y'
\ | call assert_equal([
\ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]],
\ ],
\ getregionpos(getpos("'["), getpos("']"),
\ #{ mode: visualmode() }))
\ | call assert_equal(['abcd'],
\ getregion(getpos("'["), getpos("']"),
\ #{ mode: visualmode() }))
\ | call assert_equal([
\ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]],
\ ],
\ getregionpos(getpos("']"), getpos("'["),
\ #{ mode: visualmode() }))
\ | call assert_equal(['abcd'],
\ getregion(getpos("']"), getpos("'["),
\ #{ mode: visualmode() }))
\ | endif
call setline(1, ['abcd', 'efghij'])
normal yy
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab