vim-patch:9.1.0430: getregionpos() doesn't handle one char selection (#28924)

Problem:  getregionpos() doesn't handle one char selection.
Solution: Handle startspaces differently when is_oneChar is set.
          Also add a test for an exclusive charwise selection with
          multibyte chars (zeertzjq)

closes: vim/vim#14825

52a6f34887
This commit is contained in:
zeertzjq 2024-05-23 06:08:24 +08:00 committed by GitHub
parent 3d43bdc81e
commit 5cbd6d9b9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 81 additions and 17 deletions

View File

@ -2985,8 +2985,8 @@ getregionpos({pos1}, {pos2} [, {opts}]) *getregionpos()*
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.
the offset of the character's first cell not included in the
selection, otherwise all its cells are included.
getregtype([{regname}]) *getregtype()*
The result is a String, which is type of register {regname}.

View File

@ -3596,8 +3596,8 @@ function vim.fn.getregion(pos1, pos2, opts) end
--- 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.
--- the offset of the character's first cell not included in the
--- selection, otherwise all its cells are included.
---
--- @param pos1 table
--- @param pos2 table

View File

@ -4433,8 +4433,8 @@ M.funcs = {
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.
the offset of the character's first cell not included in the
selection, otherwise all its cells are included.
]=],
name = 'getregionpos',
params = { { 'pos1', 'table' }, { 'pos2', 'table' }, { 'opts', 'table' } },

View File

@ -3041,7 +3041,6 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
}
for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) {
struct block_def bd;
pos_T ret_p1, ret_p2;
if (region_type == kMTLineWise) {
@ -3050,19 +3049,34 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
ret_p2.col = MAXCOL;
ret_p2.coladd = 0;
} else {
struct block_def bd;
if (region_type == kMTBlockWise) {
block_prep(&oa, &bd, lnum, false);
} else {
charwise_block_prep(p1, p2, &bd, lnum, inclusive);
}
if (bd.startspaces > 0) {
if (bd.is_oneChar) { // selection entirely inside one char
if (region_type == kMTBlockWise) {
ret_p1.col = bd.textcol;
ret_p1.coladd = bd.start_char_vcols - (bd.start_vcol - oa.start_vcol);
} else {
ret_p1.col = p1.col + 1;
ret_p1.coladd = p1.coladd;
}
} else 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) {
if (bd.is_oneChar) { // selection entirely inside one char
ret_p2.col = ret_p1.col;
ret_p2.coladd = ret_p1.coladd + bd.startspaces;
} else if (bd.endspaces > 0) {
ret_p2.col = bd.textcol + bd.textlen + 1;
ret_p2.coladd = bd.endspaces;
} else {

View File

@ -4253,11 +4253,12 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
{
colnr_T startcol = 0;
colnr_T endcol = MAXCOL;
bool is_oneChar = false;
colnr_T cs, ce;
char *p = ml_get(lnum);
bdp->startspaces = 0;
bdp->endspaces = 0;
bdp->is_oneChar = false;
bdp->start_char_vcols = 0;
if (lnum == start.lnum) {
@ -4287,7 +4288,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
&& utf_head_off(p, p + endcol) == 0)) {
if (start.lnum == end.lnum && start.col == end.col) {
// Special case: inside a single char
is_oneChar = true;
bdp->is_oneChar = true;
bdp->startspaces = end.coladd - start.coladd + inclusive;
endcol = startcol;
} else {
@ -4300,7 +4301,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
if (endcol == MAXCOL) {
endcol = ml_get_len(lnum);
}
if (startcol > endcol || is_oneChar) {
if (startcol > endcol || bdp->is_oneChar) {
bdp->textlen = 0;
} else {
bdp->textlen = endcol - startcol + inclusive;

View File

@ -1893,7 +1893,7 @@ func Test_visual_getregion()
\ 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('%'), 2, 10, 1], [bufnr('%'), 2, 10, 2]],
\ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
@ -1904,6 +1904,7 @@ func Test_visual_getregion()
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
#" characterwise selection with multibyte chars
call cursor(1, 1)
call feedkeys("\<Esc>vj", 'xt')
call assert_equal(['abcdefghijk«', "\U0001f1e6"],
@ -1914,8 +1915,17 @@ func Test_visual_getregion()
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
set selection=exclusive
call feedkeys('l', '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
call setpos("'a", [0, 1, 11, 0])
call setpos("'b", [0, 2, 16, 0])
call setpos("'c", [0, 2, 0, 0])
@ -2001,6 +2011,7 @@ func Test_visual_getregion()
call assert_equal(["c", "x\tz"],
\ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
set selection&
bwipe!
#" Exclusive selection 2
new
@ -2037,7 +2048,24 @@ func Test_visual_getregion()
set virtualedit=all
call cursor(1, 1)
call feedkeys("\<Esc>2lv2lj", 'xt')
call feedkeys("\<Esc>lv2l", 'xt')
call assert_equal([' '],
\ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
call assert_equal([
\ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 2]],
\ ],
\ 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, 3]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
call feedkeys('j', 'xt')
call assert_equal([' c', 'x '],
\ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
call assert_equal([
@ -2046,13 +2074,34 @@ func Test_visual_getregion()
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
call cursor(1, 1)
call feedkeys("\<Esc>6l\<C-v>2lj", 'xt')
call assert_equal([' ', ' '],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
call assert_equal([
\ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 2, 7]],
\ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 2, 7]],
\ ],
\ 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, 2]],
\ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 2]],
\ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 2]],
\ ],
\ 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, 5], [bufnr('%'), 1, 2, 0]],
\ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 2, 0]],
\ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 3]],
\ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 3]],
\ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 2]],
\ ],
\ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))