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
commit 4c499899b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.name entry in a |Dictionary|
expr8(expr1, ...) function call with |Funcref| variable
expr8->name(expr1, ...) |method| call
|expr9| number number constant
"string" string constant, backslash is special
'string' string constant, ' is doubled
[expr1, ...] |List|
{expr1: expr1, ...} |Dictionary|
#{key: expr1, ...} |Dictionary|
&option option value
(expr1) nested expression
variable internal variable
@ -939,9 +941,11 @@ expr8 *expr8*
-----
This expression is either |expr9| or a sequence of the alternatives below,
in any order. E.g., these are all possible:
expr9[expr1].name
expr9.name[expr1]
expr9(expr1, ...)[expr1].name
expr8[expr1].name
expr8.name[expr1]
expr8(expr1, ...)[expr1].name
expr8->(expr1, ...)[expr1]
Evaluation is always from left to right.
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.
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*
number
@ -2583,6 +2617,8 @@ abs({expr}) *abs()*
echo abs(-4)
< 4
Can also be used as a |method|: >
Compute()->abs()
acos({expr}) *acos()*
Return the arc cosine of {expr} measured in radians, as a
@ -2595,6 +2631,8 @@ acos({expr}) *acos()*
:echo acos(-0.5)
< 2.094395
Can also be used as a |method|: >
Compute()->acos()
add({list}, {expr}) *add()*
Append the item {expr} to |List| {list}. Returns the
@ -2605,12 +2643,16 @@ add({list}, {expr}) *add()*
item. Use |extend()| to concatenate |Lists|.
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()*
Bitwise AND on the two arguments. The arguments are converted
to a number. A List, Dict or Float argument causes an error.
Example: >
:let flag = and(bits, 0x80)
< Can also be used as a |method|: >
:let flag = bits->and(0x80)
api_info() *api_info()*
Returns Dictionary of |api-metadata|.
@ -2629,6 +2671,9 @@ append({lnum}, {text}) *append()*
:let failed = append(line('$'), "# THE END")
: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()*
Like |append()| but append the text in buffer {expr}.
@ -2647,8 +2692,10 @@ appendbufline({expr}, {lnum}, {text}) *appendbufline()*
error message is given. Example: >
:let failed = appendbufline(13, 0, "# THE START")
<
*argc()*
argc([{winid}])
Can also be used as a |method| after a List: >
mylist->appendbufline(buf, lnum)
argc([{winid}]) *argc()*
The result is the number of files in the argument list. See
|arglist|.
If {winid} is not supplied, the argument list of the current
@ -2702,6 +2749,9 @@ asin({expr}) *asin()*
:echo asin(-0.5)
< -0.523599
Can also be used as a |method|: >
Compute()->asin()
assert_ functions are documented here: |assert-functions-details|
@ -2716,6 +2766,8 @@ atan({expr}) *atan()*
:echo atan(-4.01)
< -1.326405
Can also be used as a |method|: >
Compute()->atan()
atan2({expr1}, {expr2}) *atan2()*
Return the arc tangent of {expr1} / {expr2}, measured in
@ -2727,6 +2779,8 @@ atan2({expr1}, {expr2}) *atan2()*
:echo atan2(1, -1)
< 2.356194
Can also be used as a |method|: >
Compute()->atan2(1)
*browse()*
browse({save}, {title}, {initdir}, {default})
@ -2737,8 +2791,8 @@ browse({save}, {title}, {initdir}, {default})
{title} title for the requester
{initdir} directory to start browsing in
{default} default file name
When the "Cancel" button is hit, something went wrong, or
browsing is not possible, an empty string is returned.
An empty string is returned when the "Cancel" button is hit,
something went wrong, or browsing is not possible.
*browsedir()*
browsedir({title}, {initdir})
@ -2760,6 +2814,8 @@ bufadd({name}) *bufadd()*
created buffer. When {name} is an empty string then a new
buffer is always created.
The buffer will not have' 'buflisted' set.
< Can also be used as a |method|: >
let bufnr = 'somename'->bufadd()
bufexists({expr}) *bufexists()*
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
file name.
Can also be used as a |method|: >
let exists = 'somename'->bufexists()
buflisted({expr}) *buflisted()*
The result is a Number, which is |TRUE| if a buffer called
{expr} exists and is listed (has the 'buflisted' option set).
The {expr} argument is used like with |bufexists()|.
Can also be used as a |method|: >
let listed = 'somename'->buflisted()
bufload({expr}) *bufload()*
Ensure the buffer {expr} is loaded. When the buffer name
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.
The {expr} argument is used like with |bufexists()|.
Can also be used as a |method|: >
eval 'somename'->bufload()
bufloaded({expr}) *bufloaded()*
The result is a Number, which is |TRUE| if a buffer called
{expr} exists and is loaded (shown in a window or hidden).
The {expr} argument is used like with |bufexists()|.
Can also be used as a |method|: >
let loaded = 'somename'->bufloaded()
bufname([{expr}]) *bufname()*
The result is the name of a buffer, as it is displayed by the
":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.
Number zero is the alternate buffer for the current window.
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
number, force it to be a Number by adding zero to it: >
: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
string is returned. >
bufname("#") alternate buffer name
@ -2846,6 +2917,9 @@ bufnr([{expr} [, {create}]])
number necessarily exist, because ":bwipeout" may have removed
them. Use bufexists() to test for the existence of a buffer.
Can also be used as a |method|: >
echo bufref->bufnr()
bufwinid({expr}) *bufwinid()*
The result is a Number, which is the |window-ID| of the first
window associated with buffer {expr}. For the use of {expr},
@ -2856,18 +2930,22 @@ bufwinid({expr}) *bufwinid()*
<
Only deals with the current tab page.
Can also be used as a |method|: >
FindBuffer()->bufwinid()
bufwinnr({expr}) *bufwinnr()*
The result is a Number, which is the number of the first
window associated with buffer {expr}. For the use of {expr},
see |bufname()| above. If buffer {expr} doesn't exist or
there is no such window, -1 is returned. Example: >
Like |bufwinid()| but return the window number instead of the
|window-ID|.
If buffer {expr} doesn't exist or there is no such window, -1
is returned. Example: >
echo "A window containing buffer 1 is " . (bufwinnr(1))
< The number can be used with |CTRL-W_w| and ":wincmd w"
|:wincmd|.
Only deals with the current tab page.
Can also be used as a |method|: >
FindBuffer()->bufwinnr()
byte2line({byte}) *byte2line()*
Return the line number that contains the character at byte
@ -2877,6 +2955,9 @@ byte2line({byte}) *byte2line()*
one.
Also see |line2byte()|, |go| and |:goto|.
Can also be used as a |method|: >
GetOffset()->byte2line()
byteidx({expr}, {nr}) *byteidx()*
Return byte index of the {nr}'th character in the string
{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
in bytes is returned.
Can also be used as a |method|: >
GetName()->byteidx(idx)
byteidxcomp({expr}, {nr}) *byteidxcomp()*
Like byteidx(), except that a composing character is counted
as a separate character. Example: >
@ -2912,6 +2996,9 @@ byteidxcomp({expr}, {nr}) *byteidxcomp()*
Only works differently from byteidx() when 'encoding' is set to
a Unicode encoding.
Can also be used as a |method|: >
GetName()->byteidxcomp(idx)
call({func}, {arglist} [, {dict}]) *call()* *E699*
Call function {func} with the items in |List| {arglist} as
arguments.
@ -2921,6 +3008,9 @@ call({func}, {arglist} [, {dict}]) *call()* *E699*
{dict} is for functions with the "dict" attribute. It will be
used to set the local variable "self". |Dictionary-function|
Can also be used as a |method|: >
GetFunc()->call([arg, arg], dict)
ceil({expr}) *ceil()*
Return the smallest integral value greater than or equal to
{expr} as a |Float| (round up).
@ -2933,6 +3023,9 @@ ceil({expr}) *ceil()*
echo ceil(4.0)
< 4.0
Can also be used as a |method|: >
Compute()->ceil()
changenr() *changenr()*
Return the number of the most recent change. This is the same
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.
|nr2char()| does the opposite.
Can also be used as a |method|: >
GetChar()->char2nr()
*charidx()*
charidx({string}, {idx} [, {countcc}])
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.
See |C-indenting|.
Can also be used as a |method|: >
GetLnum()->cindent()
clearmatches([{win}]) *clearmatches()*
Clears all matches previously defined for the current window
by |matchadd()| and the |:match| commands.
If {win} is specified, use the window with this number or
window ID instead of the current window.
Can also be used as a |method|: >
GetWin()->clearmatches()
<
*col()*
col({expr}) The result is a Number, which is the byte index of the column
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>:echo col(".") . "\n" <Bar>
\let &ve = save_ve<CR>
< Can also be used as a |method|: >
GetPos()->col()
<
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
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()*
Add {expr} to the list of matches. Only to be used by the
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
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()*
Check for a key typed while looking for completion matches.
This is to be used when looking for matches takes some time.
@ -3154,6 +3266,9 @@ complete_info([{what}])
call complete_info(['mode'])
" Get only 'mode' and 'pum_visible'
call complete_info(['mode', 'pum_visible'])
< Can also be used as a |method|: >
GetItems()->complete_info()
<
*confirm()*
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
the horizontal layout is always used.
Can also be used as a |method|in: >
BuildMessage()->confirm("&Yes\n&No")
*copy()*
copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
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|.
A |Dictionary| is copied in a similar way as a |List|.
Also see |deepcopy()|.
Can also be used as a |method|: >
mylist->copy()
cos({expr}) *cos()*
Return the cosine of {expr}, measured in radians, as a |Float|.
@ -3226,6 +3346,8 @@ cos({expr}) *cos()*
:echo cos(-4.01)
< -0.646043
Can also be used as a |method|: >
Compute()->cos()
cosh({expr}) *cosh()*
Return the hyperbolic cosine of {expr} as a |Float| in the range
@ -3237,6 +3359,8 @@ cosh({expr}) *cosh()*
:echo cosh(-0.5)
< -1.127626
Can also be used as a |method|: >
Compute()->cosh()
count({comp}, {expr} [, {ic} [, {start}]]) *count()*
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
{expr} is an empty string.
Can also be used as a |method|: >
mylist->count(val)
*cscope_connection()*
cscope_connection([{num} , {dbpath} [, {prepend}]])
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.
Returns 0 when the position could be set, -1 otherwise.
Can also be used as a |method|: >
GetCursorPos()->cursor()
deepcopy({expr}[, {noref}]) *deepcopy()* *E698*
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.
Also see |copy()|.
Can also be used as a |method|: >
GetObject()->deepcopy()
delete({fname} [, {flags}]) *delete()*
Without {flags} or with {flags} empty: Deletes the file by the
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
or partly failed.
Can also be used as a |method|: >
GetName()->delete()
deletebufline({expr}, {first}[, {last}]) *deletebufline()*
Delete lines {first} to {last} (inclusive) from buffer {expr}.
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 "$"
to refer to the last line in buffer {expr}.
Can also be used as a |method|: >
GetBuffer()->deletebufline(1)
dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()*
Adds a watcher to a dictionary. A dictionary watcher is
identified by three components:
@ -3464,6 +3602,9 @@ diff_filler({lnum}) *diff_filler()*
line, "'m" mark m, etc.
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()*
Returns the highlight ID for diff mode at line {lnum} column
{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
syntax information about the highlighting.
Can also be used as a |method|: >
GetLnum()->diff_hlID(col)
empty({expr}) *empty()*
Return the Number 1 if {expr} is empty, zero otherwise.
A |List| or |Dictionary| is empty when it does not have any
items. A Number is empty when its value is zero. Special
variable is empty when it is |v:false| or |v:null|.
Can also be used as a |method|: >
mylist->empty()
environ() *environ()*
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
functions.
Can also be used as a |method|: >
argv->join()->eval()
eventhandler() *eventhandler()*
Returns 1 when inside an event handler. That is that Vim got
interrupted while waiting for the user to type a character,
@ -3661,12 +3810,18 @@ exp({expr}) *exp()*
:echo exp(-1)
< 0.367879
Can also be used as a |method|: >
Compute()->exp()
debugbreak({pid}) *debugbreak()*
Specifically used to interrupt a program being debugged. It
will cause process {pid} to get a SIGTRAP. Behavior for other
processes is undefined. See |terminal-debugger|.
{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 wildcards and the following special keywords in {expr}.
'wildignorecase' applies.
@ -3795,6 +3950,8 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()*
fails.
Returns {expr1}.
Can also be used as a |method|: >
mylist->extend(otherlist)
feedkeys({string} [, {mode}]) *feedkeys()*
Characters in {string} are queued for processing as if they
@ -3848,7 +4005,11 @@ filereadable({file}) *filereadable()*
expression, which is used as a String.
If you don't care about the file being readable you can use
|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()*
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
defined with the "abort" flag.
Can also be used as a |method|: >
mylist->filter(expr2)
finddir({name} [, {path} [, {count}]]) *finddir()*
Find directory {name} in {path}. Supports both downwards and
@ -3966,6 +4129,8 @@ float2nr({expr}) *float2nr()*
echo float2nr(1.0e-100)
< 0
Can also be used as a |method|: >
Compute()->float2nr()
floor({expr}) *floor()*
Return the largest integral value less than or equal to
@ -3979,6 +4144,8 @@ floor({expr}) *floor()*
echo floor(4.0)
< 4.0
Can also be used as a |method|: >
Compute()->floor()
fmod({expr1}, {expr2}) *fmod()*
Return the remainder of {expr1} / {expr2}, even if the
@ -3994,6 +4161,8 @@ fmod({expr1}, {expr2}) *fmod()*
:echo fmod(-12.33, 1.22)
< -0.13
Can also be used as a |method|: >
Compute()->fmod(1.22)
fnameescape({string}) *fnameescape()*
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
available return {default}. Return zero when {default} is
omitted.
Can also be used as a |method|: >
mylist->get(idx)
get({dict}, {key} [, {default}])
Get item with key {key} from |Dictionary| {dict}. When this
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}
has an entry with key {key}. FALSE otherwise.
Can also be used as a |method|: >
mydict->has_key(key)
haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()*
The result is a Number, which is 1 when the tabpage or window
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
item. Use |extend()| to concatenate |Lists|.
Can also be used as a |method|: >
mylist->insert(item)
interrupt() *interrupt()*
Interrupt script execution. It works more or less like the
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
List, Dict or Float argument causes an error. Example: >
:let bits = invert(bits)
< Can also be used as a |method|: >
:let bits = bits->invert()
isdirectory({directory}) *isdirectory()*
The result is a Number, which is |TRUE| when a directory
@ -5546,6 +5725,9 @@ isinf({expr}) *isinf()*
:echo isinf(-1.0 / 0.0)
< -1
Can also be used as a |method|: >
Compute()->isinf()
islocked({expr}) *islocked()* *E786*
The result is a Number, which is |TRUE| when {expr} is the
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}
entry and the value of this entry. The |List| is in arbitrary
order.
Can also be used as a |method|: >
mydict->items()
isnan({expr}) *isnan()*
Return |TRUE| if {expr} is a float with value NaN. >
echo isnan(0.0 / 0.0)
< 1
Can also be used as a |method|: >
Compute()->isnan()
jobpid({job}) *jobpid()*
Return the PID (process id) of |job-id| {job}.
@ -5714,6 +5901,9 @@ join({list} [, {sep}]) *join()*
converted into a string like with |string()|.
The opposite function is |split()|.
Can also be used as a |method|: >
mylist->join()
json_decode({expr}) *json_decode()*
Convert {expr} from JSON object. Accepts |readfile()|-style
list as the input, as well as regular string. May output any
@ -5744,8 +5934,10 @@ json_encode({expr}) *json_encode()*
keys({dict}) *keys()*
Return a |List| with all the keys of {dict}. The |List| is in
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.
When {expr} is a String or a Number the length in bytes is
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.
Otherwise an error is given.
*libcall()* *E364* *E368*
Can also be used as a |method|: >
mylist->len()
< *libcall()* *E364* *E368*
libcall({libname}, {funcname}, {argument})
Call function {funcname} in the run-time library {libname}
with single argument {argument}.
@ -5881,6 +6076,8 @@ log({expr}) *log()*
:echo log(exp(5))
< 5.0
Can also be used as a |method|: >
Compute()->log()
log10({expr}) *log10()*
Return the logarithm of Float {expr} to base 10 as a |Float|.
@ -5891,6 +6088,9 @@ log10({expr}) *log10()*
:echo log10(0.01)
< -2.0
Can also be used as a |method|: >
Compute()->log10()
luaeval({expr}[, {expr}])
Evaluate Lua expression {expr} and return its result converted
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
defined with the "abort" flag.
Can also be used as a |method|: >
mylist->map(expr2)
maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
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
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()*
Returns a |List| of |Dictionaries| describing |menus| (defined
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
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}]])
Create directory {name}.
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.
Example: >
:let bits = or(bits, 0x80)
< Can also be used as a |method|: >
:let bits = bits->or(0x80)
pathshorten({expr}) *pathshorten()*
Shorten directory names in the path {expr} and return the
@ -6560,6 +6769,9 @@ pow({x}, {y}) *pow()*
:echo pow(32, 0.20)
< 2.0
Can also be used as a |method|: >
Compute()->pow(3)
prevnonblank({lnum}) *prevnonblank()*
Return the line number of the first line at or above {lnum}
that is not blank. Example: >
@ -6576,7 +6788,11 @@ printf({fmt}, {expr1} ...) *printf()*
< May result in:
" 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
%6S string right-aligned in 6 display cells
%6s string right-aligned in 6 bytes
@ -7086,6 +7302,10 @@ remove({list}, {idx} [, {end}]) *remove()*
Example: >
:echo "last item: " . remove(mylist, -1)
:call remove(mylist, 0, 9)
< Can also be used as a |method|: >
mylist->remove(idx)
remove({dict}, {key})
Remove the entry from {dict} with key {key} and return it.
Example: >
@ -7112,6 +7332,8 @@ repeat({expr}, {count}) *repeat()*
:let longlist = repeat(['a', 'b'], 3)
< Results in ['a', 'b', 'a', 'b', 'a', 'b'].
Can also be used as a |method|: >
mylist->repeat(count)
resolve({filename}) *resolve()* *E655*
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}.
If you want a list to remain unmodified make a copy first: >
:let revlist = reverse(copy(mylist))
< Can also be used as a |method|: >
mylist->reverse()
round({expr}) *round()*
Round off {expr} to the nearest integral value and return it
@ -7145,6 +7369,9 @@ round({expr}) *round()*
echo round(-4.5)
< -5.0
Can also be used as a |method|: >
Compute()->round()
rpcnotify({channel}, {event}[, {args}...]) *rpcnotify()*
Sends {event} to {channel} via |RPC| and returns immediately.
If {channel} is 0, the event is broadcast to all channels.
@ -8121,6 +8348,8 @@ sin({expr}) *sin()*
:echo sin(-4.01)
< 0.763301
Can also be used as a |method|: >
Compute()->sin()
sinh({expr}) *sinh()*
Return the hyperbolic sine of {expr} as a |Float| in the range
@ -8132,6 +8361,9 @@ sinh({expr}) *sinh()*
:echo sinh(-0.9)
< -1.026517
Can also be used as a |method|: >
Compute()->sinh()
sockconnect({mode}, {address}, {opts}) *sockconnect()*
Connect a socket to an address. If {mode} is "pipe" then
{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
same order as they were originally.
Also see |uniq()|.
Can also be used as a |method|: >
mylist->sort()
< Also see |uniq()|.
Example: >
func MyCompare(i1, i2)
@ -8303,6 +8538,8 @@ split({expr} [, {pattern} [, {keepempty}]]) *split()*
:let items = split(line, ':', 1)
< The opposite function is |join()|.
Can also be used as a |method|: >
GetString()->split()
sqrt({expr}) *sqrt()*
Return the non-negative square root of Float {expr} as a
@ -8316,6 +8553,8 @@ sqrt({expr}) *sqrt()*
< nan
"nan" may be different, it depends on system libraries.
Can also be used as a |method|: >
Compute()->sqrt()
stdioopen({opts}) *stdioopen()*
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
|substitute()|: >
let f = str2float(substitute(text, ',', '', 'g'))
<
Can also be used as a |method|: >
let f = text->substitute(',', '', 'g')->str2float()
str2list({expr} [, {utf8}]) *str2list()*
Return a list containing the number values which represent
@ -8381,12 +8623,18 @@ str2list({expr} [, {utf8}]) *str2list()*
properly: >
str2list("á") returns [97, 769]
< Can also be used as a |method|: >
GetString()->str2list()
str2nr({expr} [, {base}]) *str2nr()*
Convert string {expr} to a number.
{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
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
different base the result will be zero. Similarly, when {base}
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
share data with other application.
Can also be used as a |method|: >
mylist->string()
*strlen()*
strlen({expr}) The result is a Number, which is the length of the String
{expr} in bytes.
@ -8514,6 +8765,9 @@ strlen({expr}) The result is a Number, which is the length of the String
|strchars()|.
Also see |len()|, |strdisplaywidth()| and |strwidth()|.
Can also be used as a |method|: >
GetString()->strlen()
strpart({src}, {start} [, {len} [, {chars}]]) *strpart()*
The result is a String, which is part of {src}, starting from
byte {start}, with the byte length {len}.
@ -8588,6 +8842,9 @@ strtrans({expr}) *strtrans()*
< This displays a newline in register a as "^@" instead of
starting a new line.
Can also be used as a |method|: >
GetString()->strtrans()
strwidth({expr}) *strwidth()*
The result is a Number, which is the number of display cells
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'.
Also see |strlen()|, |strdisplaywidth()| and |strchars()|.
Can also be used as a |method|: >
GetString()->strwidth()
submatch({nr} [, {list}]) *submatch()* *E935*
Only for an expression in a |:substitute| command or
substitute() function.
@ -8663,6 +8923,9 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
|submatch()| returns. Example: >
: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()*
The result is a dictionary, which holds information about the
swapfile {fname}. The available fields are:
@ -8747,12 +9010,18 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()*
cursor): >
: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()*
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
highlight the character. Highlight links given with
":highlight link" are followed.
Can also be used as a |method|: >
:echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg")
synconcealed({lnum}, {col}) *synconcealed()*
The result is a |List| with currently three items:
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.
Use |:checktime| to force a check.
Can also be used as a |method|: >
:echo GetCmd()->system()
systemlist({cmd} [, {input} [, {keepempty}]]) *systemlist()*
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.
Can also be used as a |method|: >
:echo GetCmd()->systemlist()
tabpagebuflist([{arg}]) *tabpagebuflist()*
The result is a |List|, where each item is the number of the
@ -8987,6 +9260,8 @@ tan({expr}) *tan()*
:echo tan(-4.01)
< -1.181502
Can also be used as a |method|: >
Compute()->tan()
tanh({expr}) *tanh()*
Return the hyperbolic tangent of {expr} as a |Float| in the
@ -8998,6 +9273,8 @@ tanh({expr}) *tanh()*
:echo tanh(-1)
< -0.761594
Can also be used as a |method|: >
Compute()->tanh()
*timer_info()*
timer_info([{id}])
@ -9124,6 +9401,9 @@ trunc({expr}) *trunc()*
echo trunc(4.0)
< 4.0
Can also be used as a |method|: >
Compute()->trunc()
type({expr}) *type()*
The result is a Number representing the type of {expr}.
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: >
:if exists('v:t_number')
< Can also be used as a |method|: >
mylist->type()
undofile({name}) *undofile()*
Return the name of the undo file that would be used for a file
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
each item. For the use of {func} and {dict} see |sort()|.
Can also be used as a |method|: >
mylist->uniq()
values({dict}) *values()*
Return a |List| with all the values of {dict}. The |List| is
in arbitrary order.
Can also be used as a |method|: >
mydict->values()
virtcol({expr}) *virtcol()*
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.
Example: >
:echo "The file in the current window is " . bufname(winbufnr(0))
<
Can also be used as a |method|: >
FindWindow()->winbufnr()->bufname()
<
*wincol()*
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.
Example: >
: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'|
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 ~
@ -10686,7 +10981,7 @@ text...
<
*:eval*
: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,
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 >
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.
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
|assert-return|.
Can also be used as a |method|: >
GetCmd()->assert_beeps()
<
*assert_equal()*
assert_equal({expected}, {actual} [, {msg}])
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|:
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})
When the files {fname-one} and {fname-two} do not contain
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
mention that.
Can also be used as a |method|: >
GetLog()->assert_equalfile('expected.log')
assert_exception({error} [, {msg}]) *assert_exception()*
When v:exception does not contain the string {error} an error
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
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()*
When {actual} is not false an error message is added to
|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
"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()*
This asserts number and |Float| values. When {actual} is lower
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|:
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()*
Run {cmd} and add an error message to |v:errors| if it
produces a beep or visual bell.
@ -145,16 +163,27 @@ assert_notequal({expected}, {actual} [, {msg}])
|v:errors| when {expected} and {actual} are equal.
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}])
The opposite of `assert_match()`: add an error message to
|v:errors| when {pattern} matches {actual}.
Also see |assert-return|.
Can also be used as a |method|: >
getFile()->assert_notmatch('bar.*')
assert_report({msg}) *assert_report()*
Report a test failure directly, using {msg}.
Always returns one.
Can also be used as a |method|: >
GetMessage()->assert_report()
assert_true({actual} [, {msg}]) *assert_true()*
When {actual} is not true an error message is added to
|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
got {actual}" is produced.
Can also be used as a |method|: >
GetResult()->assert_true()
<
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++;
try_start();
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`
// (see above) to capture abort-causing non-exception errors.
(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,
&dummy, true, NULL, self);
vim_args, &funcexe);
if (!try_end(err)) {
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_illvar = N_("E461: Illegal variable name: %s");
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_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)
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
int dummy;
funcexe_T funcexe = FUNCEXE_INIT;
if (expr->v_type == VAR_FUNC) {
const char_u *const s = expr->vval.v_string;
if (s == NULL || *s == NUL) {
return FAIL;
}
if (call_func(s, -1, rettv, argc, argv, NULL,
0L, 0L, &dummy, true, NULL, NULL) == FAIL) {
funcexe.evaluate = true;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
return FAIL;
}
} 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) {
return FAIL;
}
if (call_func(s, -1, rettv, argc, argv, NULL,
0L, 0L, &dummy, true, partial, NULL) == FAIL) {
funcexe.evaluate = true;
funcexe.partial = partial;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) {
return FAIL;
}
} else {
@ -1050,7 +1053,6 @@ int call_vim_function(
)
FUNC_ATTR_NONNULL_ALL
{
int doesrange;
int ret;
int len = (int)STRLEN(func);
partial_T *pt = NULL;
@ -1066,9 +1068,12 @@ int call_vim_function(
}
rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this.
ret = call_func(func, len, rettv, argc, argv, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&doesrange, true, pt, NULL);
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = true;
funcexe.partial = pt;
ret = call_func(func, len, rettv, argc, argv, &funcexe);
fail:
if (ret == FAIL) {
@ -1724,7 +1729,9 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
} else {
// handle d.key, l[idx], f(expr)
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;
} else {
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;
}
/// 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
/*
@ -3161,6 +3227,8 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate)
{
int ret;
char_u *p;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
p = skipwhite(arg);
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
// been cancelled due to an aborting error, an interrupt, or an
// exception.
if (!aborting()) {
// exception, or we already gave a more specific error.
// 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);
}
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)
// trailing [] subscript in String or List
// trailing .name entry in Dictionary
// trailing ->name() method call
//
// "arg" must point to the first non-white of the expression.
// "arg" is advanced to the next non-white after the recognized expression.
@ -3815,10 +3886,10 @@ static int eval7(
{
varnumber_T n;
int len;
char_u *s;
char_u *start_leader, *end_leader;
char_u *s;
const char_u *start_leader, *end_leader;
int ret = OK;
char_u *alias;
char_u *alias;
// Initialise variable so that tv_clear() can't mistake this for a
// string and free a string that isn't there.
@ -3968,44 +4039,7 @@ static int eval7(
ret = FAIL;
} else {
if (**arg == '(') { // recursive!
partial_T *partial;
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;
}
ret = eval_func(arg, s, len, rettv, evaluate, NULL);
} else if (evaluate) {
ret = get_var_tv((const char *)s, len, rettv, NULL, true, false);
} else {
@ -4019,51 +4053,230 @@ static int eval7(
*arg = skipwhite(*arg);
// Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr).
// expr(expr), expr->name(expr)
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 '+'.
if (ret == OK && evaluate && end_leader > start_leader) {
bool error = false;
varnumber_T val = 0;
float_T f = 0.0;
ret = eval7_leader(rettv, start_leader, &end_leader);
}
return ret;
}
if (rettv->v_type == VAR_FLOAT) {
f = rettv->vval.v_float;
} else {
val = tv_get_number_chk(rettv, &error);
}
if (error) {
tv_clear(rettv);
ret = FAIL;
} else {
while (end_leader > start_leader) {
--end_leader;
if (*end_leader == '!') {
if (rettv->v_type == VAR_FLOAT) {
f = !f;
} else {
val = !val;
}
} else if (*end_leader == '-') {
if (rettv->v_type == VAR_FLOAT) {
f = -f;
} else {
val = -val;
}
/// 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;
varnumber_T val = 0;
float_T f = 0.0;
if (rettv->v_type == VAR_FLOAT) {
f = rettv->vval.v_float;
} else {
val = tv_get_number_chk(rettv, &error);
}
if (error) {
tv_clear(rettv);
ret = FAIL;
} else {
while (end_leader > start_leader) {
end_leader--;
if (*end_leader == '!') {
if (rettv->v_type == VAR_FLOAT) {
f = !f;
} else {
val = !val;
}
} else if (*end_leader == '-') {
if (rettv->v_type == VAR_FLOAT) {
f = -f;
} else {
val = -val;
}
}
if (rettv->v_type == VAR_FLOAT) {
tv_clear(rettv);
rettv->vval.v_float = f;
}
if (rettv->v_type == VAR_FLOAT) {
tv_clear(rettv);
rettv->vval.v_float = f;
} else {
tv_clear(rettv);
rettv->v_type = VAR_NUMBER;
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 {
tv_clear(rettv);
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = val;
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;
@ -7255,10 +7468,12 @@ bool callback_call(Callback *const callback, const int argcount_in,
abort();
}
int dummy;
return call_func(name, -1, rettv, argcount_in, argvars_in,
NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
true, partial, NULL);
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
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,
@ -8393,13 +8608,23 @@ static bool tv_is_luafunc(typval_T *tv)
return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial);
}
/// check the function name after "v:lua."
int check_luafunc_name(const char *str, bool paren)
/// Skips one character past the end of the name of a v:lua function.
/// @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 == '\'') {
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)) {
return 0;
} else {
@ -8407,24 +8632,26 @@ int check_luafunc_name(const char *str, bool paren)
}
}
/// Handle expr[expr], expr[expr:expr] subscript and .name lookup.
/// Also handle function call with Funcref variable: func(expr)
/// Can all be combined: dict.func(expr)[idx]['func'](expr)
/// Handle:
/// - expr[expr], expr[expr:expr] subscript
/// - ".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
handle_subscript(
const char **const arg,
typval_T *rettv,
int evaluate, // do more than finding the end
int verbose // give error messages
int evaluate, // do more than finding the end
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;
dict_T *selfdict = NULL;
const char_u *s;
int len;
typval_T functv;
int slen = 0;
bool lua = false;
dict_T *selfdict = NULL;
const char_u *lua_funcname = NULL;
if (tv_is_luafunc(rettv)) {
if (**arg != '.') {
@ -8433,55 +8660,28 @@ handle_subscript(
} else {
(*arg)++;
lua = true;
s = (char_u *)(*arg);
slen = check_luafunc_name(*arg, true);
if (slen == 0) {
lua_funcname = (char_u *)(*arg);
const int len = check_luafunc_name(*arg, true);
if (len == 0) {
tv_clear(rettv);
ret = FAIL;
}
(*arg) += slen;
(*arg) += len;
}
}
while (ret == OK
&& (**arg == '['
|| (**arg == '.' && rettv->v_type == VAR_DICT)
|| (**arg == '(' && (!evaluate || tv_is_func(*rettv))))
&& !ascii_iswhite(*(*arg - 1))) {
&& (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT)
|| (**arg == '(' && (!evaluate || tv_is_func(*rettv))))
&& !ascii_iswhite(*(*arg - 1)))
|| (**arg == '-' && (*arg)[1] == '>'))) {
if (**arg == '(') {
partial_T *pt = NULL;
// need to copy the funcref so that we can clear rettv
if (evaluate) {
functv = *rettv;
rettv->v_type = VAR_UNKNOWN;
ret = call_func_rettv((char_u **)arg, rettv, evaluate, selfdict, NULL,
lua_funcname);
// Invoke the function. Recursive!
if (functv.v_type == VAR_PARTIAL) {
pt = functv.vval.v_partial;
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. */
// 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 (ret == OK) {
tv_clear(rettv);
@ -8490,6 +8690,21 @@ handle_subscript(
}
tv_dict_unref(selfdict);
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 == '.'
tv_dict_unref(selfdict);
if (rettv->v_type == VAR_DICT) {
@ -9274,6 +9489,7 @@ void ex_echo(exarg_T *eap)
bool atstart = true;
bool need_clear = true;
const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg;
if (eap->skip)
++emsg_skip;
@ -9288,7 +9504,8 @@ void ex_echo(exarg_T *eap)
// Report the invalid expression unless the expression evaluation
// has been cancelled due to an aborting error, an interrupt, or an
// exception.
if (!aborting() && did_emsg == did_emsg_before) {
if (!aborting() && did_emsg == did_emsg_before
&& called_emsg == called_emsg_before) {
EMSG2(_(e_invexpr2), p);
}
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 };
tv_list_ref(arguments);
int dummy;
(void)call_func((const char_u *)func,
name_len,
&rettv,
2,
argvars,
NULL,
curwin->w_cursor.lnum,
curwin->w_cursor.lnum,
&dummy,
true,
NULL,
NULL);
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
funcexe.lastline = curwin->w_cursor.lnum;
funcexe.evaluate = true;
(void)call_func((const char_u *)func, name_len, &rettv, 2, argvars, &funcexe);
tv_list_unref(arguments);
// 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;
if (n) {
// 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) {
tv_clear(&tv);
}

View File

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

View File

@ -9,11 +9,16 @@ typedef void (*FunPtr)(void);
/// Prototype of C function that implements VimL function
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
typedef struct fst {
char *name; ///< Name of the function.
uint8_t min_argc; ///< Minimal 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.
FunPtr data; ///< Userdata for function implementation.
} VimLFuncDef;

View File

@ -414,12 +414,7 @@ get_func_tv(
int len, // length of "name" or -1 to use strlen()
typval_T *rettv,
char_u **arg, // argument, pointing to the '('
linenr_T firstline, // first line of range
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"
funcexe_T *funcexe // various values
)
{
char_u *argp;
@ -431,12 +426,13 @@ get_func_tv(
* Get the arguments.
*/
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 ','
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) {
if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
ret = FAIL;
break;
}
@ -463,9 +459,7 @@ get_func_tv(
((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
}
}
ret = call_func(name, len, rettv, argcount, argvars, NULL,
firstline, lastline, doesrange, evaluate,
partial, selfdict);
ret = call_func(name, len, rettv, argcount, argvars, funcexe);
funcargs.ga_len -= i;
} 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];
int argc = 0;
int dummy;
int r = 0;
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++]);
});
r = call_func(name, -1, rettv, argc, argv, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&dummy, true, partial, selfdict);
funcexe_T funcexe = FUNCEXE_INIT;
funcexe.firstline = curwin->w_cursor.lnum;
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:
// Free the arguments.
@ -1402,6 +1399,9 @@ static void user_func_error(int error, const char_u *name)
case ERROR_UNKNOWN:
emsg_funcname(N_("E117: Unknown function: %s"), name);
break;
case ERROR_NOTMETHOD:
emsg_funcname(N_("E276: Cannot use function as a method: %s"), name);
break;
case ERROR_DELETED:
emsg_funcname(N_("E933: Function was deleted: %s"), name);
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
///
/// "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
/// occurred while executing the function! Set `msg_list` to capture
/// the error, see do_cmdline()).
@ -1440,15 +1453,9 @@ call_func(
int argcount_in, // number of "argvars"
typval_T *argvars_in, // vars for arguments, must have "argcount"
// PLUS ONE elements!
ArgvFunc argv_func, // function to fill in argvars
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"
funcexe_T *funcexe // more arguments
)
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 9)
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6)
{
int ret = FAIL;
int error = ERROR_NONE;
@ -1459,9 +1466,12 @@ call_func(
char_u *name = NULL;
int argcount = argcount_in;
typval_T *argvars = argvars_in;
dict_T *selfdict = selfdict_in;
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" is not NULL
dict_T *selfdict = funcexe->selfdict;
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
// "funcexe->basetv" is not NULL
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)
// even when call_func() returns FAIL.
@ -1480,14 +1490,15 @@ call_func(
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
}
*doesrange = false;
if (funcexe->doesrange != NULL) {
*funcexe->doesrange = false;
}
if (partial != NULL) {
// When the function has a partial with a dict and there is a dict
// argument, use the dict argument. That is backwards compatible.
// When the dict was bound explicitly use the one from the partial.
if (partial->pt_dict != NULL
&& (selfdict_in == NULL || !partial->pt_auto)) {
if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) {
selfdict = partial->pt_dict;
}
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;
// Ignore "g:" before a function name.
@ -1521,7 +1532,12 @@ call_func(
if (is_luafunc(partial)) {
if (len > 0) {
error = ERROR_NONE;
argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
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)) {
// User defined function.
@ -1549,13 +1565,16 @@ call_func(
cfunc_T cb = fp->uf_cb;
error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state);
} else if (fp != NULL) {
if (argv_func != NULL) {
if (funcexe->argv_func != NULL) {
// postponed filling in the arguments, do it now
argcount = argv_func(argcount, argvars, argv_clear,
fp->uf_args.ga_len);
argcount = funcexe->argv_func(argcount, argvars, argv_clear,
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) {
error = ERROR_TOOFEW;
@ -1565,25 +1584,20 @@ call_func(
error = ERROR_DICT;
} else {
// 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);
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 {
// Find the function name in the table, call its implementation.
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
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;
}
}
error = call_internal_func(fname, argcount, argvars, rettv);
}
/*
* The function call (or "FuncUndefined" autocommand sequence) might
@ -1607,9 +1621,11 @@ theend:
user_func_error(error, (name != NULL) ? name : funcname);
}
// clear the copies made from the partial
while (argv_clear > 0) {
tv_clear(&argv[--argv_clear]);
tv_clear(&argv[--argv_clear + argv_base]);
}
xfree(tofree);
xfree(name);
@ -2901,7 +2917,7 @@ void ex_call(exarg_T *eap)
int len;
typval_T rettv;
linenr_T lnum;
int doesrange;
bool doesrange;
bool failed = false;
funcdict_T fudi;
partial_T *partial = NULL;
@ -2947,7 +2963,7 @@ void ex_call(exarg_T *eap)
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
if (*startarg != '(') {
EMSG2(_("E107: Missing parentheses: %s"), eap->arg);
EMSG2(_(e_missingparen), eap->arg);
goto end;
}
@ -2965,15 +2981,22 @@ void ex_call(exarg_T *eap)
curwin->w_cursor.coladd = 0;
}
arg = startarg;
if (get_func_tv(name, -1, &rettv, &arg,
eap->line1, eap->line2, &doesrange,
true, partial, fudi.fd_dict) == FAIL) {
funcexe_T funcexe = FUNCEXE_INIT;
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;
break;
}
// 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) {
failed = true;
break;

View File

@ -28,11 +28,37 @@ typedef enum {
ERROR_OTHER,
ERROR_BOTH,
ERROR_DELETED,
ERROR_NOTMETHOD,
} FnameTransError;
/// Used in funcexe_T. Returns the new argcount.
typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip,
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 FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]

View File

@ -42,7 +42,7 @@ gperfpipe:write([[
%language=ANSI-C
%global-table
%readonly-tables
%define initializer-suffix ,0,0,NULL,NULL
%define initializer-suffix ,0,0,BASE_NONE,NULL,NULL
%define word-array-name functions
%define hash-function-name hash_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
args[2] = 'MAX_FUNC_ARGS'
end
local base = def.base or "BASE_NONE"
local func = def.func or ('f_' .. name)
local data = def.data or "NULL"
gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n')
:format(name, args[1], args[2], func, data))
gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n')
:format(name, args[1], args[2], base, func, data))
end
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_usingsid[] INIT(= N_(
"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_(
"E363: pattern uses more memory than 'maxmempattern'"));
EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer"));

View File

@ -785,13 +785,13 @@ int nlua_call(lua_State *lstate)
try_start();
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`
// (TRY_WRAP) to capture abort-causing non-exception errors.
(void)call_func(name, (int)name_len, &rettv, nargs,
vim_args, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&dummy, true, NULL, NULL);
(void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
if (!try_end(&err)) {
nlua_push_typval(lstate, &rettv, false);
}

View File

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

View File

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

View File

@ -5,3 +5,7 @@ let foo#bar = {}
func foo#bar.echo()
let g:called_foo_bar_echo += 1
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(1, line('$'))
rew
call assert_notequal(curbuf, bufnr('%'))
call assert_equal('Xargadd', bufname('%'))
call assert_notequal(curbuf, '%'->bufnr())
call assert_equal('Xargadd', '%'->bufname())
call assert_equal(2, line('$'))
%argd

View File

@ -7,7 +7,7 @@ func Test_assert_equalfile()
let goodtext = ["one", "two", "three"]
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 remove(v:errors, 0)

View File

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

View File

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

View File

@ -18,7 +18,7 @@ function Test_getbufwintabinfo()
let l = getbufinfo('%')
call assert_equal(bufnr('%'), l[0].bufnr)
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'
call assert_equal(0, len(getbufinfo({'bufmodified' : 1})))

View File

@ -118,6 +118,16 @@ b = something();
bw!
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.
func Test_cindent_case()
new

View File

@ -725,7 +725,7 @@ func Test_diff_filler()
diffthis
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
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
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(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(synIDattr(diff_hlID(1, 2), "name"), "DiffText")
call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "")
call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText")
call diff_hlID(2, 1)->synIDattr("name")->assert_equal("")
call assert_equal(diff_hlID(3, 1), hlID("DiffAdd"))
call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "DiffAdd")
call assert_equal(synIDattr(diff_hlID(4, 1), "name"), "")
call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd")
call diff_hlID(4, 1)->synIDattr("name")->assert_equal("")
wincmd w
call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))

View File

@ -7,6 +7,8 @@ end
func Test_abs()
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(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()
call assert_equal('0.0', string(sqrt(0.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('nan')", string(sqrt(-1.0)))
call assert_equal("str2float('nan')", string(sqrt(0.0/0.0)))
@ -31,6 +34,7 @@ endfunc
func Test_log()
call assert_equal('0.0', string(log(1.0)))
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('nan')", string(log(-1.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('2.0', string(log10(100.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('nan')", string(log10(-1.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('7.389056', string(exp(2.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('0.0', string(exp(-1.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.841471', string(sin(1.0)))
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(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()
call assert_equal('0.0', string(asin(0.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("str2float('nan')", string(asin(1.1)))
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.521095', string(sinh(0.5)))
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('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('0.540302', string(cos(1.0)))
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(1.0/0.0)))
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('0.0', 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("str2float('nan')", string(acos(1.1)))
call assert_equal("str2float('nan')", string(acos(1.0/0.0)))
@ -113,6 +125,7 @@ endfunc
func Test_cosh()
call assert_equal('1.0', string(cosh(0.0)))
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('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.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(cos(0.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.463648', string(atan(0.5)))
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("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('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("str2float('nan')", string(atan2(0.0/0.0, 1.0)))
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.462117', string(tanh(0.5)))
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("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("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.
let res = string(fmod(1.0, 1.0/0.0))
if res != "str2float('nan')"
@ -177,6 +195,7 @@ endfunc
func Test_pow()
call assert_equal('1.0', string(pow(0.0, 0.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(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.23', string(str2float('1.23')))
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.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(123, float2nr(1.234e2))
call assert_equal(12, float2nr(123.4e-1))
eval 123.4e-1->float2nr()->assert_equal(12)
let max_number = 1/0
let min_number = -max_number
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.11)))
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.99)))
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.99)))
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("str2float('nan')", string(ceil(0.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('3.0', string(round(2.5)))
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('-3.0', string(round(-2.5)))
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.5)))
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.5)))
call assert_equal('-2.0', string(trunc(-2.9)))
@ -291,6 +316,7 @@ endfunc
func Test_isinf()
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(0.0/0.0))
call assert_false(isinf('a'))
@ -302,6 +328,7 @@ func Test_isnan()
call assert_true(isnan(0.0/0.0))
call assert_false(isnan(1.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('a'))
call assert_false(isnan([]))

View File

@ -852,7 +852,7 @@ func Test_byte2line_line2byte()
set fileformat=mac
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],
\ map(range(-1, 5), 'line2byte(v:val)'))
@ -875,6 +875,34 @@ func Test_byte2line_line2byte()
bw!
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()
func Test_charidx()
let a = 'xáb́y'
@ -1065,7 +1093,7 @@ func Test_col()
call assert_equal(7, col('$'))
call assert_equal(4, col("'x"))
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(0, col(''))
@ -1413,13 +1441,13 @@ func Test_bufadd_bufload()
call assert_equal([''], getbufline(buf, 1, '$'))
let curbuf = bufnr('')
call writefile(['some', 'text'], 'otherName')
let buf = bufadd('otherName')
call writefile(['some', 'text'], 'XotherName')
let buf = 'XotherName'->bufadd()
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, bufloaded(buf))
call bufload(buf)
eval buf->bufload()
call assert_equal(1, bufloaded(buf))
call assert_equal(['some', 'text'], getbufline(buf, 1, '$'))
call assert_equal(curbuf, bufnr(''))
@ -1439,8 +1467,9 @@ func Test_bufadd_bufload()
call assert_equal(0, bufexists(buf2))
bwipe someName
bwipe otherName
bwipe XotherName
call assert_equal(0, bufexists('someName'))
call delete('XotherName')
endfunc
func Test_readdir()
@ -1473,6 +1502,20 @@ func Test_readdir()
call delete('Xdir', 'rf')
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
func Test_eval()
call assert_fails("call eval('5 a')", 'E488:')

View File

@ -37,7 +37,7 @@ function Test_hide()
" :hide as a command
hide
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
new Xf1

View File

@ -61,7 +61,7 @@ endfunction
function Test_lambda_fails()
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:')
endfunc

View File

@ -242,7 +242,7 @@ func Test_matchaddpos_otherwin()
\]
call assert_equal(expect, savematches)
call clearmatches(winid)
eval winid->clearmatches()
call assert_equal([], getmatches(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! s:complTest1() abort
call complete(1, ['source', 'soundfold'])
eval ['source', 'soundfold']->complete(1)
return ''
endfunc
@ -403,7 +403,7 @@ func DummyCompleteFour(findstart, base)
return 0
else
call complete_add('four1')
call complete_add('four2')
eval 'four2'->complete_add()
call complete_check()
call complete_add('four3')
call complete_add('four4')
@ -989,7 +989,7 @@ func GetCompleteInfo()
if empty(g:compl_what)
let g:compl_info = complete_info()
else
let g:compl_info = complete_info(g:compl_what)
let g:compl_info = g:compl_what->complete_info()
endif
return ''
endfunc

View File

@ -546,8 +546,8 @@ func Test_synstack_synIDtrans()
call assert_equal([], synstack(1, 1))
norm f/
call assert_equal(['cComment', 'cCommentStart'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")'))
call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")'))
eval synstack(line("."), col("."))->map('synIDattr(v:val, "name")')->assert_equal(['cComment', 'cCommentStart'])
eval synstack(line("."), col("."))->map('synIDattr(synIDtrans(v:val), "name")')->assert_equal(['Comment', 'Comment'])
norm fA
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')
return
endif
let out = system('echo 123')
let out = 'echo 123'->system()
call assert_equal("123\n", out)
let out = systemlist('echo 123')
let out = 'echo 123'->systemlist()
if &shell =~# 'cmd.exe$'
call assert_equal(["123\r"], out)
else

View File

@ -47,7 +47,7 @@ func FuncWithRef(a)
endfunc
func Test_user_func()
let g:FuncRef=function("FuncWithRef")
let g:FuncRef = function("FuncWithRef")
let g:counter = 0
inoremap <expr> ( ListItem()
inoremap <expr> [ ListReset()
@ -62,6 +62,14 @@ func Test_user_func()
call assert_equal(9, g:retval)
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
normal oXX+-XX
@ -150,6 +158,14 @@ func Test_default_arg()
\ execute('func Args2'))
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()
try | call UnknownFunc() | catch | endtry
endfunc

View File

@ -1372,6 +1372,7 @@ func Test_bitwise_functions()
" and
call assert_equal(127, and(127, 127))
call assert_equal(16, and(127, 16))
eval 127->and(16)->assert_equal(16)
call assert_equal(0, and(127, 128))
call assert_fails("call and(1.0, 1)", 'E805:')
call assert_fails("call and([], 1)", 'E745:')
@ -1382,6 +1383,7 @@ func Test_bitwise_functions()
" or
call assert_equal(23, or(16, 7))
call assert_equal(15, or(8, 7))
eval 8->or(7)->assert_equal(15)
call assert_equal(123, or(0, 123))
call assert_fails("call or(1.0, 1)", 'E805:')
call assert_fails("call or([], 1)", 'E745:')
@ -1392,6 +1394,7 @@ func Test_bitwise_functions()
" xor
call assert_equal(0, xor(127, 127))
call assert_equal(111, xor(127, 16))
eval 127->xor(16)->assert_equal(111)
call assert_equal(255, xor(127, 128))
call assert_fails("call xor(1.0, 1)", 'E805:')
call assert_fails("call xor([], 1)", 'E745:')
@ -1401,6 +1404,7 @@ func Test_bitwise_functions()
call assert_fails("call xor(1, {})", 'E728:')
" invert
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(65407, and(invert(128), 65535))
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"')]])
screen:expect([[
^ |
|
Error detected while processing pre-vimrc command line: |
E121: Undefined variable: g:bar |
E15: Invalid expression: g:bar |
Press ENTER or type command to continue |
|
]])

View File

@ -53,7 +53,7 @@ describe('NULL', function()
-- Correct behaviour
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('is not locked', 'islocked("v:_null_list")', 0, 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 deepcopied', 'deepcopy(L)', 0, {})
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 col()', 'col(L)', 0, 0)
null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0)
@ -135,7 +135,7 @@ describe('NULL', function()
end)
describe('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'))
end)
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')
expected_errors({'command did not beep: normal 0'})
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)
-- assert_equal({expected}, {actual}, [, {msg}])
@ -133,6 +141,14 @@ describe('assert function:', function()
call('assert_false', {})
expected_errors({'Expected False but got []'})
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)
-- assert_true({actual}, [, {msg}])
@ -148,6 +164,14 @@ describe('assert function:', function()
eq(1, call('assert_true', 1.5))
expected_errors({'Expected True but got 1.5'})
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)
describe('v:errors', function()
@ -223,6 +247,15 @@ describe('assert function:', function()
call('assert_match', 'bar.*foo', 'foobar', 'wrong')
expected_errors({"wrong: Pattern 'bar.*foo' does not match 'foobar'"})
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)
-- assert_notmatch({pat}, {text}[, {msg}])
@ -237,6 +270,13 @@ describe('assert function:', function()
call('assert_notmatch', 'foo', 'foobar')
expected_errors({"Pattern 'foo' does match 'foobar'"})
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)
-- assert_fails({cmd}, [, {error}])
@ -267,6 +307,15 @@ describe('assert function:', function()
eq(1, eval([[assert_fails('echo', '', 'echo command')]]))
expected_errors({'command did not fail: echo command'})
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)
-- 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',
exc_exec("call assert_inrange(1, 1)"))
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)
-- assert_report({msg})
@ -302,6 +360,13 @@ describe('assert function:', function()
command('call remove(v:errors, 0)')
expected_empty()
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)
-- assert_exception({cmd}, [, {error}])

View File

@ -481,6 +481,21 @@ describe('v:lua', function()
pcall_err(eval, 'v:lua.mymod.crashy()'))
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()
command(":call v:lua.mymod.noisy('command')")
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(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: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)

View File

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