Merge #15293 Vimscript "method" syntax

Port VimL's method call syntax - vim-patch:8.1.{1638,1800,1803,1807,1809,1816,1820,1821,1828,1834,1835,1861,1863,1878,1879,1888,1909,1911,1912}
This commit is contained in:
Justin M. Keyes
2021-08-26 04:26:32 -07:00
committed by GitHub
39 changed files with 1424 additions and 421 deletions

View File

@@ -669,12 +669,14 @@ Expression syntax summary, from least to most significant:
expr8[expr1 : expr1] substring of a String or sublist of a |List| expr8[expr1 : expr1] substring of a String or sublist of a |List|
expr8.name entry in a |Dictionary| expr8.name entry in a |Dictionary|
expr8(expr1, ...) function call with |Funcref| variable expr8(expr1, ...) function call with |Funcref| variable
expr8->name(expr1, ...) |method| call
|expr9| number number constant |expr9| number number constant
"string" string constant, backslash is special "string" string constant, backslash is special
'string' string constant, ' is doubled 'string' string constant, ' is doubled
[expr1, ...] |List| [expr1, ...] |List|
{expr1: expr1, ...} |Dictionary| {expr1: expr1, ...} |Dictionary|
#{key: expr1, ...} |Dictionary|
&option option value &option option value
(expr1) nested expression (expr1) nested expression
variable internal variable variable internal variable
@@ -939,9 +941,11 @@ expr8 *expr8*
----- -----
This expression is either |expr9| or a sequence of the alternatives below, This expression is either |expr9| or a sequence of the alternatives below,
in any order. E.g., these are all possible: in any order. E.g., these are all possible:
expr9[expr1].name expr8[expr1].name
expr9.name[expr1] expr8.name[expr1]
expr9(expr1, ...)[expr1].name expr8(expr1, ...)[expr1].name
expr8->(expr1, ...)[expr1]
Evaluation is always from left to right.
expr8[expr1] item of String or |List| *expr-[]* *E111* expr8[expr1] item of String or |List| *expr-[]* *E111*
@@ -1043,6 +1047,36 @@ expr8(expr1, ...) |Funcref| function call
When expr8 is a |Funcref| type variable, invoke the function it refers to. When expr8 is a |Funcref| type variable, invoke the function it refers to.
expr8->name([args]) method call *method* *->*
expr8->{lambda}([args])
For methods that are also available as global functions this is the same as: >
name(expr8 [, args])
There can also be methods specifically for the type of "expr8".
This allows for chaining, passing the value that one method returns to the
next method: >
mylist->filter(filterexpr)->map(mapexpr)->sort()->join()
<
Example of using a lambda: >
GetPercentage->{x -> x * 100}()->printf('%d%%')
<
When using -> the |expr7| operators will be applied first, thus: >
-1.234->string()
Is equivalent to: >
(-1.234)->string()
And NOT: >
-(1.234->string())
<
*E274*
"->name(" must not contain white space. There can be white space before the
"->" and after the "(", thus you can split the lines like this: >
mylist
\ ->filter(filterexpr)
\ ->map(mapexpr)
\ ->sort()
\ ->join()
<
*expr9* *expr9*
number number
@@ -2583,6 +2617,8 @@ abs({expr}) *abs()*
echo abs(-4) echo abs(-4)
< 4 < 4
Can also be used as a |method|: >
Compute()->abs()
acos({expr}) *acos()* acos({expr}) *acos()*
Return the arc cosine of {expr} measured in radians, as a Return the arc cosine of {expr} measured in radians, as a
@@ -2595,6 +2631,8 @@ acos({expr}) *acos()*
:echo acos(-0.5) :echo acos(-0.5)
< 2.094395 < 2.094395
Can also be used as a |method|: >
Compute()->acos()
add({list}, {expr}) *add()* add({list}, {expr}) *add()*
Append the item {expr} to |List| {list}. Returns the Append the item {expr} to |List| {list}. Returns the
@@ -2605,12 +2643,16 @@ add({list}, {expr}) *add()*
item. Use |extend()| to concatenate |Lists|. item. Use |extend()| to concatenate |Lists|.
Use |insert()| to add an item at another position. Use |insert()| to add an item at another position.
Can also be used as a |method|: >
mylist->add(val1)->add(val2)
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.
Example: > Example: >
:let flag = and(bits, 0x80) :let flag = and(bits, 0x80)
< Can also be used as a |method|: >
:let flag = bits->and(0x80)
api_info() *api_info()* api_info() *api_info()*
Returns Dictionary of |api-metadata|. Returns Dictionary of |api-metadata|.
@@ -2629,6 +2671,9 @@ append({lnum}, {text}) *append()*
:let failed = append(line('$'), "# THE END") :let failed = append(line('$'), "# THE END")
:let failed = append(0, ["Chapter 1", "the beginning"]) :let failed = append(0, ["Chapter 1", "the beginning"])
< Can also be used as a |method| after a List: >
mylist->append(lnum)
appendbufline({expr}, {lnum}, {text}) *appendbufline()* appendbufline({expr}, {lnum}, {text}) *appendbufline()*
Like |append()| but append the text in buffer {expr}. Like |append()| but append the text in buffer {expr}.
@@ -2647,8 +2692,10 @@ appendbufline({expr}, {lnum}, {text}) *appendbufline()*
error message is given. Example: > error message is given. Example: >
:let failed = appendbufline(13, 0, "# THE START") :let failed = appendbufline(13, 0, "# THE START")
< <
*argc()* Can also be used as a |method| after a List: >
argc([{winid}]) mylist->appendbufline(buf, lnum)
argc([{winid}]) *argc()*
The result is the number of files in the argument list. See The result is the number of files in the argument list. See
|arglist|. |arglist|.
If {winid} is not supplied, the argument list of the current If {winid} is not supplied, the argument list of the current
@@ -2702,6 +2749,9 @@ asin({expr}) *asin()*
:echo asin(-0.5) :echo asin(-0.5)
< -0.523599 < -0.523599
Can also be used as a |method|: >
Compute()->asin()
assert_ functions are documented here: |assert-functions-details| assert_ functions are documented here: |assert-functions-details|
@@ -2716,6 +2766,8 @@ atan({expr}) *atan()*
:echo atan(-4.01) :echo atan(-4.01)
< -1.326405 < -1.326405
Can also be used as a |method|: >
Compute()->atan()
atan2({expr1}, {expr2}) *atan2()* atan2({expr1}, {expr2}) *atan2()*
Return the arc tangent of {expr1} / {expr2}, measured in Return the arc tangent of {expr1} / {expr2}, measured in
@@ -2727,6 +2779,8 @@ atan2({expr1}, {expr2}) *atan2()*
:echo atan2(1, -1) :echo atan2(1, -1)
< 2.356194 < 2.356194
Can also be used as a |method|: >
Compute()->atan2(1)
*browse()* *browse()*
browse({save}, {title}, {initdir}, {default}) browse({save}, {title}, {initdir}, {default})
@@ -2737,8 +2791,8 @@ browse({save}, {title}, {initdir}, {default})
{title} title for the requester {title} title for the requester
{initdir} directory to start browsing in {initdir} directory to start browsing in
{default} default file name {default} default file name
When the "Cancel" button is hit, something went wrong, or An empty string is returned when the "Cancel" button is hit,
browsing is not possible, an empty string is returned. something went wrong, or browsing is not possible.
*browsedir()* *browsedir()*
browsedir({title}, {initdir}) browsedir({title}, {initdir})
@@ -2760,6 +2814,8 @@ bufadd({name}) *bufadd()*
created buffer. When {name} is an empty string then a new created buffer. When {name} is an empty string then a new
buffer is always created. buffer is always created.
The buffer will not have' 'buflisted' set. The buffer will not have' 'buflisted' set.
< Can also be used as a |method|: >
let bufnr = 'somename'->bufadd()
bufexists({expr}) *bufexists()* bufexists({expr}) *bufexists()*
The result is a Number, which is |TRUE| if a buffer called The result is a Number, which is |TRUE| if a buffer called
@@ -2783,11 +2839,17 @@ bufexists({expr}) *bufexists()*
Use "bufexists(0)" to test for the existence of an alternate Use "bufexists(0)" to test for the existence of an alternate
file name. file name.
Can also be used as a |method|: >
let exists = 'somename'->bufexists()
buflisted({expr}) *buflisted()* buflisted({expr}) *buflisted()*
The result is a Number, which is |TRUE| if a buffer called The result is a Number, which is |TRUE| if a buffer called
{expr} exists and is listed (has the 'buflisted' option set). {expr} exists and is listed (has the 'buflisted' option set).
The {expr} argument is used like with |bufexists()|. The {expr} argument is used like with |bufexists()|.
Can also be used as a |method|: >
let listed = 'somename'->buflisted()
bufload({expr}) *bufload()* bufload({expr}) *bufload()*
Ensure the buffer {expr} is loaded. When the buffer name Ensure the buffer {expr} is loaded. When the buffer name
refers to an existing file then the file is read. Otherwise refers to an existing file then the file is read. Otherwise
@@ -2797,15 +2859,21 @@ bufload({expr}) *bufload()*
there will be no dialog, the buffer will be loaded anyway. there will be no dialog, the buffer will be loaded anyway.
The {expr} argument is used like with |bufexists()|. The {expr} argument is used like with |bufexists()|.
Can also be used as a |method|: >
eval 'somename'->bufload()
bufloaded({expr}) *bufloaded()* bufloaded({expr}) *bufloaded()*
The result is a Number, which is |TRUE| if a buffer called The result is a Number, which is |TRUE| if a buffer called
{expr} exists and is loaded (shown in a window or hidden). {expr} exists and is loaded (shown in a window or hidden).
The {expr} argument is used like with |bufexists()|. The {expr} argument is used like with |bufexists()|.
Can also be used as a |method|: >
let loaded = 'somename'->bufloaded()
bufname([{expr}]) *bufname()* bufname([{expr}]) *bufname()*
The result is the name of a buffer, as it is displayed by the The result is the name of a buffer, as it is displayed by the
":ls" command. ":ls" command.
+ If {expr} is omitted the current buffer is used. If {expr} is omitted the current buffer is used.
If {expr} is a Number, that buffer number's name is given. If {expr} is a Number, that buffer number's name is given.
Number zero is the alternate buffer for the current window. Number zero is the alternate buffer for the current window.
If {expr} is a String, it is used as a |file-pattern| to match If {expr} is a String, it is used as a |file-pattern| to match
@@ -2824,6 +2892,9 @@ bufname([{expr}]) *bufname()*
If the {expr} is a String, but you want to use it as a buffer If the {expr} is a String, but you want to use it as a buffer
number, force it to be a Number by adding zero to it: > number, force it to be a Number by adding zero to it: >
:echo bufname("3" + 0) :echo bufname("3" + 0)
< Can also be used as a |method|: >
echo bufnr->bufname()
< If the buffer doesn't exist, or doesn't have a name, an empty < If the buffer doesn't exist, or doesn't have a name, an empty
string is returned. > string is returned. >
bufname("#") alternate buffer name bufname("#") alternate buffer name
@@ -2846,6 +2917,9 @@ bufnr([{expr} [, {create}]])
number necessarily exist, because ":bwipeout" may have removed number necessarily exist, because ":bwipeout" may have removed
them. Use bufexists() to test for the existence of a buffer. them. Use bufexists() to test for the existence of a buffer.
Can also be used as a |method|: >
echo bufref->bufnr()
bufwinid({expr}) *bufwinid()* bufwinid({expr}) *bufwinid()*
The result is a Number, which is the |window-ID| of the first The result is a Number, which is the |window-ID| of the first
window associated with buffer {expr}. For the use of {expr}, window associated with buffer {expr}. For the use of {expr},
@@ -2856,18 +2930,22 @@ bufwinid({expr}) *bufwinid()*
< <
Only deals with the current tab page. Only deals with the current tab page.
Can also be used as a |method|: >
FindBuffer()->bufwinid()
bufwinnr({expr}) *bufwinnr()* bufwinnr({expr}) *bufwinnr()*
The result is a Number, which is the number of the first Like |bufwinid()| but return the window number instead of the
window associated with buffer {expr}. For the use of {expr}, |window-ID|.
see |bufname()| above. If buffer {expr} doesn't exist or If buffer {expr} doesn't exist or there is no such window, -1
there is no such window, -1 is returned. Example: > is returned. Example: >
echo "A window containing buffer 1 is " . (bufwinnr(1)) echo "A window containing buffer 1 is " . (bufwinnr(1))
< The number can be used with |CTRL-W_w| and ":wincmd w" < The number can be used with |CTRL-W_w| and ":wincmd w"
|:wincmd|. |:wincmd|.
Only deals with the current tab page.
Can also be used as a |method|: >
FindBuffer()->bufwinnr()
byte2line({byte}) *byte2line()* byte2line({byte}) *byte2line()*
Return the line number that contains the character at byte Return the line number that contains the character at byte
@@ -2877,6 +2955,9 @@ byte2line({byte}) *byte2line()*
one. one.
Also see |line2byte()|, |go| and |:goto|. Also see |line2byte()|, |go| and |:goto|.
Can also be used as a |method|: >
GetOffset()->byte2line()
byteidx({expr}, {nr}) *byteidx()* byteidx({expr}, {nr}) *byteidx()*
Return byte index of the {nr}'th character in the string Return byte index of the {nr}'th character in the string
{expr}. Use zero for the first character, it then returns {expr}. Use zero for the first character, it then returns
@@ -2899,6 +2980,9 @@ byteidx({expr}, {nr}) *byteidx()*
If there are exactly {nr} characters the length of the string If there are exactly {nr} characters the length of the string
in bytes is returned. in bytes is returned.
Can also be used as a |method|: >
GetName()->byteidx(idx)
byteidxcomp({expr}, {nr}) *byteidxcomp()* byteidxcomp({expr}, {nr}) *byteidxcomp()*
Like byteidx(), except that a composing character is counted Like byteidx(), except that a composing character is counted
as a separate character. Example: > as a separate character. Example: >
@@ -2912,6 +2996,9 @@ byteidxcomp({expr}, {nr}) *byteidxcomp()*
Only works differently from byteidx() when 'encoding' is set to Only works differently from byteidx() when 'encoding' is set to
a Unicode encoding. a Unicode encoding.
Can also be used as a |method|: >
GetName()->byteidxcomp(idx)
call({func}, {arglist} [, {dict}]) *call()* *E699* call({func}, {arglist} [, {dict}]) *call()* *E699*
Call function {func} with the items in |List| {arglist} as Call function {func} with the items in |List| {arglist} as
arguments. arguments.
@@ -2921,6 +3008,9 @@ call({func}, {arglist} [, {dict}]) *call()* *E699*
{dict} is for functions with the "dict" attribute. It will be {dict} is for functions with the "dict" attribute. It will be
used to set the local variable "self". |Dictionary-function| used to set the local variable "self". |Dictionary-function|
Can also be used as a |method|: >
GetFunc()->call([arg, arg], dict)
ceil({expr}) *ceil()* ceil({expr}) *ceil()*
Return the smallest integral value greater than or equal to Return the smallest integral value greater than or equal to
{expr} as a |Float| (round up). {expr} as a |Float| (round up).
@@ -2933,6 +3023,9 @@ ceil({expr}) *ceil()*
echo ceil(4.0) echo ceil(4.0)
< 4.0 < 4.0
Can also be used as a |method|: >
Compute()->ceil()
changenr() *changenr()* changenr() *changenr()*
Return the number of the most recent change. This is the same Return the number of the most recent change. This is the same
number as what is displayed with |:undolist| and can be used number as what is displayed with |:undolist| and can be used
@@ -2982,6 +3075,9 @@ char2nr({expr} [, {utf8}]) *char2nr()*
A combining character is a separate character. A combining character is a separate character.
|nr2char()| does the opposite. |nr2char()| does the opposite.
Can also be used as a |method|: >
GetChar()->char2nr()
*charidx()* *charidx()*
charidx({string}, {idx} [, {countcc}]) charidx({string}, {idx} [, {countcc}])
Return the character index of the byte at {idx} in {string}. Return the character index of the byte at {idx} in {string}.
@@ -3013,12 +3109,18 @@ cindent({lnum}) *cindent()*
When {lnum} is invalid -1 is returned. When {lnum} is invalid -1 is returned.
See |C-indenting|. See |C-indenting|.
Can also be used as a |method|: >
GetLnum()->cindent()
clearmatches([{win}]) *clearmatches()* clearmatches([{win}]) *clearmatches()*
Clears all matches previously defined for the current window Clears all matches previously defined for the current window
by |matchadd()| and the |:match| commands. by |matchadd()| and the |:match| commands.
If {win} is specified, use the window with this number or If {win} is specified, use the window with this number or
window ID instead of the current window. window ID instead of the current window.
Can also be used as a |method|: >
GetWin()->clearmatches()
<
*col()* *col()*
col({expr}) The result is a Number, which is the byte index of the column col({expr}) The result is a Number, which is the byte index of the column
position given with {expr}. The accepted positions are: position given with {expr}. The accepted positions are:
@@ -3054,6 +3156,9 @@ col({expr}) The result is a Number, which is the byte index of the column
\<C-O>:set ve=all<CR> \<C-O>:set ve=all<CR>
\<C-O>:echo col(".") . "\n" <Bar> \<C-O>:echo col(".") . "\n" <Bar>
\let &ve = save_ve<CR> \let &ve = save_ve<CR>
< Can also be used as a |method|: >
GetPos()->col()
< <
complete({startcol}, {matches}) *complete()* *E785* complete({startcol}, {matches}) *complete()* *E785*
@@ -3085,6 +3190,10 @@ complete({startcol}, {matches}) *complete()* *E785*
< This isn't very useful, but it shows how it works. Note that < This isn't very useful, but it shows how it works. Note that
an empty string is returned to avoid a zero being inserted. an empty string is returned to avoid a zero being inserted.
Can also be used as a |method|, the second argument is passed
in: >
GetMatches()->complete(col('.'))
complete_add({expr}) *complete_add()* complete_add({expr}) *complete_add()*
Add {expr} to the list of matches. Only to be used by the Add {expr} to the list of matches. Only to be used by the
function specified with the 'completefunc' option. function specified with the 'completefunc' option.
@@ -3094,6 +3203,9 @@ complete_add({expr}) *complete_add()*
See |complete-functions| for an explanation of {expr}. It is See |complete-functions| for an explanation of {expr}. It is
the same as one item in the list that 'omnifunc' would return. the same as one item in the list that 'omnifunc' would return.
Can also be used as a |method|: >
GetMoreMatches()->complete_add()
complete_check() *complete_check()* complete_check() *complete_check()*
Check for a key typed while looking for completion matches. Check for a key typed while looking for completion matches.
This is to be used when looking for matches takes some time. This is to be used when looking for matches takes some time.
@@ -3154,6 +3266,9 @@ complete_info([{what}])
call complete_info(['mode']) call complete_info(['mode'])
" Get only 'mode' and 'pum_visible' " Get only 'mode' and 'pum_visible'
call complete_info(['mode', 'pum_visible']) call complete_info(['mode', 'pum_visible'])
< Can also be used as a |method|: >
GetItems()->complete_info()
< <
*confirm()* *confirm()*
confirm({msg} [, {choices} [, {default} [, {type}]]]) confirm({msg} [, {choices} [, {default} [, {type}]]])
@@ -3207,6 +3322,9 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
don't fit, a vertical layout is used anyway. For some systems don't fit, a vertical layout is used anyway. For some systems
the horizontal layout is always used. the horizontal layout is always used.
Can also be used as a |method|in: >
BuildMessage()->confirm("&Yes\n&No")
*copy()* *copy()*
copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
different from using {expr} directly. different from using {expr} directly.
@@ -3216,6 +3334,8 @@ copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
changing an item changes the contents of both |Lists|. changing an item changes the contents of both |Lists|.
A |Dictionary| is copied in a similar way as a |List|. A |Dictionary| is copied in a similar way as a |List|.
Also see |deepcopy()|. Also see |deepcopy()|.
Can also be used as a |method|: >
mylist->copy()
cos({expr}) *cos()* cos({expr}) *cos()*
Return the cosine of {expr}, measured in radians, as a |Float|. Return the cosine of {expr}, measured in radians, as a |Float|.
@@ -3226,6 +3346,8 @@ cos({expr}) *cos()*
:echo cos(-4.01) :echo cos(-4.01)
< -0.646043 < -0.646043
Can also be used as a |method|: >
Compute()->cos()
cosh({expr}) *cosh()* cosh({expr}) *cosh()*
Return the hyperbolic cosine of {expr} as a |Float| in the range Return the hyperbolic cosine of {expr} as a |Float| in the range
@@ -3237,6 +3359,8 @@ cosh({expr}) *cosh()*
:echo cosh(-0.5) :echo cosh(-0.5)
< -1.127626 < -1.127626
Can also be used as a |method|: >
Compute()->cosh()
count({comp}, {expr} [, {ic} [, {start}]]) *count()* count({comp}, {expr} [, {ic} [, {start}]]) *count()*
Return the number of times an item with value {expr} appears Return the number of times an item with value {expr} appears
@@ -3251,6 +3375,9 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()*
occurrences of {expr} is returned. Zero is returned when occurrences of {expr} is returned. Zero is returned when
{expr} is an empty string. {expr} is an empty string.
Can also be used as a |method|: >
mylist->count(val)
*cscope_connection()* *cscope_connection()*
cscope_connection([{num} , {dbpath} [, {prepend}]]) cscope_connection([{num} , {dbpath} [, {prepend}]])
Checks for the existence of a |cscope| connection. If no Checks for the existence of a |cscope| connection. If no
@@ -3346,6 +3473,8 @@ cursor({list})
position within a <Tab> or after the last character. position within a <Tab> or after the last character.
Returns 0 when the position could be set, -1 otherwise. Returns 0 when the position could be set, -1 otherwise.
Can also be used as a |method|: >
GetCursorPos()->cursor()
deepcopy({expr}[, {noref}]) *deepcopy()* *E698* deepcopy({expr}[, {noref}]) *deepcopy()* *E698*
Make a copy of {expr}. For Numbers and Strings this isn't Make a copy of {expr}. For Numbers and Strings this isn't
@@ -3367,6 +3496,9 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698*
{noref} set to 1 will fail. {noref} set to 1 will fail.
Also see |copy()|. Also see |copy()|.
Can also be used as a |method|: >
GetObject()->deepcopy()
delete({fname} [, {flags}]) *delete()* delete({fname} [, {flags}]) *delete()*
Without {flags} or with {flags} empty: Deletes the file by the Without {flags} or with {flags} empty: Deletes the file by the
name {fname}. This also works when {fname} is a symbolic link. name {fname}. This also works when {fname} is a symbolic link.
@@ -3384,6 +3516,9 @@ delete({fname} [, {flags}]) *delete()*
operation was successful and -1/true when the deletion failed operation was successful and -1/true when the deletion failed
or partly failed. or partly failed.
Can also be used as a |method|: >
GetName()->delete()
deletebufline({expr}, {first}[, {last}]) *deletebufline()* deletebufline({expr}, {first}[, {last}]) *deletebufline()*
Delete lines {first} to {last} (inclusive) from buffer {expr}. Delete lines {first} to {last} (inclusive) from buffer {expr}.
If {last} is omitted then delete line {first} only. If {last} is omitted then delete line {first} only.
@@ -3398,6 +3533,9 @@ deletebufline({expr}, {first}[, {last}]) *deletebufline()*
when using |line()| this refers to the current buffer. Use "$" when using |line()| this refers to the current buffer. Use "$"
to refer to the last line in buffer {expr}. to refer to the last line in buffer {expr}.
Can also be used as a |method|: >
GetBuffer()->deletebufline(1)
dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()* dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()*
Adds a watcher to a dictionary. A dictionary watcher is Adds a watcher to a dictionary. A dictionary watcher is
identified by three components: identified by three components:
@@ -3464,6 +3602,9 @@ diff_filler({lnum}) *diff_filler()*
line, "'m" mark m, etc. line, "'m" mark m, etc.
Returns 0 if the current window is not in diff mode. Returns 0 if the current window is not in diff mode.
Can also be used as a |method|: >
GetLnum()->diff_filler()
diff_hlID({lnum}, {col}) *diff_hlID()* diff_hlID({lnum}, {col}) *diff_hlID()*
Returns the highlight ID for diff mode at line {lnum} column Returns the highlight ID for diff mode at line {lnum} column
{col} (byte index). When the current line does not have a {col} (byte index). When the current line does not have a
@@ -3475,11 +3616,16 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
The highlight ID can be used with |synIDattr()| to obtain The highlight ID can be used with |synIDattr()| to obtain
syntax information about the highlighting. syntax information about the highlighting.
Can also be used as a |method|: >
GetLnum()->diff_hlID(col)
empty({expr}) *empty()* empty({expr}) *empty()*
Return the Number 1 if {expr} is empty, zero otherwise. Return the Number 1 if {expr} is empty, zero otherwise.
A |List| or |Dictionary| is empty when it does not have any A |List| or |Dictionary| is empty when it does not have any
items. A Number is empty when its value is zero. Special items. A Number is empty when its value is zero. Special
variable is empty when it is |v:false| or |v:null|. variable is empty when it is |v:false| or |v:null|.
Can also be used as a |method|: >
mylist->empty()
environ() *environ()* environ() *environ()*
Return all of environment variables as dictionary. You can Return all of environment variables as dictionary. You can
@@ -3504,6 +3650,9 @@ eval({string}) Evaluate {string} and return the result. Especially useful to
them. Also works for |Funcref|s that refer to existing them. Also works for |Funcref|s that refer to existing
functions. functions.
Can also be used as a |method|: >
argv->join()->eval()
eventhandler() *eventhandler()* eventhandler() *eventhandler()*
Returns 1 when inside an event handler. That is that Vim got Returns 1 when inside an event handler. That is that Vim got
interrupted while waiting for the user to type a character, interrupted while waiting for the user to type a character,
@@ -3661,12 +3810,18 @@ exp({expr}) *exp()*
:echo exp(-1) :echo exp(-1)
< 0.367879 < 0.367879
Can also be used as a |method|: >
Compute()->exp()
debugbreak({pid}) *debugbreak()* debugbreak({pid}) *debugbreak()*
Specifically used to interrupt a program being debugged. It Specifically used to interrupt a program being debugged. It
will cause process {pid} to get a SIGTRAP. Behavior for other will cause process {pid} to get a SIGTRAP. Behavior for other
processes is undefined. See |terminal-debugger|. processes is undefined. See |terminal-debugger|.
{Sends a SIGINT to a process {pid} other than MS-Windows} {Sends a SIGINT to a process {pid} other than MS-Windows}
Can also be used as a |method|: >
GetPid()->debugbreak()
expand({expr} [, {nosuf} [, {list}]]) *expand()* expand({expr} [, {nosuf} [, {list}]]) *expand()*
Expand wildcards and the following special keywords in {expr}. Expand wildcards and the following special keywords in {expr}.
'wildignorecase' applies. 'wildignorecase' applies.
@@ -3795,6 +3950,8 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()*
fails. fails.
Returns {expr1}. Returns {expr1}.
Can also be used as a |method|: >
mylist->extend(otherlist)
feedkeys({string} [, {mode}]) *feedkeys()* feedkeys({string} [, {mode}]) *feedkeys()*
Characters in {string} are queued for processing as if they Characters in {string} are queued for processing as if they
@@ -3848,7 +4005,11 @@ filereadable({file}) *filereadable()*
expression, which is used as a String. expression, which is used as a String.
If you don't care about the file being readable you can use If you don't care about the file being readable you can use
|glob()|. |glob()|.
{file} is used as-is, you may want to expand wildcards first: >
echo filereadable('~/.vimrc')
0
echo filereadable(expand('~/.vimrc'))
1
filewritable({file}) *filewritable()* filewritable({file}) *filewritable()*
The result is a Number, which is 1 when a file with the The result is a Number, which is 1 when a file with the
@@ -3904,6 +4065,8 @@ filter({expr1}, {expr2}) *filter()*
Funcref errors inside a function are ignored, unless it was Funcref errors inside a function are ignored, unless it was
defined with the "abort" flag. defined with the "abort" flag.
Can also be used as a |method|: >
mylist->filter(expr2)
finddir({name} [, {path} [, {count}]]) *finddir()* finddir({name} [, {path} [, {count}]]) *finddir()*
Find directory {name} in {path}. Supports both downwards and Find directory {name} in {path}. Supports both downwards and
@@ -3966,6 +4129,8 @@ float2nr({expr}) *float2nr()*
echo float2nr(1.0e-100) echo float2nr(1.0e-100)
< 0 < 0
Can also be used as a |method|: >
Compute()->float2nr()
floor({expr}) *floor()* floor({expr}) *floor()*
Return the largest integral value less than or equal to Return the largest integral value less than or equal to
@@ -3979,6 +4144,8 @@ floor({expr}) *floor()*
echo floor(4.0) echo floor(4.0)
< 4.0 < 4.0
Can also be used as a |method|: >
Compute()->floor()
fmod({expr1}, {expr2}) *fmod()* fmod({expr1}, {expr2}) *fmod()*
Return the remainder of {expr1} / {expr2}, even if the Return the remainder of {expr1} / {expr2}, even if the
@@ -3994,6 +4161,8 @@ fmod({expr1}, {expr2}) *fmod()*
:echo fmod(-12.33, 1.22) :echo fmod(-12.33, 1.22)
< -0.13 < -0.13
Can also be used as a |method|: >
Compute()->fmod(1.22)
fnameescape({string}) *fnameescape()* fnameescape({string}) *fnameescape()*
Escape {string} for use as file name command argument. All Escape {string} for use as file name command argument. All
@@ -4160,6 +4329,8 @@ get({list}, {idx} [, {default}]) *get()*
Get item {idx} from |List| {list}. When this item is not Get item {idx} from |List| {list}. When this item is not
available return {default}. Return zero when {default} is available return {default}. Return zero when {default} is
omitted. omitted.
Can also be used as a |method|: >
mylist->get(idx)
get({dict}, {key} [, {default}]) get({dict}, {key} [, {default}])
Get item with key {key} from |Dictionary| {dict}. When this Get item with key {key} from |Dictionary| {dict}. When this
item is not available return {default}. Return zero when item is not available return {default}. Return zero when
@@ -5171,6 +5342,9 @@ has_key({dict}, {key}) *has_key()*
The result is a Number, which is TRUE if |Dictionary| {dict} The result is a Number, which is TRUE if |Dictionary| {dict}
has an entry with key {key}. FALSE otherwise. has an entry with key {key}. FALSE otherwise.
Can also be used as a |method|: >
mydict->has_key(key)
haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()* haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()*
The result is a Number, which is 1 when the tabpage or window The result is a Number, which is 1 when the tabpage or window
has set a local path via |:tcd| or |:lcd|, otherwise 0. has set a local path via |:tcd| or |:lcd|, otherwise 0.
@@ -5514,6 +5688,9 @@ insert({list}, {item} [, {idx}]) *insert()*
Note that when {item} is a |List| it is inserted as a single Note that when {item} is a |List| it is inserted as a single
item. Use |extend()| to concatenate |Lists|. item. Use |extend()| to concatenate |Lists|.
Can also be used as a |method|: >
mylist->insert(item)
interrupt() *interrupt()* interrupt() *interrupt()*
Interrupt script execution. It works more or less like the Interrupt script execution. It works more or less like the
user typing CTRL-C, most commands won't execute and control user typing CTRL-C, most commands won't execute and control
@@ -5531,6 +5708,8 @@ invert({expr}) *invert()*
Bitwise invert. The argument is converted to a number. A Bitwise invert. The argument is converted to a number. A
List, Dict or Float argument causes an error. Example: > List, Dict or Float argument causes an error. Example: >
:let bits = invert(bits) :let bits = invert(bits)
< Can also be used as a |method|: >
:let bits = bits->invert()
isdirectory({directory}) *isdirectory()* isdirectory({directory}) *isdirectory()*
The result is a Number, which is |TRUE| when a directory The result is a Number, which is |TRUE| when a directory
@@ -5546,6 +5725,9 @@ isinf({expr}) *isinf()*
:echo isinf(-1.0 / 0.0) :echo isinf(-1.0 / 0.0)
< -1 < -1
Can also be used as a |method|: >
Compute()->isinf()
islocked({expr}) *islocked()* *E786* islocked({expr}) *islocked()* *E786*
The result is a Number, which is |TRUE| when {expr} is the The result is a Number, which is |TRUE| when {expr} is the
name of a locked variable. name of a locked variable.
@@ -5581,12 +5763,17 @@ items({dict}) *items()*
|List| item is a list with two items: the key of a {dict} |List| item is a list with two items: the key of a {dict}
entry and the value of this entry. The |List| is in arbitrary entry and the value of this entry. The |List| is in arbitrary
order. order.
Can also be used as a |method|: >
mydict->items()
isnan({expr}) *isnan()* isnan({expr}) *isnan()*
Return |TRUE| if {expr} is a float with value NaN. > Return |TRUE| if {expr} is a float with value NaN. >
echo isnan(0.0 / 0.0) echo isnan(0.0 / 0.0)
< 1 < 1
Can also be used as a |method|: >
Compute()->isnan()
jobpid({job}) *jobpid()* jobpid({job}) *jobpid()*
Return the PID (process id) of |job-id| {job}. Return the PID (process id) of |job-id| {job}.
@@ -5714,6 +5901,9 @@ join({list} [, {sep}]) *join()*
converted into a string like with |string()|. converted into a string like with |string()|.
The opposite function is |split()|. The opposite function is |split()|.
Can also be used as a |method|: >
mylist->join()
json_decode({expr}) *json_decode()* json_decode({expr}) *json_decode()*
Convert {expr} from JSON object. Accepts |readfile()|-style Convert {expr} from JSON object. Accepts |readfile()|-style
list as the input, as well as regular string. May output any list as the input, as well as regular string. May output any
@@ -5744,8 +5934,10 @@ json_encode({expr}) *json_encode()*
keys({dict}) *keys()* keys({dict}) *keys()*
Return a |List| with all the keys of {dict}. The |List| is in Return a |List| with all the keys of {dict}. The |List| is in
arbitrary order. arbitrary order.
Can also be used as a |method|: >
mydict->keys()
*len()* *E701* < *len()* *E701*
len({expr}) The result is a Number, which is the length of the argument. len({expr}) The result is a Number, which is the length of the argument.
When {expr} is a String or a Number the length in bytes is When {expr} is a String or a Number the length in bytes is
used, as with |strlen()|. used, as with |strlen()|.
@@ -5756,7 +5948,10 @@ len({expr}) The result is a Number, which is the length of the argument.
|Dictionary| is returned. |Dictionary| is returned.
Otherwise an error is given. Otherwise an error is given.
*libcall()* *E364* *E368* Can also be used as a |method|: >
mylist->len()
< *libcall()* *E364* *E368*
libcall({libname}, {funcname}, {argument}) libcall({libname}, {funcname}, {argument})
Call function {funcname} in the run-time library {libname} Call function {funcname} in the run-time library {libname}
with single argument {argument}. with single argument {argument}.
@@ -5881,6 +6076,8 @@ log({expr}) *log()*
:echo log(exp(5)) :echo log(exp(5))
< 5.0 < 5.0
Can also be used as a |method|: >
Compute()->log()
log10({expr}) *log10()* log10({expr}) *log10()*
Return the logarithm of Float {expr} to base 10 as a |Float|. Return the logarithm of Float {expr} to base 10 as a |Float|.
@@ -5891,6 +6088,9 @@ log10({expr}) *log10()*
:echo log10(0.01) :echo log10(0.01)
< -2.0 < -2.0
Can also be used as a |method|: >
Compute()->log10()
luaeval({expr}[, {expr}]) luaeval({expr}[, {expr}])
Evaluate Lua expression {expr} and return its result converted Evaluate Lua expression {expr} and return its result converted
to Vim data structures. See |lua-eval| for more details. to Vim data structures. See |lua-eval| for more details.
@@ -5939,6 +6139,8 @@ map({expr1}, {expr2}) *map()*
Funcref errors inside a function are ignored, unless it was Funcref errors inside a function are ignored, unless it was
defined with the "abort" flag. defined with the "abort" flag.
Can also be used as a |method|: >
mylist->map(expr2)
maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
When {dict} is omitted or zero: Return the rhs of mapping When {dict} is omitted or zero: Return the rhs of mapping
@@ -6274,6 +6476,9 @@ max({expr}) Return the maximum value of all items in {expr}.
items in {expr} cannot be used as a Number this results in items in {expr} cannot be used as a Number this results in
an error. An empty |List| or |Dictionary| results in zero. an error. An empty |List| or |Dictionary| results in zero.
Can also be used as a |method|: >
mylist->max()
menu_get({path}, {modes}) *menu_get()* menu_get({path}, {modes}) *menu_get()*
Returns a |List| of |Dictionaries| describing |menus| (defined Returns a |List| of |Dictionaries| describing |menus| (defined
by |:menu|, |:amenu|, …), including |hidden-menus|. by |:menu|, |:amenu|, …), including |hidden-menus|.
@@ -6328,7 +6533,10 @@ min({expr}) Return the minimum value of all items in {expr}.
items in {expr} cannot be used as a Number this results in items in {expr} cannot be used as a Number this results in
an error. An empty |List| or |Dictionary| results in zero. an error. An empty |List| or |Dictionary| results in zero.
*mkdir()* *E739* Can also be used as a |method|: >
mylist->min()
< *mkdir()* *E739*
mkdir({name} [, {path} [, {prot}]]) mkdir({name} [, {path} [, {prot}]])
Create directory {name}. Create directory {name}.
If {path} is "p" then intermediate directories are created as If {path} is "p" then intermediate directories are created as
@@ -6523,7 +6731,8 @@ or({expr}, {expr}) *or()*
to a number. A List, Dict or Float argument causes an error. to a number. A List, Dict or Float argument causes an error.
Example: > Example: >
:let bits = or(bits, 0x80) :let bits = or(bits, 0x80)
< Can also be used as a |method|: >
:let bits = bits->or(0x80)
pathshorten({expr}) *pathshorten()* pathshorten({expr}) *pathshorten()*
Shorten directory names in the path {expr} and return the Shorten directory names in the path {expr} and return the
@@ -6560,6 +6769,9 @@ pow({x}, {y}) *pow()*
:echo pow(32, 0.20) :echo pow(32, 0.20)
< 2.0 < 2.0
Can also be used as a |method|: >
Compute()->pow(3)
prevnonblank({lnum}) *prevnonblank()* prevnonblank({lnum}) *prevnonblank()*
Return the line number of the first line at or above {lnum} Return the line number of the first line at or above {lnum}
that is not blank. Example: > that is not blank. Example: >
@@ -6576,7 +6788,11 @@ printf({fmt}, {expr1} ...) *printf()*
< May result in: < May result in:
" 99: E42 asdfasdfasdfasdfasdfasdfasdfas" ~ " 99: E42 asdfasdfasdfasdfasdfasdfasdfas" ~
Often used items are: When used as a |method| the base is passed as the second
argument: >
Compute()->printf("result: %d")
< Often used items are:
%s string %s string
%6S string right-aligned in 6 display cells %6S string right-aligned in 6 display cells
%6s string right-aligned in 6 bytes %6s string right-aligned in 6 bytes
@@ -7086,6 +7302,10 @@ remove({list}, {idx} [, {end}]) *remove()*
Example: > Example: >
:echo "last item: " . remove(mylist, -1) :echo "last item: " . remove(mylist, -1)
:call remove(mylist, 0, 9) :call remove(mylist, 0, 9)
< Can also be used as a |method|: >
mylist->remove(idx)
remove({dict}, {key}) remove({dict}, {key})
Remove the entry from {dict} with key {key} and return it. Remove the entry from {dict} with key {key} and return it.
Example: > Example: >
@@ -7112,6 +7332,8 @@ repeat({expr}, {count}) *repeat()*
:let longlist = repeat(['a', 'b'], 3) :let longlist = repeat(['a', 'b'], 3)
< Results in ['a', 'b', 'a', 'b', 'a', 'b']. < Results in ['a', 'b', 'a', 'b', 'a', 'b'].
Can also be used as a |method|: >
mylist->repeat(count)
resolve({filename}) *resolve()* *E655* resolve({filename}) *resolve()* *E655*
On MS-Windows, when {filename} is a shortcut (a .lnk file), On MS-Windows, when {filename} is a shortcut (a .lnk file),
@@ -7131,6 +7353,8 @@ reverse({list}) Reverse the order of items in {list} in-place. Returns
{list}. {list}.
If you want a list to remain unmodified make a copy first: > If you want a list to remain unmodified make a copy first: >
:let revlist = reverse(copy(mylist)) :let revlist = reverse(copy(mylist))
< Can also be used as a |method|: >
mylist->reverse()
round({expr}) *round()* round({expr}) *round()*
Round off {expr} to the nearest integral value and return it Round off {expr} to the nearest integral value and return it
@@ -7145,6 +7369,9 @@ round({expr}) *round()*
echo round(-4.5) echo round(-4.5)
< -5.0 < -5.0
Can also be used as a |method|: >
Compute()->round()
rpcnotify({channel}, {event}[, {args}...]) *rpcnotify()* rpcnotify({channel}, {event}[, {args}...]) *rpcnotify()*
Sends {event} to {channel} via |RPC| and returns immediately. Sends {event} to {channel} via |RPC| and returns immediately.
If {channel} is 0, the event is broadcast to all channels. If {channel} is 0, the event is broadcast to all channels.
@@ -8121,6 +8348,8 @@ sin({expr}) *sin()*
:echo sin(-4.01) :echo sin(-4.01)
< 0.763301 < 0.763301
Can also be used as a |method|: >
Compute()->sin()
sinh({expr}) *sinh()* sinh({expr}) *sinh()*
Return the hyperbolic sine of {expr} as a |Float| in the range Return the hyperbolic sine of {expr} as a |Float| in the range
@@ -8132,6 +8361,9 @@ sinh({expr}) *sinh()*
:echo sinh(-0.9) :echo sinh(-0.9)
< -1.026517 < -1.026517
Can also be used as a |method|: >
Compute()->sinh()
sockconnect({mode}, {address}, {opts}) *sockconnect()* sockconnect({mode}, {address}, {opts}) *sockconnect()*
Connect a socket to an address. If {mode} is "pipe" then Connect a socket to an address. If {mode} is "pipe" then
{address} should be the path of a named pipe. If {mode} is {address} should be the path of a named pipe. If {mode} is
@@ -8210,7 +8442,10 @@ sort({list} [, {func} [, {dict}]]) *sort()* *E702*
on numbers, text strings will sort next to each other, in the on numbers, text strings will sort next to each other, in the
same order as they were originally. same order as they were originally.
Also see |uniq()|. Can also be used as a |method|: >
mylist->sort()
< Also see |uniq()|.
Example: > Example: >
func MyCompare(i1, i2) func MyCompare(i1, i2)
@@ -8303,6 +8538,8 @@ split({expr} [, {pattern} [, {keepempty}]]) *split()*
:let items = split(line, ':', 1) :let items = split(line, ':', 1)
< The opposite function is |join()|. < The opposite function is |join()|.
Can also be used as a |method|: >
GetString()->split()
sqrt({expr}) *sqrt()* sqrt({expr}) *sqrt()*
Return the non-negative square root of Float {expr} as a Return the non-negative square root of Float {expr} as a
@@ -8316,6 +8553,8 @@ sqrt({expr}) *sqrt()*
< nan < nan
"nan" may be different, it depends on system libraries. "nan" may be different, it depends on system libraries.
Can also be used as a |method|: >
Compute()->sqrt()
stdioopen({opts}) *stdioopen()* stdioopen({opts}) *stdioopen()*
With |--headless| this opens stdin and stdout as a |channel|. With |--headless| this opens stdin and stdout as a |channel|.
@@ -8367,6 +8606,9 @@ str2float({expr}) *str2float()*
12.0. You can strip out thousands separators with 12.0. You can strip out thousands separators with
|substitute()|: > |substitute()|: >
let f = str2float(substitute(text, ',', '', 'g')) let f = str2float(substitute(text, ',', '', 'g'))
<
Can also be used as a |method|: >
let f = text->substitute(',', '', 'g')->str2float()
str2list({expr} [, {utf8}]) *str2list()* str2list({expr} [, {utf8}]) *str2list()*
Return a list containing the number values which represent Return a list containing the number values which represent
@@ -8381,12 +8623,18 @@ str2list({expr} [, {utf8}]) *str2list()*
properly: > properly: >
str2list("á") returns [97, 769] str2list("á") returns [97, 769]
< Can also be used as a |method|: >
GetString()->str2list()
str2nr({expr} [, {base}]) *str2nr()* str2nr({expr} [, {base}]) *str2nr()*
Convert string {expr} to a number. Convert string {expr} to a number.
{base} is the conversion base, it can be 2, 8, 10 or 16. {base} is the conversion base, it can be 2, 8, 10 or 16.
When {base} is omitted base 10 is used. This also means that When {base} is omitted base 10 is used. This also means that
a leading zero doesn't cause octal conversion to be used, as a leading zero doesn't cause octal conversion to be used, as
with the default String to Number conversion. with the default String to Number conversion. Example: >
let nr = str2nr('123')
<
When {base} is 16 a leading "0x" or "0X" is ignored. With a When {base} is 16 a leading "0x" or "0X" is ignored. With a
different base the result will be zero. Similarly, when {base} different base the result will be zero. Similarly, when {base}
is 8 a leading "0" is ignored, and when {base} is 2 a leading is 8 a leading "0" is ignored, and when {base} is 2 a leading
@@ -8505,6 +8753,9 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number,
method, use |msgpackdump()| or |json_encode()| if you need to method, use |msgpackdump()| or |json_encode()| if you need to
share data with other application. share data with other application.
Can also be used as a |method|: >
mylist->string()
*strlen()* *strlen()*
strlen({expr}) The result is a Number, which is the length of the String strlen({expr}) The result is a Number, which is the length of the String
{expr} in bytes. {expr} in bytes.
@@ -8514,6 +8765,9 @@ strlen({expr}) The result is a Number, which is the length of the String
|strchars()|. |strchars()|.
Also see |len()|, |strdisplaywidth()| and |strwidth()|. Also see |len()|, |strdisplaywidth()| and |strwidth()|.
Can also be used as a |method|: >
GetString()->strlen()
strpart({src}, {start} [, {len} [, {chars}]]) *strpart()* strpart({src}, {start} [, {len} [, {chars}]]) *strpart()*
The result is a String, which is part of {src}, starting from The result is a String, which is part of {src}, starting from
byte {start}, with the byte length {len}. byte {start}, with the byte length {len}.
@@ -8588,6 +8842,9 @@ strtrans({expr}) *strtrans()*
< This displays a newline in register a as "^@" instead of < This displays a newline in register a as "^@" instead of
starting a new line. starting a new line.
Can also be used as a |method|: >
GetString()->strtrans()
strwidth({expr}) *strwidth()* strwidth({expr}) *strwidth()*
The result is a Number, which is the number of display cells The result is a Number, which is the number of display cells
String {expr} occupies. A Tab character is counted as one String {expr} occupies. A Tab character is counted as one
@@ -8596,6 +8853,9 @@ strwidth({expr}) *strwidth()*
Ambiguous, this function's return value depends on 'ambiwidth'. Ambiguous, this function's return value depends on 'ambiwidth'.
Also see |strlen()|, |strdisplaywidth()| and |strchars()|. Also see |strlen()|, |strdisplaywidth()| and |strchars()|.
Can also be used as a |method|: >
GetString()->strwidth()
submatch({nr} [, {list}]) *submatch()* *E935* submatch({nr} [, {list}]) *submatch()* *E935*
Only for an expression in a |:substitute| command or Only for an expression in a |:substitute| command or
substitute() function. substitute() function.
@@ -8663,6 +8923,9 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
|submatch()| returns. Example: > |submatch()| returns. Example: >
:echo substitute(s, '%\(\x\x\)', {m -> '0x' . m[1]}, 'g') :echo substitute(s, '%\(\x\x\)', {m -> '0x' . m[1]}, 'g')
< Can also be used as a |method|: >
GetString()->substitute(pat, sub, flags)
swapinfo({fname}) *swapinfo()* swapinfo({fname}) *swapinfo()*
The result is a dictionary, which holds information about the The result is a dictionary, which holds information about the
swapfile {fname}. The available fields are: swapfile {fname}. The available fields are:
@@ -8747,12 +9010,18 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()*
cursor): > cursor): >
:echo synIDattr(synIDtrans(synID(line("."), col("."), 1)), "fg") :echo synIDattr(synIDtrans(synID(line("."), col("."), 1)), "fg")
< <
Can also be used as a |method|: >
:echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg")
synIDtrans({synID}) *synIDtrans()* synIDtrans({synID}) *synIDtrans()*
The result is a Number, which is the translated syntax ID of The result is a Number, which is the translated syntax ID of
{synID}. This is the syntax group ID of what is being used to {synID}. This is the syntax group ID of what is being used to
highlight the character. Highlight links given with highlight the character. Highlight links given with
":highlight link" are followed. ":highlight link" are followed.
Can also be used as a |method|: >
:echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg")
synconcealed({lnum}, {col}) *synconcealed()* synconcealed({lnum}, {col}) *synconcealed()*
The result is a |List| with currently three items: The result is a |List| with currently three items:
1. The first item in the list is 0 if the character at the 1. The first item in the list is 0 if the character at the
@@ -8849,6 +9118,8 @@ system({cmd} [, {input}]) *system()* *E677*
Unlike ":!cmd" there is no automatic check for changed files. Unlike ":!cmd" there is no automatic check for changed files.
Use |:checktime| to force a check. Use |:checktime| to force a check.
Can also be used as a |method|: >
:echo GetCmd()->system()
systemlist({cmd} [, {input} [, {keepempty}]]) *systemlist()* systemlist({cmd} [, {input} [, {keepempty}]]) *systemlist()*
Same as |system()|, but returns a |List| with lines (parts of Same as |system()|, but returns a |List| with lines (parts of
@@ -8864,6 +9135,8 @@ systemlist({cmd} [, {input} [, {keepempty}]]) *systemlist()*
< <
Returns an empty string on error. Returns an empty string on error.
Can also be used as a |method|: >
:echo GetCmd()->systemlist()
tabpagebuflist([{arg}]) *tabpagebuflist()* tabpagebuflist([{arg}]) *tabpagebuflist()*
The result is a |List|, where each item is the number of the The result is a |List|, where each item is the number of the
@@ -8987,6 +9260,8 @@ tan({expr}) *tan()*
:echo tan(-4.01) :echo tan(-4.01)
< -1.181502 < -1.181502
Can also be used as a |method|: >
Compute()->tan()
tanh({expr}) *tanh()* tanh({expr}) *tanh()*
Return the hyperbolic tangent of {expr} as a |Float| in the Return the hyperbolic tangent of {expr} as a |Float| in the
@@ -8998,6 +9273,8 @@ tanh({expr}) *tanh()*
:echo tanh(-1) :echo tanh(-1)
< -0.761594 < -0.761594
Can also be used as a |method|: >
Compute()->tanh()
*timer_info()* *timer_info()*
timer_info([{id}]) timer_info([{id}])
@@ -9124,6 +9401,9 @@ trunc({expr}) *trunc()*
echo trunc(4.0) echo trunc(4.0)
< 4.0 < 4.0
Can also be used as a |method|: >
Compute()->trunc()
type({expr}) *type()* type({expr}) *type()*
The result is a Number representing the type of {expr}. The result is a Number representing the type of {expr}.
Instead of using the number directly, it is better to use the Instead of using the number directly, it is better to use the
@@ -9150,6 +9430,9 @@ type({expr}) *type()*
< To check if the v:t_ variables exist use this: > < To check if the v:t_ variables exist use this: >
:if exists('v:t_number') :if exists('v:t_number')
< Can also be used as a |method|: >
mylist->type()
undofile({name}) *undofile()* undofile({name}) *undofile()*
Return the name of the undo file that would be used for a file Return the name of the undo file that would be used for a file
with name {name} when writing. This uses the 'undodir' with name {name} when writing. This uses the 'undodir'
@@ -9212,10 +9495,15 @@ uniq({list} [, {func} [, {dict}]]) *uniq()* *E882*
< The default compare function uses the string representation of < The default compare function uses the string representation of
each item. For the use of {func} and {dict} see |sort()|. each item. For the use of {func} and {dict} see |sort()|.
Can also be used as a |method|: >
mylist->uniq()
values({dict}) *values()* values({dict}) *values()*
Return a |List| with all the values of {dict}. The |List| is Return a |List| with all the values of {dict}. The |List| is
in arbitrary order. in arbitrary order.
Can also be used as a |method|: >
mydict->values()
virtcol({expr}) *virtcol()* virtcol({expr}) *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
@@ -9392,6 +9680,9 @@ winbufnr({nr}) The result is a Number, which is the number of the buffer
When window {nr} doesn't exist, -1 is returned. When window {nr} doesn't exist, -1 is returned.
Example: > Example: >
:echo "The file in the current window is " . bufname(winbufnr(0)) :echo "The file in the current window is " . bufname(winbufnr(0))
<
Can also be used as a |method|: >
FindWindow()->winbufnr()->bufname()
< <
*wincol()* *wincol()*
wincol() The result is a Number, which is the virtual column of the wincol() The result is a Number, which is the virtual column of the
@@ -9606,6 +9897,8 @@ xor({expr}, {expr}) *xor()*
to a number. A List, Dict or Float argument causes an error. to a number. A List, Dict or Float argument causes an error.
Example: > Example: >
:let bits = xor(bits, 0x80) :let bits = xor(bits, 0x80)
< Can also be used as a |method|: >
:let bits = bits->xor(0x80)
< <
@@ -9943,7 +10236,9 @@ This function can then be called with: >
The recursiveness of user functions is restricted with the |'maxfuncdepth'| The recursiveness of user functions is restricted with the |'maxfuncdepth'|
option. option.
It is also possible to use `:eval`. It does not support a range. It is also possible to use `:eval`. It does not support a range, but does
allow for method chaining, e.g.: >
eval GetList()->Filter()->append('$')
AUTOMATICALLY LOADING FUNCTIONS ~ AUTOMATICALLY LOADING FUNCTIONS ~
@@ -10686,7 +10981,7 @@ text...
< <
*:eval* *:eval*
:eval {expr} Evaluate {expr} and discard the result. Example: > :eval {expr} Evaluate {expr} and discard the result. Example: >
:eval append(Filter(Getlist()), '$') :eval Getlist()->Filter()->append('$')
< The expression is supposed to have a side effect, < The expression is supposed to have a side effect,
since the resulting value is not used. In the example since the resulting value is not used. In the example

View File

@@ -391,6 +391,10 @@ where the args are converted to Lua values. The expression >
is equivalent to the Lua chunk > is equivalent to the Lua chunk >
return somemod.func(...) return somemod.func(...)
The `v:lua` prefix may be used to call Lua functions as |method|s. For
example: >
arg1->v:lua.somemod.func(arg2)
You can use `v:lua` in "func" options like 'tagfunc', 'omnifunc', etc. You can use `v:lua` in "func" options like 'tagfunc', 'omnifunc', etc.
For example consider the following Lua omnifunc handler: > For example consider the following Lua omnifunc handler: >

View File

@@ -53,6 +53,9 @@ assert_beeps({cmd}) *assert_beeps()*
Also see |assert_fails()|, |assert_nobeep()| and Also see |assert_fails()|, |assert_nobeep()| and
|assert-return|. |assert-return|.
Can also be used as a |method|: >
GetCmd()->assert_beeps()
<
*assert_equal()* *assert_equal()*
assert_equal({expected}, {actual} [, {msg}]) assert_equal({expected}, {actual} [, {msg}])
When {expected} and {actual} are not equal an error message is When {expected} and {actual} are not equal an error message is
@@ -69,7 +72,10 @@ assert_equal({expected}, {actual} [, {msg}])
< Will result in a string to be added to |v:errors|: < Will result in a string to be added to |v:errors|:
test.vim line 12: Expected 'foo' but got 'bar' ~ test.vim line 12: Expected 'foo' but got 'bar' ~
*assert_equalfile()* Can also be used as a |method|: >
mylist->assert_equal([1, 2, 3])
< *assert_equalfile()*
assert_equalfile({fname-one}, {fname-two}) assert_equalfile({fname-one}, {fname-two})
When the files {fname-one} and {fname-two} do not contain When the files {fname-one} and {fname-two} do not contain
exactly the same text an error message is added to |v:errors|. exactly the same text an error message is added to |v:errors|.
@@ -77,6 +83,9 @@ assert_equalfile({fname-one}, {fname-two})
When {fname-one} or {fname-two} does not exist the error will When {fname-one} or {fname-two} does not exist the error will
mention that. mention that.
Can also be used as a |method|: >
GetLog()->assert_equalfile('expected.log')
assert_exception({error} [, {msg}]) *assert_exception()* assert_exception({error} [, {msg}]) *assert_exception()*
When v:exception does not contain the string {error} an error When v:exception does not contain the string {error} an error
message is added to |v:errors|. Also see |assert-return|. message is added to |v:errors|. Also see |assert-return|.
@@ -97,6 +106,9 @@ assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()*
Note that beeping is not considered an error, and some failing Note that beeping is not considered an error, and some failing
commands only beep. Use |assert_beeps()| for those. commands only beep. Use |assert_beeps()| for those.
Can also be used as a |method|: >
GetCmd()->assert_fails('E99:')
assert_false({actual} [, {msg}]) *assert_false()* assert_false({actual} [, {msg}]) *assert_false()*
When {actual} is not false an error message is added to When {actual} is not false an error message is added to
|v:errors|, like with |assert_equal()|. |v:errors|, like with |assert_equal()|.
@@ -106,6 +118,9 @@ assert_false({actual} [, {msg}]) *assert_false()*
When {msg} is omitted an error in the form When {msg} is omitted an error in the form
"Expected False but got {actual}" is produced. "Expected False but got {actual}" is produced.
Can also be used as a |method|: >
GetResult()->assert_false()
assert_inrange({lower}, {upper}, {actual} [, {msg}]) *assert_inrange()* assert_inrange({lower}, {upper}, {actual} [, {msg}]) *assert_inrange()*
This asserts number and |Float| values. When {actual} is lower This asserts number and |Float| values. When {actual} is lower
than {lower} or higher than {upper} an error message is added than {lower} or higher than {upper} an error message is added
@@ -134,6 +149,9 @@ assert_match({pattern}, {actual} [, {msg}])
< Will result in a string to be added to |v:errors|: < Will result in a string to be added to |v:errors|:
test.vim line 12: Pattern '^f.*o$' does not match 'foobar' ~ test.vim line 12: Pattern '^f.*o$' does not match 'foobar' ~
Can also be used as a |method|: >
getFile()->assert_match('foo.*')
<
assert_nobeep({cmd}) *assert_nobeep()* assert_nobeep({cmd}) *assert_nobeep()*
Run {cmd} and add an error message to |v:errors| if it Run {cmd} and add an error message to |v:errors| if it
produces a beep or visual bell. produces a beep or visual bell.
@@ -145,16 +163,27 @@ assert_notequal({expected}, {actual} [, {msg}])
|v:errors| when {expected} and {actual} are equal. |v:errors| when {expected} and {actual} are equal.
Also see |assert-return|. Also see |assert-return|.
*assert_notmatch()* Can also be used as a |method|: >
mylist->assert_notequal([1, 2, 3])
< *assert_notmatch()*
assert_notmatch({pattern}, {actual} [, {msg}]) assert_notmatch({pattern}, {actual} [, {msg}])
The opposite of `assert_match()`: add an error message to The opposite of `assert_match()`: add an error message to
|v:errors| when {pattern} matches {actual}. |v:errors| when {pattern} matches {actual}.
Also see |assert-return|. Also see |assert-return|.
Can also be used as a |method|: >
getFile()->assert_notmatch('bar.*')
assert_report({msg}) *assert_report()* assert_report({msg}) *assert_report()*
Report a test failure directly, using {msg}. Report a test failure directly, using {msg}.
Always returns one. Always returns one.
Can also be used as a |method|: >
GetMessage()->assert_report()
assert_true({actual} [, {msg}]) *assert_true()* assert_true({actual} [, {msg}]) *assert_true()*
When {actual} is not true an error message is added to When {actual} is not true an error message is added to
|v:errors|, like with |assert_equal()|. |v:errors|, like with |assert_equal()|.
@@ -164,5 +193,8 @@ assert_true({actual} [, {msg}]) *assert_true()*
When {msg} is omitted an error in the form "Expected True but When {msg} is omitted an error in the form "Expected True but
got {actual}" is produced. got {actual}" is produced.
Can also be used as a |method|: >
GetResult()->assert_true()
<
vim:tw=78:ts=8:noet:ft=help:norl: vim:tw=78:ts=8:noet:ft=help:norl:

View File

@@ -608,12 +608,15 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
recursive++; recursive++;
try_start(); try_start();
typval_T rettv; typval_T rettv;
int dummy; funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = true;
funcexe.selfdict = self;
// call_func() retval is deceptive, ignore it. Instead we set `msg_list` // call_func() retval is deceptive, ignore it. Instead we set `msg_list`
// (see above) to capture abort-causing non-exception errors. // (see above) to capture abort-causing non-exception errors.
(void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size, (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, vim_args, &funcexe);
&dummy, true, NULL, self);
if (!try_end(err)) { if (!try_end(err)) {
rv = vim_to_object(&rettv); rv = vim_to_object(&rettv);
} }

View File

@@ -65,6 +65,8 @@ static char *e_missbrac = N_("E111: Missing ']'");
static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s");
static char *e_cannot_mod = N_("E995: Cannot modify existing variable"); static char *e_cannot_mod = N_("E995: Cannot modify existing variable");
static char *e_nowhitespace
= N_("E274: No white space allowed before parenthesis");
static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_invalwindow = N_("E957: Invalid window number");
static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s");
@@ -736,15 +738,15 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
int argc, typval_T *rettv) int argc, typval_T *rettv)
FUNC_ATTR_NONNULL_ARG(1, 2, 4) FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{ {
int dummy; funcexe_T funcexe = FUNCEXE_INIT;
if (expr->v_type == VAR_FUNC) { if (expr->v_type == VAR_FUNC) {
const char_u *const s = expr->vval.v_string; const char_u *const s = expr->vval.v_string;
if (s == NULL || *s == NUL) { if (s == NULL || *s == NUL) {
return FAIL; return FAIL;
} }
if (call_func(s, -1, rettv, argc, argv, NULL, funcexe.evaluate = true;
0L, 0L, &dummy, true, NULL, NULL) == FAIL) { if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
return FAIL; return FAIL;
} }
} else if (expr->v_type == VAR_PARTIAL) { } else if (expr->v_type == VAR_PARTIAL) {
@@ -753,8 +755,9 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
if (s == NULL || *s == NUL) { if (s == NULL || *s == NUL) {
return FAIL; return FAIL;
} }
if (call_func(s, -1, rettv, argc, argv, NULL, funcexe.evaluate = true;
0L, 0L, &dummy, true, partial, NULL) == FAIL) { funcexe.partial = partial;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
return FAIL; return FAIL;
} }
} else { } else {
@@ -1050,7 +1053,6 @@ int call_vim_function(
) )
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
int doesrange;
int ret; int ret;
int len = (int)STRLEN(func); int len = (int)STRLEN(func);
partial_T *pt = NULL; partial_T *pt = NULL;
@@ -1066,9 +1068,12 @@ int call_vim_function(
} }
rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this.
ret = call_func(func, len, rettv, argc, argv, NULL, funcexe_T funcexe = FUNCEXE_INIT;
curwin->w_cursor.lnum, curwin->w_cursor.lnum, funcexe.firstline = curwin->w_cursor.lnum;
&doesrange, true, pt, NULL); funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = true;
funcexe.partial = pt;
ret = call_func(func, len, rettv, argc, argv, &funcexe);
fail: fail:
if (ret == FAIL) { if (ret == FAIL) {
@@ -1724,7 +1729,9 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
} else { } else {
// handle d.key, l[idx], f(expr) // handle d.key, l[idx], f(expr)
const char *const arg_subsc = arg; const char *const arg_subsc = arg;
if (handle_subscript(&arg, &tv, true, true) == FAIL) { if (handle_subscript(&arg, &tv, true, true, (const char_u *)name,
(const char_u **)&name)
== FAIL) {
error = true; error = true;
} else { } else {
if (arg == arg_subsc && len == 2 && name[1] == ':') { if (arg == arg_subsc && len == 2 && name[1] == ':') {
@@ -3142,6 +3149,65 @@ static int pattern_match(char_u *pat, char_u *text, bool ic)
return matches; return matches;
} }
/// Handle a name followed by "(". Both for just "name(arg)" and for
/// "expr->name(arg)".
//
/// @param arg Points to "(", will be advanced
/// @param basetv "expr" for "expr->name(arg)"
//
/// @return OK or FAIL.
static int eval_func(char_u **const arg, char_u *const name, const int name_len,
typval_T *const rettv, const bool evaluate,
typval_T *const basetv)
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
char_u *s = name;
int len = name_len;
if (!evaluate) {
check_vars((const char *)s, len);
}
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
partial_T *partial;
s = deref_func_name((const char *)s, &len, &partial, !evaluate);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
s = xmemdupz(s, len);
// Invoke the function.
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = evaluate;
funcexe.partial = partial;
funcexe.basetv = basetv;
int ret = get_func_tv(s, len, rettv, arg, &funcexe);
xfree(s);
// If evaluate is false rettv->v_type was not set in
// get_func_tv, but it's needed in handle_subscript() to parse
// what follows. So set it here.
if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') {
rettv->vval.v_string = (char_u *)tv_empty_string;
rettv->v_type = VAR_FUNC;
}
// Stop the expression evaluation when immediately
// aborting on error, or when an interrupt occurred or
// an exception was thrown but not caught.
if (evaluate && aborting()) {
if (ret == OK) {
tv_clear(rettv);
}
ret = FAIL;
}
return ret;
}
// TODO(ZyX-I): move to eval/expressions // TODO(ZyX-I): move to eval/expressions
/* /*
@@ -3161,6 +3227,8 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
{ {
int ret; int ret;
char_u *p; char_u *p;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
p = skipwhite(arg); p = skipwhite(arg);
ret = eval1(&p, rettv, evaluate); ret = eval1(&p, rettv, evaluate);
@@ -3170,8 +3238,10 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
} }
// Report the invalid expression unless the expression evaluation has // Report the invalid expression unless the expression evaluation has
// been cancelled due to an aborting error, an interrupt, or an // been cancelled due to an aborting error, an interrupt, or an
// exception. // exception, or we already gave a more specific error.
if (!aborting()) { // Also check called_emsg for when using assert_fails().
if (!aborting() && did_emsg == did_emsg_before
&& called_emsg == called_emsg_before) {
emsgf(_(e_invexpr2), arg); emsgf(_(e_invexpr2), arg);
} }
ret = FAIL; ret = FAIL;
@@ -3801,6 +3871,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
// + in front unary plus (ignored) // + in front unary plus (ignored)
// trailing [] subscript in String or List // trailing [] subscript in String or List
// trailing .name entry in Dictionary // trailing .name entry in Dictionary
// trailing ->name() method call
// //
// "arg" must point to the first non-white of the expression. // "arg" must point to the first non-white of the expression.
// "arg" is advanced to the next non-white after the recognized expression. // "arg" is advanced to the next non-white after the recognized expression.
@@ -3816,7 +3887,7 @@ static int eval7(
varnumber_T n; varnumber_T n;
int len; int len;
char_u *s; char_u *s;
char_u *start_leader, *end_leader; const char_u *start_leader, *end_leader;
int ret = OK; int ret = OK;
char_u *alias; char_u *alias;
@@ -3968,44 +4039,7 @@ static int eval7(
ret = FAIL; ret = FAIL;
} else { } else {
if (**arg == '(') { // recursive! if (**arg == '(') { // recursive!
partial_T *partial; ret = eval_func(arg, s, len, rettv, evaluate, NULL);
if (!evaluate) {
check_vars((const char *)s, len);
}
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
s = deref_func_name((const char *)s, &len, &partial, !evaluate);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
s = xmemdupz(s, len);
// Invoke the function.
ret = get_func_tv(s, len, rettv, arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, partial, NULL);
xfree(s);
// If evaluate is false rettv->v_type was not set in
// get_func_tv, but it's needed in handle_subscript() to parse
// what follows. So set it here.
if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') {
rettv->vval.v_string = (char_u *)tv_empty_string;
rettv->v_type = VAR_FUNC;
}
// Stop the expression evaluation when immediately
// aborting on error, or when an interrupt occurred or
// an exception was thrown but not caught.
if (evaluate && aborting()) {
if (ret == OK) {
tv_clear(rettv);
}
ret = FAIL;
}
} else if (evaluate) { } else if (evaluate) {
ret = get_var_tv((const char *)s, len, rettv, NULL, true, false); ret = get_var_tv((const char *)s, len, rettv, NULL, true, false);
} else { } else {
@@ -4019,13 +4053,28 @@ static int eval7(
*arg = skipwhite(*arg); *arg = skipwhite(*arg);
// Handle following '[', '(' and '.' for expr[expr], expr.name, // Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr). // expr(expr), expr->name(expr)
if (ret == OK) { if (ret == OK) {
ret = handle_subscript((const char **)arg, rettv, evaluate, true); ret = handle_subscript((const char **)arg, rettv, evaluate, true,
start_leader, &end_leader);
} }
// Apply logical NOT and unary '-', from right to left, ignore '+'. // Apply logical NOT and unary '-', from right to left, ignore '+'.
if (ret == OK && evaluate && end_leader > start_leader) { if (ret == OK && evaluate && end_leader > start_leader) {
ret = eval7_leader(rettv, start_leader, &end_leader);
}
return ret;
}
/// Apply the leading "!" and "-" before an eval7 expression to "rettv".
/// Adjusts "end_leaderp" until it is at "start_leader".
/// @return OK on success, FAIL on failure.
static int eval7_leader(typval_T *const rettv, const char_u *const start_leader,
const char_u **const end_leaderp)
FUNC_ATTR_NONNULL_ALL
{
const char_u *end_leader = *end_leaderp;
int ret = OK;
bool error = false; bool error = false;
varnumber_T val = 0; varnumber_T val = 0;
float_T f = 0.0; float_T f = 0.0;
@@ -4040,7 +4089,7 @@ static int eval7(
ret = FAIL; ret = FAIL;
} else { } else {
while (end_leader > start_leader) { while (end_leader > start_leader) {
--end_leader; end_leader--;
if (*end_leader == '!') { if (*end_leader == '!') {
if (rettv->v_type == VAR_FLOAT) { if (rettv->v_type == VAR_FLOAT) {
f = !f; f = !f;
@@ -4064,6 +4113,170 @@ static int eval7(
rettv->vval.v_number = val; rettv->vval.v_number = val;
} }
} }
*end_leaderp = end_leader;
return ret;
}
/// Call the function referred to in "rettv".
/// @param lua_funcname If `rettv` refers to a v:lua function, this must point
/// to the name of the Lua function to call (after the
/// "v:lua." prefix).
/// @return OK on success, FAIL on failure.
static int call_func_rettv(char_u **const arg,
typval_T *const rettv,
const bool evaluate,
dict_T *const selfdict,
typval_T *const basetv,
const char_u *const lua_funcname)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
partial_T *pt = NULL;
typval_T functv;
const char_u *funcname;
bool is_lua = false;
// need to copy the funcref so that we can clear rettv
if (evaluate) {
functv = *rettv;
rettv->v_type = VAR_UNKNOWN;
// Invoke the function. Recursive!
if (functv.v_type == VAR_PARTIAL) {
pt = functv.vval.v_partial;
is_lua = is_luafunc(pt);
funcname = is_lua ? lua_funcname : partial_name(pt);
} else {
funcname = functv.vval.v_string;
}
} else {
funcname = (char_u *)"";
}
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = evaluate;
funcexe.partial = pt;
funcexe.selfdict = selfdict;
funcexe.basetv = basetv;
const int ret = get_func_tv(funcname, is_lua ? *arg - funcname : -1, rettv,
(char_u **)arg, &funcexe);
// Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55).
if (evaluate) {
tv_clear(&functv);
}
return ret;
}
/// Evaluate "->method()".
/// @param verbose if true, give error messages.
/// @note "*arg" points to the '-'.
/// @return FAIL or OK. @note "*arg" is advanced to after the ')'.
static int eval_lambda(char_u **const arg, typval_T *const rettv,
const bool evaluate, const bool verbose)
FUNC_ATTR_NONNULL_ALL
{
// Skip over the ->.
*arg += 2;
typval_T base = *rettv;
rettv->v_type = VAR_UNKNOWN;
int ret = get_lambda_tv(arg, rettv, evaluate);
if (ret == NOTDONE) {
return FAIL;
} else if (**arg != '(') {
if (verbose) {
if (*skipwhite(*arg) == '(') {
EMSG(_(e_nowhitespace));
} else {
EMSG2(_(e_missingparen), "lambda");
}
}
tv_clear(rettv);
ret = FAIL;
} else {
ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL);
}
// Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55).
if (evaluate) {
tv_clear(&base);
}
return ret;
}
/// Evaluate "->method()" or "->v:lua.method()".
/// @note "*arg" points to the '-'.
/// @return FAIL or OK. "*arg" is advanced to after the ')'.
static int eval_method(char_u **const arg, typval_T *const rettv,
const bool evaluate, const bool verbose)
FUNC_ATTR_NONNULL_ALL
{
// Skip over the ->.
*arg += 2;
typval_T base = *rettv;
rettv->v_type = VAR_UNKNOWN;
// Locate the method name.
int len;
char_u *name = *arg;
char_u *lua_funcname = NULL;
if (STRNCMP(name, "v:lua.", 6) == 0) {
lua_funcname = name + 6;
*arg = (char_u *)skip_luafunc_name((const char *)lua_funcname);
*arg = skipwhite(*arg); // to detect trailing whitespace later
len = *arg - lua_funcname;
} else {
char_u *alias;
len = get_name_len((const char **)arg, (char **)&alias, evaluate, true);
if (alias != NULL) {
name = alias;
}
}
int ret;
if (len <= 0) {
if (verbose) {
if (lua_funcname == NULL) {
EMSG(_("E260: Missing name after ->"));
} else {
EMSG2(_(e_invexpr2), name);
}
}
ret = FAIL;
} else {
if (**arg != '(') {
if (verbose) {
EMSG2(_(e_missingparen), name);
}
ret = FAIL;
} else if (ascii_iswhite((*arg)[-1])) {
if (verbose) {
EMSG(_(e_nowhitespace));
}
ret = FAIL;
} else if (lua_funcname != NULL) {
if (evaluate) {
rettv->v_type = VAR_PARTIAL;
rettv->vval.v_partial = vvlua_partial;
rettv->vval.v_partial->pt_refcount++;
}
ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname);
} else {
ret = eval_func(arg, name, len, rettv, evaluate, &base);
}
}
// Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55).
if (evaluate) {
tv_clear(&base);
} }
return ret; return ret;
@@ -7255,10 +7468,12 @@ bool callback_call(Callback *const callback, const int argcount_in,
abort(); abort();
} }
int dummy; funcexe_T funcexe = FUNCEXE_INIT;
return call_func(name, -1, rettv, argcount_in, argvars_in, funcexe.firstline = curwin->w_cursor.lnum;
NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, funcexe.lastline = curwin->w_cursor.lnum;
true, partial, NULL); funcexe.evaluate = true;
funcexe.partial = partial;
return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe);
} }
static bool set_ref_in_callback(Callback *callback, int copyID, static bool set_ref_in_callback(Callback *callback, int copyID,
@@ -8393,13 +8608,23 @@ static bool tv_is_luafunc(typval_T *tv)
return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial); return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial);
} }
/// check the function name after "v:lua." /// Skips one character past the end of the name of a v:lua function.
int check_luafunc_name(const char *str, bool paren) /// @param p Pointer to the char AFTER the "v:lua." prefix.
/// @return Pointer to the char one past the end of the function's name.
const char *skip_luafunc_name(const char *p)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{ {
const char *p = str;
while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') { while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') {
p++; p++;
} }
return p;
}
/// check the function name after "v:lua."
int check_luafunc_name(const char *const str, const bool paren)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
const char *const p = skip_luafunc_name(str);
if (*p != (paren ? '(' : NUL)) { if (*p != (paren ? '(' : NUL)) {
return 0; return 0;
} else { } else {
@@ -8407,24 +8632,26 @@ int check_luafunc_name(const char *str, bool paren)
} }
} }
/// Handle expr[expr], expr[expr:expr] subscript and .name lookup. /// Handle:
/// Also handle function call with Funcref variable: func(expr) /// - expr[expr], expr[expr:expr] subscript
/// Can all be combined: dict.func(expr)[idx]['func'](expr) /// - ".name" lookup
/// - function call with Funcref variable: func(expr)
/// - method call: var->method()
///
/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len()
int int
handle_subscript( handle_subscript(
const char **const arg, const char **const arg,
typval_T *rettv, typval_T *rettv,
int evaluate, // do more than finding the end int evaluate, // do more than finding the end
int verbose // give error messages int verbose, // give error messages
const char_u *const start_leader, // start of '!' and '-' prefixes
const char_u **const end_leaderp // end of '!' and '-' prefixes
) )
{ {
int ret = OK; int ret = OK;
dict_T *selfdict = NULL; dict_T *selfdict = NULL;
const char_u *s; const char_u *lua_funcname = NULL;
int len;
typval_T functv;
int slen = 0;
bool lua = false;
if (tv_is_luafunc(rettv)) { if (tv_is_luafunc(rettv)) {
if (**arg != '.') { if (**arg != '.') {
@@ -8433,55 +8660,28 @@ handle_subscript(
} else { } else {
(*arg)++; (*arg)++;
lua = true; lua_funcname = (char_u *)(*arg);
s = (char_u *)(*arg); const int len = check_luafunc_name(*arg, true);
slen = check_luafunc_name(*arg, true); if (len == 0) {
if (slen == 0) {
tv_clear(rettv); tv_clear(rettv);
ret = FAIL; ret = FAIL;
} }
(*arg) += slen; (*arg) += len;
} }
} }
while (ret == OK while (ret == OK
&& (**arg == '[' && (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT)
|| (**arg == '.' && rettv->v_type == VAR_DICT)
|| (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) || (**arg == '(' && (!evaluate || tv_is_func(*rettv))))
&& !ascii_iswhite(*(*arg - 1))) { && !ascii_iswhite(*(*arg - 1)))
|| (**arg == '-' && (*arg)[1] == '>'))) {
if (**arg == '(') { if (**arg == '(') {
partial_T *pt = NULL; ret = call_func_rettv((char_u **)arg, rettv, evaluate, selfdict, NULL,
// need to copy the funcref so that we can clear rettv lua_funcname);
if (evaluate) {
functv = *rettv;
rettv->v_type = VAR_UNKNOWN;
// Invoke the function. Recursive! // Stop the expression evaluation when immediately aborting on
if (functv.v_type == VAR_PARTIAL) { // error, or when an interrupt occurred or an exception was thrown
pt = functv.vval.v_partial; // but not caught.
if (!lua) {
s = partial_name(pt);
}
} else {
s = functv.vval.v_string;
}
} else {
s = (char_u *)"";
}
ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, pt, selfdict);
// Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55).
if (evaluate) {
tv_clear(&functv);
}
/* Stop the expression evaluation when immediately aborting on
* error, or when an interrupt occurred or an exception was thrown
* but not caught. */
if (aborting()) { if (aborting()) {
if (ret == OK) { if (ret == OK) {
tv_clear(rettv); tv_clear(rettv);
@@ -8490,6 +8690,21 @@ handle_subscript(
} }
tv_dict_unref(selfdict); tv_dict_unref(selfdict);
selfdict = NULL; selfdict = NULL;
} else if (**arg == '-') {
// Expression "-1.0->method()" applies the leader "-" before
// applying ->.
if (evaluate && *end_leaderp > start_leader) {
ret = eval7_leader(rettv, start_leader, end_leaderp);
}
if (ret == OK) {
if ((*arg)[2] == '{') {
// expr->{lambda}()
ret = eval_lambda((char_u **)arg, rettv, evaluate, verbose);
} else {
// expr->name()
ret = eval_method((char_u **)arg, rettv, evaluate, verbose);
}
}
} else { // **arg == '[' || **arg == '.' } else { // **arg == '[' || **arg == '.'
tv_dict_unref(selfdict); tv_dict_unref(selfdict);
if (rettv->v_type == VAR_DICT) { if (rettv->v_type == VAR_DICT) {
@@ -9274,6 +9489,7 @@ void ex_echo(exarg_T *eap)
bool atstart = true; bool atstart = true;
bool need_clear = true; bool need_clear = true;
const int did_emsg_before = did_emsg; const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
if (eap->skip) if (eap->skip)
++emsg_skip; ++emsg_skip;
@@ -9288,7 +9504,8 @@ void ex_echo(exarg_T *eap)
// Report the invalid expression unless the expression evaluation // Report the invalid expression unless the expression evaluation
// has been cancelled due to an aborting error, an interrupt, or an // has been cancelled due to an aborting error, an interrupt, or an
// exception. // exception.
if (!aborting() && did_emsg == did_emsg_before) { if (!aborting() && did_emsg == did_emsg_before
&& called_emsg == called_emsg_before) {
EMSG2(_(e_invexpr2), p); EMSG2(_(e_invexpr2), p);
} }
need_clr_eos = false; need_clr_eos = false;
@@ -10409,19 +10626,11 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments,
typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
tv_list_ref(arguments); tv_list_ref(arguments);
int dummy; funcexe_T funcexe = FUNCEXE_INIT;
(void)call_func((const char_u *)func, funcexe.firstline = curwin->w_cursor.lnum;
name_len, funcexe.lastline = curwin->w_cursor.lnum;
&rettv, funcexe.evaluate = true;
2, (void)call_func((const char_u *)func, name_len, &rettv, 2, argvars, &funcexe);
argvars,
NULL,
curwin->w_cursor.lnum,
curwin->w_cursor.lnum,
&dummy,
true,
NULL,
NULL);
tv_list_unref(arguments); tv_list_unref(arguments);
// Restore caller scope information // Restore caller scope information
@@ -10779,7 +10988,9 @@ bool var_exists(const char *var)
n = get_var_tv(name, len, &tv, NULL, false, true) == OK; n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
if (n) { if (n) {
// Handle d.key, l[idx], f(expr). // Handle d.key, l[idx], f(expr).
n = handle_subscript(&var, &tv, true, false) == OK; n = handle_subscript(&var, &tv, true, false, (const char_u *)name,
(const char_u **)&name)
== OK;
if (n) { if (n) {
tv_clear(&tv); tv_clear(&tv);
} }

View File

@@ -5,6 +5,9 @@
-- args Number of arguments, list with maximum and minimum number of arguments -- args Number of arguments, list with maximum and minimum number of arguments
-- or list with a minimum number of arguments only. Defaults to zero -- or list with a minimum number of arguments only. Defaults to zero
-- arguments. -- arguments.
-- base For methods: the argument to use as the base argument (1-indexed):
-- base->method()
-- Defaults to BASE_NONE (function cannot be used as a method).
-- func Name of the C function which implements the VimL function. Defaults to -- func Name of the C function which implements the VimL function. Defaults to
-- `f_{funcname}`. -- `f_{funcname}`.
@@ -12,111 +15,115 @@ local varargs = function(nr)
return {nr} return {nr}
end end
-- Usable with the base key: use the last function argument as the method base.
-- Value is from funcs.h file. "BASE_" prefix is omitted.
local LAST = "BASE_LAST"
return { return {
funcs={ funcs={
abs={args=1}, abs={args=1, base=1},
acos={args=1, func="float_op_wrapper", data="&acos"}, -- WJMc acos={args=1, base=1, func="float_op_wrapper", data="&acos"}, -- WJMc
add={args=2}, add={args=2, base=1},
['and']={args=2}, ['and']={args=2, base=1},
api_info={}, api_info={},
append={args=2}, append={args=2, base=LAST},
appendbufline={args=3}, appendbufline={args=3, base=LAST},
argc={args={0, 1}}, argc={args={0, 1}},
argidx={}, argidx={},
arglistid={args={0, 2}}, arglistid={args={0, 2}},
argv={args={0, 2}}, argv={args={0, 2}},
asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc
assert_beeps={args={1}}, assert_beeps={args={1}, base=1},
assert_equal={args={2, 3}}, assert_equal={args={2, 3}, base=2},
assert_equalfile={args={2, 3}}, assert_equalfile={args={2, 3}, base=1},
assert_exception={args={1, 2}}, assert_exception={args={1, 2}},
assert_fails={args={1, 3}}, assert_fails={args={1, 3}, base=1},
assert_false={args={1, 2}}, assert_false={args={1, 2}, base=1},
assert_inrange={args={3, 4}}, assert_inrange={args={3, 4}, base=3},
assert_match={args={2, 3}}, assert_match={args={2, 3}, base=2},
assert_nobeep={args={1}}, assert_nobeep={args={1}},
assert_notequal={args={2, 3}}, assert_notequal={args={2, 3}, base=2},
assert_notmatch={args={2, 3}}, assert_notmatch={args={2, 3}, base=2},
assert_report={args=1}, assert_report={args=1, base=1},
assert_true={args={1, 2}}, assert_true={args={1, 2}, base=1},
atan={args=1, func="float_op_wrapper", data="&atan"}, atan={args=1, base=1, func="float_op_wrapper", data="&atan"},
atan2={args=2}, atan2={args=2, base=1},
browse={args=4}, browse={args=4},
browsedir={args=2}, browsedir={args=2},
bufadd={args=1}, bufadd={args=1, base=1},
bufexists={args=1}, bufexists={args=1, base=1},
buffer_exists={args=1, func='f_bufexists'}, -- obsolete buffer_exists={args=1, base=1, func='f_bufexists'}, -- obsolete
buffer_name={args={0, 1}, func='f_bufname'}, -- obsolete buffer_name={args={0, 1}, func='f_bufname'}, -- obsolete
buffer_number={args={0, 1}, func='f_bufnr'}, -- obsolete buffer_number={args={0, 1}, func='f_bufnr'}, -- obsolete
buflisted={args=1}, buflisted={args=1, base=1},
bufload={args=1}, bufload={args=1, base=1},
bufloaded={args=1}, bufloaded={args=1, base=1},
bufname={args={0, 1}}, bufname={args={0, 1}, base=1},
bufnr={args={0, 2}}, bufnr={args={0, 2}, base=1},
bufwinid={args=1}, bufwinid={args=1, base=1},
bufwinnr={args=1}, bufwinnr={args=1, base=1},
byte2line={args=1}, byte2line={args=1, base=1},
byteidx={args=2}, byteidx={args=2, base=1},
byteidxcomp={args=2}, byteidxcomp={args=2, base=1},
call={args={2, 3}}, call={args={2, 3}, base=1},
ceil={args=1, func="float_op_wrapper", data="&ceil"}, ceil={args=1, base=1, func="float_op_wrapper", data="&ceil"},
changenr={}, changenr={},
chanclose={args={1, 2}}, chanclose={args={1, 2}},
chansend={args=2}, chansend={args=2},
char2nr={args={1, 2}}, char2nr={args={1, 2}, base=1},
charidx={args={2, 3}}, charidx={args={2, 3}},
cindent={args=1}, cindent={args=1, base=1},
clearmatches={args={0, 1}}, clearmatches={args={0, 1}, base=1},
col={args=1}, col={args=1, base=1},
complete={args=2}, complete={args=2, base=2},
complete_add={args=1}, complete_add={args=1, base=1},
complete_check={}, complete_check={},
complete_info={args={0, 1}}, complete_info={args={0, 1}, base=1},
confirm={args={1, 4}}, confirm={args={1, 4}, base=1},
copy={args=1}, copy={args=1, base=1},
cos={args=1, func="float_op_wrapper", data="&cos"}, cos={args=1, base=1, func="float_op_wrapper", data="&cos"},
cosh={args=1, func="float_op_wrapper", data="&cosh"}, cosh={args=1, base=1, func="float_op_wrapper", data="&cosh"},
count={args={2, 4}}, count={args={2, 4}, base=1},
cscope_connection={args={0, 3}}, cscope_connection={args={0, 3}},
ctxget={args={0, 1}}, ctxget={args={0, 1}},
ctxpop={}, ctxpop={},
ctxpush={args={0, 1}}, ctxpush={args={0, 1}},
ctxset={args={1, 2}}, ctxset={args={1, 2}},
ctxsize={}, ctxsize={},
cursor={args={1, 3}}, cursor={args={1, 3}, base=1},
debugbreak={args={1, 1}}, debugbreak={args={1, 1}, base=1},
deepcopy={args={1, 2}}, deepcopy={args={1, 2}, base=1},
delete={args={1,2}}, delete={args={1,2}, base=1},
deletebufline={args={2,3}}, deletebufline={args={2,3}, base=1},
dictwatcheradd={args=3}, dictwatcheradd={args=3},
dictwatcherdel={args=3}, dictwatcherdel={args=3},
did_filetype={}, did_filetype={},
diff_filler={args=1}, diff_filler={args=1, base=1},
diff_hlID={args=2}, diff_hlID={args=2, base=1},
empty={args=1}, empty={args=1, base=1},
environ={}, environ={},
escape={args=2}, escape={args=2},
eval={args=1}, eval={args=1, base=1},
eventhandler={}, eventhandler={},
executable={args=1}, executable={args=1},
execute={args={1, 2}}, execute={args={1, 2}},
exepath={args=1}, exepath={args=1},
exists={args=1}, exists={args=1},
exp={args=1, func="float_op_wrapper", data="&exp"}, exp={args=1, base=1, func="float_op_wrapper", data="&exp"},
expand={args={1, 3}}, expand={args={1, 3}},
expandcmd={args=1}, expandcmd={args=1},
extend={args={2, 3}}, extend={args={2, 3}, base=1},
feedkeys={args={1, 2}}, feedkeys={args={1, 2}},
file_readable={args=1, func='f_filereadable'}, -- obsolete file_readable={args=1, func='f_filereadable'}, -- obsolete
filereadable={args=1}, filereadable={args=1},
filewritable={args=1}, filewritable={args=1},
filter={args=2}, filter={args=2, base=1},
finddir={args={1, 3}}, finddir={args={1, 3}},
findfile={args={1, 3}}, findfile={args={1, 3}},
flatten={args={1, 2}}, flatten={args={1, 2}},
float2nr={args=1}, float2nr={args=1, base=1},
floor={args=1, func="float_op_wrapper", data="&floor"}, floor={args=1, base=1, func="float_op_wrapper", data="&floor"},
fmod={args=2}, fmod={args=2, base=1},
fnameescape={args=1}, fnameescape={args=1},
fnamemodify={args=2}, fnamemodify={args=2},
foldclosed={args=1}, foldclosed={args=1},
@@ -128,7 +135,7 @@ return {
funcref={args={1, 3}}, funcref={args={1, 3}},
['function']={args={1, 3}}, ['function']={args={1, 3}},
garbagecollect={args={0, 1}}, garbagecollect={args={0, 1}},
get={args={2, 3}}, get={args={2, 3}, base=1},
getbufinfo={args={0, 1}}, getbufinfo={args={0, 1}},
getbufline={args={2, 3}}, getbufline={args={2, 3}},
getbufvar={args={2, 3}}, getbufvar={args={2, 3}},
@@ -173,7 +180,7 @@ return {
glob2regpat={args=1}, glob2regpat={args=1},
globpath={args={2, 5}}, globpath={args={2, 5}},
has={args=1}, has={args=1},
has_key={args=2}, has_key={args=2, base=1},
haslocaldir={args={0,2}}, haslocaldir={args={0,2}},
hasmapto={args={1, 3}}, hasmapto={args={1, 3}},
highlightID={args=1, func='f_hlID'}, -- obsolete highlightID={args=1, func='f_hlID'}, -- obsolete
@@ -187,22 +194,22 @@ return {
hostname={}, hostname={},
iconv={args=3}, iconv={args=3},
indent={args=1}, indent={args=1},
index={args={2, 4}}, index={args={2, 4}, base=1},
input={args={1, 3}}, input={args={1, 3}},
inputdialog={args={1, 3}}, inputdialog={args={1, 3}},
inputlist={args=1}, inputlist={args=1},
inputrestore={}, inputrestore={},
inputsave={}, inputsave={},
inputsecret={args={1, 2}}, inputsecret={args={1, 2}},
insert={args={2, 3}}, insert={args={2, 3}, base=1},
interrupt={args=0}, interrupt={args=0},
invert={args=1}, invert={args=1, base=1},
isdirectory={args=1}, isdirectory={args=1},
isinf={args=1}, isinf={args=1, base=1},
islocked={args=1}, islocked={args=1},
isnan={args=1}, isnan={args=1, base=1},
id={args=1}, id={args=1},
items={args=1}, items={args=1, base=1},
jobclose={args={1, 2}, func="f_chanclose"}, jobclose={args={1, 2}, func="f_chanclose"},
jobpid={args=1}, jobpid={args=1},
jobresize={args=3}, jobresize={args=3},
@@ -210,12 +217,12 @@ return {
jobstart={args={1, 2}}, jobstart={args={1, 2}},
jobstop={args=1}, jobstop={args=1},
jobwait={args={1, 2}}, jobwait={args={1, 2}},
join={args={1, 2}}, join={args={1, 2}, base=1},
json_decode={args=1}, json_decode={args=1},
json_encode={args=1}, json_encode={args=1},
keys={args=1}, keys={args=1, base=1},
last_buffer_nr={}, -- obsolete last_buffer_nr={}, -- obsolete
len={args=1}, len={args=1, base=1},
libcall={args=3}, libcall={args=3},
libcallnr={args=3}, libcallnr={args=3},
line={args={1, 2}}, line={args={1, 2}},
@@ -223,10 +230,10 @@ return {
lispindent={args=1}, lispindent={args=1},
list2str={args={1, 2}}, list2str={args={1, 2}},
localtime={}, localtime={},
log={args=1, func="float_op_wrapper", data="&log"}, log={args=1, base=1, func="float_op_wrapper", data="&log"},
log10={args=1, func="float_op_wrapper", data="&log10"}, log10={args=1, base=1, func="float_op_wrapper", data="&log10"},
luaeval={args={1, 2}}, luaeval={args={1, 2}},
map={args=2}, map={args=2, base=1},
maparg={args={1, 4}}, maparg={args={1, 4}},
mapcheck={args={1, 3}}, mapcheck={args={1, 3}},
match={args={2, 4}}, match={args={2, 4}},
@@ -238,20 +245,20 @@ return {
matchlist={args={2, 4}}, matchlist={args={2, 4}},
matchstr={args={2, 4}}, matchstr={args={2, 4}},
matchstrpos={args={2,4}}, matchstrpos={args={2,4}},
max={args=1}, max={args=1, base=1},
menu_get={args={1, 2}}, menu_get={args={1, 2}},
min={args=1}, min={args=1, base=1},
mkdir={args={1, 3}}, mkdir={args={1, 3}},
mode={args={0, 1}}, mode={args={0, 1}},
msgpackdump={args=1}, msgpackdump={args=1},
msgpackparse={args=1}, msgpackparse={args=1},
nextnonblank={args=1}, nextnonblank={args=1},
nr2char={args={1, 2}}, nr2char={args={1, 2}},
['or']={args=2}, ['or']={args=2, base=1},
pathshorten={args=1}, pathshorten={args=1},
pow={args=2}, pow={args=2, base=1},
prevnonblank={args=1}, prevnonblank={args=1},
printf={args=varargs(1)}, printf={args=varargs(1), base=2},
prompt_getprompt={args=1}, prompt_getprompt={args=1},
prompt_setcallback={args={2, 2}}, prompt_setcallback={args={2, 2}},
prompt_setinterrupt={args={2, 2}}, prompt_setinterrupt={args={2, 2}},
@@ -270,12 +277,12 @@ return {
reltime={args={0, 2}}, reltime={args={0, 2}},
reltimefloat={args=1}, reltimefloat={args=1},
reltimestr={args=1}, reltimestr={args=1},
remove={args={2, 3}}, remove={args={2, 3}, base=1},
rename={args=2}, rename={args=2},
['repeat']={args=2}, ['repeat']={args=2, base=1},
resolve={args=1}, resolve={args=1},
reverse={args=1}, reverse={args=1, base=1},
round={args=1, func="float_op_wrapper", data="&round"}, round={args=1, base=1, func="float_op_wrapper", data="&round"},
rpcnotify={args=varargs(2)}, rpcnotify={args=varargs(2)},
rpcrequest={args=varargs(2)}, rpcrequest={args=varargs(2)},
rpcstart={args={1, 2}}, rpcstart={args={1, 2}},
@@ -324,19 +331,19 @@ return {
sign_unplace={args={1, 2}}, sign_unplace={args={1, 2}},
sign_unplacelist={args={1}}, sign_unplacelist={args={1}},
simplify={args=1}, simplify={args=1},
sin={args=1, func="float_op_wrapper", data="&sin"}, sin={args=1, base=1, func="float_op_wrapper", data="&sin"},
sinh={args=1, func="float_op_wrapper", data="&sinh"}, sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"},
sockconnect={args={2,3}}, sockconnect={args={2,3}},
sort={args={1, 3}}, sort={args={1, 3}, base=1},
soundfold={args=1}, soundfold={args=1},
stdioopen={args=1}, stdioopen={args=1},
spellbadword={args={0, 1}}, spellbadword={args={0, 1}},
spellsuggest={args={1, 3}}, spellsuggest={args={1, 3}},
split={args={1, 3}}, split={args={1, 3}, base=1},
sqrt={args=1, func="float_op_wrapper", data="&sqrt"}, sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"},
stdpath={args=1}, stdpath={args=1},
str2float={args=1}, str2float={args=1, base=1},
str2list={args={1, 2}}, str2list={args={1, 2}, base=1},
str2nr={args={1, 2}}, str2nr={args={1, 2}},
strcharpart={args={2, 3}}, strcharpart={args={2, 3}},
strchars={args={1,2}}, strchars={args={1,2}},
@@ -344,31 +351,31 @@ return {
strftime={args={1, 2}}, strftime={args={1, 2}},
strgetchar={args={2, 2}}, strgetchar={args={2, 2}},
stridx={args={2, 3}}, stridx={args={2, 3}},
string={args=1}, string={args=1, base=1},
strlen={args=1}, strlen={args=1, base=1},
strpart={args={2, 4}}, strpart={args={2, 4}},
strptime={args=2}, strptime={args=2},
strridx={args={2, 3}}, strridx={args={2, 3}},
strtrans={args=1}, strtrans={args=1, base=1},
strwidth={args=1}, strwidth={args=1, base=1},
submatch={args={1, 2}}, submatch={args={1, 2}},
substitute={args=4}, substitute={args=4, base=1},
swapinfo={args={1}}, swapinfo={args={1}},
swapname={args={1}}, swapname={args={1}},
synID={args=3}, synID={args=3},
synIDattr={args={2, 3}}, synIDattr={args={2, 3}, base=1},
synIDtrans={args=1}, synIDtrans={args=1, base=1},
synconcealed={args=2}, synconcealed={args=2},
synstack={args=2}, synstack={args=2},
system={args={1, 2}}, system={args={1, 2}, base=1},
systemlist={args={1, 3}}, systemlist={args={1, 3}, base=1},
tabpagebuflist={args={0, 1}}, tabpagebuflist={args={0, 1}},
tabpagenr={args={0, 1}}, tabpagenr={args={0, 1}},
tabpagewinnr={args={1, 2}}, tabpagewinnr={args={1, 2}},
tagfiles={}, tagfiles={},
taglist={args={1, 2}}, taglist={args={1, 2}},
tan={args=1, func="float_op_wrapper", data="&tan"}, tan={args=1, base=1, func="float_op_wrapper", data="&tan"},
tanh={args=1, func="float_op_wrapper", data="&tanh"}, tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"},
tempname={}, tempname={},
termopen={args={1, 2}}, termopen={args={1, 2}},
test_garbagecollect_now={}, test_garbagecollect_now={},
@@ -382,12 +389,12 @@ return {
toupper={args=1}, toupper={args=1},
tr={args=3}, tr={args=3},
trim={args={1,3}}, trim={args={1,3}},
trunc={args=1, func="float_op_wrapper", data="&trunc"}, trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"},
type={args=1}, type={args=1, base=1},
undofile={args=1}, undofile={args=1},
undotree={}, undotree={},
uniq={args={1, 3}}, uniq={args={1, 3}, base=1},
values={args=1}, values={args=1, base=1},
virtcol={args=1}, virtcol={args=1},
visualmode={args={0, 1}}, visualmode={args={0, 1}},
wait={args={2,3}}, wait={args={2,3}},
@@ -401,7 +408,7 @@ return {
win_id2win={args=1}, win_id2win={args=1},
win_screenpos={args=1}, win_screenpos={args=1},
win_splitmove={args={2, 3}}, win_splitmove={args={2, 3}},
winbufnr={args=1}, winbufnr={args=1, base=1},
wincol={}, wincol={},
windowsversion={}, windowsversion={},
winheight={args=1}, winheight={args=1},
@@ -414,6 +421,6 @@ return {
winwidth={args=1}, winwidth={args=1},
wordcount={}, wordcount={},
writefile={args={2, 3}}, writefile={args={2, 3}},
xor={args=2}, xor={args=2, base=1},
}, },
} }

View File

@@ -175,6 +175,53 @@ const VimLFuncDef *find_internal_func(const char *const name)
return find_internal_func_gperf(name, len); return find_internal_func_gperf(name, len);
} }
int call_internal_func(const char_u *const fname, const int argcount,
typval_T *const argvars, typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL
{
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
if (fdef == NULL) {
return ERROR_UNKNOWN;
} else if (argcount < fdef->min_argc) {
return ERROR_TOOFEW;
} else if (argcount > fdef->max_argc) {
return ERROR_TOOMANY;
}
argvars[argcount].v_type = VAR_UNKNOWN;
fdef->func(argvars, rettv, fdef->data);
return ERROR_NONE;
}
/// Invoke a method for base->method().
int call_internal_method(const char_u *const fname, const int argcount,
typval_T *const argvars, typval_T *const rettv,
typval_T *const basetv)
FUNC_ATTR_NONNULL_ALL
{
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
if (fdef == NULL) {
return ERROR_UNKNOWN;
} else if (fdef->base_arg == BASE_NONE) {
return ERROR_NOTMETHOD;
} else if (argcount + 1 < fdef->min_argc) {
return ERROR_TOOFEW;
} else if (argcount + 1 > fdef->max_argc) {
return ERROR_TOOMANY;
}
typval_T argv[MAX_FUNC_ARGS + 1];
const ptrdiff_t base_index
= fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1;
memcpy(argv, argvars, base_index * sizeof(typval_T));
argv[base_index] = *basetv;
memcpy(argv + base_index + 1, argvars + base_index,
(argcount - base_index) * sizeof(typval_T));
argv[argcount + 1].v_type = VAR_UNKNOWN;
fdef->func(argv, rettv, fdef->data);
return ERROR_NONE;
}
/* /*
* Return TRUE for a non-zero Number and a non-empty String. * Return TRUE for a non-zero Number and a non-empty String.
*/ */
@@ -9420,7 +9467,6 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
int res; int res;
typval_T rettv; typval_T rettv;
typval_T argv[3]; typval_T argv[3];
int dummy;
const char *func_name; const char *func_name;
partial_T *partial = sortinfo->item_compare_partial; partial_T *partial = sortinfo->item_compare_partial;
@@ -9444,10 +9490,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]);
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this
res = call_func((const char_u *)func_name, funcexe_T funcexe = FUNCEXE_INIT;
-1, funcexe.evaluate = true;
&rettv, 2, argv, NULL, 0L, 0L, &dummy, true, funcexe.partial = partial;
partial, sortinfo->item_compare_selfdict); funcexe.selfdict = sortinfo->item_compare_selfdict;
res = call_func((const char_u *)func_name, -1, &rettv, 2, argv, &funcexe);
tv_clear(&argv[0]); tv_clear(&argv[0]);
tv_clear(&argv[1]); tv_clear(&argv[1]);

View File

@@ -9,11 +9,16 @@ typedef void (*FunPtr)(void);
/// Prototype of C function that implements VimL function /// Prototype of C function that implements VimL function
typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data);
/// Special flags for base_arg @see VimLFuncDef
#define BASE_NONE 0 ///< Not a method (no base argument).
#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base.
/// Structure holding VimL function definition /// Structure holding VimL function definition
typedef struct fst { typedef struct fst {
char *name; ///< Name of the function. char *name; ///< Name of the function.
uint8_t min_argc; ///< Minimal number of arguments. uint8_t min_argc; ///< Minimal number of arguments.
uint8_t max_argc; ///< Maximal number of arguments. uint8_t max_argc; ///< Maximal number of arguments.
uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST.
VimLFunc func; ///< Function implementation. VimLFunc func; ///< Function implementation.
FunPtr data; ///< Userdata for function implementation. FunPtr data; ///< Userdata for function implementation.
} VimLFuncDef; } VimLFuncDef;

View File

@@ -414,12 +414,7 @@ get_func_tv(
int len, // length of "name" or -1 to use strlen() int len, // length of "name" or -1 to use strlen()
typval_T *rettv, typval_T *rettv,
char_u **arg, // argument, pointing to the '(' char_u **arg, // argument, pointing to the '('
linenr_T firstline, // first line of range funcexe_T *funcexe // various values
linenr_T lastline, // last line of range
int *doesrange, // return: function handled range
int evaluate,
partial_T *partial, // for extra arguments
dict_T *selfdict // Dictionary for "self"
) )
{ {
char_u *argp; char_u *argp;
@@ -431,12 +426,13 @@ get_func_tv(
* Get the arguments. * Get the arguments.
*/ */
argp = *arg; argp = *arg;
while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { while (argcount < MAX_FUNC_ARGS
- (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) {
argp = skipwhite(argp + 1); // skip the '(' or ',' argp = skipwhite(argp + 1); // skip the '(' or ','
if (*argp == ')' || *argp == ',' || *argp == NUL) { if (*argp == ')' || *argp == ',' || *argp == NUL) {
break; break;
} }
if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) { if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
ret = FAIL; ret = FAIL;
break; break;
} }
@@ -463,9 +459,7 @@ get_func_tv(
((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i]; ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
} }
} }
ret = call_func(name, len, rettv, argcount, argvars, NULL, ret = call_func(name, len, rettv, argcount, argvars, funcexe);
firstline, lastline, doesrange, evaluate,
partial, selfdict);
funcargs.ga_len -= i; funcargs.ga_len -= i;
} else if (!aborting()) { } else if (!aborting()) {
@@ -1367,7 +1361,6 @@ int func_call(char_u *name, typval_T *args, partial_T *partial,
{ {
typval_T argv[MAX_FUNC_ARGS + 1]; typval_T argv[MAX_FUNC_ARGS + 1];
int argc = 0; int argc = 0;
int dummy;
int r = 0; int r = 0;
TV_LIST_ITER(args->vval.v_list, item, { TV_LIST_ITER(args->vval.v_list, item, {
@@ -1380,9 +1373,13 @@ int func_call(char_u *name, typval_T *args, partial_T *partial,
tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]);
}); });
r = call_func(name, -1, rettv, argc, argv, NULL, funcexe_T funcexe = FUNCEXE_INIT;
curwin->w_cursor.lnum, curwin->w_cursor.lnum, funcexe.firstline = curwin->w_cursor.lnum;
&dummy, true, partial, selfdict); funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = true;
funcexe.partial = partial;
funcexe.selfdict = selfdict;
r = call_func(name, -1, rettv, argc, argv, &funcexe);
func_call_skip_call: func_call_skip_call:
// Free the arguments. // Free the arguments.
@@ -1402,6 +1399,9 @@ static void user_func_error(int error, const char_u *name)
case ERROR_UNKNOWN: case ERROR_UNKNOWN:
emsg_funcname(N_("E117: Unknown function: %s"), name); emsg_funcname(N_("E117: Unknown function: %s"), name);
break; break;
case ERROR_NOTMETHOD:
emsg_funcname(N_("E276: Cannot use function as a method: %s"), name);
break;
case ERROR_DELETED: case ERROR_DELETED:
emsg_funcname(N_("E933: Function was deleted: %s"), name); emsg_funcname(N_("E933: Function was deleted: %s"), name);
break; break;
@@ -1423,12 +1423,25 @@ static void user_func_error(int error, const char_u *name)
} }
} }
/// Used by call_func to add a method base (if any) to a function argument list
/// as the first argument. @see call_func
static void argv_add_base(typval_T *const basetv, typval_T **const argvars,
int *const argcount, typval_T *const new_argvars,
int *const argv_base)
FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5)
{
if (basetv != NULL) {
// Method call: base->Method()
memmove(&new_argvars[1], *argvars, sizeof(typval_T) * (*argcount));
new_argvars[0] = *basetv;
(*argcount)++;
*argvars = new_argvars;
*argv_base = 1;
}
}
/// Call a function with its resolved parameters /// Call a function with its resolved parameters
/// ///
/// "argv_func", when not NULL, can be used to fill in arguments only when the
/// invoked function uses them. It is called like this:
/// new_argcount = argv_func(current_argcount, argv, called_func_argcount)
///
/// @return FAIL if function cannot be called, else OK (even if an error /// @return FAIL if function cannot be called, else OK (even if an error
/// occurred while executing the function! Set `msg_list` to capture /// occurred while executing the function! Set `msg_list` to capture
/// the error, see do_cmdline()). /// the error, see do_cmdline()).
@@ -1440,15 +1453,9 @@ call_func(
int argcount_in, // number of "argvars" int argcount_in, // number of "argvars"
typval_T *argvars_in, // vars for arguments, must have "argcount" typval_T *argvars_in, // vars for arguments, must have "argcount"
// PLUS ONE elements! // PLUS ONE elements!
ArgvFunc argv_func, // function to fill in argvars funcexe_T *funcexe // more arguments
linenr_T firstline, // first line of range
linenr_T lastline, // last line of range
int *doesrange, // [out] function handled range
bool evaluate,
partial_T *partial, // optional, can be NULL
dict_T *selfdict_in // Dictionary for "self"
) )
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 9) FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6)
{ {
int ret = FAIL; int ret = FAIL;
int error = ERROR_NONE; int error = ERROR_NONE;
@@ -1459,9 +1466,12 @@ call_func(
char_u *name = NULL; char_u *name = NULL;
int argcount = argcount_in; int argcount = argcount_in;
typval_T *argvars = argvars_in; typval_T *argvars = argvars_in;
dict_T *selfdict = selfdict_in; dict_T *selfdict = funcexe->selfdict;
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" is not NULL typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
// "funcexe->basetv" is not NULL
int argv_clear = 0; int argv_clear = 0;
int argv_base = 0;
partial_T *partial = funcexe->partial;
// Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv)
// even when call_func() returns FAIL. // even when call_func() returns FAIL.
@@ -1480,14 +1490,15 @@ call_func(
fname = fname_trans_sid(name, fname_buf, &tofree, &error); fname = fname_trans_sid(name, fname_buf, &tofree, &error);
} }
*doesrange = false; if (funcexe->doesrange != NULL) {
*funcexe->doesrange = false;
}
if (partial != NULL) { if (partial != NULL) {
// When the function has a partial with a dict and there is a dict // When the function has a partial with a dict and there is a dict
// argument, use the dict argument. That is backwards compatible. // argument, use the dict argument. That is backwards compatible.
// When the dict was bound explicitly use the one from the partial. // When the dict was bound explicitly use the one from the partial.
if (partial->pt_dict != NULL if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) {
&& (selfdict_in == NULL || !partial->pt_auto)) {
selfdict = partial->pt_dict; selfdict = partial->pt_dict;
} }
if (error == ERROR_NONE && partial->pt_argc > 0) { if (error == ERROR_NONE && partial->pt_argc > 0) {
@@ -1506,7 +1517,7 @@ call_func(
} }
} }
if (error == ERROR_NONE && evaluate) { if (error == ERROR_NONE && funcexe->evaluate) {
char_u *rfname = fname; char_u *rfname = fname;
// Ignore "g:" before a function name. // Ignore "g:" before a function name.
@@ -1521,7 +1532,12 @@ call_func(
if (is_luafunc(partial)) { if (is_luafunc(partial)) {
if (len > 0) { if (len > 0) {
error = ERROR_NONE; error = ERROR_NONE;
argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv); nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv);
} else {
// v:lua was called directly; show its name in the emsg
XFREE_CLEAR(name);
funcname = (const char_u *)"v:lua";
} }
} else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) {
// User defined function. // User defined function.
@@ -1549,13 +1565,16 @@ call_func(
cfunc_T cb = fp->uf_cb; cfunc_T cb = fp->uf_cb;
error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state); error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state);
} else if (fp != NULL) { } else if (fp != NULL) {
if (argv_func != NULL) { if (funcexe->argv_func != NULL) {
// postponed filling in the arguments, do it now // postponed filling in the arguments, do it now
argcount = argv_func(argcount, argvars, argv_clear, argcount = funcexe->argv_func(argcount, argvars, argv_clear,
fp->uf_args.ga_len); fp->uf_args.ga_len);
} }
if (fp->uf_flags & FC_RANGE) {
*doesrange = true; argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) {
*funcexe->doesrange = true;
} }
if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) { if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
error = ERROR_TOOFEW; error = ERROR_TOOFEW;
@@ -1565,25 +1584,20 @@ call_func(
error = ERROR_DICT; error = ERROR_DICT;
} else { } else {
// Call the user function. // Call the user function.
call_user_func(fp, argcount, argvars, rettv, firstline, lastline, call_user_func(fp, argcount, argvars, rettv, funcexe->firstline,
funcexe->lastline,
(fp->uf_flags & FC_DICT) ? selfdict : NULL); (fp->uf_flags & FC_DICT) ? selfdict : NULL);
error = ERROR_NONE; error = ERROR_NONE;
} }
} }
} else if (funcexe->basetv != NULL) {
// expr->method(): Find the method name in the table, call its
// implementation with the base as one of the arguments.
error = call_internal_method(fname, argcount, argvars, rettv,
funcexe->basetv);
} else { } else {
// Find the function name in the table, call its implementation. // Find the function name in the table, call its implementation.
const VimLFuncDef *const fdef = find_internal_func((const char *)fname); error = call_internal_func(fname, argcount, argvars, rettv);
if (fdef != NULL) {
if (argcount < fdef->min_argc) {
error = ERROR_TOOFEW;
} else if (argcount > fdef->max_argc) {
error = ERROR_TOOMANY;
} else {
argvars[argcount].v_type = VAR_UNKNOWN;
fdef->func(argvars, rettv, fdef->data);
error = ERROR_NONE;
}
}
} }
/* /*
* The function call (or "FuncUndefined" autocommand sequence) might * The function call (or "FuncUndefined" autocommand sequence) might
@@ -1607,9 +1621,11 @@ theend:
user_func_error(error, (name != NULL) ? name : funcname); user_func_error(error, (name != NULL) ? name : funcname);
} }
// clear the copies made from the partial
while (argv_clear > 0) { while (argv_clear > 0) {
tv_clear(&argv[--argv_clear]); tv_clear(&argv[--argv_clear + argv_base]);
} }
xfree(tofree); xfree(tofree);
xfree(name); xfree(name);
@@ -2901,7 +2917,7 @@ void ex_call(exarg_T *eap)
int len; int len;
typval_T rettv; typval_T rettv;
linenr_T lnum; linenr_T lnum;
int doesrange; bool doesrange;
bool failed = false; bool failed = false;
funcdict_T fudi; funcdict_T fudi;
partial_T *partial = NULL; partial_T *partial = NULL;
@@ -2947,7 +2963,7 @@ void ex_call(exarg_T *eap)
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
if (*startarg != '(') { if (*startarg != '(') {
EMSG2(_("E107: Missing parentheses: %s"), eap->arg); EMSG2(_(e_missingparen), eap->arg);
goto end; goto end;
} }
@@ -2965,15 +2981,22 @@ void ex_call(exarg_T *eap)
curwin->w_cursor.coladd = 0; curwin->w_cursor.coladd = 0;
} }
arg = startarg; arg = startarg;
if (get_func_tv(name, -1, &rettv, &arg,
eap->line1, eap->line2, &doesrange, funcexe_T funcexe = FUNCEXE_INIT;
true, partial, fudi.fd_dict) == FAIL) { funcexe.firstline = eap->line1;
funcexe.lastline = eap->line2;
funcexe.doesrange = &doesrange;
funcexe.evaluate = true;
funcexe.partial = partial;
funcexe.selfdict = fudi.fd_dict;
if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
failed = true; failed = true;
break; break;
} }
// Handle a function returning a Funcref, Dictionary or List. // Handle a function returning a Funcref, Dictionary or List.
if (handle_subscript((const char **)&arg, &rettv, true, true) if (handle_subscript((const char **)&arg, &rettv, true, true,
(const char_u *)name, (const char_u **)&name)
== FAIL) { == FAIL) {
failed = true; failed = true;
break; break;

View File

@@ -28,11 +28,37 @@ typedef enum {
ERROR_OTHER, ERROR_OTHER,
ERROR_BOTH, ERROR_BOTH,
ERROR_DELETED, ERROR_DELETED,
ERROR_NOTMETHOD,
} FnameTransError; } FnameTransError;
/// Used in funcexe_T. Returns the new argcount.
typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip, typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip,
int called_func_argcount); int called_func_argcount);
/// Structure passed between functions dealing with function call execution.
typedef struct {
ArgvFunc argv_func; ///< when not NULL, can be used to fill in arguments only
///< when the invoked function uses them
linenr_T firstline; ///< first line of range
linenr_T lastline; ///< last line of range
bool *doesrange; ///< [out] if not NULL: function handled range
bool evaluate; ///< actually evaluate expressions
partial_T *partial; ///< for extra arguments
dict_T *selfdict; ///< Dictionary for "self"
typval_T *basetv; ///< base for base->method()
} funcexe_T;
#define FUNCEXE_INIT (funcexe_T) { \
.argv_func = NULL, \
.firstline = 0, \
.lastline = 0, \
.doesrange = NULL, \
.evaluate = false, \
.partial = NULL, \
.selfdict = NULL, \
.basetv = NULL, \
}
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]

View File

@@ -42,7 +42,7 @@ gperfpipe:write([[
%language=ANSI-C %language=ANSI-C
%global-table %global-table
%readonly-tables %readonly-tables
%define initializer-suffix ,0,0,NULL,NULL %define initializer-suffix ,0,0,BASE_NONE,NULL,NULL
%define word-array-name functions %define word-array-name functions
%define hash-function-name hash_internal_func_gperf %define hash-function-name hash_internal_func_gperf
%define lookup-function-name find_internal_func_gperf %define lookup-function-name find_internal_func_gperf
@@ -59,9 +59,10 @@ for name, def in pairs(funcs) do
elseif #args == 1 then elseif #args == 1 then
args[2] = 'MAX_FUNC_ARGS' args[2] = 'MAX_FUNC_ARGS'
end end
local base = def.base or "BASE_NONE"
local func = def.func or ('f_' .. name) local func = def.func or ('f_' .. name)
local data = def.data or "NULL" local data = def.data or "NULL"
gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n') gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n')
:format(name, args[1], args[2], func, data)) :format(name, args[1], args[2], base, func, data))
end end
gperfpipe:close() gperfpipe:close()

View File

@@ -972,6 +972,7 @@ EXTERN char_u e_write[] INIT(= N_("E80: Error while writing"));
EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required")); EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required"));
EXTERN char_u e_usingsid[] INIT(= N_( EXTERN char_u e_usingsid[] INIT(= N_(
"E81: Using <SID> not in a script context")); "E81: Using <SID> not in a script context"));
EXTERN char_u e_missingparen[] INIT(= N_("E107: Missing parentheses: %s"));
EXTERN char_u e_maxmempat[] INIT(= N_( EXTERN char_u e_maxmempat[] INIT(= N_(
"E363: pattern uses more memory than 'maxmempattern'")); "E363: pattern uses more memory than 'maxmempattern'"));
EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer")); EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer"));

View File

@@ -785,13 +785,13 @@ int nlua_call(lua_State *lstate)
try_start(); try_start();
typval_T rettv; typval_T rettv;
int dummy; funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = true;
// call_func() retval is deceptive, ignore it. Instead we set `msg_list` // call_func() retval is deceptive, ignore it. Instead we set `msg_list`
// (TRY_WRAP) to capture abort-causing non-exception errors. // (TRY_WRAP) to capture abort-causing non-exception errors.
(void)call_func(name, (int)name_len, &rettv, nargs, (void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
vim_args, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&dummy, true, NULL, NULL);
if (!try_end(&err)) { if (!try_end(&err)) {
nlua_push_typval(lstate, &rettv, false); nlua_push_typval(lstate, &rettv, false);
} }

View File

@@ -2531,12 +2531,12 @@ do_mouse (
} }
}; };
typval_T rettv; typval_T rettv;
int doesrange; funcexe_T funcexe = FUNCEXE_INIT;
(void)call_func((char_u *)tab_page_click_defs[mouse_col].func, funcexe.firstline = curwin->w_cursor.lnum;
-1, funcexe.lastline = curwin->w_cursor.lnum;
&rettv, ARRAY_SIZE(argv), argv, NULL, funcexe.evaluate = true;
curwin->w_cursor.lnum, curwin->w_cursor.lnum, (void)call_func((char_u *)tab_page_click_defs[mouse_col].func, -1,
&doesrange, true, NULL, NULL); &rettv, ARRAY_SIZE(argv), argv, &funcexe);
tv_clear(&rettv); tv_clear(&rettv);
break; break;
} }

View File

@@ -6726,26 +6726,24 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
if (expr != NULL) { if (expr != NULL) {
typval_T argv[2]; typval_T argv[2];
int dummy;
typval_T rettv; typval_T rettv;
staticList10_T matchList = TV_LIST_STATIC10_INIT; staticList10_T matchList = TV_LIST_STATIC10_INIT;
rettv.v_type = VAR_STRING; rettv.v_type = VAR_STRING;
rettv.vval.v_string = NULL; rettv.vval.v_string = NULL;
argv[0].v_type = VAR_LIST; argv[0].v_type = VAR_LIST;
argv[0].vval.v_list = &matchList.sl_list; argv[0].vval.v_list = &matchList.sl_list;
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.argv_func = fill_submatch_list;
funcexe.evaluate = true;
if (expr->v_type == VAR_FUNC) { if (expr->v_type == VAR_FUNC) {
s = expr->vval.v_string; s = expr->vval.v_string;
call_func(s, -1, &rettv, 1, argv, call_func(s, -1, &rettv, 1, argv, &funcexe);
fill_submatch_list, 0L, 0L, &dummy,
true, NULL, NULL);
} else if (expr->v_type == VAR_PARTIAL) { } else if (expr->v_type == VAR_PARTIAL) {
partial_T *partial = expr->vval.v_partial; partial_T *partial = expr->vval.v_partial;
s = partial_name(partial); s = partial_name(partial);
call_func(s, -1, &rettv, 1, argv, funcexe.partial = partial;
fill_submatch_list, 0L, 0L, &dummy, call_func(s, -1, &rettv, 1, argv, &funcexe);
true, partial, NULL);
} }
if (tv_list_len(&matchList.sl_list) > 0) { if (tv_list_len(&matchList.sl_list) > 0) {
// fill_submatch_list() was called. // fill_submatch_list() was called.

View File

@@ -5,3 +5,7 @@ let foo#bar = {}
func foo#bar.echo() func foo#bar.echo()
let g:called_foo_bar_echo += 1 let g:called_foo_bar_echo += 1
endfunc endfunc
func foo#addFoo(head)
return a:head .. 'foo'
endfunc

View File

@@ -90,8 +90,8 @@ func Test_argadd_empty_curbuf()
call assert_equal('', bufname('%')) call assert_equal('', bufname('%'))
call assert_equal(1, line('$')) call assert_equal(1, line('$'))
rew rew
call assert_notequal(curbuf, bufnr('%')) call assert_notequal(curbuf, '%'->bufnr())
call assert_equal('Xargadd', bufname('%')) call assert_equal('Xargadd', '%'->bufname())
call assert_equal(2, line('$')) call assert_equal(2, line('$'))
%argd %argd

View File

@@ -7,7 +7,7 @@ func Test_assert_equalfile()
let goodtext = ["one", "two", "three"] let goodtext = ["one", "two", "three"]
call writefile(goodtext, 'Xone') call writefile(goodtext, 'Xone')
call assert_equal(1, assert_equalfile('Xone', 'xyzxyz')) call assert_equal(1, 'Xone'->assert_equalfile('xyzxyz'))
call assert_match("E485: Can't read file xyzxyz", v:errors[0]) call assert_match("E485: Can't read file xyzxyz", v:errors[0])
call remove(v:errors, 0) call remove(v:errors, 0)

View File

@@ -8,6 +8,8 @@ func Test_autoload_dict_func()
call g:foo#bar.echo() call g:foo#bar.echo()
call assert_equal(1, g:loaded_foo_vim) call assert_equal(1, g:loaded_foo_vim)
call assert_equal(1, g:called_foo_bar_echo) call assert_equal(1, g:called_foo_bar_echo)
eval 'bar'->g:foo#addFoo()->assert_equal('barfoo')
endfunc endfunc
func Test_source_autoload() func Test_source_autoload()

View File

@@ -102,7 +102,7 @@ func Test_deletebufline()
call assert_equal(0, deletebufline(b, 2, 8)) call assert_equal(0, deletebufline(b, 2, 8))
call assert_equal(['aaa'], getbufline(b, 1, 2)) call assert_equal(['aaa'], getbufline(b, 1, 2))
exe "bd!" b exe "bd!" b
call assert_equal(1, deletebufline(b, 1)) call assert_equal(1, b->deletebufline(1))
split Xtest split Xtest
call setline(1, ['a', 'b', 'c']) call setline(1, ['a', 'b', 'c'])
@@ -131,11 +131,11 @@ func Test_appendbufline_redraw()
endif endif
let lines =<< trim END let lines =<< trim END
new foo new foo
let winnr=bufwinnr('foo') let winnr = 'foo'->bufwinnr()
let buf = bufnr('foo') let buf = bufnr('foo')
wincmd p wincmd p
call appendbufline(buf, '$', range(1,200)) call appendbufline(buf, '$', range(1,200))
exe winnr. 'wincmd w' exe winnr .. 'wincmd w'
norm! G norm! G
wincmd p wincmd p
call deletebufline(buf, 1, '$') call deletebufline(buf, 1, '$')

View File

@@ -18,7 +18,7 @@ function Test_getbufwintabinfo()
let l = getbufinfo('%') let l = getbufinfo('%')
call assert_equal(bufnr('%'), l[0].bufnr) call assert_equal(bufnr('%'), l[0].bufnr)
call assert_equal('vim', l[0].variables.editor) call assert_equal('vim', l[0].variables.editor)
call assert_notequal(-1, index(l[0].windows, bufwinid('%'))) call assert_notequal(-1, index(l[0].windows, '%'->bufwinid()))
" Test for getbufinfo() with 'bufmodified' " Test for getbufinfo() with 'bufmodified'
call assert_equal(0, len(getbufinfo({'bufmodified' : 1}))) call assert_equal(0, len(getbufinfo({'bufmodified' : 1})))

View File

@@ -118,6 +118,16 @@ b = something();
bw! bw!
endfunc endfunc
func Test_cindent_func()
new
setlocal cindent
call setline(1, ['int main(void)', '{', 'return 0;', '}'])
call assert_equal(-1, cindent(0))
call assert_equal(&sw, 3->cindent())
call assert_equal(-1, cindent(line('$')+1))
bwipe!
endfunc
" this was going beyond the end of the line. " this was going beyond the end of the line.
func Test_cindent_case() func Test_cindent_case()
new new

View File

@@ -725,7 +725,7 @@ func Test_diff_filler()
diffthis diffthis
redraw redraw
call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'diff_filler(v:val)')) call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'v:val->diff_filler()'))
wincmd w wincmd w
call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)')) call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)'))
@@ -741,16 +741,16 @@ func Test_diff_hlID()
diffthis diffthis
redraw redraw
call assert_equal(synIDattr(diff_hlID(-1, 1), "name"), "") call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("")
call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange") call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange")
call assert_equal(diff_hlID(1, 2), hlID("DiffText")) call assert_equal(diff_hlID(1, 2), hlID("DiffText"))
call assert_equal(synIDattr(diff_hlID(1, 2), "name"), "DiffText") call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText")
call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "") call diff_hlID(2, 1)->synIDattr("name")->assert_equal("")
call assert_equal(diff_hlID(3, 1), hlID("DiffAdd")) call assert_equal(diff_hlID(3, 1), hlID("DiffAdd"))
call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "DiffAdd") call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd")
call assert_equal(synIDattr(diff_hlID(4, 1), "name"), "") call diff_hlID(4, 1)->synIDattr("name")->assert_equal("")
wincmd w wincmd w
call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))

View File

@@ -7,6 +7,8 @@ end
func Test_abs() func Test_abs()
call assert_equal('1.23', string(abs(1.23))) call assert_equal('1.23', string(abs(1.23)))
call assert_equal('1.23', string(abs(-1.23))) call assert_equal('1.23', string(abs(-1.23)))
eval -1.23->abs()->string()->assert_equal('1.23')
call assert_equal('0.0', string(abs(0.0))) call assert_equal('0.0', string(abs(0.0)))
call assert_equal('0.0', string(abs(1.0/(1.0/0.0)))) call assert_equal('0.0', string(abs(1.0/(1.0/0.0))))
call assert_equal('0.0', string(abs(-1.0/(1.0/0.0)))) call assert_equal('0.0', string(abs(-1.0/(1.0/0.0))))
@@ -22,6 +24,7 @@ endfunc
func Test_sqrt() func Test_sqrt()
call assert_equal('0.0', string(sqrt(0.0))) call assert_equal('0.0', string(sqrt(0.0)))
call assert_equal('1.414214', string(sqrt(2.0))) call assert_equal('1.414214', string(sqrt(2.0)))
eval 2.0->sqrt()->string()->assert_equal('1.414214')
call assert_equal("str2float('inf')", string(sqrt(1.0/0.0))) call assert_equal("str2float('inf')", string(sqrt(1.0/0.0)))
call assert_equal("str2float('nan')", string(sqrt(-1.0))) call assert_equal("str2float('nan')", string(sqrt(-1.0)))
call assert_equal("str2float('nan')", string(sqrt(0.0/0.0))) call assert_equal("str2float('nan')", string(sqrt(0.0/0.0)))
@@ -31,6 +34,7 @@ endfunc
func Test_log() func Test_log()
call assert_equal('0.0', string(log(1.0))) call assert_equal('0.0', string(log(1.0)))
call assert_equal('-0.693147', string(log(0.5))) call assert_equal('-0.693147', string(log(0.5)))
eval 0.5->log()->string()->assert_equal('-0.693147')
call assert_equal("-str2float('inf')", string(log(0.0))) call assert_equal("-str2float('inf')", string(log(0.0)))
call assert_equal("str2float('nan')", string(log(-1.0))) call assert_equal("str2float('nan')", string(log(-1.0)))
call assert_equal("str2float('inf')", string(log(1.0/0.0))) call assert_equal("str2float('inf')", string(log(1.0/0.0)))
@@ -42,6 +46,7 @@ func Test_log10()
call assert_equal('0.0', string(log10(1.0))) call assert_equal('0.0', string(log10(1.0)))
call assert_equal('2.0', string(log10(100.0))) call assert_equal('2.0', string(log10(100.0)))
call assert_equal('2.079181', string(log10(120.0))) call assert_equal('2.079181', string(log10(120.0)))
eval 120.0->log10()->string()->assert_equal('2.079181')
call assert_equal("-str2float('inf')", string(log10(0.0))) call assert_equal("-str2float('inf')", string(log10(0.0)))
call assert_equal("str2float('nan')", string(log10(-1.0))) call assert_equal("str2float('nan')", string(log10(-1.0)))
call assert_equal("str2float('inf')", string(log10(1.0/0.0))) call assert_equal("str2float('inf')", string(log10(1.0/0.0)))
@@ -53,6 +58,7 @@ func Test_exp()
call assert_equal('1.0', string(exp(0.0))) call assert_equal('1.0', string(exp(0.0)))
call assert_equal('7.389056', string(exp(2.0))) call assert_equal('7.389056', string(exp(2.0)))
call assert_equal('0.367879', string(exp(-1.0))) call assert_equal('0.367879', string(exp(-1.0)))
eval -1.0->exp()->string()->assert_equal('0.367879')
call assert_equal("str2float('inf')", string(exp(1.0/0.0))) call assert_equal("str2float('inf')", string(exp(1.0/0.0)))
call assert_equal('0.0', string(exp(-1.0/0.0))) call assert_equal('0.0', string(exp(-1.0/0.0)))
call assert_equal("str2float('nan')", string(exp(0.0/0.0))) call assert_equal("str2float('nan')", string(exp(0.0/0.0)))
@@ -63,6 +69,7 @@ func Test_sin()
call assert_equal('0.0', string(sin(0.0))) call assert_equal('0.0', string(sin(0.0)))
call assert_equal('0.841471', string(sin(1.0))) call assert_equal('0.841471', string(sin(1.0)))
call assert_equal('-0.479426', string(sin(-0.5))) call assert_equal('-0.479426', string(sin(-0.5)))
eval -0.5->sin()->string()->assert_equal('-0.479426')
call assert_equal("str2float('nan')", string(sin(0.0/0.0))) call assert_equal("str2float('nan')", string(sin(0.0/0.0)))
call assert_equal("str2float('nan')", string(sin(1.0/0.0))) call assert_equal("str2float('nan')", string(sin(1.0/0.0)))
call assert_equal('0.0', string(sin(1.0/(1.0/0.0)))) call assert_equal('0.0', string(sin(1.0/(1.0/0.0))))
@@ -73,6 +80,8 @@ endfunc
func Test_asin() func Test_asin()
call assert_equal('0.0', string(asin(0.0))) call assert_equal('0.0', string(asin(0.0)))
call assert_equal('1.570796', string(asin(1.0))) call assert_equal('1.570796', string(asin(1.0)))
eval 1.0->asin()->string()->assert_equal('1.570796')
call assert_equal('-0.523599', string(asin(-0.5))) call assert_equal('-0.523599', string(asin(-0.5)))
call assert_equal("str2float('nan')", string(asin(1.1))) call assert_equal("str2float('nan')", string(asin(1.1)))
call assert_equal("str2float('nan')", string(asin(1.0/0.0))) call assert_equal("str2float('nan')", string(asin(1.0/0.0)))
@@ -84,6 +93,7 @@ func Test_sinh()
call assert_equal('0.0', string(sinh(0.0))) call assert_equal('0.0', string(sinh(0.0)))
call assert_equal('0.521095', string(sinh(0.5))) call assert_equal('0.521095', string(sinh(0.5)))
call assert_equal('-1.026517', string(sinh(-0.9))) call assert_equal('-1.026517', string(sinh(-0.9)))
eval -0.9->sinh()->string()->assert_equal('-1.026517')
call assert_equal("str2float('inf')", string(sinh(1.0/0.0))) call assert_equal("str2float('inf')", string(sinh(1.0/0.0)))
call assert_equal("-str2float('inf')", string(sinh(-1.0/0.0))) call assert_equal("-str2float('inf')", string(sinh(-1.0/0.0)))
call assert_equal("str2float('nan')", string(sinh(0.0/0.0))) call assert_equal("str2float('nan')", string(sinh(0.0/0.0)))
@@ -94,6 +104,7 @@ func Test_cos()
call assert_equal('1.0', string(cos(0.0))) call assert_equal('1.0', string(cos(0.0)))
call assert_equal('0.540302', string(cos(1.0))) call assert_equal('0.540302', string(cos(1.0)))
call assert_equal('0.877583', string(cos(-0.5))) call assert_equal('0.877583', string(cos(-0.5)))
eval -0.5->cos()->string()->assert_equal('0.877583')
call assert_equal("str2float('nan')", string(cos(0.0/0.0))) call assert_equal("str2float('nan')", string(cos(0.0/0.0)))
call assert_equal("str2float('nan')", string(cos(1.0/0.0))) call assert_equal("str2float('nan')", string(cos(1.0/0.0)))
call assert_fails('call cos("")', 'E808:') call assert_fails('call cos("")', 'E808:')
@@ -103,6 +114,7 @@ func Test_acos()
call assert_equal('1.570796', string(acos(0.0))) call assert_equal('1.570796', string(acos(0.0)))
call assert_equal('0.0', string(acos(1.0))) call assert_equal('0.0', string(acos(1.0)))
call assert_equal('3.141593', string(acos(-1.0))) call assert_equal('3.141593', string(acos(-1.0)))
eval -1.0->acos()->string()->assert_equal('3.141593')
call assert_equal('2.094395', string(acos(-0.5))) call assert_equal('2.094395', string(acos(-0.5)))
call assert_equal("str2float('nan')", string(acos(1.1))) call assert_equal("str2float('nan')", string(acos(1.1)))
call assert_equal("str2float('nan')", string(acos(1.0/0.0))) call assert_equal("str2float('nan')", string(acos(1.0/0.0)))
@@ -113,6 +125,7 @@ endfunc
func Test_cosh() func Test_cosh()
call assert_equal('1.0', string(cosh(0.0))) call assert_equal('1.0', string(cosh(0.0)))
call assert_equal('1.127626', string(cosh(0.5))) call assert_equal('1.127626', string(cosh(0.5)))
eval 0.5->cosh()->string()->assert_equal('1.127626')
call assert_equal("str2float('inf')", string(cosh(1.0/0.0))) call assert_equal("str2float('inf')", string(cosh(1.0/0.0)))
call assert_equal("str2float('inf')", string(cosh(-1.0/0.0))) call assert_equal("str2float('inf')", string(cosh(-1.0/0.0)))
call assert_equal("str2float('nan')", string(cosh(0.0/0.0))) call assert_equal("str2float('nan')", string(cosh(0.0/0.0)))
@@ -123,6 +136,7 @@ func Test_tan()
call assert_equal('0.0', string(tan(0.0))) call assert_equal('0.0', string(tan(0.0)))
call assert_equal('0.546302', string(tan(0.5))) call assert_equal('0.546302', string(tan(0.5)))
call assert_equal('-0.546302', string(tan(-0.5))) call assert_equal('-0.546302', string(tan(-0.5)))
eval -0.5->tan()->string()->assert_equal('-0.546302')
call assert_equal("str2float('nan')", string(tan(1.0/0.0))) call assert_equal("str2float('nan')", string(tan(1.0/0.0)))
call assert_equal("str2float('nan')", string(cos(0.0/0.0))) call assert_equal("str2float('nan')", string(cos(0.0/0.0)))
call assert_equal('0.0', string(tan(1.0/(1.0/0.0)))) call assert_equal('0.0', string(tan(1.0/(1.0/0.0))))
@@ -134,6 +148,7 @@ func Test_atan()
call assert_equal('0.0', string(atan(0.0))) call assert_equal('0.0', string(atan(0.0)))
call assert_equal('0.463648', string(atan(0.5))) call assert_equal('0.463648', string(atan(0.5)))
call assert_equal('-0.785398', string(atan(-1.0))) call assert_equal('-0.785398', string(atan(-1.0)))
eval -1.0->atan()->string()->assert_equal('-0.785398')
call assert_equal('1.570796', string(atan(1.0/0.0))) call assert_equal('1.570796', string(atan(1.0/0.0)))
call assert_equal('-1.570796', string(atan(-1.0/0.0))) call assert_equal('-1.570796', string(atan(-1.0/0.0)))
call assert_equal("str2float('nan')", string(atan(0.0/0.0))) call assert_equal("str2float('nan')", string(atan(0.0/0.0)))
@@ -144,6 +159,7 @@ func Test_atan2()
call assert_equal('-2.356194', string(atan2(-1, -1))) call assert_equal('-2.356194', string(atan2(-1, -1)))
call assert_equal('2.356194', string(atan2(1, -1))) call assert_equal('2.356194', string(atan2(1, -1)))
call assert_equal('0.0', string(atan2(1.0, 1.0/0.0))) call assert_equal('0.0', string(atan2(1.0, 1.0/0.0)))
eval 1.0->atan2(1.0/0.0)->string()->assert_equal('0.0')
call assert_equal('1.570796', string(atan2(1.0/0.0, 1.0))) call assert_equal('1.570796', string(atan2(1.0/0.0, 1.0)))
call assert_equal("str2float('nan')", string(atan2(0.0/0.0, 1.0))) call assert_equal("str2float('nan')", string(atan2(0.0/0.0, 1.0)))
call assert_fails('call atan2("", -1)', 'E808:') call assert_fails('call atan2("", -1)', 'E808:')
@@ -154,6 +170,7 @@ func Test_tanh()
call assert_equal('0.0', string(tanh(0.0))) call assert_equal('0.0', string(tanh(0.0)))
call assert_equal('0.462117', string(tanh(0.5))) call assert_equal('0.462117', string(tanh(0.5)))
call assert_equal('-0.761594', string(tanh(-1.0))) call assert_equal('-0.761594', string(tanh(-1.0)))
eval -1.0->tanh()->string()->assert_equal('-0.761594')
call assert_equal('1.0', string(tanh(1.0/0.0))) call assert_equal('1.0', string(tanh(1.0/0.0)))
call assert_equal('-1.0', string(tanh(-1.0/0.0))) call assert_equal('-1.0', string(tanh(-1.0/0.0)))
call assert_equal("str2float('nan')", string(tanh(0.0/0.0))) call assert_equal("str2float('nan')", string(tanh(0.0/0.0)))
@@ -164,6 +181,7 @@ func Test_fmod()
call assert_equal('0.13', string(fmod(12.33, 1.22))) call assert_equal('0.13', string(fmod(12.33, 1.22)))
call assert_equal('-0.13', string(fmod(-12.33, 1.22))) call assert_equal('-0.13', string(fmod(-12.33, 1.22)))
call assert_equal("str2float('nan')", string(fmod(1.0/0.0, 1.0))) call assert_equal("str2float('nan')", string(fmod(1.0/0.0, 1.0)))
eval (1.0/0.0)->fmod(1.0)->string()->assert_equal("str2float('nan')")
" On Windows we get "nan" instead of 1.0, accept both. " On Windows we get "nan" instead of 1.0, accept both.
let res = string(fmod(1.0, 1.0/0.0)) let res = string(fmod(1.0, 1.0/0.0))
if res != "str2float('nan')" if res != "str2float('nan')"
@@ -177,6 +195,7 @@ endfunc
func Test_pow() func Test_pow()
call assert_equal('1.0', string(pow(0.0, 0.0))) call assert_equal('1.0', string(pow(0.0, 0.0)))
call assert_equal('8.0', string(pow(2.0, 3.0))) call assert_equal('8.0', string(pow(2.0, 3.0)))
eval 2.0->pow(3.0)->string()->assert_equal('8.0')
call assert_equal("str2float('nan')", string(pow(2.0, 0.0/0.0))) call assert_equal("str2float('nan')", string(pow(2.0, 0.0/0.0)))
call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0))) call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0)))
call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0))) call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0)))
@@ -192,6 +211,7 @@ func Test_str2float()
call assert_equal('1.0', string(str2float(' 1.0 '))) call assert_equal('1.0', string(str2float(' 1.0 ')))
call assert_equal('1.23', string(str2float('1.23'))) call assert_equal('1.23', string(str2float('1.23')))
call assert_equal('1.23', string(str2float('1.23abc'))) call assert_equal('1.23', string(str2float('1.23abc')))
eval '1.23abc'->str2float()->string()->assert_equal('1.23')
call assert_equal('1.0e40', string(str2float('1e40'))) call assert_equal('1.0e40', string(str2float('1e40')))
call assert_equal('-1.23', string(str2float('-1.23'))) call assert_equal('-1.23', string(str2float('-1.23')))
call assert_equal('1.23', string(str2float(' + 1.23 '))) call assert_equal('1.23', string(str2float(' + 1.23 ')))
@@ -228,6 +248,7 @@ func Test_float2nr()
call assert_equal(1, float2nr(1.234)) call assert_equal(1, float2nr(1.234))
call assert_equal(123, float2nr(1.234e2)) call assert_equal(123, float2nr(1.234e2))
call assert_equal(12, float2nr(123.4e-1)) call assert_equal(12, float2nr(123.4e-1))
eval 123.4e-1->float2nr()->assert_equal(12)
let max_number = 1/0 let max_number = 1/0
let min_number = -max_number let min_number = -max_number
call assert_equal(max_number/2+1, float2nr(pow(2, 62))) call assert_equal(max_number/2+1, float2nr(pow(2, 62)))
@@ -242,6 +263,7 @@ func Test_floor()
call assert_equal('2.0', string(floor(2.0))) call assert_equal('2.0', string(floor(2.0)))
call assert_equal('2.0', string(floor(2.11))) call assert_equal('2.0', string(floor(2.11)))
call assert_equal('2.0', string(floor(2.99))) call assert_equal('2.0', string(floor(2.99)))
eval 2.99->floor()->string()->assert_equal('2.0')
call assert_equal('-3.0', string(floor(-2.11))) call assert_equal('-3.0', string(floor(-2.11)))
call assert_equal('-3.0', string(floor(-2.99))) call assert_equal('-3.0', string(floor(-2.99)))
call assert_equal("str2float('nan')", string(floor(0.0/0.0))) call assert_equal("str2float('nan')", string(floor(0.0/0.0)))
@@ -255,6 +277,7 @@ func Test_ceil()
call assert_equal('3.0', string(ceil(2.11))) call assert_equal('3.0', string(ceil(2.11)))
call assert_equal('3.0', string(ceil(2.99))) call assert_equal('3.0', string(ceil(2.99)))
call assert_equal('-2.0', string(ceil(-2.11))) call assert_equal('-2.0', string(ceil(-2.11)))
eval -2.11->ceil()->string()->assert_equal('-2.0')
call assert_equal('-2.0', string(ceil(-2.99))) call assert_equal('-2.0', string(ceil(-2.99)))
call assert_equal("str2float('nan')", string(ceil(0.0/0.0))) call assert_equal("str2float('nan')", string(ceil(0.0/0.0)))
call assert_equal("str2float('inf')", string(ceil(1.0/0.0))) call assert_equal("str2float('inf')", string(ceil(1.0/0.0)))
@@ -266,6 +289,7 @@ func Test_round()
call assert_equal('2.0', string(round(2.1))) call assert_equal('2.0', string(round(2.1)))
call assert_equal('3.0', string(round(2.5))) call assert_equal('3.0', string(round(2.5)))
call assert_equal('3.0', string(round(2.9))) call assert_equal('3.0', string(round(2.9)))
eval 2.9->round()->string()->assert_equal('3.0')
call assert_equal('-2.0', string(round(-2.1))) call assert_equal('-2.0', string(round(-2.1)))
call assert_equal('-3.0', string(round(-2.5))) call assert_equal('-3.0', string(round(-2.5)))
call assert_equal('-3.0', string(round(-2.9))) call assert_equal('-3.0', string(round(-2.9)))
@@ -279,6 +303,7 @@ func Test_trunc()
call assert_equal('2.0', string(trunc(2.1))) call assert_equal('2.0', string(trunc(2.1)))
call assert_equal('2.0', string(trunc(2.5))) call assert_equal('2.0', string(trunc(2.5)))
call assert_equal('2.0', string(trunc(2.9))) call assert_equal('2.0', string(trunc(2.9)))
eval 2.9->trunc()->string()->assert_equal('2.0')
call assert_equal('-2.0', string(trunc(-2.1))) call assert_equal('-2.0', string(trunc(-2.1)))
call assert_equal('-2.0', string(trunc(-2.5))) call assert_equal('-2.0', string(trunc(-2.5)))
call assert_equal('-2.0', string(trunc(-2.9))) call assert_equal('-2.0', string(trunc(-2.9)))
@@ -291,6 +316,7 @@ endfunc
func Test_isinf() func Test_isinf()
call assert_equal(1, isinf(1.0/0.0)) call assert_equal(1, isinf(1.0/0.0))
call assert_equal(-1, isinf(-1.0/0.0)) call assert_equal(-1, isinf(-1.0/0.0))
eval (-1.0/0.0)->isinf()->assert_equal(-1)
call assert_false(isinf(1.0)) call assert_false(isinf(1.0))
call assert_false(isinf(0.0/0.0)) call assert_false(isinf(0.0/0.0))
call assert_false(isinf('a')) call assert_false(isinf('a'))
@@ -302,6 +328,7 @@ func Test_isnan()
call assert_true(isnan(0.0/0.0)) call assert_true(isnan(0.0/0.0))
call assert_false(isnan(1.0)) call assert_false(isnan(1.0))
call assert_false(isnan(1.0/0.0)) call assert_false(isnan(1.0/0.0))
eval (1.0/0.0)->isnan()->assert_false()
call assert_false(isnan(-1.0/0.0)) call assert_false(isnan(-1.0/0.0))
call assert_false(isnan('a')) call assert_false(isnan('a'))
call assert_false(isnan([])) call assert_false(isnan([]))

View File

@@ -852,7 +852,7 @@ func Test_byte2line_line2byte()
set fileformat=mac set fileformat=mac
call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1], call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1],
\ map(range(-1, 8), 'byte2line(v:val)')) \ map(range(-1, 8), 'v:val->byte2line()'))
call assert_equal([-1, -1, 1, 3, 6, 8, -1], call assert_equal([-1, -1, 1, 3, 6, 8, -1],
\ map(range(-1, 5), 'line2byte(v:val)')) \ map(range(-1, 5), 'line2byte(v:val)'))
@@ -875,6 +875,34 @@ func Test_byte2line_line2byte()
bw! bw!
endfunc endfunc
func Test_byteidx()
let a = '.é.' " one char of two bytes
call assert_equal(0, byteidx(a, 0))
call assert_equal(0, byteidxcomp(a, 0))
call assert_equal(1, byteidx(a, 1))
call assert_equal(1, byteidxcomp(a, 1))
call assert_equal(3, byteidx(a, 2))
call assert_equal(3, byteidxcomp(a, 2))
call assert_equal(4, byteidx(a, 3))
call assert_equal(4, byteidxcomp(a, 3))
call assert_equal(-1, byteidx(a, 4))
call assert_equal(-1, byteidxcomp(a, 4))
let b = '.é.' " normal e with composing char
call assert_equal(0, b->byteidx(0))
call assert_equal(1, b->byteidx(1))
call assert_equal(4, b->byteidx(2))
call assert_equal(5, b->byteidx(3))
call assert_equal(-1, b->byteidx(4))
call assert_equal(0, b->byteidxcomp(0))
call assert_equal(1, b->byteidxcomp(1))
call assert_equal(2, b->byteidxcomp(2))
call assert_equal(4, b->byteidxcomp(3))
call assert_equal(5, b->byteidxcomp(4))
call assert_equal(-1, b->byteidxcomp(5))
endfunc
" Test for charidx() " Test for charidx()
func Test_charidx() func Test_charidx()
let a = 'xáb́y' let a = 'xáb́y'
@@ -1065,7 +1093,7 @@ func Test_col()
call assert_equal(7, col('$')) call assert_equal(7, col('$'))
call assert_equal(4, col("'x")) call assert_equal(4, col("'x"))
call assert_equal(6, col("'Y")) call assert_equal(6, col("'Y"))
call assert_equal(2, col([1, 2])) call assert_equal(2, [1, 2]->col())
call assert_equal(7, col([1, '$'])) call assert_equal(7, col([1, '$']))
call assert_equal(0, col('')) call assert_equal(0, col(''))
@@ -1413,13 +1441,13 @@ func Test_bufadd_bufload()
call assert_equal([''], getbufline(buf, 1, '$')) call assert_equal([''], getbufline(buf, 1, '$'))
let curbuf = bufnr('') let curbuf = bufnr('')
call writefile(['some', 'text'], 'otherName') call writefile(['some', 'text'], 'XotherName')
let buf = bufadd('otherName') let buf = 'XotherName'->bufadd()
call assert_notequal(0, buf) call assert_notequal(0, buf)
call assert_equal(1, bufexists('otherName')) eval 'XotherName'->bufexists()->assert_equal(1)
call assert_equal(0, getbufvar(buf, '&buflisted')) call assert_equal(0, getbufvar(buf, '&buflisted'))
call assert_equal(0, bufloaded(buf)) call assert_equal(0, bufloaded(buf))
call bufload(buf) eval buf->bufload()
call assert_equal(1, bufloaded(buf)) call assert_equal(1, bufloaded(buf))
call assert_equal(['some', 'text'], getbufline(buf, 1, '$')) call assert_equal(['some', 'text'], getbufline(buf, 1, '$'))
call assert_equal(curbuf, bufnr('')) call assert_equal(curbuf, bufnr(''))
@@ -1439,8 +1467,9 @@ func Test_bufadd_bufload()
call assert_equal(0, bufexists(buf2)) call assert_equal(0, bufexists(buf2))
bwipe someName bwipe someName
bwipe otherName bwipe XotherName
call assert_equal(0, bufexists('someName')) call assert_equal(0, bufexists('someName'))
call delete('XotherName')
endfunc endfunc
func Test_readdir() func Test_readdir()
@@ -1473,6 +1502,20 @@ func Test_readdir()
call delete('Xdir', 'rf') call delete('Xdir', 'rf')
endfunc endfunc
func Test_call()
call assert_equal(3, call('len', [123]))
call assert_equal(3, 'len'->call([123]))
call assert_fails("call call('len', 123)", 'E714:')
call assert_equal(0, call('', []))
function Mylen() dict
return len(self.data)
endfunction
let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")}
eval mydict.len->call([], mydict)->assert_equal(4)
call assert_fails("call call('Mylen', [], 0)", 'E715:')
endfunc
" Test for the eval() function " Test for the eval() function
func Test_eval() func Test_eval()
call assert_fails("call eval('5 a')", 'E488:') call assert_fails("call eval('5 a')", 'E488:')

View File

@@ -37,7 +37,7 @@ function Test_hide()
" :hide as a command " :hide as a command
hide hide
call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')]) call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')])
call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) call assert_equal([1, 1], ['Xf1'->buflisted(), 'Xf1'->bufloaded()])
bwipeout! Xf1 bwipeout! Xf1
new Xf1 new Xf1

View File

@@ -61,7 +61,7 @@ endfunction
function Test_lambda_fails() function Test_lambda_fails()
call assert_equal(3, {a, b -> a + b}(1, 2)) call assert_equal(3, {a, b -> a + b}(1, 2))
call assert_fails('echo {a, a -> a + a}(1, 2)', 'E15:') call assert_fails('echo {a, a -> a + a}(1, 2)', 'E853:')
call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:') call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:')
endfunc endfunc

View File

@@ -242,7 +242,7 @@ func Test_matchaddpos_otherwin()
\] \]
call assert_equal(expect, savematches) call assert_equal(expect, savematches)
call clearmatches(winid) eval winid->clearmatches()
call assert_equal([], getmatches(winid)) call assert_equal([], getmatches(winid))
call setmatches(savematches, winid) call setmatches(savematches, winid)

View File

@@ -0,0 +1,159 @@
" Tests for ->method()
func Test_list_method()
let l = [1, 2, 3]
call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4))
eval l->assert_equal(l)
eval l->assert_equal(l, 'wrong')
eval l->assert_notequal([3, 2, 1])
eval l->assert_notequal([3, 2, 1], 'wrong')
call assert_equal(l, l->copy())
call assert_equal(l, l->deepcopy())
call assert_equal(1, l->count(2))
call assert_false(l->empty())
call assert_true([]->empty())
call assert_equal(579, ['123', '+', '456']->join()->eval())
call assert_equal([1, 2, 3, 4, 5], [1, 2, 3]->extend([4, 5]))
call assert_equal([1, 3], [1, 2, 3]->filter('v:val != 2'))
call assert_equal(2, l->get(1))
call assert_equal(1, l->index(2))
call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0))
call assert_fails('eval l->items()', 'E715:')
call assert_equal('1 2 3', l->join())
call assert_fails('eval l->keys()', 'E715:')
call assert_equal(3, l->len())
call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1'))
call assert_equal(3, l->max())
call assert_equal(1, l->min())
call assert_equal(2, [1, 2, 3]->remove(1))
call assert_equal([1, 2, 3, 1, 2, 3], l->repeat(2))
call assert_equal([3, 2, 1], [1, 2, 3]->reverse())
call assert_equal([1, 2, 3, 4], [4, 2, 3, 1]->sort())
call assert_equal('[1, 2, 3]', l->string())
call assert_equal(v:t_list, l->type())
call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq())
call assert_fails('eval l->values()', 'E715:')
endfunc
func Test_dict_method()
let d = #{one: 1, two: 2, three: 3}
call assert_equal(d, d->copy())
call assert_equal(d, d->deepcopy())
call assert_equal(1, d->count(2))
call assert_false(d->empty())
call assert_true({}->empty())
call assert_equal(#{one: 1, two: 2, three: 3, four: 4}, d->extend(#{four: 4}))
call assert_equal(#{one: 1, two: 2, three: 3}, d->filter('v:val != 4'))
call assert_equal(2, d->get('two'))
" Nvim doesn't support Blobs yet; expect a different emsg
" call assert_fails("let x = d->index(2)", 'E897:')
" call assert_fails("let x = d->insert(0)", 'E899:')
call assert_fails("let x = d->index(2)", 'E714:')
call assert_fails("let x = d->insert(0)", 'E686:')
call assert_true(d->has_key('two'))
call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items())
call assert_fails("let x = d->join()", 'E714:')
call assert_equal(['one', 'two', 'three'], d->keys())
call assert_equal(3, d->len())
call assert_equal(#{one: 2, two: 3, three: 4}, d->map('v:val + 1'))
call assert_equal(#{one: 1, two: 2, three: 3}, d->map('v:val - 1'))
call assert_equal(3, d->max())
call assert_equal(1, d->min())
call assert_equal(2, d->remove("two"))
let d.two = 2
call assert_fails('let x = d->repeat(2)', 'E731:')
" Nvim doesn't support Blobs yet; expect a different emsg
" call assert_fails('let x = d->reverse()', 'E899:')
call assert_fails('let x = d->reverse()', 'E686:')
call assert_fails('let x = d->sort()', 'E686:')
call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string())
call assert_equal(v:t_dict, d->type())
call assert_fails('let x = d->uniq()', 'E686:')
call assert_equal([1, 2, 3], d->values())
endfunc
func Test_string_method()
eval '1 2 3'->split()->assert_equal(['1', '2', '3'])
eval '1 2 3'->split()->map({i, v -> str2nr(v)})->assert_equal([1, 2, 3])
eval 'ABC'->str2list()->assert_equal([65, 66, 67])
eval 'ABC'->strlen()->assert_equal(3)
eval "a\rb\ec"->strtrans()->assert_equal('a^Mb^[c')
eval "aあb"->strwidth()->assert_equal(4)
eval 'abc'->substitute('b', 'x', '')->assert_equal('axc')
eval 'abc'->printf('the %s arg')->assert_equal('the abc arg')
endfunc
func Test_method_append()
new
eval ['one', 'two', 'three']->append(1)
call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
%del
let bnr = bufnr('')
wincmd w
eval ['one', 'two', 'three']->appendbufline(bnr, 1)
call assert_equal(['', 'one', 'two', 'three'], getbufline(bnr, 1, '$'))
exe 'bwipe! ' .. bnr
endfunc
func Test_method_funcref()
func Concat(one, two, three)
return a:one .. a:two .. a:three
endfunc
let FuncRef = function('Concat')
eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail')
" not enough arguments
call assert_fails("eval 'foo'->FuncRef('bar')", 'E119:')
" too many arguments
call assert_fails("eval 'foo'->FuncRef('bar', 'tail', 'four')", 'E118:')
let Partial = function('Concat', ['two'])
eval 'one'->Partial('three')->assert_equal('onetwothree')
" not enough arguments
call assert_fails("eval 'one'->Partial()", 'E119:')
" too many arguments
call assert_fails("eval 'one'->Partial('three', 'four')", 'E118:')
delfunc Concat
endfunc
func Test_method_float()
eval 1.234->string()->assert_equal('1.234')
eval -1.234->string()->assert_equal('-1.234')
endfunc
func Test_method_syntax()
eval [1, 2, 3] ->sort( )
eval [1, 2, 3]
\ ->sort(
\ )
call assert_fails('eval [1, 2, 3]-> sort()', 'E260:')
call assert_fails('eval [1, 2, 3]->sort ()', 'E274:')
call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:')
endfunc
func Test_method_lambda()
eval "text"->{x -> x .. " extended"}()->assert_equal('text extended')
eval "text"->{x, y -> x .. " extended " .. y}('more')->assert_equal('text extended more')
call assert_fails('eval "text"->{x -> x .. " extended"} ()', 'E274:')
" todo: lambda accepts more arguments than it consumes
" call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:')
" Nvim doesn't include test_refcount().
" let l = [1, 2, 3]
" eval l->{x -> x}()
" call assert_equal(1, test_refcount(l))
endfunc
func Test_method_not_supported()
call assert_fails('eval 123->changenr()', 'E276:')
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -250,7 +250,7 @@ endfunc
func Test_noinsert_complete() func Test_noinsert_complete()
func! s:complTest1() abort func! s:complTest1() abort
call complete(1, ['source', 'soundfold']) eval ['source', 'soundfold']->complete(1)
return '' return ''
endfunc endfunc
@@ -403,7 +403,7 @@ func DummyCompleteFour(findstart, base)
return 0 return 0
else else
call complete_add('four1') call complete_add('four1')
call complete_add('four2') eval 'four2'->complete_add()
call complete_check() call complete_check()
call complete_add('four3') call complete_add('four3')
call complete_add('four4') call complete_add('four4')
@@ -989,7 +989,7 @@ func GetCompleteInfo()
if empty(g:compl_what) if empty(g:compl_what)
let g:compl_info = complete_info() let g:compl_info = complete_info()
else else
let g:compl_info = complete_info(g:compl_what) let g:compl_info = g:compl_what->complete_info()
endif endif
return '' return ''
endfunc endfunc

View File

@@ -546,8 +546,8 @@ func Test_synstack_synIDtrans()
call assert_equal([], synstack(1, 1)) call assert_equal([], synstack(1, 1))
norm f/ norm f/
call assert_equal(['cComment', 'cCommentStart'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) eval synstack(line("."), col("."))->map('synIDattr(v:val, "name")')->assert_equal(['cComment', 'cCommentStart'])
call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")')) eval synstack(line("."), col("."))->map('synIDattr(synIDtrans(v:val), "name")')->assert_equal(['Comment', 'Comment'])
norm fA norm fA
call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))

View File

@@ -7,10 +7,10 @@ func Test_System()
if !executable('echo') || !executable('cat') || !executable('wc') if !executable('echo') || !executable('cat') || !executable('wc')
return return
endif endif
let out = system('echo 123') let out = 'echo 123'->system()
call assert_equal("123\n", out) call assert_equal("123\n", out)
let out = systemlist('echo 123') let out = 'echo 123'->systemlist()
if &shell =~# 'cmd.exe$' if &shell =~# 'cmd.exe$'
call assert_equal(["123\r"], out) call assert_equal(["123\r"], out)
else else

View File

@@ -62,6 +62,14 @@ func Test_user_func()
call assert_equal(9, g:retval) call assert_equal(9, g:retval)
call assert_equal(333, g:FuncRef(333)) call assert_equal(333, g:FuncRef(333))
let g:retval = "nop"
call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf"))
call assert_equal('fail', 45->Compute(0, "retval"))
call assert_equal('nop', g:retval)
call assert_equal('ok', 45->Compute(5, "retval"))
call assert_equal(9, g:retval)
" call assert_equal(333, 333->g:FuncRef())
enew enew
normal oXX+-XX normal oXX+-XX
@@ -150,6 +158,14 @@ func Test_default_arg()
\ execute('func Args2')) \ execute('func Args2'))
endfunc endfunc
func s:addFoo(lead)
return a:lead .. 'foo'
endfunc
func Test_user_method()
eval 'bar'->s:addFoo()->assert_equal('barfoo')
endfunc
func Test_failed_call_in_try() func Test_failed_call_in_try()
try | call UnknownFunc() | catch | endtry try | call UnknownFunc() | catch | endtry
endfunc endfunc

View File

@@ -1372,6 +1372,7 @@ func Test_bitwise_functions()
" and " and
call assert_equal(127, and(127, 127)) call assert_equal(127, and(127, 127))
call assert_equal(16, and(127, 16)) call assert_equal(16, and(127, 16))
eval 127->and(16)->assert_equal(16)
call assert_equal(0, and(127, 128)) call assert_equal(0, and(127, 128))
call assert_fails("call and(1.0, 1)", 'E805:') call assert_fails("call and(1.0, 1)", 'E805:')
call assert_fails("call and([], 1)", 'E745:') call assert_fails("call and([], 1)", 'E745:')
@@ -1382,6 +1383,7 @@ func Test_bitwise_functions()
" or " or
call assert_equal(23, or(16, 7)) call assert_equal(23, or(16, 7))
call assert_equal(15, or(8, 7)) call assert_equal(15, or(8, 7))
eval 8->or(7)->assert_equal(15)
call assert_equal(123, or(0, 123)) call assert_equal(123, or(0, 123))
call assert_fails("call or(1.0, 1)", 'E805:') call assert_fails("call or(1.0, 1)", 'E805:')
call assert_fails("call or([], 1)", 'E745:') call assert_fails("call or([], 1)", 'E745:')
@@ -1392,6 +1394,7 @@ func Test_bitwise_functions()
" xor " xor
call assert_equal(0, xor(127, 127)) call assert_equal(0, xor(127, 127))
call assert_equal(111, xor(127, 16)) call assert_equal(111, xor(127, 16))
eval 127->xor(16)->assert_equal(111)
call assert_equal(255, xor(127, 128)) call assert_equal(255, xor(127, 128))
call assert_fails("call xor(1.0, 1)", 'E805:') call assert_fails("call xor(1.0, 1)", 'E805:')
call assert_fails("call xor([], 1)", 'E745:') call assert_fails("call xor([], 1)", 'E745:')
@@ -1401,6 +1404,7 @@ func Test_bitwise_functions()
call assert_fails("call xor(1, {})", 'E728:') call assert_fails("call xor(1, {})", 'E728:')
" invert " invert
call assert_equal(65408, and(invert(127), 65535)) call assert_equal(65408, and(invert(127), 65535))
eval 127->invert()->and(65535)->assert_equal(65408)
call assert_equal(65519, and(invert(16), 65535)) call assert_equal(65519, and(invert(16), 65535))
call assert_equal(65407, and(invert(128), 65535)) call assert_equal(65407, and(invert(128), 65535))
call assert_fails("call invert(1.0)", 'E805:') call assert_fails("call invert(1.0)", 'E805:')

View File

@@ -247,9 +247,9 @@ describe('startup', function()
[[" -u NONE -i NONE --cmd "set noruler" --cmd "let g:foo = g:bar"')]]) [[" -u NONE -i NONE --cmd "set noruler" --cmd "let g:foo = g:bar"')]])
screen:expect([[ screen:expect([[
^ | ^ |
|
Error detected while processing pre-vimrc command line: | Error detected while processing pre-vimrc command line: |
E121: Undefined variable: g:bar | E121: Undefined variable: g:bar |
E15: Invalid expression: g:bar |
Press ENTER or type command to continue | Press ENTER or type command to continue |
| |
]]) ]])

View File

@@ -53,7 +53,7 @@ describe('NULL', function()
-- Correct behaviour -- Correct behaviour
null_expr_test('can be indexed with error message for empty list', 'L[0]', null_expr_test('can be indexed with error message for empty list', 'L[0]',
'E684: list index out of range: 0\nE15: Invalid expression: L[0]', nil) 'E684: list index out of range: 0', nil)
null_expr_test('can be splice-indexed', 'L[:]', 0, {}) null_expr_test('can be splice-indexed', 'L[:]', 0, {})
null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0)
null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) null_test('is accepted by :for', 'for x in L|throw x|endfor', 0)
@@ -68,7 +68,7 @@ describe('NULL', function()
null_expr_test('can be copied', 'copy(L)', 0, {}) null_expr_test('can be copied', 'copy(L)', 0, {})
null_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) null_expr_test('can be deepcopied', 'deepcopy(L)', 0, {})
null_expr_test('does not crash when indexed', 'L[1]', null_expr_test('does not crash when indexed', 'L[1]',
'E684: list index out of range: 1\nE15: Invalid expression: L[1]', nil) 'E684: list index out of range: 1', nil)
null_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) null_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0)
null_expr_test('does not crash col()', 'col(L)', 0, 0) null_expr_test('does not crash col()', 'col(L)', 0, 0)
null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0)
@@ -135,7 +135,7 @@ describe('NULL', function()
end) end)
describe('dict', function() describe('dict', function()
it('does not crash when indexing NULL dict', function() it('does not crash when indexing NULL dict', function()
eq('\nE716: Key not present in Dictionary: "test"\nE15: Invalid expression: v:_null_dict.test', eq('\nE716: Key not present in Dictionary: "test"',
redir_exec('echo v:_null_dict.test')) redir_exec('echo v:_null_dict.test'))
end) end)
null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0)

View File

@@ -26,6 +26,14 @@ describe('assert function:', function()
call('assert_beeps', 'normal 0') call('assert_beeps', 'normal 0')
expected_errors({'command did not beep: normal 0'}) expected_errors({'command did not beep: normal 0'})
end) end)
it('can be used as a method', function()
local tmpname = source [[
call assert_equal(0, 'normal h'->assert_beeps())
call assert_equal(1, 'normal 0'->assert_beeps())
]]
expected_errors({tmpname .. ' line 2: command did not beep: normal 0'})
end)
end) end)
-- assert_equal({expected}, {actual}, [, {msg}]) -- assert_equal({expected}, {actual}, [, {msg}])
@@ -133,6 +141,14 @@ describe('assert function:', function()
call('assert_false', {}) call('assert_false', {})
expected_errors({'Expected False but got []'}) expected_errors({'Expected False but got []'})
end) end)
it('can be used as a method', function()
local tmpname = source [[
call assert_equal(0, v:false->assert_false())
call assert_equal(1, 123->assert_false())
]]
expected_errors({tmpname .. ' line 2: Expected False but got 123'})
end)
end) end)
-- assert_true({actual}, [, {msg}]) -- assert_true({actual}, [, {msg}])
@@ -148,6 +164,14 @@ describe('assert function:', function()
eq(1, call('assert_true', 1.5)) eq(1, call('assert_true', 1.5))
expected_errors({'Expected True but got 1.5'}) expected_errors({'Expected True but got 1.5'})
end) end)
it('can be used as a method', function()
local tmpname = source [[
call assert_equal(0, v:true->assert_true())
call assert_equal(1, 0->assert_true())
]]
expected_errors({tmpname .. ' line 2: Expected True but got 0'})
end)
end) end)
describe('v:errors', function() describe('v:errors', function()
@@ -223,6 +247,15 @@ describe('assert function:', function()
call('assert_match', 'bar.*foo', 'foobar', 'wrong') call('assert_match', 'bar.*foo', 'foobar', 'wrong')
expected_errors({"wrong: Pattern 'bar.*foo' does not match 'foobar'"}) expected_errors({"wrong: Pattern 'bar.*foo' does not match 'foobar'"})
end) end)
it('can be used as a method', function()
local tmpname = source [[
call assert_equal(1, 'foobar'->assert_match('bar.*foo', 'wrong'))
]]
expected_errors({
tmpname .. " line 1: wrong: Pattern 'bar.*foo' does not match 'foobar'"
})
end)
end) end)
-- assert_notmatch({pat}, {text}[, {msg}]) -- assert_notmatch({pat}, {text}[, {msg}])
@@ -237,6 +270,13 @@ describe('assert function:', function()
call('assert_notmatch', 'foo', 'foobar') call('assert_notmatch', 'foo', 'foobar')
expected_errors({"Pattern 'foo' does match 'foobar'"}) expected_errors({"Pattern 'foo' does match 'foobar'"})
end) end)
it('can be used as a method', function()
local tmpname = source [[
call assert_equal(1, 'foobar'->assert_notmatch('foo'))
]]
expected_errors({tmpname .. " line 1: Pattern 'foo' does match 'foobar'"})
end)
end) end)
-- assert_fails({cmd}, [, {error}]) -- assert_fails({cmd}, [, {error}])
@@ -267,6 +307,15 @@ describe('assert function:', function()
eq(1, eval([[assert_fails('echo', '', 'echo command')]])) eq(1, eval([[assert_fails('echo', '', 'echo command')]]))
expected_errors({'command did not fail: echo command'}) expected_errors({'command did not fail: echo command'})
end) end)
it('can be used as a method', function()
local tmpname = source [[
call assert_equal(1, 'echo'->assert_fails('', 'echo command'))
]]
expected_errors({
tmpname .. ' line 1: command did not fail: echo command'
})
end)
end) end)
-- assert_inrange({lower}, {upper}, {actual}[, {msg}]) -- assert_inrange({lower}, {upper}, {actual}[, {msg}])
@@ -292,6 +341,15 @@ describe('assert function:', function()
eq('Vim(call):E119: Not enough arguments for function: assert_inrange', eq('Vim(call):E119: Not enough arguments for function: assert_inrange',
exc_exec("call assert_inrange(1, 1)")) exc_exec("call assert_inrange(1, 1)"))
end) end)
it('can be used as a method', function()
local tmpname = source [[
call assert_equal(0, 5->assert_inrange(5, 7))
call assert_equal(0, 7->assert_inrange(5, 7))
call assert_equal(1, 8->assert_inrange(5, 7))
]]
expected_errors({tmpname .. ' line 3: Expected range 5 - 7, but got 8'})
end)
end) end)
-- assert_report({msg}) -- assert_report({msg})
@@ -302,6 +360,13 @@ describe('assert function:', function()
command('call remove(v:errors, 0)') command('call remove(v:errors, 0)')
expected_empty() expected_empty()
end) end)
it('can be used as a method', function()
local tmpname = source [[
call assert_equal(1, 'also wrong'->assert_report())
]]
expected_errors({tmpname .. ' line 1: also wrong'})
end)
end) end)
-- assert_exception({cmd}, [, {error}]) -- assert_exception({cmd}, [, {error}])

View File

@@ -481,6 +481,21 @@ describe('v:lua', function()
pcall_err(eval, 'v:lua.mymod.crashy()')) pcall_err(eval, 'v:lua.mymod.crashy()'))
end) end)
it('works when called as a method', function()
eq(123, eval('110->v:lua.foo(13)'))
eq(true, exec_lua([[return _G.val == nil]]))
eq(321, eval('300->v:lua.foo(21, "boop")'))
eq("boop", exec_lua([[return _G.val]]))
eq(NIL, eval('"there"->v:lua.mymod.noisy()'))
eq("hey there", meths.get_current_line())
eq({5, 10, 15, 20}, eval('[[1], [2, 3], [4]]->v:lua.vim.tbl_flatten()->map({_, v -> v * 5})'))
eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)",
pcall_err(eval, '"huh?"->v:lua.mymod.crashy()'))
end)
it('works in :call', function() it('works in :call', function()
command(":call v:lua.mymod.noisy('command')") command(":call v:lua.mymod.noisy('command')")
eq("hey command", meths.get_current_line()) eq("hey command", meths.get_current_line())
@@ -518,8 +533,15 @@ describe('v:lua', function()
eq("Vim:E15: Invalid expression: v:['lua'].foo()", pcall_err(eval, "v:['lua'].foo()")) eq("Vim:E15: Invalid expression: v:['lua'].foo()", pcall_err(eval, "v:['lua'].foo()"))
eq("Vim(call):E461: Illegal variable name: v:['lua']", pcall_err(command, "call v:['lua'].baar()")) eq("Vim(call):E461: Illegal variable name: v:['lua']", pcall_err(command, "call v:['lua'].baar()"))
eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "v:lua()"))
eq("Vim(let):E46: Cannot change read-only variable \"v:['lua']\"", pcall_err(command, "let v:['lua'] = 'xx'")) eq("Vim(let):E46: Cannot change read-only variable \"v:['lua']\"", pcall_err(command, "let v:['lua'] = 'xx'"))
eq("Vim(let):E46: Cannot change read-only variable \"v:lua\"", pcall_err(command, "let v:lua = 'xx'")) eq("Vim(let):E46: Cannot change read-only variable \"v:lua\"", pcall_err(command, "let v:lua = 'xx'"))
eq("Vim:E107: Missing parentheses: v:lua.func", pcall_err(eval, "'bad'->v:lua.func"))
eq("Vim:E274: No white space allowed before parenthesis", pcall_err(eval, "'bad'->v:lua.func ()"))
eq("Vim:E107: Missing parentheses: v:lua", pcall_err(eval, "'bad'->v:lua"))
eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "'bad'->v:lua()"))
eq("Vim:E15: Invalid expression: v:lua.()", pcall_err(eval, "'bad'->v:lua.()"))
end) end)
end) end)

View File

@@ -653,14 +653,12 @@ describe('clipboard (with fake clipboard.vim)', function()
'', '',
'', '',
'E121: Undefined variable: doesnotexist', 'E121: Undefined variable: doesnotexist',
'E15: Invalid expression: doesnotexist',
}, 'v'}, eval("g:test_clip['*']")) }, 'v'}, eval("g:test_clip['*']"))
feed_command(':echo "Howdy!"') feed_command(':echo "Howdy!"')
eq({{ eq({{
'', '',
'', '',
'E121: Undefined variable: doesnotexist', 'E121: Undefined variable: doesnotexist',
'E15: Invalid expression: doesnotexist',
'', '',
'Howdy!', 'Howdy!',
}, 'v'}, eval("g:test_clip['*']")) }, 'v'}, eval("g:test_clip['*']"))