0
0
mirror of https://github.com/vim/vim.git synced 2025-07-25 10:54:51 -04:00

patch 8.2.2324: not easy to get mark en cursor posotion by character count

Problem:    Not easy to get mark en cursor posotion by character count.
Solution:   Add functions that use character index. (Yegappan Lakshmanan,
            closes #7648)
This commit is contained in:
Bram Moolenaar 2021-01-10 20:22:54 +01:00
parent 31a11b942a
commit 6f02b00bb0
9 changed files with 668 additions and 190 deletions

View File

@ -1,4 +1,4 @@
*eval.txt* For Vim version 8.2. Last change: 2020 Dec 29
*eval.txt* For Vim version 8.2. Last change: 2021 Jan 10
VIM REFERENCE MANUAL by Bram Moolenaar
@ -49,7 +49,7 @@ There are ten types of variables:
*Number* *Integer*
Number A 32 or 64 bit signed number. |expr-number|
The number of bits is available in |v:numbersize|.
Examples: -123 0x10 0177 0b1011
Examples: -123 0x10 0177 0o177 0b1011
Float A floating point number. |floating-point-format| *Float*
{only when compiled with the |+float| feature}
@ -97,9 +97,10 @@ the Number. Examples:
Conversion from a String to a Number only happens in legacy Vim script, not in
Vim9 script. It is done by converting the first digits to a number.
Hexadecimal "0xf9", Octal "017" or "0o17", and Binary "0b10"
numbers are recognized (NOTE: when using |scriptversion-4| octal with a
leading "0" is not recognized). If the String doesn't start with digits, the
result is zero.
numbers are recognized
NOTE: when using |scriptversion-4| octal with a leading "0" is not recognized.
The 0o notation requires patch 8.2.0886.
If the String doesn't start with digits, the result is zero.
Examples:
String "456" --> Number 456 ~
String "6bar" --> Number 6 ~
@ -1150,7 +1151,7 @@ expr7 *expr7*
For '!' |TRUE| becomes |FALSE|, |FALSE| becomes |TRUE| (one).
For '-' the sign of the number is changed.
For '+' the number is unchanged.
For '+' the number is unchanged. Note: "++" has no effect.
A String will be converted to a Number first.
@ -1191,6 +1192,7 @@ start with one!
If the length of the String is less than the index, the result is an empty
String. A negative index always results in an empty string (reason: backward
compatibility). Use [-1:] to get the last byte or character.
In Vim9 script a negative index is used like with a list: count from the end.
If expr8 is a |List| then it results the item at index expr1. See |list-index|
for possible index values. If the index is out of range this results in an
@ -1318,8 +1320,8 @@ When using the lambda form there must be no white space between the } and the
number
------
number number constant *expr-number*
*hex-number* *octal-number* *binary-number*
*0x* *hex-number* *0o* *octal-number* *binary-number*
Decimal, Hexadecimal (starting with 0x or 0X), Binary (starting with 0b or 0B)
and Octal (starting with 0, 0o or 0O).
@ -1572,7 +1574,7 @@ Note how execute() is used to execute an Ex command. That's ugly though.
Lambda expressions have internal names like '<lambda>42'. If you get an error
for a lambda expression, you can find what it is with the following command: >
:function {'<lambda>42'}
:function <lambda>42
See also: |numbered-function|
==============================================================================
@ -2475,12 +2477,13 @@ ch_status({handle} [, {options}])
changenr() Number current change number
char2nr({expr} [, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
charclass({string}) Number character class of {string}
charcol({expr}) Number column number of cursor or mark
charidx({string}, {idx} [, {countcc}])
Number char index of byte {idx} in {string}
chdir({dir}) String change current working directory
cindent({lnum}) Number C indent for line {lnum}
clearmatches([{win}]) none clear all matches
col({expr}) Number column nr of cursor or mark
col({expr}) Number column byte index of cursor or mark
complete({startcol}, {matches}) none set Insert mode completion
complete_add({expr}) Number add completion match
complete_check() Number check for key typed during completion
@ -2558,6 +2561,7 @@ getbufvar({expr}, {varname} [, {def}])
getchangelist([{expr}]) List list of change list items
getchar([expr]) Number get one character from the user
getcharmod() Number modifiers for the last typed character
getcharpos({expr}) List position of cursor, mark, etc.
getcharsearch() Dict last character search
getcmdline() String return the current command-line
getcmdpos() Number return cursor position in command-line
@ -2566,6 +2570,7 @@ getcmdwintype() String return current command-line window type
getcompletion({pat}, {type} [, {filtered}])
List list of cmdline completion matches
getcurpos([{winnr}]) List position of the cursor
getcursorcharpos([{winnr}]) List character position of the cursor
getcwd([{winnr} [, {tabnr}]]) String get the current working directory
getenv({name}) String return environment variable
getfontname([{name}]) String name of font being used
@ -2828,8 +2833,10 @@ setbufline({expr}, {lnum}, {text})
setbufvar({expr}, {varname}, {val})
none set {varname} in buffer {expr} to {val}
setcellwidths({list}) none set character cell width overrides
setcharpos({expr}, {list}) Number set the {expr} position to {list}
setcharsearch({dict}) Dict set character search from {dict}
setcmdpos({pos}) Number set cursor position in command-line
setcursorcharpos({list}) Number move cursor to position in {list}
setenv({name}, {val}) none set environment variable
setfperm({fname}, {mode}) Number set {fname} file permissions to {mode}
setline({lnum}, {line}) Number set line {lnum} to {line}
@ -3513,8 +3520,8 @@ byteidxcomp({expr}, {nr}) *byteidxcomp()*
< The first and third echo result in 3 ('e' plus composing
character is 3 bytes), the second echo results in 1 ('e' is
one byte).
Only works differently from byteidx() when 'encoding' is set to
a Unicode encoding.
Only works differently from byteidx() when 'encoding' is set
to a Unicode encoding.
Can also be used as a |method|: >
GetName()->byteidxcomp(idx)
@ -3590,6 +3597,18 @@ charclass({string}) *charclass()*
other specific Unicode class
The class is used in patterns and word motions.
*charcol()*
charcol({expr}) Same as |col()| but returns the character index of the column
position given with {expr} instead of the byte position.
Example:
With the cursor on '세' in line 5 with text "여보세요": >
charcol('.') returns 3
col('.') returns 7
< Can also be used as a |method|: >
GetPos()->col()
<
*charidx()*
charidx({string}, {idx} [, {countcc}])
Return the character index of the byte at {idx} in {string}.
@ -3680,7 +3699,8 @@ col({expr}) The result is a Number, which is the byte index of the column
out of range then col() returns zero.
To get the line number use |line()|. To get both use
|getpos()|.
For the screen column position use |virtcol()|.
For the screen column position use |virtcol()|. For the
character position use |charcol()|.
Note that only marks in the current file can be used.
Examples: >
col(".") column of cursor
@ -3981,6 +4001,9 @@ cursor({list})
This is like the return value of |getpos()| or |getcurpos()|,
but without the first item.
To position the cursor using the character count, use
|setcursorcharpos()|.
Does not change the jumplist.
If {lnum} is greater than the number of lines in the buffer,
the cursor will be positioned at the last line in the buffer.
@ -5220,6 +5243,20 @@ getcharmod() *getcharmod()*
character itself are obtained. Thus Shift-a results in "A"
without a modifier.
*getcharpos()*
getcharpos({expr})
Get the position for {expr}. Same as |getpos()| but the column
number in the returned List is a character index instead of
a byte index.
Example:
With the cursor on '세' in line 5 with text "여보세요": >
getcharpos('.') returns [0, 5, 3, 0]
getpos('.') returns [0, 5, 7, 0]
<
Can also be used as a |method|: >
GetMark()->getcharpos()
getcharsearch() *getcharsearch()*
Return the current character search information as a {dict}
with the following entries:
@ -5345,8 +5382,11 @@ getcurpos([{winid}])
includes an extra "curswant" item in the list:
[0, lnum, col, off, curswant] ~
The "curswant" number is the preferred column when moving the
cursor vertically. Also see |getpos()|.
The first "bufnum" item is always zero.
cursor vertically. Also see |getcursorcharpos()| and
|getpos()|.
The first "bufnum" item is always zero. The byte position of
the cursor is returned in 'col'. To get the character
position, use |getcursorcharpos()|.
The optional {winid} argument can specify the window. It can
be the window number or the |window-ID|. The last known
@ -5360,7 +5400,24 @@ getcurpos([{winid}])
call setpos('.', save_cursor)
< Note that this only works within the window. See
|winrestview()| for restoring more state.
*getcwd()*
Can also be used as a |method|: >
GetWinid()->getcurpos()
< *getcursorcharpos()*
getcursorcharpos([{winid}])
Same as |getcurpos()| but the column number in the returned
List is a character index instead of a byte index.
Example:
With the cursor on '보' in line 3 with text "여보세요": >
getcursorcharpos() returns [0, 3, 2, 0, 3]
getcurpos() returns [0, 3, 4, 0, 3]
< Can also be used as a |method|: >
GetWinid()->getcursorcharpos()
< *getcwd()*
getcwd([{winnr} [, {tabnr}]])
The result is a String, which is the name of the current
working directory.
@ -5667,16 +5724,18 @@ getpos({expr}) Get the position for {expr}. For possible values of {expr}
Note that for '< and '> Visual mode matters: when it is "V"
(visual line mode) the column of '< is zero and the column of
'> is a large number.
The column number in the returned List is the byte position
within the line. To get the character position in the line,
use |getcharpos()|
This can be used to save and restore the position of a mark: >
let save_a_mark = getpos("'a")
...
call setpos("'a", save_a_mark)
< Also see |getcurpos()| and |setpos()|.
< Also see |getcharpos()|, |getcurpos()| and |setpos()|.
Can also be used as a |method|: >
GetMark()->getpos()
getqflist([{what}]) *getqflist()*
Returns a |List| with all the current quickfix errors. Each
list item is a dictionary with these entries:
@ -7542,8 +7601,10 @@ matchstrpos({expr}, {pat} [, {start} [, {count}]]) *matchstrpos()*
<
*max()*
max({expr}) Return the maximum value of all items in {expr}.
{expr} can be a |List| or a |Dictionary|. For a Dictionary,
max({expr}) Return the maximum value of all items in {expr}. Example: >
echo max([apples, pears, oranges])
< {expr} can be a |List| or a |Dictionary|. For a Dictionary,
it returns the maximum of all values in the Dictionary.
If {expr} is neither a List nor a Dictionary, or one of the
items in {expr} cannot be used as a Number this results in
@ -7613,8 +7674,10 @@ menu_info({name} [, {mode}]) *menu_info()*
< *min()*
min({expr}) Return the minimum value of all items in {expr}.
{expr} can be a |List| or a |Dictionary|. For a Dictionary,
min({expr}) Return the minimum value of all items in {expr}. Example: >
echo min([apples, pears, oranges])
< {expr} can be a |List| or a |Dictionary|. For a Dictionary,
it returns the minimum of all values in the Dictionary.
If {expr} is neither a List nor a Dictionary, or one of the
items in {expr} cannot be used as a Number this results in
@ -7631,13 +7694,13 @@ mkdir({name} [, {path} [, {prot}]])
necessary. Otherwise it must be "".
If {prot} is given it is used to set the protection bits of
the new directory. The default is 0755 (rwxr-xr-x: r/w for
the user readable for others). Use 0700 to make it unreadable
for others. This is only used for the last part of {name}.
Thus if you create /tmp/foo/bar then /tmp/foo will be created
with 0755.
the new directory. The default is 0o755 (rwxr-xr-x: r/w for
the user, readable for others). Use 0o700 to make it
unreadable for others. This is only used for the last part of
{name}. Thus if you create /tmp/foo/bar then /tmp/foo will be
created with 0o755.
Example: >
:call mkdir($HOME . "/tmp/foo/bar", "p", 0700)
:call mkdir($HOME . "/tmp/foo/bar", "p", 0o700)
< This function is not available in the |sandbox|.
@ -9200,6 +9263,19 @@ setcellwidths({list}) *setcellwidths()*
< You can use the script $VIMRUNTIME/tools/emoji_list.vim to see
the effect for known emoji characters.
setcharpos({expr}, {list}) *setcharpos()*
Same as |setpos()| but uses the specified column number as the
character index instead of the byte index in the line.
Example:
With the text "여보세요" in line 8: >
call setcharpos('.', [0, 8, 4, 0])
< positions the cursor on the fourth character '요'. >
call setpos('.', [0, 8, 4, 0])
< positions the cursor on the second character '보'.
Can also be used as a |method|: >
GetPosition()->setcharpos('.')
setcharsearch({dict}) *setcharsearch()*
Set the current character search information to {dict},
@ -9242,6 +9318,21 @@ setcmdpos({pos}) *setcmdpos()*
Can also be used as a |method|: >
GetPos()->setcmdpos()
setcursorcharpos({lnum}, {col} [, {off}]) *setcursorcharpos()*
setcursorcharpos({list})
Same as |cursor()| but uses the specified column number as the
character index instead of the byte index in the line.
Example:
With the text "여보세요" in line 4: >
call setcursorcharpos(4, 3)
< positions the cursor on the third character '세'. >
call cursor(4, 3)
< positions the cursor on the first character '여'.
Can also be used as a |method|: >
GetCursorPos()->setcursorcharpos()
setenv({name}, {val}) *setenv()*
Set environment variable {name} to {val}.
When {val} is |v:null| the environment variable is deleted.
@ -9353,7 +9444,8 @@ setpos({expr}, {list})
"lnum" and "col" are the position in the buffer. The first
column is 1. Use a zero "lnum" to delete a mark. If "col" is
smaller than 1 then 1 is used.
smaller than 1 then 1 is used. To use the character count
instead of the byte count, use |setcharpos()|.
The "off" number is only used when 'virtualedit' is set. Then
it is the offset in screen columns from the start of the
@ -9373,7 +9465,7 @@ setpos({expr}, {list})
Returns 0 when the position could be set, -1 otherwise.
An error message is given if {expr} is invalid.
Also see |getpos()| and |getcurpos()|.
Also see |setcharpos()|, |getpos()| and |getcurpos()|.
This does not restore the preferred column for moving
vertically; if you set the cursor position with this, |j| and

View File

@ -753,6 +753,11 @@ Cursor and mark position: *cursor-functions* *mark-functions*
screenchar() get character code at a screen line/row
screenchars() get character codes at a screen line/row
screenstring() get string of characters at a screen line/row
charcol() character number of the cursor or a mark
getcharpos() get character position of cursor, mark, etc.
setcharpos() set character position of cursor, mark, etc.
getcursorcharpos() get character position of the cursor
setcursorcharpos() set character position of the cursor
Working with text in the current buffer: *text-functions*
getline() get a line or list of lines from the buffer

View File

@ -5053,6 +5053,61 @@ string2float(
}
#endif
/*
* Convert the specified byte index of line 'lnum' in buffer 'buf' to a
* character index. Works only for loaded buffers. Returns -1 on failure.
* The index of the first character is one.
*/
int
buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx)
{
char_u *str;
if (buf == NULL || buf->b_ml.ml_mfp == NULL)
return -1;
if (lnum > buf->b_ml.ml_line_count)
lnum = buf->b_ml.ml_line_count;
str = ml_get_buf(buf, lnum, FALSE);
if (str == NULL)
return -1;
if (*str == NUL)
return 1;
return mb_charlen_len(str, byteidx + 1);
}
/*
* Convert the specified character index of line 'lnum' in buffer 'buf' to a
* byte index. Works only for loaded buffers. Returns -1 on failure. The index
* of the first byte and the first character is one.
*/
int
buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx)
{
char_u *str;
char_u *t;
if (buf == NULL || buf->b_ml.ml_mfp == NULL)
return -1;
if (lnum > buf->b_ml.ml_line_count)
lnum = buf->b_ml.ml_line_count;
str = ml_get_buf(buf, lnum, FALSE);
if (str == NULL)
return -1;
// Convert the character offset to a byte offset
t = str;
while (*t != NUL && --charidx > 0)
t += mb_ptr2len(t);
return t - str + 1;
}
/*
* Translate a String variable into a position.
* Returns NULL when there is an error.
@ -5061,7 +5116,8 @@ string2float(
var2fpos(
typval_T *varp,
int dollar_lnum, // TRUE when $ is last line
int *fnum) // set to fnum for '0, 'A, etc.
int *fnum, // set to fnum for '0, 'A, etc.
int charcol) // return character column
{
char_u *name;
static pos_T pos;
@ -5083,7 +5139,10 @@ var2fpos(
pos.lnum = list_find_nr(l, 0L, &error);
if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count)
return NULL; // invalid line number
len = (long)STRLEN(ml_get(pos.lnum));
if (charcol)
len = (long)mb_charlen(ml_get(pos.lnum));
else
len = (long)STRLEN(ml_get(pos.lnum));
// Get the column number
// We accept "$" for the column number: last column.
@ -5118,18 +5177,29 @@ var2fpos(
if (name == NULL)
return NULL;
if (name[0] == '.') // cursor
return &curwin->w_cursor;
{
pos = curwin->w_cursor;
if (charcol)
pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1;
return &pos;
}
if (name[0] == 'v' && name[1] == NUL) // Visual start
{
if (VIsual_active)
return &VIsual;
return &curwin->w_cursor;
pos = VIsual;
else
pos = curwin->w_cursor;
if (charcol)
pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1;
return &pos;
}
if (name[0] == '\'') // mark
{
pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum);
if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0)
return NULL;
if (charcol)
pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col) - 1;
return pp;
}
@ -5164,7 +5234,10 @@ var2fpos(
else
{
pos.lnum = curwin->w_cursor.lnum;
pos.col = (colnr_T)STRLEN(ml_get_curline());
if (charcol)
pos.col = (colnr_T)mb_charlen(ml_get_curline());
else
pos.col = (colnr_T)STRLEN(ml_get_curline());
}
return &pos;
}
@ -5184,7 +5257,8 @@ list2fpos(
typval_T *arg,
pos_T *posp,
int *fnump,
colnr_T *curswantp)
colnr_T *curswantp,
int charcol)
{
list_T *l = arg->vval.v_list;
long i = 0;
@ -5216,6 +5290,18 @@ list2fpos(
n = list_find_nr(l, i++, NULL); // col
if (n < 0)
return FAIL;
// If character position is specified, then convert to byte position
if (charcol)
{
buf_T *buf;
// Get the text for the specified line in a loaded buffer
buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump);
if (buf == NULL || buf->b_ml.ml_mfp == NULL)
return FAIL;
n = buf_charidx_to_byteidx(buf, posp->lnum, n);
}
posp->col = n;
n = list_find_nr(l, i, NULL); // off

View File

@ -47,6 +47,7 @@ static void f_ceil(typval_T *argvars, typval_T *rettv);
#endif
static void f_changenr(typval_T *argvars, typval_T *rettv);
static void f_char2nr(typval_T *argvars, typval_T *rettv);
static void f_charcol(typval_T *argvars, typval_T *rettv);
static void f_charidx(typval_T *argvars, typval_T *rettv);
static void f_col(typval_T *argvars, typval_T *rettv);
static void f_confirm(typval_T *argvars, typval_T *rettv);
@ -87,12 +88,14 @@ static void f_function(typval_T *argvars, typval_T *rettv);
static void f_garbagecollect(typval_T *argvars, typval_T *rettv);
static void f_get(typval_T *argvars, typval_T *rettv);
static void f_getchangelist(typval_T *argvars, typval_T *rettv);
static void f_getcharpos(typval_T *argvars, typval_T *rettv);
static void f_getcharsearch(typval_T *argvars, typval_T *rettv);
static void f_getenv(typval_T *argvars, typval_T *rettv);
static void f_getfontname(typval_T *argvars, typval_T *rettv);
static void f_getjumplist(typval_T *argvars, typval_T *rettv);
static void f_getpid(typval_T *argvars, typval_T *rettv);
static void f_getcurpos(typval_T *argvars, typval_T *rettv);
static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv);
static void f_getpos(typval_T *argvars, typval_T *rettv);
static void f_getreg(typval_T *argvars, typval_T *rettv);
static void f_getreginfo(typval_T *argvars, typval_T *rettv);
@ -190,7 +193,9 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv);
static void f_searchpair(typval_T *argvars, typval_T *rettv);
static void f_searchpairpos(typval_T *argvars, typval_T *rettv);
static void f_searchpos(typval_T *argvars, typval_T *rettv);
static void f_setcharpos(typval_T *argvars, typval_T *rettv);
static void f_setcharsearch(typval_T *argvars, typval_T *rettv);
static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv);
static void f_setenv(typval_T *argvars, typval_T *rettv);
static void f_setfperm(typval_T *argvars, typval_T *rettv);
static void f_setpos(typval_T *argvars, typval_T *rettv);
@ -790,6 +795,8 @@ static funcentry_T global_functions[] =
ret_number, f_char2nr},
{"charclass", 1, 1, FEARG_1, NULL,
ret_number, f_charclass},
{"charcol", 1, 1, FEARG_1, NULL,
ret_number, f_charcol},
{"charidx", 2, 3, FEARG_1, NULL,
ret_number, f_charidx},
{"chdir", 1, 1, FEARG_1, NULL,
@ -928,6 +935,8 @@ static funcentry_T global_functions[] =
ret_number, f_getchar},
{"getcharmod", 0, 0, 0, NULL,
ret_number, f_getcharmod},
{"getcharpos", 1, 1, FEARG_1, NULL,
ret_list_number, f_getcharpos},
{"getcharsearch", 0, 0, 0, NULL,
ret_dict_any, f_getcharsearch},
{"getcmdline", 0, 0, 0, NULL,
@ -942,6 +951,8 @@ static funcentry_T global_functions[] =
ret_list_string, f_getcompletion},
{"getcurpos", 0, 1, FEARG_1, NULL,
ret_list_number, f_getcurpos},
{"getcursorcharpos", 0, 1, FEARG_1, NULL,
ret_list_number, f_getcursorcharpos},
{"getcwd", 0, 2, FEARG_1, NULL,
ret_string, f_getcwd},
{"getenv", 1, 1, FEARG_1, NULL,
@ -1394,10 +1405,14 @@ static funcentry_T global_functions[] =
ret_void, f_setbufvar},
{"setcellwidths", 1, 1, FEARG_1, NULL,
ret_void, f_setcellwidths},
{"setcharpos", 2, 2, FEARG_2, NULL,
ret_number, f_setcharpos},
{"setcharsearch", 1, 1, FEARG_1, NULL,
ret_void, f_setcharsearch},
{"setcmdpos", 1, 1, FEARG_1, NULL,
ret_number, f_setcmdpos},
{"setcursorcharpos", 1, 3, FEARG_1, NULL,
ret_number, f_setcursorcharpos},
{"setenv", 2, 2, FEARG_2, NULL,
ret_void, f_setenv},
{"setfperm", 2, 2, FEARG_1, NULL,
@ -2423,6 +2438,61 @@ f_char2nr(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = tv_get_string(&argvars[0])[0];
}
/*
* Get the current cursor column and store it in 'rettv'. If 'charcol' is TRUE,
* returns the character index of the column. Otherwise, returns the byte index
* of the column.
*/
static void
get_col(typval_T *argvars, typval_T *rettv, int charcol)
{
colnr_T col = 0;
pos_T *fp;
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], FALSE, &fnum, charcol);
if (fp != NULL && fnum == curbuf->b_fnum)
{
if (fp->col == MAXCOL)
{
// '> can be MAXCOL, get the length of the line then
if (fp->lnum <= curbuf->b_ml.ml_line_count)
col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
else
col = MAXCOL;
}
else
{
col = fp->col + 1;
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor)
{
char_u *p = ml_get_cursor();
if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p,
curwin->w_virtcol - curwin->w_cursor.coladd))
{
int l;
if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL)
col += l;
}
}
}
}
rettv->vval.v_number = col;
}
/*
* "charcol()" function
*/
static void
f_charcol(typval_T *argvars, typval_T *rettv)
{
get_col(argvars, rettv, TRUE);
}
/*
* "charidx()" function
*/
@ -2497,42 +2567,7 @@ get_optional_window(typval_T *argvars, int idx)
static void
f_col(typval_T *argvars, typval_T *rettv)
{
colnr_T col = 0;
pos_T *fp;
int fnum = curbuf->b_fnum;
fp = var2fpos(&argvars[0], FALSE, &fnum);
if (fp != NULL && fnum == curbuf->b_fnum)
{
if (fp->col == MAXCOL)
{
// '> can be MAXCOL, get the length of the line then
if (fp->lnum <= curbuf->b_ml.ml_line_count)
col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1;
else
col = MAXCOL;
}
else
{
col = fp->col + 1;
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor)
{
char_u *p = ml_get_cursor();
if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p,
curwin->w_virtcol - curwin->w_cursor.coladd))
{
int l;
if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL)
col += l;
}
}
}
}
rettv->vval.v_number = col;
get_col(argvars, rettv, FALSE);
}
/*
@ -2633,26 +2668,24 @@ f_cosh(typval_T *argvars, typval_T *rettv)
#endif
/*
* "cursor(lnum, col)" function, or
* "cursor(list)"
*
* Moves the cursor to the specified line and column.
* Returns 0 when the position could be set, -1 otherwise.
* Set the cursor position.
* If 'charcol' is TRUE, then use the column number as a character offet.
* Otherwise use the column number as a byte offset.
*/
static void
f_cursor(typval_T *argvars, typval_T *rettv)
set_cursorpos(typval_T *argvars, typval_T *rettv, int charcol)
{
long line, col;
long coladd = 0;
int set_curswant = TRUE;
rettv->vval.v_number = -1;
if (argvars[1].v_type == VAR_UNKNOWN)
if (argvars[0].v_type == VAR_LIST)
{
pos_T pos;
colnr_T curswant = -1;
if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL)
if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL)
{
emsg(_(e_invarg));
return;
@ -2666,15 +2699,24 @@ f_cursor(typval_T *argvars, typval_T *rettv)
set_curswant = FALSE;
}
}
else
else if ((argvars[0].v_type == VAR_NUMBER ||
argvars[0].v_type == VAR_STRING)
&& argvars[1].v_type == VAR_NUMBER)
{
line = tv_get_lnum(argvars);
if (line < 0)
semsg(_(e_invarg2), tv_get_string(&argvars[0]));
col = (long)tv_get_number_chk(&argvars[1], NULL);
if (charcol)
col = buf_charidx_to_byteidx(curbuf, line, col);
if (argvars[2].v_type != VAR_UNKNOWN)
coladd = (long)tv_get_number_chk(&argvars[2], NULL);
}
else
{
emsg(_(e_invarg));
return;
}
if (line < 0 || col < 0 || coladd < 0)
return; // type error; errmsg already given
if (line > 0)
@ -2693,6 +2735,19 @@ f_cursor(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = 0;
}
/*
* "cursor(lnum, col)" function, or
* "cursor(list)"
*
* Moves the cursor to the specified line and column.
* Returns 0 when the position could be set, -1 otherwise.
*/
static void
f_cursor(typval_T *argvars, typval_T *rettv)
{
set_cursorpos(argvars, rettv, FALSE);
}
#ifdef MSWIN
/*
* "debugbreak()" function
@ -3887,6 +3942,88 @@ f_getchangelist(typval_T *argvars, typval_T *rettv)
#endif
}
static void
getpos_both(
typval_T *argvars,
typval_T *rettv,
int getcurpos,
int charcol)
{
pos_T *fp = NULL;
pos_T pos;
win_T *wp = curwin;
list_T *l;
int fnum = -1;
if (rettv_list_alloc(rettv) == OK)
{
l = rettv->vval.v_list;
if (getcurpos)
{
if (argvars[0].v_type != VAR_UNKNOWN)
{
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp != NULL)
fp = &wp->w_cursor;
}
else
fp = &curwin->w_cursor;
if (fp != NULL && charcol)
{
pos = *fp;
pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum,
pos.col) - 1;
fp = &pos;
}
}
else
fp = var2fpos(&argvars[0], TRUE, &fnum, charcol);
if (fnum != -1)
list_append_number(l, (varnumber_T)fnum);
else
list_append_number(l, (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum
: (varnumber_T)0);
list_append_number(l, (fp != NULL)
? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
: (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd :
(varnumber_T)0);
if (getcurpos)
{
int save_set_curswant = curwin->w_set_curswant;
colnr_T save_curswant = curwin->w_curswant;
colnr_T save_virtcol = curwin->w_virtcol;
if (wp == curwin)
update_curswant();
list_append_number(l, wp == NULL ? 0 : wp->w_curswant == MAXCOL
? (varnumber_T)MAXCOL : (varnumber_T)wp->w_curswant + 1);
// Do not change "curswant", as it is unexpected that a get
// function has a side effect.
if (wp == curwin && save_set_curswant)
{
curwin->w_set_curswant = save_set_curswant;
curwin->w_curswant = save_curswant;
curwin->w_virtcol = save_virtcol;
curwin->w_valid &= ~VALID_VIRTCOL;
}
}
}
else
rettv->vval.v_number = FALSE;
}
/*
* "getcharpos()" function
*/
static void
f_getcharpos(typval_T *argvars UNUSED, typval_T *rettv)
{
getpos_both(argvars, rettv, FALSE, TRUE);
}
/*
* "getcharsearch()" function
*/
@ -4019,77 +4156,19 @@ f_getpid(typval_T *argvars UNUSED, typval_T *rettv)
rettv->vval.v_number = mch_get_pid();
}
static void
getpos_both(
typval_T *argvars,
typval_T *rettv,
int getcurpos)
{
pos_T *fp = NULL;
win_T *wp = curwin;
list_T *l;
int fnum = -1;
if (rettv_list_alloc(rettv) == OK)
{
l = rettv->vval.v_list;
if (getcurpos)
{
if (argvars[0].v_type != VAR_UNKNOWN)
{
wp = find_win_by_nr_or_id(&argvars[0]);
if (wp != NULL)
fp = &wp->w_cursor;
}
else
fp = &curwin->w_cursor;
}
else
fp = var2fpos(&argvars[0], TRUE, &fnum);
if (fnum != -1)
list_append_number(l, (varnumber_T)fnum);
else
list_append_number(l, (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum
: (varnumber_T)0);
list_append_number(l, (fp != NULL)
? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1)
: (varnumber_T)0);
list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd :
(varnumber_T)0);
if (getcurpos)
{
int save_set_curswant = curwin->w_set_curswant;
colnr_T save_curswant = curwin->w_curswant;
colnr_T save_virtcol = curwin->w_virtcol;
if (wp == curwin)
update_curswant();
list_append_number(l, wp == NULL ? 0 : wp->w_curswant == MAXCOL
? (varnumber_T)MAXCOL : (varnumber_T)wp->w_curswant + 1);
// Do not change "curswant", as it is unexpected that a get
// function has a side effect.
if (wp == curwin && save_set_curswant)
{
curwin->w_set_curswant = save_set_curswant;
curwin->w_curswant = save_curswant;
curwin->w_virtcol = save_virtcol;
curwin->w_valid &= ~VALID_VIRTCOL;
}
}
}
else
rettv->vval.v_number = FALSE;
}
/*
* "getcurpos()" function
*/
static void
f_getcurpos(typval_T *argvars, typval_T *rettv)
{
getpos_both(argvars, rettv, TRUE);
getpos_both(argvars, rettv, TRUE, FALSE);
}
static void
f_getcursorcharpos(typval_T *argvars, typval_T *rettv)
{
getpos_both(argvars, rettv, TRUE, TRUE);
}
/*
@ -4098,7 +4177,7 @@ f_getcurpos(typval_T *argvars, typval_T *rettv)
static void
f_getpos(typval_T *argvars, typval_T *rettv)
{
getpos_both(argvars, rettv, FALSE);
getpos_both(argvars, rettv, FALSE, FALSE);
}
/*
@ -6183,14 +6262,14 @@ f_line(typval_T *argvars, typval_T *rettv)
== OK)
{
check_cursor();
fp = var2fpos(&argvars[0], TRUE, &fnum);
fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
}
restore_win_noblock(save_curwin, save_curtab, TRUE);
}
}
else
// use current window
fp = var2fpos(&argvars[0], TRUE, &fnum);
fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
if (fp != NULL)
lnum = fp->lnum;
@ -8065,6 +8144,60 @@ f_searchpos(typval_T *argvars, typval_T *rettv)
list_append_number(rettv->vval.v_list, (varnumber_T)n);
}
/*
* Set the cursor or mark position.
* If 'charpos' is TRUE, then use the column number as a character offet.
* Otherwise use the column number as a byte offset.
*/
static void
set_position(typval_T *argvars, typval_T *rettv, int charpos)
{
pos_T pos;
int fnum;
char_u *name;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
name = tv_get_string_chk(argvars);
if (name != NULL)
{
if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) == OK)
{
if (pos.col != MAXCOL && --pos.col < 0)
pos.col = 0;
if ((name[0] == '.' && name[1] == NUL))
{
// set cursor; "fnum" is ignored
curwin->w_cursor = pos;
if (curswant >= 0)
{
curwin->w_curswant = curswant - 1;
curwin->w_set_curswant = FALSE;
}
check_cursor();
rettv->vval.v_number = 0;
}
else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL)
{
// set mark
if (setmark_pos(name[1], &pos, fnum) == OK)
rettv->vval.v_number = 0;
}
else
emsg(_(e_invarg));
}
}
}
/*
* "setcharpos()" function
*/
static void
f_setcharpos(typval_T *argvars, typval_T *rettv)
{
set_position(argvars, rettv, TRUE);
}
static void
f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED)
{
@ -8106,6 +8239,15 @@ f_setcharsearch(typval_T *argvars, typval_T *rettv UNUSED)
}
}
/*
* "setcursorcharpos" function
*/
static void
f_setcursorcharpos(typval_T *argvars, typval_T *rettv UNUSED)
{
set_cursorpos(argvars, rettv, TRUE);
}
/*
* "setenv()" function
*/
@ -8165,41 +8307,7 @@ f_setfperm(typval_T *argvars, typval_T *rettv)
static void
f_setpos(typval_T *argvars, typval_T *rettv)
{
pos_T pos;
int fnum;
char_u *name;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
name = tv_get_string_chk(argvars);
if (name != NULL)
{
if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK)
{
if (pos.col != MAXCOL && --pos.col < 0)
pos.col = 0;
if (name[0] == '.' && name[1] == NUL)
{
// set cursor; "fnum" is ignored
curwin->w_cursor = pos;
if (curswant >= 0)
{
curwin->w_curswant = curswant - 1;
curwin->w_set_curswant = FALSE;
}
check_cursor();
rettv->vval.v_number = 0;
}
else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL)
{
// set mark
if (setmark_pos(name[1], &pos, fnum) == OK)
rettv->vval.v_number = 0;
}
else
emsg(_(e_invarg));
}
}
set_position(argvars, rettv, FALSE);
}
/*
@ -9947,7 +10055,7 @@ f_virtcol(typval_T *argvars, typval_T *rettv)
int fnum = curbuf->b_fnum;
int len;
fp = var2fpos(&argvars[0], FALSE, &fnum);
fp = var2fpos(&argvars[0], FALSE, &fnum, FALSE);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum)
{

View File

@ -55,8 +55,10 @@ char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copy
char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
char_u *string_quote(char_u *str, int function);
int string2float(char_u *text, float_T *value);
pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum);
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp);
int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx);
int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx);
pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum, int charcol);
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, int char_col);
int get_env_len(char_u **arg);
int get_id_len(char_u **arg);
int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose);

View File

@ -4201,7 +4201,7 @@ tagstack_push_items(win_T *wp, list_T *l)
// parse 'from' for the cursor position before the tag jump
if ((di = dict_find(itemdict, (char_u *)"from", -1)) == NULL)
continue;
if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK)
if (list2fpos(&di->di_tv, &mark, &fnum, NULL, FALSE) != OK)
continue;
if ((tagname =
dict_get_string(itemdict, (char_u *)"tagname", TRUE)) == NULL)

View File

@ -1,4 +1,4 @@
" Tests for cursor().
" Tests for cursor() and other functions that get/set the cursor position
func Test_wrong_arguments()
call assert_fails('call cursor(1. 3)', 'E474:')
@ -123,4 +123,187 @@ func Test_screenpos_number()
bwipe!
endfunc
func SaveVisualStartCharPos()
call add(g:VisualStartPos, getcharpos('v'))
return ''
endfunc
" Test for the getcharpos() function
func Test_getcharpos()
call assert_fails('call getcharpos({})', 'E731:')
call assert_equal([0, 0, 0, 0], getcharpos(0))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
" Test for '.' and '$'
normal 1G
call assert_equal([0, 1, 1, 0], getcharpos('.'))
call assert_equal([0, 4, 1, 0], getcharpos('$'))
normal 2G6l
call assert_equal([0, 2, 7, 0], getcharpos('.'))
normal 3G$
call assert_equal([0, 3, 1, 0], getcharpos('.'))
normal 4G$
call assert_equal([0, 4, 9, 0], getcharpos('.'))
" Test for a mark
normal 2G7lmmgg
call assert_equal([0, 2, 8, 0], getcharpos("'m"))
delmarks m
call assert_equal([0, 0, 0, 0], getcharpos("'m"))
" Test for the visual start column
vnoremap <expr> <F3> SaveVisualStartCharPos()
let g:VisualStartPos = []
exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>"
call assert_equal([[0, 2, 7, 0], [0, 2, 9, 0], [0, 2, 5, 0]], g:VisualStartPos)
call assert_equal([0, 2, 9, 0], getcharpos('v'))
let g:VisualStartPos = []
exe "normal 3Gv$\<F3>o\<F3>"
call assert_equal([[0, 3, 1, 0], [0, 3, 1, 0]], g:VisualStartPos)
let g:VisualStartPos = []
exe "normal 1Gv$\<F3>o\<F3>"
call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos)
vunmap <F3>
%bw!
endfunc
" Test for the setcharpos() function
func Test_setcharpos()
call assert_equal(-1, setcharpos('.', test_null_list()))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
call setcharpos('.', [0, 1, 1, 0])
call assert_equal([1, 1], [line('.'), col('.')])
call setcharpos('.', [0, 2, 7, 0])
call assert_equal([2, 9], [line('.'), col('.')])
call setcharpos('.', [0, 3, 4, 0])
call assert_equal([3, 1], [line('.'), col('.')])
call setcharpos('.', [0, 3, 1, 0])
call assert_equal([3, 1], [line('.'), col('.')])
call setcharpos('.', [0, 4, 0, 0])
call assert_equal([4, 1], [line('.'), col('.')])
call setcharpos('.', [0, 4, 20, 0])
call assert_equal([4, 9], [line('.'), col('.')])
" Test for mark
delmarks m
call setcharpos("'m", [0, 2, 9, 0])
normal `m
call assert_equal([2, 11], [line('.'), col('.')])
%bw!
call assert_equal(-1, setcharpos('.', [10, 3, 1, 0]))
endfunc
func SaveVisualStartCharCol()
call add(g:VisualStartCol, charcol('v'))
return ''
endfunc
" Test for the charcol() function
func Test_charcol()
call assert_fails('call charcol({})', 'E731:')
call assert_equal(0, charcol(0))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
" Test for '.' and '$'
normal 1G
call assert_equal(1, charcol('.'))
call assert_equal(1, charcol('$'))
normal 2G6l
call assert_equal(7, charcol('.'))
call assert_equal(10, charcol('$'))
normal 3G$
call assert_equal(1, charcol('.'))
call assert_equal(2, charcol('$'))
normal 4G$
call assert_equal(9, charcol('.'))
call assert_equal(10, charcol('$'))
" Test for [lnum, '$']
call assert_equal(1, charcol([1, '$']))
call assert_equal(10, charcol([2, '$']))
call assert_equal(2, charcol([3, '$']))
call assert_equal(0, charcol([5, '$']))
" Test for a mark
normal 2G7lmmgg
call assert_equal(8, charcol("'m"))
delmarks m
call assert_equal(0, charcol("'m"))
" Test for the visual start column
vnoremap <expr> <F3> SaveVisualStartCharCol()
let g:VisualStartCol = []
exe "normal 2G6lv$\<F3>ohh\<F3>o\<F3>"
call assert_equal([7, 9, 5], g:VisualStartCol)
call assert_equal(9, charcol('v'))
let g:VisualStartCol = []
exe "normal 3Gv$\<F3>o\<F3>"
call assert_equal([1, 1], g:VisualStartCol)
let g:VisualStartCol = []
exe "normal 1Gv$\<F3>o\<F3>"
call assert_equal([1, 1], g:VisualStartCol)
vunmap <F3>
%bw!
endfunc
" Test for getcursorcharpos()
func Test_getcursorcharpos()
call assert_equal(getcursorcharpos(), getcursorcharpos(0))
call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(-1))
call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(1999))
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
normal 1G9l
call assert_equal([0, 1, 1, 0, 1], getcursorcharpos())
normal 2G9l
call assert_equal([0, 2, 9, 0, 14], getcursorcharpos())
normal 3G9l
call assert_equal([0, 3, 1, 0, 1], getcursorcharpos())
normal 4G9l
call assert_equal([0, 4, 9, 0, 9], getcursorcharpos())
let winid = win_getid()
normal 2G5l
wincmd w
call assert_equal([0, 2, 6, 0, 11], getcursorcharpos(winid))
%bw!
endfunc
" Test for setcursorcharpos()
func Test_setcursorcharpos()
call assert_fails('call setcursorcharpos(test_null_list())', 'E474:')
call assert_fails('call setcursorcharpos([1])', 'E474:')
call assert_fails('call setcursorcharpos([1, 1, 1, 1, 1])', 'E474:')
new
call setline(1, ['', "01\tà4è678", 'Ⅵ', '012345678'])
normal G
call setcursorcharpos([1, 1])
call assert_equal([1, 1], [line('.'), col('.')])
call setcursorcharpos([2, 7, 0])
call assert_equal([2, 9], [line('.'), col('.')])
call setcursorcharpos(3, 4)
call assert_equal([3, 1], [line('.'), col('.')])
call setcursorcharpos([3, 1])
call assert_equal([3, 1], [line('.'), col('.')])
call setcursorcharpos([4, 0, 0, 0])
call assert_equal([4, 1], [line('.'), col('.')])
call setcursorcharpos([4, 20])
call assert_equal([4, 9], [line('.'), col('.')])
normal 1G
call setcursorcharpos([100, 100, 100, 100])
call assert_equal([4, 9], [line('.'), col('.')])
normal 1G
call setcursorcharpos('$', 1)
call assert_equal([4, 1], [line('.'), col('.')])
%bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -1579,7 +1579,7 @@ tv_get_lnum(typval_T *argvars)
if (lnum <= 0) // no valid number, try using arg like line()
{
int fnum;
pos_T *fp = var2fpos(&argvars[0], TRUE, &fnum);
pos_T *fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
if (fp != NULL)
lnum = fp->lnum;

View File

@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2324,
/**/
2323,
/**/