0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 8.2.5019: cannot get the first screen column of a character

Problem:    Cannot get the first screen column of a character.
Solution:   Let virtcol() optionally return a list. (closes #10482,
            closes #7964)
This commit is contained in:
LemonBoy 2022-05-26 12:10:37 +01:00 committed by Bram Moolenaar
parent c3caa7f788
commit 0f7a3e1de6
5 changed files with 96 additions and 26 deletions

View File

@ -689,7 +689,8 @@ undotree() List undo file tree
uniq({list} [, {func} [, {dict}]]) uniq({list} [, {func} [, {dict}]])
List remove adjacent duplicates from a list List remove adjacent duplicates from a list
values({dict}) List values in {dict} values({dict}) List values in {dict}
virtcol({expr}) Number screen column of cursor or mark virtcol({expr} [, {list}]) Number or List
screen column of cursor or mark
visualmode([expr]) String last visual mode used visualmode([expr]) String last visual mode used
wildmenumode() Number whether 'wildmenu' mode is active wildmenumode() Number whether 'wildmenu' mode is active
win_execute({id}, {command} [, {silent}]) win_execute({id}, {command} [, {silent}])
@ -780,6 +781,7 @@ add({object}, {expr}) *add()*
and({expr}, {expr}) *and()* and({expr}, {expr}) *and()*
Bitwise AND on the two arguments. The arguments are converted Bitwise AND on the two arguments. The arguments are converted
to a number. A List, Dict or Float argument causes an error. to a number. A List, Dict or Float argument causes an error.
Also see `or()` and `xor()`.
Example: > Example: >
:let flag = and(bits, 0x80) :let flag = and(bits, 0x80)
< Can also be used as a |method|: > < Can also be used as a |method|: >
@ -936,13 +938,14 @@ autocmd_add({acmds}) *autocmd_add()*
item is ignored. item is ignored.
cmd Ex command to execute for this autocmd event cmd Ex command to execute for this autocmd event
event autocmd event name. Refer to |autocmd-events|. event autocmd event name. Refer to |autocmd-events|.
TODO: currently only accepts one event.
group autocmd group name. Refer to |autocmd-groups|. group autocmd group name. Refer to |autocmd-groups|.
If this group doesn't exist then it is If this group doesn't exist then it is
created. If not specified or empty, then the created. If not specified or empty, then the
default group is used. default group is used.
nested boolean flag, set to v:true to add a nested nested boolean flag, set to v:true to add a nested
autocmd. Refer to |autocmd-nested|. autocmd. Refer to |autocmd-nested|.
once boolean flag, set to v:true to add a autocmd once boolean flag, set to v:true to add an autocmd
which executes only once. Refer to which executes only once. Refer to
|autocmd-once|. |autocmd-once|.
pattern autocmd pattern string. Refer to pattern autocmd pattern string. Refer to
@ -952,7 +955,7 @@ autocmd_add({acmds}) *autocmd_add()*
commands associated with the specified autocmd commands associated with the specified autocmd
event and group and add the {cmd}. This is event and group and add the {cmd}. This is
useful to avoid adding the same command useful to avoid adding the same command
multiple times for a autocmd event in a group. multiple times for an autocmd event in a group.
Returns v:true on success and v:false on failure. Returns v:true on success and v:false on failure.
Examples: > Examples: >
@ -9727,7 +9730,7 @@ values({dict}) *values()*
Can also be used as a |method|: > Can also be used as a |method|: >
mydict->values() mydict->values()
virtcol({expr}) *virtcol()* virtcol({expr} [, {list}]) *virtcol()*
The result is a Number, which is the screen column of the file The result is a Number, which is the screen column of the file
position given with {expr}. That is, the last screen position position given with {expr}. That is, the last screen position
occupied by the character at that position, when the screen occupied by the character at that position, when the screen
@ -9736,13 +9739,17 @@ virtcol({expr}) *virtcol()*
the <Tab>. For example, for a <Tab> in column 1, with 'ts' the <Tab>. For example, for a <Tab> in column 1, with 'ts'
set to 8, it returns 8. |conceal| is ignored. set to 8, it returns 8. |conceal| is ignored.
For the byte position use |col()|. For the byte position use |col()|.
For the use of {expr} see |col()|. For the use of {expr} see |col()|.
When 'virtualedit' is used {expr} can be [lnum, col, off], where
"off" is the offset in screen columns from the start of the When 'virtualedit' is used {expr} can be [lnum, col, off],
character. E.g., a position within a <Tab> or after the last where "off" is the offset in screen columns from the start of
character. When "off" is omitted zero is used. the character. E.g., a position within a <Tab> or after the
When Virtual editing is active in the current mode, a position last character. When "off" is omitted zero is used. When
beyond the end of the line can be returned. |'virtualedit'| Virtual editing is active in the current mode, a position
beyond the end of the line can be returned. Also see
|'virtualedit'|
The accepted positions are: The accepted positions are:
. the cursor position . the cursor position
$ the end of the cursor line (the result is the $ the end of the cursor line (the result is the
@ -9754,11 +9761,22 @@ virtcol({expr}) *virtcol()*
cursor is the end). When not in Visual mode cursor is the end). When not in Visual mode
returns the cursor position. Differs from |'<| in returns the cursor position. Differs from |'<| in
that it's updated right away. that it's updated right away.
If {list} is present and non-zero then virtcol() returns a List
with the first and last screen position occupied by the
character.
Note that only marks in the current file can be used. Note that only marks in the current file can be used.
Examples: > Examples: >
virtcol(".") with text "foo^Lbar", with cursor on the "^L", returns 5 " With text "foo^Lbar" and cursor on the "^L":
virtcol("$") with text "foo^Lbar", returns 9
virtcol("'t") with text " there", with 't at 'h', returns 6 virtcol(".") " returns 5
virtcol(".", 1) " returns [4, 5]
virtcol("$") " returns 9
" With text " there", with 't at 'h':
virtcol("'t") " returns 6
< The first column is 1. 0 is returned for an error. < The first column is 1. 0 is returned for an error.
A more advanced example that echoes the maximum length of A more advanced example that echoes the maximum length of
all lines: > all lines: >

View File

@ -994,6 +994,7 @@ static argcheck_T arg2_string_dict[] = {arg_string, arg_dict_any};
static argcheck_T arg2_string_list_number[] = {arg_string, arg_list_number}; static argcheck_T arg2_string_list_number[] = {arg_string, arg_list_number};
static argcheck_T arg2_string_number[] = {arg_string, arg_number}; static argcheck_T arg2_string_number[] = {arg_string, arg_number};
static argcheck_T arg2_string_or_list_dict[] = {arg_string_or_list_any, arg_dict_any}; static argcheck_T arg2_string_or_list_dict[] = {arg_string_or_list_any, arg_dict_any};
static argcheck_T arg2_string_or_list_bool[] = {arg_string_or_list_any, arg_bool};
static argcheck_T arg2_string_string_or_number[] = {arg_string, arg_string_or_nr}; static argcheck_T arg2_string_string_or_number[] = {arg_string, arg_string_or_nr};
static argcheck_T arg3_any_list_dict[] = {NULL, arg_list_any, arg_dict_any}; static argcheck_T arg3_any_list_dict[] = {NULL, arg_list_any, arg_dict_any};
static argcheck_T arg3_buffer_lnum_lnum[] = {arg_buffer, arg_lnum, arg_lnum}; static argcheck_T arg3_buffer_lnum_lnum[] = {arg_buffer, arg_lnum, arg_lnum};
@ -1457,6 +1458,20 @@ ret_getreg(int argcount,
return &t_string; return &t_string;
} }
static type_T *
ret_virtcol(int argcount,
type2_T *argtypes UNUSED,
type_T **decl_type)
{
// Assume that if the second argument is passed it's non-zero
if (argcount == 2)
{
*decl_type = &t_list_any;
return &t_list_number;
}
return &t_number;
}
static type_T * static type_T *
ret_maparg(int argcount, ret_maparg(int argcount,
type2_T *argtypes UNUSED, type2_T *argtypes UNUSED,
@ -2665,8 +2680,8 @@ static funcentry_T global_functions[] =
ret_first_arg, f_uniq}, ret_first_arg, f_uniq},
{"values", 1, 1, FEARG_1, arg1_dict_any, {"values", 1, 1, FEARG_1, arg1_dict_any,
ret_list_any, f_values}, ret_list_any, f_values},
{"virtcol", 1, 1, FEARG_1, arg1_string_or_list_any, {"virtcol", 1, 2, FEARG_1, arg2_string_or_list_bool,
ret_number, f_virtcol}, ret_virtcol, f_virtcol},
{"visualmode", 0, 1, 0, arg1_bool, {"visualmode", 0, 1, 0, arg1_bool,
ret_string, f_visualmode}, ret_string, f_visualmode},
{"wildmenumode", 0, 0, 0, NULL, {"wildmenumode", 0, 0, 0, NULL,
@ -10380,23 +10395,26 @@ f_type(typval_T *argvars, typval_T *rettv)
} }
/* /*
* "virtcol(string)" function * "virtcol(string, bool)" function
*/ */
static void static void
f_virtcol(typval_T *argvars, typval_T *rettv) f_virtcol(typval_T *argvars, typval_T *rettv)
{ {
colnr_T vcol = 0; colnr_T vcol_start = 0;
colnr_T vcol_end = 0;
pos_T *fp; pos_T *fp;
int fnum = curbuf->b_fnum; int fnum = curbuf->b_fnum;
int len; int len;
if (in_vim9script() if (in_vim9script()
&& check_for_string_or_list_arg(argvars, 0) == FAIL) && (check_for_string_or_list_arg(argvars, 0) == FAIL
|| (argvars[1].v_type != VAR_UNKNOWN
&& check_for_bool_arg(argvars, 1) == FAIL)))
return; return;
fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE); fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum) && fnum == curbuf->b_fnum)
{ {
// Limit the column to a valid value, getvvcol() doesn't check. // Limit the column to a valid value, getvvcol() doesn't check.
if (fp->col < 0) if (fp->col < 0)
@ -10407,11 +10425,23 @@ f_virtcol(typval_T *argvars, typval_T *rettv)
if (fp->col > len) if (fp->col > len)
fp->col = len; fp->col = len;
} }
getvvcol(curwin, fp, NULL, NULL, &vcol); getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end);
++vcol; ++vcol_start;
++vcol_end;
} }
rettv->vval.v_number = vcol; if (argvars[1].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[1]))
{
if (rettv_list_alloc(rettv) == OK)
{
list_append_number(rettv->vval.v_list, vcol_start);
list_append_number(rettv->vval.v_list, vcol_end);
}
else
rettv->vval.v_number = 0;
}
else
rettv->vval.v_number = vcol_end;
} }
/* /*

View File

@ -2947,4 +2947,15 @@ func Test_exepath()
endif endif
endfunc endfunc
" Test for virtcol()
func Test_virtcol()
enew!
call setline(1, "the\tquick\tbrown\tfox")
norm! 4|
call assert_equal(8, virtcol('.'))
call assert_equal(8, virtcol('.', v:false))
call assert_equal([4, 8], virtcol('.', v:true))
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -4494,14 +4494,23 @@ def Test_values()
enddef enddef
def Test_virtcol() def Test_virtcol()
v9.CheckDefAndScriptFailure(['virtcol(1.1)'], ['E1013: Argument 1: type mismatch, expected string but got float', 'E1222: String or List required for argument 1']) v9.CheckDefAndScriptFailure(['virtcol(1.1)'], [
v9.CheckDefExecAndScriptFailure(['virtcol("")'], 'E1209: Invalid value for a line number') 'E1013: Argument 1: type mismatch, expected string but got float',
'E1222: String or List required for argument 1'])
v9.CheckDefAndScriptFailure(['virtcol(".", "a")'], [
'E1013: Argument 2: type mismatch, expected bool but got string',
'E1212: Bool required for argument 2'])
v9.CheckDefExecAndScriptFailure(['virtcol("")'],
'E1209: Invalid value for a line number')
new new
setline(1, ['abcdefgh']) setline(1, ['abcde和平fgh'])
cursor(1, 4) cursor(1, 4)
assert_equal(4, virtcol('.')) assert_equal(4, virtcol('.'))
assert_equal([4, 4], virtcol('.', 1))
cursor(1, 6)
assert_equal([6, 7], virtcol('.', 1))
assert_equal(4, virtcol([1, 4])) assert_equal(4, virtcol([1, 4]))
assert_equal(9, virtcol([1, '$'])) assert_equal(13, virtcol([1, '$']))
assert_equal(0, virtcol([10, '$'])) assert_equal(0, virtcol([10, '$']))
bw! bw!
enddef enddef

View File

@ -734,6 +734,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 */
/**/
5019,
/**/ /**/
5018, 5018,
/**/ /**/