mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
commit
9752a333c3
@ -112,9 +112,10 @@ You will not get an error if you try to change the type of a variable.
|
|||||||
|
|
||||||
1.2 Function references ~
|
1.2 Function references ~
|
||||||
*Funcref* *E695* *E718*
|
*Funcref* *E695* *E718*
|
||||||
A Funcref variable is obtained with the |function()| function. It can be used
|
A Funcref variable is obtained with the |function()| function or created with
|
||||||
in an expression in the place of a function name, before the parenthesis
|
the lambda expression |expr-lambda|. It can be used in an expression in the
|
||||||
around the arguments, to invoke the function it refers to. Example: >
|
place of a function name, before the parenthesis around the arguments, to
|
||||||
|
invoke the function it refers to. Example: >
|
||||||
|
|
||||||
:let Fn = function("MyFunc")
|
:let Fn = function("MyFunc")
|
||||||
:echo Fn()
|
:echo Fn()
|
||||||
@ -667,6 +668,7 @@ Expression syntax summary, from least to most significant:
|
|||||||
@r contents of register 'r'
|
@r contents of register 'r'
|
||||||
function(expr1, ...) function call
|
function(expr1, ...) function call
|
||||||
func{ti}on(expr1, ...) function call with curly braces
|
func{ti}on(expr1, ...) function call with curly braces
|
||||||
|
{args -> expr1} lambda expression
|
||||||
|
|
||||||
|
|
||||||
".." indicates that the operations in this level can be concatenated.
|
".." indicates that the operations in this level can be concatenated.
|
||||||
@ -1174,6 +1176,62 @@ function(expr1, ...) function call
|
|||||||
See below |functions|.
|
See below |functions|.
|
||||||
|
|
||||||
|
|
||||||
|
lambda expression *expr-lambda* *lambda*
|
||||||
|
-----------------
|
||||||
|
{args -> expr1} lambda expression
|
||||||
|
|
||||||
|
A lambda expression creates a new unnamed function which returns the result of
|
||||||
|
evaluating |expr1|. Lambda expressions are differ from |user-functions| in
|
||||||
|
the following ways:
|
||||||
|
|
||||||
|
1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
|
||||||
|
commands.
|
||||||
|
2. The prefix "a:" should not be used for arguments. E.g.: >
|
||||||
|
:let F = {arg1, arg2 -> arg1 - arg2}
|
||||||
|
:echo F(5, 2)
|
||||||
|
< 3
|
||||||
|
|
||||||
|
The arguments are optional. Example: >
|
||||||
|
:let F = {-> 'error function'}
|
||||||
|
:echo F()
|
||||||
|
< error function
|
||||||
|
*closure*
|
||||||
|
Lambda expressions can access outer scope variables and arguments. This is
|
||||||
|
often called a closure. Example where "i" a and "a:arg" are used in a lambda
|
||||||
|
while they exists in the function scope. They remain valid even after the
|
||||||
|
function returns: >
|
||||||
|
:function Foo(arg)
|
||||||
|
: let i = 3
|
||||||
|
: return {x -> x + i - a:arg}
|
||||||
|
:endfunction
|
||||||
|
:let Bar = Foo(4)
|
||||||
|
:echo Bar(6)
|
||||||
|
< 5
|
||||||
|
See also |:func-closure|. Lambda and closure support can be checked with: >
|
||||||
|
if has('lambda')
|
||||||
|
|
||||||
|
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
|
||||||
|
:echo map([1, 2, 3], {idx, val -> val + 1})
|
||||||
|
< [2, 3, 4] >
|
||||||
|
:echo sort([3,7,2,1,4], {a, b -> a - b})
|
||||||
|
< [1, 2, 3, 4, 7]
|
||||||
|
|
||||||
|
The lambda expression is also useful for Channel, Job and timer: >
|
||||||
|
:let timer = timer_start(500,
|
||||||
|
\ {-> execute("echo 'Handler called'", "")},
|
||||||
|
\ {'repeat': 3})
|
||||||
|
< Handler called
|
||||||
|
Handler called
|
||||||
|
Handler called
|
||||||
|
|
||||||
|
Note how execute() is used to execute an Ex command. That's ugly though.
|
||||||
|
|
||||||
|
|
||||||
|
Lambda expressions have internal names like '<lambda>42'. If you get an error
|
||||||
|
for a lambda expression, you can find what it is with the following command: >
|
||||||
|
:function {'<lambda>42'}
|
||||||
|
See also: |numbered-function|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
3. Internal variable *internal-variables* *E461*
|
3. Internal variable *internal-variables* *E461*
|
||||||
|
|
||||||
@ -1481,7 +1539,7 @@ v:false Special value used to put "false" in JSON and msgpack. See
|
|||||||
|json_encode()|. This value is converted to "v:false" when used
|
|json_encode()|. This value is converted to "v:false" when used
|
||||||
as a String (e.g. in |expr5| with string concatenation
|
as a String (e.g. in |expr5| with string concatenation
|
||||||
operator) and to zero when used as a Number (e.g. in |expr5|
|
operator) and to zero when used as a Number (e.g. in |expr5|
|
||||||
or |expr7| when used with numeric operators).
|
or |expr7| when used with numeric operators). Read-only.
|
||||||
|
|
||||||
*v:fcs_reason* *fcs_reason-variable*
|
*v:fcs_reason* *fcs_reason-variable*
|
||||||
v:fcs_reason The reason why the |FileChangedShell| event was triggered.
|
v:fcs_reason The reason why the |FileChangedShell| event was triggered.
|
||||||
@ -1631,7 +1689,7 @@ v:null Special value used to put "null" in JSON and NIL in msgpack.
|
|||||||
See |json_encode()|. This value is converted to "v:null" when
|
See |json_encode()|. This value is converted to "v:null" when
|
||||||
used as a String (e.g. in |expr5| with string concatenation
|
used as a String (e.g. in |expr5| with string concatenation
|
||||||
operator) and to zero when used as a Number (e.g. in |expr5|
|
operator) and to zero when used as a Number (e.g. in |expr5|
|
||||||
or |expr7| when used with numeric operators).
|
or |expr7| when used with numeric operators). Read-only.
|
||||||
|
|
||||||
*v:oldfiles* *oldfiles-variable*
|
*v:oldfiles* *oldfiles-variable*
|
||||||
v:oldfiles List of file names that is loaded from the |shada| file on
|
v:oldfiles List of file names that is loaded from the |shada| file on
|
||||||
@ -1791,6 +1849,9 @@ v:termresponse The escape sequence returned by the terminal for the |t_RV|
|
|||||||
always 95 or bigger). Pc is always zero.
|
always 95 or bigger). Pc is always zero.
|
||||||
{only when compiled with |+termresponse| feature}
|
{only when compiled with |+termresponse| feature}
|
||||||
|
|
||||||
|
*v:testing* *testing-variable*
|
||||||
|
v:testing Must be set before using `test_garbagecollect_now()`.
|
||||||
|
|
||||||
*v:this_session* *this_session-variable*
|
*v:this_session* *this_session-variable*
|
||||||
v:this_session Full filename of the last loaded or saved session file. See
|
v:this_session Full filename of the last loaded or saved session file. See
|
||||||
|:mksession|. It is allowed to set this variable. When no
|
|:mksession|. It is allowed to set this variable. When no
|
||||||
@ -1814,7 +1875,7 @@ v:true Special value used to put "true" in JSON and msgpack. See
|
|||||||
|json_encode()|. This value is converted to "v:true" when used
|
|json_encode()|. This value is converted to "v:true" when used
|
||||||
as a String (e.g. in |expr5| with string concatenation
|
as a String (e.g. in |expr5| with string concatenation
|
||||||
operator) and to one when used as a Number (e.g. in |expr5| or
|
operator) and to one when used as a Number (e.g. in |expr5| or
|
||||||
|expr7| when used with numeric operators).
|
|expr7| when used with numeric operators). Read-only.
|
||||||
|
|
||||||
*v:val* *val-variable*
|
*v:val* *val-variable*
|
||||||
v:val Value of the current item of a |List| or |Dictionary|. Only
|
v:val Value of the current item of a |List| or |Dictionary|. Only
|
||||||
@ -1954,8 +2015,10 @@ foldlevel({lnum}) Number fold level at {lnum}
|
|||||||
foldtext() String line displayed for closed fold
|
foldtext() String line displayed for closed fold
|
||||||
foldtextresult({lnum}) String text for closed fold at {lnum}
|
foldtextresult({lnum}) String text for closed fold at {lnum}
|
||||||
foreground() Number bring the Vim window to the foreground
|
foreground() Number bring the Vim window to the foreground
|
||||||
function({name} [, {arglist}] [, {dict}])
|
funcref({name} [, {arglist}] [, {dict}])
|
||||||
Funcref reference to function {name}
|
Funcref reference to function {name}
|
||||||
|
function({name} [, {arglist}] [, {dict}])
|
||||||
|
Funcref named reference to function {name}
|
||||||
garbagecollect([{atexit}]) none free memory, breaking cyclic references
|
garbagecollect([{atexit}]) none free memory, breaking cyclic references
|
||||||
get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
|
get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
|
||||||
get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
|
get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
|
||||||
@ -2215,6 +2278,7 @@ tagfiles() List tags files used
|
|||||||
tan({expr}) Float tangent of {expr}
|
tan({expr}) Float tangent of {expr}
|
||||||
tanh({expr}) Float hyperbolic tangent of {expr}
|
tanh({expr}) Float hyperbolic tangent of {expr}
|
||||||
tempname() String name for a temporary file
|
tempname() String name for a temporary file
|
||||||
|
test_garbagecollect_now() none free memory right now for testing
|
||||||
timer_start({time}, {callback} [, {options}])
|
timer_start({time}, {callback} [, {options}])
|
||||||
Number create a timer
|
Number create a timer
|
||||||
timer_stop({timer}) none stop a timer
|
timer_stop({timer}) none stop a timer
|
||||||
@ -3369,31 +3433,47 @@ filewritable({file}) *filewritable()*
|
|||||||
directory, and we can write to it, the result is 2.
|
directory, and we can write to it, the result is 2.
|
||||||
|
|
||||||
|
|
||||||
filter({expr}, {string}) *filter()*
|
filter({expr1}, {expr2}) *filter()*
|
||||||
{expr} must be a |List| or a |Dictionary|.
|
{expr1} must be a |List| or a |Dictionary|.
|
||||||
For each item in {expr} evaluate {string} and when the result
|
For each item in {expr1} evaluate {expr2} and when the result
|
||||||
is zero remove the item from the |List| or |Dictionary|.
|
is zero remove the item from the |List| or |Dictionary|.
|
||||||
Inside {string} |v:val| has the value of the current item.
|
{expr2} must be a |string| or |Funcref|.
|
||||||
|
|
||||||
|
if {expr2} is a |string|, inside {expr2} |v:val| has the value
|
||||||
|
of the current item. For a |Dictionary| |v:key| has the key
|
||||||
|
of the current item.
|
||||||
For a |Dictionary| |v:key| has the key of the current item.
|
For a |Dictionary| |v:key| has the key of the current item.
|
||||||
Examples: >
|
Examples: >
|
||||||
:call filter(mylist, 'v:val !~ "OLD"')
|
call filter(mylist, 'v:val !~ "OLD"')
|
||||||
< Removes the items where "OLD" appears. >
|
< Removes the items where "OLD" appears. >
|
||||||
:call filter(mydict, 'v:key >= 8')
|
call filter(mydict, 'v:key >= 8')
|
||||||
< Removes the items with a key below 8. >
|
< Removes the items with a key below 8. >
|
||||||
:call filter(var, 0)
|
call filter(var, 0)
|
||||||
< Removes all the items, thus clears the |List| or |Dictionary|.
|
< Removes all the items, thus clears the |List| or |Dictionary|.
|
||||||
|
|
||||||
Note that {string} is the result of expression and is then
|
Note that {expr2} is the result of expression and is then
|
||||||
used as an expression again. Often it is good to use a
|
used as an expression again. Often it is good to use a
|
||||||
|literal-string| to avoid having to double backslashes.
|
|literal-string| to avoid having to double backslashes.
|
||||||
|
|
||||||
|
If {expr2} is a |Funcref| it must take two arguments:
|
||||||
|
1. the key or the index of the current item.
|
||||||
|
2. the value of the current item.
|
||||||
|
The function must return TRUE if the item should be kept.
|
||||||
|
Example that keeps the odd items of a list: >
|
||||||
|
func Odd(idx, val)
|
||||||
|
return a:idx % 2 == 1
|
||||||
|
endfunc
|
||||||
|
call filter(mylist, function('Odd'))
|
||||||
|
<
|
||||||
The operation is done in-place. If you want a |List| or
|
The operation is done in-place. If you want a |List| or
|
||||||
|Dictionary| to remain unmodified make a copy first: >
|
|Dictionary| to remain unmodified make a copy first: >
|
||||||
:let l = filter(copy(mylist), 'v:val =~ "KEEP"')
|
:let l = filter(copy(mylist), 'v:val =~ "KEEP"')
|
||||||
|
|
||||||
< Returns {expr}, the |List| or |Dictionary| that was filtered.
|
< Returns {expr1}, the |List| or |Dictionary| that was filtered.
|
||||||
When an error is encountered while evaluating {string} no
|
When an error is encountered while evaluating {expr2} no
|
||||||
further items in {expr} are processed.
|
further items in {expr1} are processed. When {expr2} is a
|
||||||
|
Funcref errors inside a function are ignored, unless it was
|
||||||
|
defined with the "abort" flag.
|
||||||
|
|
||||||
|
|
||||||
finddir({name}[, {path}[, {count}]]) *finddir()*
|
finddir({name}[, {path}[, {count}]]) *finddir()*
|
||||||
@ -3545,12 +3625,31 @@ foreground() Move the Vim window to the foreground. Useful when sent from
|
|||||||
|remote_foreground()| instead.
|
|remote_foreground()| instead.
|
||||||
{only in the Win32 GUI and console version}
|
{only in the Win32 GUI and console version}
|
||||||
|
|
||||||
|
*funcref()*
|
||||||
|
funcref({name} [, {arglist}] [, {dict}])
|
||||||
|
Just like |function()|, but the returned Funcref will lookup
|
||||||
|
the function by reference, not by name. This matters when the
|
||||||
|
function {name} is redefined later.
|
||||||
|
|
||||||
|
Unlike |function()|, {name} must be an existing user function.
|
||||||
|
Also for autoloaded functions. {name} cannot be a builtin
|
||||||
|
function.
|
||||||
|
|
||||||
*function()* *E700* *E922* *E923*
|
*function()* *E700* *E922* *E923*
|
||||||
function({name} [, {arglist}] [, {dict}])
|
function({name} [, {arglist}] [, {dict}])
|
||||||
Return a |Funcref| variable that refers to function {name}.
|
Return a |Funcref| variable that refers to function {name}.
|
||||||
{name} can be a user defined function or an internal function.
|
{name} can be a user defined function or an internal function.
|
||||||
|
|
||||||
|
{name} can also be a Funcref or a partial. When it is a
|
||||||
|
partial the dict stored in it will be used and the {dict}
|
||||||
|
argument is not allowed. E.g.: >
|
||||||
|
let FuncWithArg = function(dict.Func, [arg])
|
||||||
|
let Broken = function(dict.Func, [arg], dict)
|
||||||
|
<
|
||||||
|
When using the Funcref the function will be found by {name},
|
||||||
|
also when it was redefined later. Use |funcref()| to keep the
|
||||||
|
same function.
|
||||||
|
|
||||||
When {arglist} or {dict} is present this creates a partial.
|
When {arglist} or {dict} is present this creates a partial.
|
||||||
That mans the argument list and/or the dictionary is stored in
|
That mans the argument list and/or the dictionary is stored in
|
||||||
the Funcref and will be used when the Funcref is called.
|
the Funcref and will be used when the Funcref is called.
|
||||||
@ -3589,18 +3688,25 @@ function({name} [, {arglist}] [, {dict}])
|
|||||||
|
|
||||||
garbagecollect([{atexit}]) *garbagecollect()*
|
garbagecollect([{atexit}]) *garbagecollect()*
|
||||||
Cleanup unused |Lists| and |Dictionaries| that have circular
|
Cleanup unused |Lists| and |Dictionaries| that have circular
|
||||||
references. There is hardly ever a need to invoke this
|
references.
|
||||||
function, as it is automatically done when Vim runs out of
|
|
||||||
memory or is waiting for the user to press a key after
|
There is hardly ever a need to invoke this function, as it is
|
||||||
'updatetime'. Items without circular references are always
|
automatically done when Vim runs out of memory or is waiting
|
||||||
freed when they become unused.
|
for the user to press a key after 'updatetime'. Items without
|
||||||
|
circular references are always freed when they become unused.
|
||||||
This is useful if you have deleted a very big |List| and/or
|
This is useful if you have deleted a very big |List| and/or
|
||||||
|Dictionary| with circular references in a script that runs
|
|Dictionary| with circular references in a script that runs
|
||||||
for a long time.
|
for a long time.
|
||||||
|
|
||||||
When the optional {atexit} argument is one, garbage
|
When the optional {atexit} argument is one, garbage
|
||||||
collection will also be done when exiting Vim, if it wasn't
|
collection will also be done when exiting Vim, if it wasn't
|
||||||
done before. This is useful when checking for memory leaks.
|
done before. This is useful when checking for memory leaks.
|
||||||
|
|
||||||
|
The garbage collection is not done immediately but only when
|
||||||
|
it's safe to perform. This is when waiting for the user to
|
||||||
|
type a character. To force garbage collection immediately use
|
||||||
|
|test_garbagecollect_now()|.
|
||||||
|
|
||||||
get({list}, {idx} [, {default}]) *get()*
|
get({list}, {idx} [, {default}]) *get()*
|
||||||
Get item {idx} from |List| {list}. When this item is not
|
Get item {idx} from |List| {list}. When this item is not
|
||||||
available return {default}. Return zero when {default} is
|
available return {default}. Return zero when {default} is
|
||||||
@ -4937,29 +5043,43 @@ log10({expr}) *log10()*
|
|||||||
< -2.0
|
< -2.0
|
||||||
|
|
||||||
|
|
||||||
map({expr}, {string}) *map()*
|
map({expr1}, {expr2}) *map()*
|
||||||
{expr} must be a |List| or a |Dictionary|.
|
{expr1} must be a |List| or a |Dictionary|.
|
||||||
Replace each item in {expr} with the result of evaluating
|
Replace each item in {expr1} with the result of evaluating
|
||||||
{string}.
|
{expr2}. {expr2} must be a |string| or |Funcref|.
|
||||||
Inside {string} |v:val| has the value of the current item.
|
|
||||||
For a |Dictionary| |v:key| has the key of the current item
|
If {expr2} is a |string|, inside {expr2} |v:val| has the value
|
||||||
and for a |List| |v:key| has the index of the current item.
|
of the current item. For a |Dictionary| |v:key| has the key
|
||||||
|
of the current item and for a |List| |v:key| has the index of
|
||||||
|
the current item.
|
||||||
Example: >
|
Example: >
|
||||||
:call map(mylist, '"> " . v:val . " <"')
|
:call map(mylist, '"> " . v:val . " <"')
|
||||||
< This puts "> " before and " <" after each item in "mylist".
|
< This puts "> " before and " <" after each item in "mylist".
|
||||||
|
|
||||||
Note that {string} is the result of an expression and is then
|
Note that {expr2} is the result of an expression and is then
|
||||||
used as an expression again. Often it is good to use a
|
used as an expression again. Often it is good to use a
|
||||||
|literal-string| to avoid having to double backslashes. You
|
|literal-string| to avoid having to double backslashes. You
|
||||||
still have to double ' quotes
|
still have to double ' quotes
|
||||||
|
|
||||||
|
If {expr2} is a |Funcref| it is called with two arguments:
|
||||||
|
1. The key or the index of the current item.
|
||||||
|
2. the value of the current item.
|
||||||
|
The function must return the new value of the item. Example
|
||||||
|
that changes each value by "key-value": >
|
||||||
|
func KeyValue(key, val)
|
||||||
|
return a:key . '-' . a:val
|
||||||
|
endfunc
|
||||||
|
call map(myDict, function('KeyValue'))
|
||||||
|
<
|
||||||
The operation is done in-place. If you want a |List| or
|
The operation is done in-place. If you want a |List| or
|
||||||
|Dictionary| to remain unmodified make a copy first: >
|
|Dictionary| to remain unmodified make a copy first: >
|
||||||
:let tlist = map(copy(mylist), ' v:val . "\t"')
|
:let tlist = map(copy(mylist), ' v:val . "\t"')
|
||||||
|
|
||||||
< Returns {expr}, the |List| or |Dictionary| that was filtered.
|
< Returns {expr1}, the |List| or |Dictionary| that was filtered.
|
||||||
When an error is encountered while evaluating {string} no
|
When an error is encountered while evaluating {expr2} no
|
||||||
further items in {expr} are processed.
|
further items in {expr1} are processed. When {expr2} is a
|
||||||
|
Funcref errors inside a function are ignored, unless it was
|
||||||
|
defined with the "abort" flag.
|
||||||
|
|
||||||
|
|
||||||
maparg({name}[, {mode} [, {abbr} [, {dict}]]]) *maparg()*
|
maparg({name}[, {mode} [, {abbr} [, {dict}]]]) *maparg()*
|
||||||
@ -6003,6 +6123,7 @@ screenrow() *screenrow()*
|
|||||||
The result is a Number, which is the current screen row of the
|
The result is a Number, which is the current screen row of the
|
||||||
cursor. The top line has number one.
|
cursor. The top line has number one.
|
||||||
This function is mainly used for testing.
|
This function is mainly used for testing.
|
||||||
|
Alternatively you can use |winline()|.
|
||||||
|
|
||||||
Note: Same restrictions as with |screencol()|.
|
Note: Same restrictions as with |screencol()|.
|
||||||
|
|
||||||
@ -7029,6 +7150,14 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
|
|||||||
:echo substitute(s, '%\(\x\x\)',
|
:echo substitute(s, '%\(\x\x\)',
|
||||||
\ '\=nr2char("0x" . submatch(1))', 'g')
|
\ '\=nr2char("0x" . submatch(1))', 'g')
|
||||||
|
|
||||||
|
< When {sub} is a Funcref that function is called, with one
|
||||||
|
optional argument. Example: >
|
||||||
|
:echo substitute(s, '%\(\x\x\)', SubNr, 'g')
|
||||||
|
< The optional argument is a list which contains the whole
|
||||||
|
matched string and up to nine submatches,like what
|
||||||
|
|submatch()| returns. Example: >
|
||||||
|
:echo substitute(s, '\(\x\x\)', {m -> '0x' . m[1]}, 'g')
|
||||||
|
|
||||||
synID({lnum}, {col}, {trans}) *synID()*
|
synID({lnum}, {col}, {trans}) *synID()*
|
||||||
The result is a Number, which is the syntax ID at the position
|
The result is a Number, which is the syntax ID at the position
|
||||||
{lnum} and {col} in the current window.
|
{lnum} and {col} in the current window.
|
||||||
@ -7288,6 +7417,12 @@ termopen({cmd}[, {opts}]) {Nvim} *termopen()*
|
|||||||
|
|
||||||
See |terminal-emulator| for more information.
|
See |terminal-emulator| for more information.
|
||||||
|
|
||||||
|
test_garbagecollect_now() *test_garbagecollect_now()*
|
||||||
|
Like garbagecollect(), but executed right away. This must
|
||||||
|
only be called directly to avoid any structure to exist
|
||||||
|
internally, and |v:testing| must have been set before calling
|
||||||
|
any function.
|
||||||
|
|
||||||
tan({expr}) *tan()*
|
tan({expr}) *tan()*
|
||||||
Return the tangent of {expr}, measured in radians, as a |Float|
|
Return the tangent of {expr}, measured in radians, as a |Float|
|
||||||
in the range [-inf, inf].
|
in the range [-inf, inf].
|
||||||
@ -7379,16 +7514,18 @@ trunc({expr}) *trunc()*
|
|||||||
< 4.0
|
< 4.0
|
||||||
|
|
||||||
type({expr}) *type()*
|
type({expr}) *type()*
|
||||||
The result is a Number, depending on the type of {expr}:
|
The result is a Number representing the type of {expr}.
|
||||||
Number: 0
|
Instead of using the number directly, it is better to use the
|
||||||
String: 1
|
v:t_ variable that has the value:
|
||||||
Funcref: 2
|
Number: 0 (|v:t_number|)
|
||||||
List: 3
|
String: 1 (|v:t_string|)
|
||||||
Dictionary: 4
|
Funcref: 2 (|v:t_func|)
|
||||||
Float: 5
|
List: 3 (|v:t_list|)
|
||||||
|
Dictionary: 4 (|v:t_dict|)
|
||||||
|
Float: 5 (|v:t_float|)
|
||||||
Boolean: 6 (|v:true| and |v:false|)
|
Boolean: 6 (|v:true| and |v:false|)
|
||||||
Null: 7 (|v:null|)
|
Null: 7 (|v:null|)
|
||||||
To avoid the magic numbers it should be used this way: >
|
For backward compatibility, this method can be used: >
|
||||||
:if type(myvar) == type(0)
|
:if type(myvar) == type(0)
|
||||||
:if type(myvar) == type("")
|
:if type(myvar) == type("")
|
||||||
:if type(myvar) == type(function("tr"))
|
:if type(myvar) == type(function("tr"))
|
||||||
@ -7399,6 +7536,8 @@ type({expr}) *type()*
|
|||||||
< In place of checking for |v:null| type it is better to check
|
< In place of checking for |v:null| type it is better to check
|
||||||
for |v:null| directly as it is the only value of this type: >
|
for |v:null| directly as it is the only value of this type: >
|
||||||
:if myvar is v:null
|
:if myvar is v:null
|
||||||
|
< To check if the v:t_ variables exist use this: >
|
||||||
|
:if exists('v:t_number')
|
||||||
|
|
||||||
undofile({name}) *undofile()*
|
undofile({name}) *undofile()*
|
||||||
Return the name of the undo file that would be used for a file
|
Return the name of the undo file that would be used for a file
|
||||||
@ -7806,6 +7945,7 @@ insert_expand Compiled with support for CTRL-X expansion commands in
|
|||||||
Insert mode.
|
Insert mode.
|
||||||
jumplist Compiled with |jumplist| support.
|
jumplist Compiled with |jumplist| support.
|
||||||
keymap Compiled with 'keymap' support.
|
keymap Compiled with 'keymap' support.
|
||||||
|
lambda Compiled with |lambda| support.
|
||||||
langmap Compiled with 'langmap' support.
|
langmap Compiled with 'langmap' support.
|
||||||
libcall Compiled with |libcall()| support.
|
libcall Compiled with |libcall()| support.
|
||||||
linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and
|
linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and
|
||||||
@ -7957,7 +8097,7 @@ last defined. Example: >
|
|||||||
See |:verbose-cmd| for more information.
|
See |:verbose-cmd| for more information.
|
||||||
|
|
||||||
*E124* *E125* *E853* *E884*
|
*E124* *E125* *E853* *E884*
|
||||||
:fu[nction][!] {name}([arguments]) [range] [abort] [dict]
|
:fu[nction][!] {name}([arguments]) [range] [abort] [dict] [closure]
|
||||||
Define a new function by the name {name}. The name
|
Define a new function by the name {name}. The name
|
||||||
must be made of alphanumeric characters and '_', and
|
must be made of alphanumeric characters and '_', and
|
||||||
must start with a capital or "s:" (see above). Note
|
must start with a capital or "s:" (see above). Note
|
||||||
@ -8000,6 +8140,28 @@ See |:verbose-cmd| for more information.
|
|||||||
be invoked through an entry in a |Dictionary|. The
|
be invoked through an entry in a |Dictionary|. The
|
||||||
local variable "self" will then be set to the
|
local variable "self" will then be set to the
|
||||||
dictionary. See |Dictionary-function|.
|
dictionary. See |Dictionary-function|.
|
||||||
|
*:func-closure* *E932*
|
||||||
|
When the [closure] argument is added, the function
|
||||||
|
can access variables and arguments from the outer
|
||||||
|
scope. This is usually called a closure. In this
|
||||||
|
example Bar() uses "x" from the scope of Foo(). It
|
||||||
|
remains referenced even after Foo() returns: >
|
||||||
|
:function! Foo()
|
||||||
|
: let x = 0
|
||||||
|
: function! Bar() closure
|
||||||
|
: let x += 1
|
||||||
|
: return x
|
||||||
|
: endfunction
|
||||||
|
: return function('Bar')
|
||||||
|
:endfunction
|
||||||
|
|
||||||
|
:let F = Foo()
|
||||||
|
:echo F()
|
||||||
|
< 1 >
|
||||||
|
:echo F()
|
||||||
|
< 2 >
|
||||||
|
:echo F()
|
||||||
|
< 3
|
||||||
|
|
||||||
*function-search-undo*
|
*function-search-undo*
|
||||||
The last used search pattern and the redo command "."
|
The last used search pattern and the redo command "."
|
||||||
@ -8011,7 +8173,7 @@ See |:verbose-cmd| for more information.
|
|||||||
:endf[unction] The end of a function definition. Must be on a line
|
:endf[unction] The end of a function definition. Must be on a line
|
||||||
by its own, without other commands.
|
by its own, without other commands.
|
||||||
|
|
||||||
*:delf* *:delfunction* *E130* *E131*
|
*:delf* *:delfunction* *E130* *E131* *E933*
|
||||||
:delf[unction] {name} Delete function {name}.
|
:delf[unction] {name} Delete function {name}.
|
||||||
{name} can also be a |Dictionary| entry that is a
|
{name} can also be a |Dictionary| entry that is a
|
||||||
|Funcref|: >
|
|Funcref|: >
|
||||||
@ -8047,10 +8209,10 @@ can be 0). "a:000" is set to a |List| that contains these arguments. Note
|
|||||||
that "a:1" is the same as "a:000[0]".
|
that "a:1" is the same as "a:000[0]".
|
||||||
*E742*
|
*E742*
|
||||||
The a: scope and the variables in it cannot be changed, they are fixed.
|
The a: scope and the variables in it cannot be changed, they are fixed.
|
||||||
However, if a |List| or |Dictionary| is used, you can change their contents.
|
However, if a composite type is used, such as |List| or |Dictionary| , you can
|
||||||
Thus you can pass a |List| to a function and have the function add an item to
|
change their contents. Thus you can pass a |List| to a function and have the
|
||||||
it. If you want to make sure the function cannot change a |List| or
|
function add an item to it. If you want to make sure the function cannot
|
||||||
|Dictionary| use |:lockvar|.
|
change a |List| or |Dictionary| use |:lockvar|.
|
||||||
|
|
||||||
When not using "...", the number of arguments in a function call must be equal
|
When not using "...", the number of arguments in a function call must be equal
|
||||||
to the number of named arguments. When using "...", the number of arguments
|
to the number of named arguments. When using "...", the number of arguments
|
||||||
@ -8062,9 +8224,8 @@ until the matching |:endfunction|. It is allowed to define another function
|
|||||||
inside a function body.
|
inside a function body.
|
||||||
|
|
||||||
*local-variables*
|
*local-variables*
|
||||||
Inside a function variables can be used. These are local variables, which
|
Inside a function local variables can be used. These will disappear when the
|
||||||
will disappear when the function returns. Global variables need to be
|
function returns. Global variables need to be accessed with "g:".
|
||||||
accessed with "g:".
|
|
||||||
|
|
||||||
Example: >
|
Example: >
|
||||||
:function Table(title, ...)
|
:function Table(title, ...)
|
||||||
|
@ -221,11 +221,10 @@ Object nvim_call_function(String fname, Array args, Error *err)
|
|||||||
// Call the function
|
// Call the function
|
||||||
typval_T rettv;
|
typval_T rettv;
|
||||||
int dummy;
|
int dummy;
|
||||||
int r = call_func((char_u *) fname.data, (int) fname.size,
|
int r = call_func((char_u *)fname.data, (int)fname.size,
|
||||||
&rettv, (int) args.size, vim_args,
|
&rettv, (int)args.size, vim_args, NULL,
|
||||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
|
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
|
||||||
true,
|
true, NULL, NULL);
|
||||||
NULL, NULL);
|
|
||||||
if (r == FAIL) {
|
if (r == FAIL) {
|
||||||
api_set_error(err, Exception, _("Error calling function."));
|
api_set_error(err, Exception, _("Error calling function."));
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,6 @@ typedef struct {
|
|||||||
#include "nvim/hashtab.h"
|
#include "nvim/hashtab.h"
|
||||||
// for dict_T
|
// for dict_T
|
||||||
#include "nvim/eval_defs.h"
|
#include "nvim/eval_defs.h"
|
||||||
// for proftime_T
|
|
||||||
#include "nvim/profile.h"
|
|
||||||
// for String
|
// for String
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
// for Map(K, V)
|
// for Map(K, V)
|
||||||
@ -90,7 +88,6 @@ typedef struct {
|
|||||||
typedef struct window_S win_T;
|
typedef struct window_S win_T;
|
||||||
typedef struct wininfo_S wininfo_T;
|
typedef struct wininfo_S wininfo_T;
|
||||||
typedef struct frame_S frame_T;
|
typedef struct frame_S frame_T;
|
||||||
typedef int scid_T; /* script ID */
|
|
||||||
|
|
||||||
// for struct memline (it needs memfile_T)
|
// for struct memline (it needs memfile_T)
|
||||||
#include "nvim/memline_defs.h"
|
#include "nvim/memline_defs.h"
|
||||||
|
1544
src/nvim/eval.c
1544
src/nvim/eval.c
File diff suppressed because it is too large
Load Diff
@ -12,39 +12,6 @@
|
|||||||
|
|
||||||
// All user-defined functions are found in this hashtable.
|
// All user-defined functions are found in this hashtable.
|
||||||
extern hashtab_T func_hashtab;
|
extern hashtab_T func_hashtab;
|
||||||
|
|
||||||
// Structure to hold info for a user function.
|
|
||||||
typedef struct ufunc ufunc_T;
|
|
||||||
|
|
||||||
struct ufunc {
|
|
||||||
int uf_varargs; ///< variable nr of arguments
|
|
||||||
int uf_flags;
|
|
||||||
int uf_calls; ///< nr of active calls
|
|
||||||
garray_T uf_args; ///< arguments
|
|
||||||
garray_T uf_lines; ///< function lines
|
|
||||||
int uf_profiling; ///< true when func is being profiled
|
|
||||||
// Profiling the function as a whole.
|
|
||||||
int uf_tm_count; ///< nr of calls
|
|
||||||
proftime_T uf_tm_total; ///< time spent in function + children
|
|
||||||
proftime_T uf_tm_self; ///< time spent in function itself
|
|
||||||
proftime_T uf_tm_children; ///< time spent in children this call
|
|
||||||
// Profiling the function per line.
|
|
||||||
int *uf_tml_count; ///< nr of times line was executed
|
|
||||||
proftime_T *uf_tml_total; ///< time spent in a line + children
|
|
||||||
proftime_T *uf_tml_self; ///< time spent in a line itself
|
|
||||||
proftime_T uf_tml_start; ///< start time for current line
|
|
||||||
proftime_T uf_tml_children; ///< time spent in children for this line
|
|
||||||
proftime_T uf_tml_wait; ///< start wait time for current line
|
|
||||||
int uf_tml_idx; ///< index of line being timed; -1 if none
|
|
||||||
int uf_tml_execed; ///< line being timed was executed
|
|
||||||
scid_T uf_script_ID; ///< ID of script where function was defined,
|
|
||||||
// used for s: variables
|
|
||||||
int uf_refcount; ///< for numbered function: reference count
|
|
||||||
char_u uf_name[1]; ///< name of function (actually longer); can
|
|
||||||
// start with <SNR>123_ (<SNR> is K_SPECIAL
|
|
||||||
// KS_EXTRA KE_SNR)
|
|
||||||
};
|
|
||||||
|
|
||||||
// From user function to hashitem and back.
|
// From user function to hashitem and back.
|
||||||
EXTERN ufunc_T dumuf;
|
EXTERN ufunc_T dumuf;
|
||||||
#define UF2HIKEY(fp) ((fp)->uf_name)
|
#define UF2HIKEY(fp) ((fp)->uf_name)
|
||||||
@ -127,6 +94,7 @@ typedef enum {
|
|||||||
VV__NULL_LIST, // List with NULL value. For test purposes only.
|
VV__NULL_LIST, // List with NULL value. For test purposes only.
|
||||||
VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
|
VV__NULL_DICT, // Dictionary with NULL value. For test purposes only.
|
||||||
VV_VIM_DID_ENTER,
|
VV_VIM_DID_ENTER,
|
||||||
|
VV_TESTING,
|
||||||
VV_TYPE_NUMBER,
|
VV_TYPE_NUMBER,
|
||||||
VV_TYPE_STRING,
|
VV_TYPE_STRING,
|
||||||
VV_TYPE_FUNC,
|
VV_TYPE_FUNC,
|
||||||
@ -156,8 +124,8 @@ extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
|
|||||||
|
|
||||||
#undef LAST_MSGPACK_TYPE
|
#undef LAST_MSGPACK_TYPE
|
||||||
|
|
||||||
/// Maximum number of function arguments
|
typedef int (*ArgvFunc)(int current_argcount, typval_T *argv,
|
||||||
#define MAX_FUNC_ARGS 20
|
int called_func_argcount);
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "eval.h.generated.h"
|
# include "eval.h.generated.h"
|
||||||
|
@ -103,6 +103,7 @@ return {
|
|||||||
foldtext={},
|
foldtext={},
|
||||||
foldtextresult={args=1},
|
foldtextresult={args=1},
|
||||||
foreground={},
|
foreground={},
|
||||||
|
funcref={args={1, 3}},
|
||||||
['function']={args={1, 3}},
|
['function']={args={1, 3}},
|
||||||
garbagecollect={args={0, 1}},
|
garbagecollect={args={0, 1}},
|
||||||
get={args={2, 3}},
|
get={args={2, 3}},
|
||||||
@ -303,6 +304,7 @@ return {
|
|||||||
tanh={args=1, func="float_op_wrapper", data="&tanh"},
|
tanh={args=1, func="float_op_wrapper", data="&tanh"},
|
||||||
tempname={},
|
tempname={},
|
||||||
termopen={args={1, 2}},
|
termopen={args={1, 2}},
|
||||||
|
test_garbagecollect_now={},
|
||||||
timer_start={args={2,3}},
|
timer_start={args={2,3}},
|
||||||
timer_stop={args=1},
|
timer_stop={args=1},
|
||||||
tolower={args=1},
|
tolower={args=1},
|
||||||
|
@ -344,7 +344,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
|||||||
case VAR_PARTIAL: {
|
case VAR_PARTIAL: {
|
||||||
partial_T *const pt = tv->vval.v_partial;
|
partial_T *const pt = tv->vval.v_partial;
|
||||||
(void)pt;
|
(void)pt;
|
||||||
TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : pt->pt_name));
|
TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : partial_name(pt)));
|
||||||
_mp_push(*mpstack, ((MPConvStackVal) {
|
_mp_push(*mpstack, ((MPConvStackVal) {
|
||||||
.type = kMPConvPartial,
|
.type = kMPConvPartial,
|
||||||
.tv = tv,
|
.tv = tv,
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include "nvim/hashtab.h"
|
#include "nvim/hashtab.h"
|
||||||
#include "nvim/lib/queue.h"
|
#include "nvim/lib/queue.h"
|
||||||
|
#include "nvim/garray.h" // for garray_T
|
||||||
|
#include "nvim/profile.h" // for proftime_T
|
||||||
|
#include "nvim/pos.h" // for linenr_T
|
||||||
|
|
||||||
typedef int varnumber_T;
|
typedef int varnumber_T;
|
||||||
typedef double float_T;
|
typedef double float_T;
|
||||||
@ -104,15 +107,19 @@ struct listvar_S {
|
|||||||
list_T *lv_used_prev; /* previous list in used lists list */
|
list_T *lv_used_prev; /* previous list in used lists list */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
// Static list with 10 items. Use init_static_list() to initialize.
|
||||||
* Structure to hold an item of a Dictionary.
|
typedef struct {
|
||||||
* Also used for a variable.
|
list_T sl_list; // must be first
|
||||||
* The key is copied into "di_key" to avoid an extra alloc/free for it.
|
listitem_T sl_items[10];
|
||||||
*/
|
} staticList10_T;
|
||||||
|
|
||||||
|
// Structure to hold an item of a Dictionary.
|
||||||
|
// Also used for a variable.
|
||||||
|
// The key is copied into "di_key" to avoid an extra alloc/free for it.
|
||||||
struct dictitem_S {
|
struct dictitem_S {
|
||||||
typval_T di_tv; /* type and value of the variable */
|
typval_T di_tv; ///< type and value of the variable
|
||||||
char_u di_flags; /* flags (only used for variable) */
|
char_u di_flags; ///< flags (only used for variable)
|
||||||
char_u di_key[1]; /* key (actually longer!) */
|
char_u di_key[1]; ///< key (actually longer!)
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct dictitem_S dictitem_T;
|
typedef struct dictitem_S dictitem_T;
|
||||||
@ -147,9 +154,88 @@ struct dictvar_S {
|
|||||||
QUEUE watchers; ///< Dictionary key watchers set by user code.
|
QUEUE watchers; ///< Dictionary key watchers set by user code.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef int scid_T; // script ID
|
||||||
|
typedef struct funccall_S funccall_T;
|
||||||
|
|
||||||
|
// Structure to hold info for a user function.
|
||||||
|
typedef struct ufunc ufunc_T;
|
||||||
|
|
||||||
|
struct ufunc {
|
||||||
|
int uf_varargs; ///< variable nr of arguments
|
||||||
|
int uf_flags;
|
||||||
|
int uf_calls; ///< nr of active calls
|
||||||
|
bool uf_cleared; ///< func_clear() was already called
|
||||||
|
garray_T uf_args; ///< arguments
|
||||||
|
garray_T uf_lines; ///< function lines
|
||||||
|
int uf_profiling; ///< true when func is being profiled
|
||||||
|
// Profiling the function as a whole.
|
||||||
|
int uf_tm_count; ///< nr of calls
|
||||||
|
proftime_T uf_tm_total; ///< time spent in function + children
|
||||||
|
proftime_T uf_tm_self; ///< time spent in function itself
|
||||||
|
proftime_T uf_tm_children; ///< time spent in children this call
|
||||||
|
// Profiling the function per line.
|
||||||
|
int *uf_tml_count; ///< nr of times line was executed
|
||||||
|
proftime_T *uf_tml_total; ///< time spent in a line + children
|
||||||
|
proftime_T *uf_tml_self; ///< time spent in a line itself
|
||||||
|
proftime_T uf_tml_start; ///< start time for current line
|
||||||
|
proftime_T uf_tml_children; ///< time spent in children for this line
|
||||||
|
proftime_T uf_tml_wait; ///< start wait time for current line
|
||||||
|
int uf_tml_idx; ///< index of line being timed; -1 if none
|
||||||
|
int uf_tml_execed; ///< line being timed was executed
|
||||||
|
scid_T uf_script_ID; ///< ID of script where function was defined,
|
||||||
|
// used for s: variables
|
||||||
|
int uf_refcount; ///< reference count, see func_name_refcount()
|
||||||
|
funccall_T *uf_scoped; ///< l: local variables for closure
|
||||||
|
char_u uf_name[1]; ///< name of function (actually longer); can
|
||||||
|
// start with <SNR>123_ (<SNR> is K_SPECIAL
|
||||||
|
// KS_EXTRA KE_SNR)
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Maximum number of function arguments
|
||||||
|
#define MAX_FUNC_ARGS 20
|
||||||
|
#define VAR_SHORT_LEN 20 // short variable name length
|
||||||
|
#define FIXVAR_CNT 12 // number of fixed variables
|
||||||
|
|
||||||
|
// structure to hold info for a function that is currently being executed.
|
||||||
|
struct funccall_S {
|
||||||
|
ufunc_T *func; ///< function being called
|
||||||
|
int linenr; ///< next line to be executed
|
||||||
|
int returned; ///< ":return" used
|
||||||
|
struct { ///< fixed variables for arguments
|
||||||
|
dictitem_T var; ///< variable (without room for name)
|
||||||
|
char_u room[VAR_SHORT_LEN]; ///< room for the name
|
||||||
|
} fixvar[FIXVAR_CNT];
|
||||||
|
dict_T l_vars; ///< l: local function variables
|
||||||
|
dictitem_T l_vars_var; ///< variable for l: scope
|
||||||
|
dict_T l_avars; ///< a: argument variables
|
||||||
|
dictitem_T l_avars_var; ///< variable for a: scope
|
||||||
|
list_T l_varlist; ///< list for a:000
|
||||||
|
listitem_T l_listitems[MAX_FUNC_ARGS]; ///< listitems for a:000
|
||||||
|
typval_T *rettv; ///< return value
|
||||||
|
linenr_T breakpoint; ///< next line with breakpoint or zero
|
||||||
|
int dbg_tick; ///< debug_tick when breakpoint was set
|
||||||
|
int level; ///< top nesting level of executed function
|
||||||
|
proftime_T prof_child; ///< time spent in a child
|
||||||
|
funccall_T *caller; ///< calling function or NULL
|
||||||
|
int fc_refcount; ///< number of user functions that reference
|
||||||
|
// this funccal
|
||||||
|
int fc_copyID; ///< for garbage collection
|
||||||
|
garray_T fc_funcs; ///< list of ufunc_T* which keep a reference
|
||||||
|
// to "func"
|
||||||
|
};
|
||||||
|
|
||||||
|
// structure used by trans_function_name()
|
||||||
|
typedef struct {
|
||||||
|
dict_T *fd_dict; ///< Dictionary used.
|
||||||
|
char_u *fd_newkey; ///< New key in "dict" in allocated memory.
|
||||||
|
dictitem_T *fd_di; ///< Dictionary item used.
|
||||||
|
} funcdict_T;
|
||||||
|
|
||||||
struct partial_S {
|
struct partial_S {
|
||||||
int pt_refcount; ///< Reference count.
|
int pt_refcount; ///< Reference count.
|
||||||
char_u *pt_name; ///< Function name.
|
char_u *pt_name; ///< Function name; when NULL use pt_func->name.
|
||||||
|
ufunc_T *pt_func; ///< Function pointer; when NULL lookup function
|
||||||
|
///< with pt_name.
|
||||||
bool pt_auto; ///< when true the partial was created for using
|
bool pt_auto; ///< when true the partial was created for using
|
||||||
///< dict.member in handle_subscript().
|
///< dict.member in handle_subscript().
|
||||||
int pt_argc; ///< Number of arguments.
|
int pt_argc; ///< Number of arguments.
|
||||||
|
@ -1327,8 +1327,9 @@ int using_script(void)
|
|||||||
void before_blocking(void)
|
void before_blocking(void)
|
||||||
{
|
{
|
||||||
updatescript(0);
|
updatescript(0);
|
||||||
if (may_garbage_collect)
|
if (may_garbage_collect) {
|
||||||
garbage_collect();
|
garbage_collect(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1366,10 +1367,11 @@ int vgetc(void)
|
|||||||
char_u buf[MB_MAXBYTES + 1];
|
char_u buf[MB_MAXBYTES + 1];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Do garbage collection when garbagecollect() was called previously and
|
// Do garbage collection when garbagecollect() was called previously and
|
||||||
* we are now at the toplevel. */
|
// we are now at the toplevel.
|
||||||
if (may_garbage_collect && want_garbage_collect)
|
if (may_garbage_collect && want_garbage_collect) {
|
||||||
garbage_collect();
|
garbage_collect(false);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a character was put back with vungetc, it was already processed.
|
* If a character was put back with vungetc, it was already processed.
|
||||||
|
@ -1236,6 +1236,9 @@ EXTERN char *ignoredp;
|
|||||||
|
|
||||||
EXTERN bool in_free_unref_items INIT(= false);
|
EXTERN bool in_free_unref_items INIT(= false);
|
||||||
|
|
||||||
|
// Used for checking if local variables or arguments used in a lambda.
|
||||||
|
EXTERN int *eval_lavars_used INIT(= NULL);
|
||||||
|
|
||||||
// If a msgpack-rpc channel should be started over stdin/stdout
|
// If a msgpack-rpc channel should be started over stdin/stdout
|
||||||
EXTERN bool embedded_mode INIT(= false);
|
EXTERN bool embedded_mode INIT(= false);
|
||||||
|
|
||||||
|
@ -624,8 +624,9 @@ void getout(int exitval)
|
|||||||
iconv_end();
|
iconv_end();
|
||||||
#endif
|
#endif
|
||||||
cs_end();
|
cs_end();
|
||||||
if (garbage_collect_at_exit)
|
if (garbage_collect_at_exit) {
|
||||||
garbage_collect();
|
garbage_collect(false);
|
||||||
|
}
|
||||||
|
|
||||||
mch_exit(exitval);
|
mch_exit(exitval);
|
||||||
}
|
}
|
||||||
|
@ -2460,11 +2460,11 @@ do_mouse (
|
|||||||
};
|
};
|
||||||
typval_T rettv;
|
typval_T rettv;
|
||||||
int doesrange;
|
int doesrange;
|
||||||
(void) call_func((char_u *) tab_page_click_defs[mouse_col].func,
|
(void)call_func((char_u *)tab_page_click_defs[mouse_col].func,
|
||||||
(int) strlen(tab_page_click_defs[mouse_col].func),
|
(int)strlen(tab_page_click_defs[mouse_col].func),
|
||||||
&rettv, ARRAY_SIZE(argv), argv,
|
&rettv, ARRAY_SIZE(argv), argv, NULL,
|
||||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||||
&doesrange, true, NULL, NULL);
|
&doesrange, true, NULL, NULL);
|
||||||
clear_tv(&rettv);
|
clear_tv(&rettv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -6442,32 +6442,72 @@ static linenr_T submatch_firstlnum;
|
|||||||
static linenr_T submatch_maxline;
|
static linenr_T submatch_maxline;
|
||||||
static int submatch_line_lbr;
|
static int submatch_line_lbr;
|
||||||
|
|
||||||
/*
|
/// Put the submatches in "argv[0]" which is a list passed into call_func() by
|
||||||
* vim_regsub() - perform substitutions after a vim_regexec() or
|
/// vim_regsub_both().
|
||||||
* vim_regexec_multi() match.
|
static int fill_submatch_list(int argc, typval_T *argv, int argcount)
|
||||||
*
|
{
|
||||||
* If "copy" is TRUE really copy into "dest".
|
listitem_T *li;
|
||||||
* If "copy" is FALSE nothing is copied, this is just to find out the length
|
int i;
|
||||||
* of the result.
|
char_u *s;
|
||||||
*
|
|
||||||
* If "backslash" is TRUE, a backslash will be removed later, need to double
|
if (argcount == 0) {
|
||||||
* them to keep them, and insert a backslash before a CR to avoid it being
|
// called function doesn't take an argument
|
||||||
* replaced with a line break later.
|
return 0;
|
||||||
*
|
}
|
||||||
* Note: The matched text must not change between the call of
|
|
||||||
* vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back
|
// Relies on sl_list to be the first item in staticList10_T.
|
||||||
* references invalid!
|
init_static_list((staticList10_T *)(argv->vval.v_list));
|
||||||
*
|
|
||||||
* Returns the size of the replacement, including terminating NUL.
|
// There are always 10 list items in staticList10_T.
|
||||||
*/
|
li = argv->vval.v_list->lv_first;
|
||||||
int vim_regsub(regmatch_T *rmp, char_u *source, char_u *dest, int copy, int magic, int backslash)
|
for (i = 0; i < 10; i++) {
|
||||||
|
s = submatch_match->startp[i];
|
||||||
|
if (s == NULL || submatch_match->endp[i] == NULL) {
|
||||||
|
s = NULL;
|
||||||
|
} else {
|
||||||
|
s = vim_strnsave(s, (int)(submatch_match->endp[i] - s));
|
||||||
|
}
|
||||||
|
li->li_tv.v_type = VAR_STRING;
|
||||||
|
li->li_tv.vval.v_string = s;
|
||||||
|
li = li->li_next;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_submatch_list(staticList10_T *sl)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
xfree(sl->sl_items[i].li_tv.vval.v_string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// vim_regsub() - perform substitutions after a vim_regexec() or
|
||||||
|
/// vim_regexec_multi() match.
|
||||||
|
///
|
||||||
|
/// If "copy" is TRUE really copy into "dest".
|
||||||
|
/// If "copy" is FALSE nothing is copied, this is just to find out the length
|
||||||
|
/// of the result.
|
||||||
|
///
|
||||||
|
/// If "backslash" is TRUE, a backslash will be removed later, need to double
|
||||||
|
/// them to keep them, and insert a backslash before a CR to avoid it being
|
||||||
|
/// replaced with a line break later.
|
||||||
|
///
|
||||||
|
/// Note: The matched text must not change between the call of
|
||||||
|
/// vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back
|
||||||
|
/// references invalid!
|
||||||
|
///
|
||||||
|
/// Returns the size of the replacement, including terminating NUL.
|
||||||
|
int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest,
|
||||||
|
int copy, int magic, int backslash)
|
||||||
{
|
{
|
||||||
reg_match = rmp;
|
reg_match = rmp;
|
||||||
reg_mmatch = NULL;
|
reg_mmatch = NULL;
|
||||||
reg_maxline = 0;
|
reg_maxline = 0;
|
||||||
reg_buf = curbuf;
|
reg_buf = curbuf;
|
||||||
reg_line_lbr = TRUE;
|
reg_line_lbr = true;
|
||||||
return vim_regsub_both(source, dest, copy, magic, backslash);
|
return vim_regsub_both(source, expr, dest, copy, magic, backslash);
|
||||||
}
|
}
|
||||||
|
|
||||||
int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash)
|
int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash)
|
||||||
@ -6477,11 +6517,12 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
|
|||||||
reg_buf = curbuf; /* always works on the current buffer! */
|
reg_buf = curbuf; /* always works on the current buffer! */
|
||||||
reg_firstlnum = lnum;
|
reg_firstlnum = lnum;
|
||||||
reg_maxline = curbuf->b_ml.ml_line_count - lnum;
|
reg_maxline = curbuf->b_ml.ml_line_count - lnum;
|
||||||
reg_line_lbr = FALSE;
|
reg_line_lbr = false;
|
||||||
return vim_regsub_both(source, dest, copy, magic, backslash);
|
return vim_regsub_both(source, NULL, dest, copy, magic, backslash);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, int backslash)
|
static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
|
||||||
|
int copy, int magic, int backslash)
|
||||||
{
|
{
|
||||||
char_u *src;
|
char_u *src;
|
||||||
char_u *dst;
|
char_u *dst;
|
||||||
@ -6495,8 +6536,8 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
|
|||||||
int len = 0; /* init for GCC */
|
int len = 0; /* init for GCC */
|
||||||
static char_u *eval_result = NULL;
|
static char_u *eval_result = NULL;
|
||||||
|
|
||||||
/* Be paranoid... */
|
// Be paranoid...
|
||||||
if (source == NULL || dest == NULL) {
|
if ((source == NULL && expr == NULL) || dest == NULL) {
|
||||||
EMSG(_(e_null));
|
EMSG(_(e_null));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -6505,16 +6546,13 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
|
|||||||
src = source;
|
src = source;
|
||||||
dst = dest;
|
dst = dest;
|
||||||
|
|
||||||
/*
|
// When the substitute part starts with "\=" evaluate it as an expression.
|
||||||
* When the substitute part starts with "\=" evaluate it as an expression.
|
if (expr != NULL || (source[0] == '\\' && source[1] == '='
|
||||||
*/
|
&& !can_f_submatch)) { // can't do this recursively
|
||||||
if (source[0] == '\\' && source[1] == '='
|
// To make sure that the length doesn't change between checking the
|
||||||
&& !can_f_submatch /* can't do this recursively */
|
// length and copying the string, and to speed up things, the
|
||||||
) {
|
// resulting string is saved from the call with "copy" == FALSE to the
|
||||||
/* To make sure that the length doesn't change between checking the
|
// call with "copy" == TRUE.
|
||||||
* length and copying the string, and to speed up things, the
|
|
||||||
* resulting string is saved from the call with "copy" == FALSE to the
|
|
||||||
* call with "copy" == TRUE. */
|
|
||||||
if (copy) {
|
if (copy) {
|
||||||
if (eval_result != NULL) {
|
if (eval_result != NULL) {
|
||||||
STRCPY(dest, eval_result);
|
STRCPY(dest, eval_result);
|
||||||
@ -6525,6 +6563,7 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
|
|||||||
} else {
|
} else {
|
||||||
win_T *save_reg_win;
|
win_T *save_reg_win;
|
||||||
int save_ireg_ic;
|
int save_ireg_ic;
|
||||||
|
bool prev_can_f_submatch = can_f_submatch;
|
||||||
|
|
||||||
xfree(eval_result);
|
xfree(eval_result);
|
||||||
|
|
||||||
@ -6539,9 +6578,50 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
|
|||||||
submatch_line_lbr = reg_line_lbr;
|
submatch_line_lbr = reg_line_lbr;
|
||||||
save_reg_win = reg_win;
|
save_reg_win = reg_win;
|
||||||
save_ireg_ic = ireg_ic;
|
save_ireg_ic = ireg_ic;
|
||||||
can_f_submatch = TRUE;
|
can_f_submatch = true;
|
||||||
|
|
||||||
|
if (expr != NULL) {
|
||||||
|
typval_T argv[2];
|
||||||
|
int dummy;
|
||||||
|
char_u buf[NUMBUFLEN];
|
||||||
|
typval_T rettv;
|
||||||
|
staticList10_T matchList;
|
||||||
|
|
||||||
|
rettv.v_type = VAR_STRING;
|
||||||
|
rettv.vval.v_string = NULL;
|
||||||
|
if (prev_can_f_submatch) {
|
||||||
|
// can't do this recursively
|
||||||
|
} else {
|
||||||
|
argv[0].v_type = VAR_LIST;
|
||||||
|
argv[0].vval.v_list = &matchList.sl_list;
|
||||||
|
matchList.sl_list.lv_len = 0;
|
||||||
|
if (expr->v_type == VAR_FUNC) {
|
||||||
|
s = expr->vval.v_string;
|
||||||
|
call_func(s, (int)STRLEN(s), &rettv, 1, argv,
|
||||||
|
fill_submatch_list, 0L, 0L, &dummy,
|
||||||
|
true, NULL, NULL);
|
||||||
|
} else if (expr->v_type == VAR_PARTIAL) {
|
||||||
|
partial_T *partial = expr->vval.v_partial;
|
||||||
|
|
||||||
|
s = partial_name(partial);
|
||||||
|
call_func(s, (int)STRLEN(s), &rettv, 1, argv,
|
||||||
|
fill_submatch_list, 0L, 0L, &dummy,
|
||||||
|
true, partial, NULL);
|
||||||
|
}
|
||||||
|
if (matchList.sl_list.lv_len > 0) {
|
||||||
|
// fill_submatch_list() was called.
|
||||||
|
clear_submatch_list(&matchList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eval_result = get_tv_string_buf_chk(&rettv, buf);
|
||||||
|
if (eval_result != NULL) {
|
||||||
|
eval_result = vim_strsave(eval_result);
|
||||||
|
}
|
||||||
|
clear_tv(&rettv);
|
||||||
|
} else {
|
||||||
|
eval_result = eval_to_string(source + 2, NULL, true);
|
||||||
|
}
|
||||||
|
|
||||||
eval_result = eval_to_string(source + 2, NULL, TRUE);
|
|
||||||
if (eval_result != NULL) {
|
if (eval_result != NULL) {
|
||||||
int had_backslash = FALSE;
|
int had_backslash = FALSE;
|
||||||
|
|
||||||
|
@ -34,12 +34,14 @@ NEW_TESTS ?= \
|
|||||||
test_cscope.res \
|
test_cscope.res \
|
||||||
test_digraph.res \
|
test_digraph.res \
|
||||||
test_diffmode.res \
|
test_diffmode.res \
|
||||||
|
test_filter_map.res \
|
||||||
test_gn.res \
|
test_gn.res \
|
||||||
test_hardcopy.res \
|
test_hardcopy.res \
|
||||||
test_help_tagjump.res \
|
test_help_tagjump.res \
|
||||||
test_history.res \
|
test_history.res \
|
||||||
test_increment.res \
|
test_increment.res \
|
||||||
test_increment_dbcs.res \
|
test_increment_dbcs.res \
|
||||||
|
test_lambda.res \
|
||||||
test_langmap.res \
|
test_langmap.res \
|
||||||
test_match.res \
|
test_match.res \
|
||||||
test_matchadd_conceal.res \
|
test_matchadd_conceal.res \
|
||||||
|
@ -62,6 +62,12 @@ lang mess C
|
|||||||
" Always use forward slashes.
|
" Always use forward slashes.
|
||||||
set shellslash
|
set shellslash
|
||||||
|
|
||||||
|
" Make sure $HOME does not get read or written.
|
||||||
|
let $HOME = '/does/not/exist'
|
||||||
|
|
||||||
|
" Prepare for calling garbagecollect_for_testing().
|
||||||
|
let v:testing = 1
|
||||||
|
|
||||||
" Align with vim defaults.
|
" Align with vim defaults.
|
||||||
set directory^=.
|
set directory^=.
|
||||||
set nohidden
|
set nohidden
|
||||||
|
@ -9,8 +9,10 @@ source test_ex_undo.vim
|
|||||||
source test_expr.vim
|
source test_expr.vim
|
||||||
source test_expr_utf8.vim
|
source test_expr_utf8.vim
|
||||||
source test_feedkeys.vim
|
source test_feedkeys.vim
|
||||||
|
source test_filter_map.vim
|
||||||
source test_goto.vim
|
source test_goto.vim
|
||||||
source test_jumps.vim
|
source test_jumps.vim
|
||||||
|
source test_lambda.vim
|
||||||
source test_match.vim
|
source test_match.vim
|
||||||
source test_matchadd_conceal_utf8.vim
|
source test_matchadd_conceal_utf8.vim
|
||||||
source test_menu.vim
|
source test_menu.vim
|
||||||
|
@ -85,6 +85,11 @@ func Test_getcompletion()
|
|||||||
let l = getcompletion('paint', 'function')
|
let l = getcompletion('paint', 'function')
|
||||||
call assert_equal([], l)
|
call assert_equal([], l)
|
||||||
|
|
||||||
|
let Flambda = {-> 'hello'}
|
||||||
|
let l = getcompletion('', 'function')
|
||||||
|
let l = filter(l, {i, v -> v =~ 'lambda'})
|
||||||
|
call assert_equal(0, len(l))
|
||||||
|
|
||||||
let l = getcompletion('run', 'file')
|
let l = getcompletion('run', 'file')
|
||||||
call assert_true(index(l, 'runtest.vim') >= 0)
|
call assert_true(index(l, 'runtest.vim') >= 0)
|
||||||
let l = getcompletion('walk', 'file')
|
let l = getcompletion('walk', 'file')
|
||||||
|
@ -106,3 +106,68 @@ func Test_setmatches()
|
|||||||
call setmatches(set)
|
call setmatches(set)
|
||||||
call assert_equal(exp, getmatches())
|
call assert_equal(exp, getmatches())
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_substitute_expr()
|
||||||
|
let g:val = 'XXX'
|
||||||
|
call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
|
||||||
|
call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
|
||||||
|
call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
|
||||||
|
\ '\=nr2char("0x" . submatch(1))', 'g'))
|
||||||
|
call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
|
||||||
|
\ {-> nr2char("0x" . submatch(1))}, 'g'))
|
||||||
|
|
||||||
|
call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
|
||||||
|
\ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
|
||||||
|
|
||||||
|
func Recurse()
|
||||||
|
return substitute('yyy', 'y*', {-> g:val}, '')
|
||||||
|
endfunc
|
||||||
|
call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, ''))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_invalid_submatch()
|
||||||
|
" This was causing invalid memory access in Vim-7.4.2232 and older
|
||||||
|
call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_substitute_expr_arg()
|
||||||
|
call assert_equal('123456789-123456789=', substitute('123456789',
|
||||||
|
\ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
|
||||||
|
\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
|
||||||
|
|
||||||
|
call assert_equal('123456-123456=789', substitute('123456789',
|
||||||
|
\ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
|
||||||
|
\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
|
||||||
|
|
||||||
|
call assert_equal('123456789-123456789x=', substitute('123456789',
|
||||||
|
\ '\(.\)\(.\)\(.*\)',
|
||||||
|
\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
|
||||||
|
|
||||||
|
call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
|
||||||
|
call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
|
||||||
|
call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
|
||||||
|
call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_function_with_funcref()
|
||||||
|
let s:f = function('type')
|
||||||
|
let s:fref = function(s:f)
|
||||||
|
call assert_equal(v:t_string, s:fref('x'))
|
||||||
|
call assert_fails("call function('s:f')", 'E700:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_funcref()
|
||||||
|
func! One()
|
||||||
|
return 1
|
||||||
|
endfunc
|
||||||
|
let OneByName = function('One')
|
||||||
|
let OneByRef = funcref('One')
|
||||||
|
func! One()
|
||||||
|
return 2
|
||||||
|
endfunc
|
||||||
|
call assert_equal(2, OneByName())
|
||||||
|
call assert_equal(1, OneByRef())
|
||||||
|
let OneByRef = funcref('One')
|
||||||
|
call assert_equal(2, OneByRef())
|
||||||
|
call assert_fails('echo funcref("{")', 'E475:')
|
||||||
|
endfunc
|
||||||
|
81
src/nvim/testdir/test_filter_map.vim
Normal file
81
src/nvim/testdir/test_filter_map.vim
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
" Test filter() and map()
|
||||||
|
|
||||||
|
" list with expression string
|
||||||
|
func Test_filter_map_list_expr_string()
|
||||||
|
" filter()
|
||||||
|
call assert_equal([2, 3, 4], filter([1, 2, 3, 4], 'v:val > 1'))
|
||||||
|
call assert_equal([3, 4], filter([1, 2, 3, 4], 'v:key > 1'))
|
||||||
|
call assert_equal([], filter([1, 2, 3, 4], 0))
|
||||||
|
|
||||||
|
" map()
|
||||||
|
call assert_equal([2, 4, 6, 8], map([1, 2, 3, 4], 'v:val * 2'))
|
||||||
|
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], 'v:key * 2'))
|
||||||
|
call assert_equal([9, 9, 9, 9], map([1, 2, 3, 4], 9))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" dict with expression string
|
||||||
|
func Test_filter_map_dict_expr_string()
|
||||||
|
let dict = {"foo": 1, "bar": 2, "baz": 3}
|
||||||
|
|
||||||
|
" filter()
|
||||||
|
call assert_equal({"bar": 2, "baz": 3}, filter(copy(dict), 'v:val > 1'))
|
||||||
|
call assert_equal({"foo": 1, "baz": 3}, filter(copy(dict), 'v:key > "bar"'))
|
||||||
|
call assert_equal({}, filter(copy(dict), 0))
|
||||||
|
|
||||||
|
" map()
|
||||||
|
call assert_equal({"foo": 2, "bar": 4, "baz": 6}, map(copy(dict), 'v:val * 2'))
|
||||||
|
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), 'v:key[0]'))
|
||||||
|
call assert_equal({"foo": 9, "bar": 9, "baz": 9}, map(copy(dict), 9))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" list with funcref
|
||||||
|
func Test_filter_map_list_expr_funcref()
|
||||||
|
" filter()
|
||||||
|
func! s:filter1(index, val) abort
|
||||||
|
return a:val > 1
|
||||||
|
endfunc
|
||||||
|
call assert_equal([2, 3, 4], filter([1, 2, 3, 4], function('s:filter1')))
|
||||||
|
|
||||||
|
func! s:filter2(index, val) abort
|
||||||
|
return a:index > 1
|
||||||
|
endfunc
|
||||||
|
call assert_equal([3, 4], filter([1, 2, 3, 4], function('s:filter2')))
|
||||||
|
|
||||||
|
" map()
|
||||||
|
func! s:filter3(index, val) abort
|
||||||
|
return a:val * 2
|
||||||
|
endfunc
|
||||||
|
call assert_equal([2, 4, 6, 8], map([1, 2, 3, 4], function('s:filter3')))
|
||||||
|
|
||||||
|
func! s:filter4(index, val) abort
|
||||||
|
return a:index * 2
|
||||||
|
endfunc
|
||||||
|
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], function('s:filter4')))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" dict with funcref
|
||||||
|
func Test_filter_map_dict_expr_funcref()
|
||||||
|
let dict = {"foo": 1, "bar": 2, "baz": 3}
|
||||||
|
|
||||||
|
" filter()
|
||||||
|
func! s:filter1(key, val) abort
|
||||||
|
return a:val > 1
|
||||||
|
endfunc
|
||||||
|
call assert_equal({"bar": 2, "baz": 3}, filter(copy(dict), function('s:filter1')))
|
||||||
|
|
||||||
|
func! s:filter2(key, val) abort
|
||||||
|
return a:key > "bar"
|
||||||
|
endfunc
|
||||||
|
call assert_equal({"foo": 1, "baz": 3}, filter(copy(dict), function('s:filter2')))
|
||||||
|
|
||||||
|
" map()
|
||||||
|
func! s:filter3(key, val) abort
|
||||||
|
return a:val * 2
|
||||||
|
endfunc
|
||||||
|
call assert_equal({"foo": 2, "bar": 4, "baz": 6}, map(copy(dict), function('s:filter3')))
|
||||||
|
|
||||||
|
func! s:filter4(key, val) abort
|
||||||
|
return a:key[0]
|
||||||
|
endfunc
|
||||||
|
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), function('s:filter4')))
|
||||||
|
endfunc
|
287
src/nvim/testdir/test_lambda.vim
Normal file
287
src/nvim/testdir/test_lambda.vim
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
" Test for lambda and closure
|
||||||
|
|
||||||
|
function! Test_lambda_feature()
|
||||||
|
call assert_equal(1, has('lambda'))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_with_filter()
|
||||||
|
let s:x = 2
|
||||||
|
call assert_equal([2, 3], filter([1, 2, 3], {i, v -> v >= s:x}))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_with_map()
|
||||||
|
let s:x = 1
|
||||||
|
call assert_equal([2, 3, 4], map([1, 2, 3], {i, v -> v + s:x}))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_with_sort()
|
||||||
|
call assert_equal([1, 2, 3, 4, 7], sort([3,7,2,1,4], {a, b -> a - b}))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_with_timer()
|
||||||
|
if !has('timers')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:n = 0
|
||||||
|
let s:timer_id = 0
|
||||||
|
function! s:Foo()
|
||||||
|
"let n = 0
|
||||||
|
let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n", "")}, {"repeat": -1})
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call s:Foo()
|
||||||
|
sleep 200ms
|
||||||
|
" do not collect lambda
|
||||||
|
call garbagecollect()
|
||||||
|
let m = s:n
|
||||||
|
sleep 200ms
|
||||||
|
call timer_stop(s:timer_id)
|
||||||
|
call assert_true(m > 1)
|
||||||
|
call assert_true(s:n > m + 1)
|
||||||
|
call assert_true(s:n < 9)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_with_partial()
|
||||||
|
let l:Cb = function({... -> ['zero', a:1, a:2, a:3]}, ['one', 'two'])
|
||||||
|
call assert_equal(['zero', 'one', 'two', 'three'], l:Cb('three'))
|
||||||
|
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, b -> a + b)}(1, 2)', 'E15:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_not_lambda()
|
||||||
|
let x = {'>' : 'foo'}
|
||||||
|
call assert_equal('foo', x['>'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
function! Test_lambda_capture_by_reference()
|
||||||
|
let v = 1
|
||||||
|
let l:F = {x -> x + v}
|
||||||
|
let v = 2
|
||||||
|
call assert_equal(12, l:F(10))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_side_effect()
|
||||||
|
function! s:update_and_return(arr)
|
||||||
|
let a:arr[1] = 5
|
||||||
|
return a:arr
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:foo(arr)
|
||||||
|
return {-> s:update_and_return(a:arr)}
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let arr = [3,2,1]
|
||||||
|
call assert_equal([3, 5, 1], s:foo(arr)())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_refer_local_variable_from_other_scope()
|
||||||
|
function! s:foo(X)
|
||||||
|
return a:X() " refer l:x in s:bar()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:bar()
|
||||||
|
let x = 123
|
||||||
|
return s:foo({-> x})
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call assert_equal(123, s:bar())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_do_not_share_local_variable()
|
||||||
|
function! s:define_funcs()
|
||||||
|
let l:One = {-> split(execute("let a = 'abc' | echo a"))[0]}
|
||||||
|
let l:Two = {-> exists("a") ? a : "no"}
|
||||||
|
return [l:One, l:Two]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let l:F = s:define_funcs()
|
||||||
|
|
||||||
|
call assert_equal('no', l:F[1]())
|
||||||
|
call assert_equal('abc', l:F[0]())
|
||||||
|
call assert_equal('no', l:F[1]())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_closure_counter()
|
||||||
|
function! s:foo()
|
||||||
|
let x = 0
|
||||||
|
return {-> [execute("let x += 1"), x][-1]}
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let l:F = s:foo()
|
||||||
|
call garbagecollect()
|
||||||
|
call assert_equal(1, l:F())
|
||||||
|
call assert_equal(2, l:F())
|
||||||
|
call assert_equal(3, l:F())
|
||||||
|
call assert_equal(4, l:F())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_with_a_var()
|
||||||
|
function! s:foo()
|
||||||
|
let x = 2
|
||||||
|
return {... -> a:000 + [x]}
|
||||||
|
endfunction
|
||||||
|
function! s:bar()
|
||||||
|
return s:foo()(1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call assert_equal([1, 2], s:bar())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_call_lambda_from_lambda()
|
||||||
|
function! s:foo(x)
|
||||||
|
let l:F1 = {-> {-> a:x}}
|
||||||
|
return {-> l:F1()}
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let l:F = s:foo(1)
|
||||||
|
call assert_equal(1, l:F()())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_delfunc()
|
||||||
|
function! s:gen()
|
||||||
|
let pl = l:
|
||||||
|
let l:Foo = {-> get(pl, "Foo", get(pl, "Bar", {-> 0}))}
|
||||||
|
let l:Bar = l:Foo
|
||||||
|
delfunction l:Foo
|
||||||
|
return l:Bar
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let l:F = s:gen()
|
||||||
|
call assert_fails(':call l:F()', 'E933:')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_scope()
|
||||||
|
function! s:NewCounter()
|
||||||
|
let c = 0
|
||||||
|
return {-> [execute('let c += 1'), c][-1]}
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:NewCounter2()
|
||||||
|
return {-> [execute('let c += 100'), c][-1]}
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let l:C = s:NewCounter()
|
||||||
|
let l:D = s:NewCounter2()
|
||||||
|
|
||||||
|
call assert_equal(1, l:C())
|
||||||
|
call assert_fails(':call l:D()', 'E15:') " E121: then E15:
|
||||||
|
call assert_equal(2, l:C())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_share_scope()
|
||||||
|
function! s:New()
|
||||||
|
let c = 0
|
||||||
|
let l:Inc0 = {-> [execute('let c += 1'), c][-1]}
|
||||||
|
let l:Dec0 = {-> [execute('let c -= 1'), c][-1]}
|
||||||
|
return [l:Inc0, l:Dec0]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let [l:Inc, l:Dec] = s:New()
|
||||||
|
|
||||||
|
call assert_equal(1, l:Inc())
|
||||||
|
call assert_equal(2, l:Inc())
|
||||||
|
call assert_equal(1, l:Dec())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_circular_reference()
|
||||||
|
function! s:Foo()
|
||||||
|
let d = {}
|
||||||
|
let d.f = {-> d}
|
||||||
|
return d.f
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call s:Foo()
|
||||||
|
call garbagecollect()
|
||||||
|
let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile
|
||||||
|
call garbagecollect()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_lambda_combination()
|
||||||
|
call assert_equal(2, {x -> {x -> x}}(1)(2))
|
||||||
|
call assert_equal(10, {y -> {x -> x(y)(10)}({y -> y})}({z -> z}))
|
||||||
|
call assert_equal(5.0, {x -> {y -> x / y}}(10)(2.0))
|
||||||
|
call assert_equal(6, {x -> {y -> {z -> x + y + z}}}(1)(2)(3))
|
||||||
|
|
||||||
|
call assert_equal(6, {x -> {f -> f(x)}}(3)({x -> x * 2}))
|
||||||
|
call assert_equal(6, {f -> {x -> f(x)}}({x -> x * 2})(3))
|
||||||
|
|
||||||
|
" Z combinator
|
||||||
|
let Z = {f -> {x -> f({y -> x(x)(y)})}({x -> f({y -> x(x)(y)})})}
|
||||||
|
let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}}
|
||||||
|
call assert_equal(120, Z(Fact)(5))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_closure_counter()
|
||||||
|
function! s:foo()
|
||||||
|
let x = 0
|
||||||
|
function! s:bar() closure
|
||||||
|
let x += 1
|
||||||
|
return x
|
||||||
|
endfunction
|
||||||
|
return function('s:bar')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let l:F = s:foo()
|
||||||
|
call garbagecollect()
|
||||||
|
call assert_equal(1, l:F())
|
||||||
|
call assert_equal(2, l:F())
|
||||||
|
call assert_equal(3, l:F())
|
||||||
|
call assert_equal(4, l:F())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_closure_unlet()
|
||||||
|
function! s:foo()
|
||||||
|
let x = 1
|
||||||
|
function! s:bar() closure
|
||||||
|
unlet x
|
||||||
|
endfunction
|
||||||
|
call s:bar()
|
||||||
|
return l:
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call assert_false(has_key(s:foo(), 'x'))
|
||||||
|
call garbagecollect()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! LambdaFoo()
|
||||||
|
let x = 0
|
||||||
|
function! LambdaBar() closure
|
||||||
|
let x += 1
|
||||||
|
return x
|
||||||
|
endfunction
|
||||||
|
return function('LambdaBar')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
func Test_closure_refcount()
|
||||||
|
let g:Count = LambdaFoo()
|
||||||
|
call test_garbagecollect_now()
|
||||||
|
call assert_equal(1, g:Count())
|
||||||
|
let g:Count2 = LambdaFoo()
|
||||||
|
call test_garbagecollect_now()
|
||||||
|
call assert_equal(1, g:Count2())
|
||||||
|
call assert_equal(2, g:Count())
|
||||||
|
call assert_equal(3, g:Count2())
|
||||||
|
|
||||||
|
delfunc LambdaFoo
|
||||||
|
delfunc LambdaBar
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" This test is causing a use-after-free on shutdown.
|
||||||
|
func Test_named_function_closure()
|
||||||
|
func! Afoo()
|
||||||
|
let x = 14
|
||||||
|
func! s:Abar() closure
|
||||||
|
return x
|
||||||
|
endfunc
|
||||||
|
call assert_equal(14, s:Abar())
|
||||||
|
endfunc
|
||||||
|
call Afoo()
|
||||||
|
call assert_equal(14, s:Abar())
|
||||||
|
call garbagecollect()
|
||||||
|
call assert_equal(14, s:Abar())
|
||||||
|
endfunc
|
@ -14,6 +14,14 @@ func MySort(up, one, two)
|
|||||||
return a:one < a:two ? 1 : -1
|
return a:one < a:two ? 1 : -1
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func MyMap(sub, index, val)
|
||||||
|
return a:val - a:sub
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func MyFilter(threshold, index, val)
|
||||||
|
return a:val > a:threshold
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_partial_args()
|
func Test_partial_args()
|
||||||
let Cb = function('MyFunc', ["foo", "bar"])
|
let Cb = function('MyFunc', ["foo", "bar"])
|
||||||
|
|
||||||
@ -36,6 +44,16 @@ func Test_partial_args()
|
|||||||
call assert_equal([1, 2, 3], sort([3, 1, 2], Sort))
|
call assert_equal([1, 2, 3], sort([3, 1, 2], Sort))
|
||||||
let Sort = function('MySort', [0])
|
let Sort = function('MySort', [0])
|
||||||
call assert_equal([3, 2, 1], sort([3, 1, 2], Sort))
|
call assert_equal([3, 2, 1], sort([3, 1, 2], Sort))
|
||||||
|
|
||||||
|
let Map = function('MyMap', [2])
|
||||||
|
call assert_equal([-1, 0, 1], map([1, 2, 3], Map))
|
||||||
|
let Map = function('MyMap', [3])
|
||||||
|
call assert_equal([-2, -1, 0], map([1, 2, 3], Map))
|
||||||
|
|
||||||
|
let Filter = function('MyFilter', [1])
|
||||||
|
call assert_equal([2, 3], filter([1, 2, 3], Filter))
|
||||||
|
let Filter = function('MyFilter', [2])
|
||||||
|
call assert_equal([3], filter([1, 2, 3], Filter))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func MyDictFunc(arg1, arg2) dict
|
func MyDictFunc(arg1, arg2) dict
|
||||||
@ -59,6 +77,9 @@ func Test_partial_dict()
|
|||||||
call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy"))
|
call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy"))
|
||||||
call assert_fails('Cb("fff")', 'E492:')
|
call assert_fails('Cb("fff")', 'E492:')
|
||||||
|
|
||||||
|
let Cb = function('MyDictFunc', dict)
|
||||||
|
call assert_equal({"foo": "hello/foo/1", "bar": "hello/bar/2"}, map({"foo": 1, "bar": 2}, Cb))
|
||||||
|
|
||||||
let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
|
let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
|
||||||
call assert_equal("Hello", dict.tr())
|
call assert_equal("Hello", dict.tr())
|
||||||
endfunc
|
endfunc
|
||||||
|
@ -205,9 +205,9 @@ static int included_patches[] = {
|
|||||||
// 2238 NA
|
// 2238 NA
|
||||||
2237,
|
2237,
|
||||||
// 2236,
|
// 2236,
|
||||||
// 2235,
|
2235,
|
||||||
// 2234 NA
|
// 2234 NA
|
||||||
// 2233,
|
2233,
|
||||||
// 2232 NA
|
// 2232 NA
|
||||||
// 2231,
|
// 2231,
|
||||||
// 2230,
|
// 2230,
|
||||||
@ -243,7 +243,7 @@ static int included_patches[] = {
|
|||||||
// 2200,
|
// 2200,
|
||||||
// 2199 NA
|
// 2199 NA
|
||||||
// 2198 NA
|
// 2198 NA
|
||||||
// 2197,
|
2197,
|
||||||
// 2196,
|
// 2196,
|
||||||
// 2195 NA
|
// 2195 NA
|
||||||
2194,
|
2194,
|
||||||
@ -297,16 +297,16 @@ static int included_patches[] = {
|
|||||||
2146,
|
2146,
|
||||||
// 2145 NA
|
// 2145 NA
|
||||||
// 2144,
|
// 2144,
|
||||||
// 2143,
|
2143,
|
||||||
// 2142,
|
2142,
|
||||||
// 2141,
|
2141,
|
||||||
// 2140 NA
|
// 2140 NA
|
||||||
// 2139,
|
2139,
|
||||||
// 2138 NA
|
// 2138 NA
|
||||||
// 2137,
|
2137,
|
||||||
// 2136,
|
2136,
|
||||||
// 2135,
|
// 2135,
|
||||||
// 2134,
|
2134,
|
||||||
// 2133 NA
|
// 2133 NA
|
||||||
// 2132 NA
|
// 2132 NA
|
||||||
// 2131 NA
|
// 2131 NA
|
||||||
@ -319,9 +319,9 @@ static int included_patches[] = {
|
|||||||
2124,
|
2124,
|
||||||
2123,
|
2123,
|
||||||
// 2122 NA
|
// 2122 NA
|
||||||
// 2121,
|
2121,
|
||||||
// 2120,
|
2120,
|
||||||
// 2119,
|
2119,
|
||||||
// 2118 NA
|
// 2118 NA
|
||||||
2117,
|
2117,
|
||||||
// 2116 NA
|
// 2116 NA
|
||||||
@ -344,13 +344,13 @@ static int included_patches[] = {
|
|||||||
2099,
|
2099,
|
||||||
// 2098,
|
// 2098,
|
||||||
// 2097,
|
// 2097,
|
||||||
// 2096,
|
2096,
|
||||||
// 2095,
|
// 2095,
|
||||||
// 2094 NA
|
// 2094 NA
|
||||||
// 2093 NA
|
// 2093 NA
|
||||||
// 2092 NA
|
// 2092 NA
|
||||||
// 2091 NA
|
// 2091 NA
|
||||||
// 2090,
|
2090,
|
||||||
// 2089 NA
|
// 2089 NA
|
||||||
2088,
|
2088,
|
||||||
2087,
|
2087,
|
||||||
@ -364,11 +364,11 @@ static int included_patches[] = {
|
|||||||
// 2079 NA
|
// 2079 NA
|
||||||
// 2078 NA
|
// 2078 NA
|
||||||
2077,
|
2077,
|
||||||
// 2076,
|
2076,
|
||||||
2075,
|
2075,
|
||||||
2074,
|
2074,
|
||||||
// 2073 NA
|
// 2073 NA
|
||||||
// 2072,
|
2072,
|
||||||
2071,
|
2071,
|
||||||
// 2070 NA
|
// 2070 NA
|
||||||
// 2069,
|
// 2069,
|
||||||
@ -396,7 +396,7 @@ static int included_patches[] = {
|
|||||||
// 2047,
|
// 2047,
|
||||||
// 2046,
|
// 2046,
|
||||||
// 2045 NA
|
// 2045 NA
|
||||||
// 2044,
|
2044,
|
||||||
2043,
|
2043,
|
||||||
// 2042 NA
|
// 2042 NA
|
||||||
// 2041 NA
|
// 2041 NA
|
||||||
@ -438,7 +438,7 @@ static int included_patches[] = {
|
|||||||
2005,
|
2005,
|
||||||
// 2004 NA
|
// 2004 NA
|
||||||
// 2003 NA
|
// 2003 NA
|
||||||
// 2002,
|
2002,
|
||||||
// 2001 NA
|
// 2001 NA
|
||||||
2000,
|
2000,
|
||||||
1999,
|
1999,
|
||||||
@ -451,7 +451,7 @@ static int included_patches[] = {
|
|||||||
// 1992,
|
// 1992,
|
||||||
// 1991,
|
// 1991,
|
||||||
1990,
|
1990,
|
||||||
// 1989,
|
1989,
|
||||||
// 1988 NA
|
// 1988 NA
|
||||||
// 1987 NA
|
// 1987 NA
|
||||||
// 1986,
|
// 1986,
|
||||||
@ -714,7 +714,7 @@ static int included_patches[] = {
|
|||||||
1730,
|
1730,
|
||||||
// 1729 NA
|
// 1729 NA
|
||||||
1728,
|
1728,
|
||||||
// 1727 NA
|
1727,
|
||||||
// 1726 NA
|
// 1726 NA
|
||||||
// 1725 NA
|
// 1725 NA
|
||||||
// 1724 NA
|
// 1724 NA
|
||||||
|
@ -391,6 +391,27 @@ describe('jobs', function()
|
|||||||
eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg())
|
eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('jobstart() works with closures', function()
|
||||||
|
source([[
|
||||||
|
fun! MkFun()
|
||||||
|
let a1 = 'foo'
|
||||||
|
let a2 = 'bar'
|
||||||
|
return {id, data, event -> rpcnotify(g:channel, '1', a1, a2, Normalize(data), event)}
|
||||||
|
endfun
|
||||||
|
let g:job_opts = {'on_stdout': MkFun()}
|
||||||
|
call jobstart('echo "some text"', g:job_opts)
|
||||||
|
]])
|
||||||
|
eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('jobstart() works when closure passed directly to `jobstart`', function()
|
||||||
|
source([[
|
||||||
|
let g:job_opts = {'on_stdout': {id, data, event -> rpcnotify(g:channel, '1', 'foo', 'bar', Normalize(data), event)}}
|
||||||
|
call jobstart('echo "some text"', g:job_opts)
|
||||||
|
]])
|
||||||
|
eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg())
|
||||||
|
end)
|
||||||
|
|
||||||
describe('jobwait', function()
|
describe('jobwait', function()
|
||||||
it('returns a list of status codes', function()
|
it('returns a list of status codes', function()
|
||||||
source([[
|
source([[
|
||||||
|
@ -254,23 +254,33 @@ describe('dictionary change notifications', function()
|
|||||||
command('call g:ReplaceWatcher2()')
|
command('call g:ReplaceWatcher2()')
|
||||||
command('let g:key = "value"')
|
command('let g:key = "value"')
|
||||||
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
|
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
|
||||||
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('does not crash when freeing a watched dictionary', function()
|
it('does not crash when freeing a watched dictionary', function()
|
||||||
source([[
|
source([[
|
||||||
function! Watcher(dict, key, value)
|
function! Watcher(dict, key, value)
|
||||||
echo a:key string(a:value)
|
echo a:key string(a:value)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! MakeWatch()
|
function! MakeWatch()
|
||||||
let d = {'foo': 'bar'}
|
let d = {'foo': 'bar'}
|
||||||
call dictwatcheradd(d, 'foo', function('Watcher'))
|
call dictwatcheradd(d, 'foo', function('Watcher'))
|
||||||
endfunction
|
endfunction
|
||||||
]])
|
]])
|
||||||
|
|
||||||
command('call MakeWatch()')
|
command('call MakeWatch()')
|
||||||
eq(2, eval('1+1')) -- Still alive?
|
eq(2, eval('1+1')) -- Still alive?
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('with lambdas', function()
|
||||||
|
it('works correctly', function()
|
||||||
|
source([[
|
||||||
|
let d = {'foo': 'baz'}
|
||||||
|
call dictwatcheradd(d, 'foo', {dict, key, value -> rpcnotify(g:channel, '2', key, value)})
|
||||||
|
let d.foo = 'bar'
|
||||||
|
]])
|
||||||
|
eq({'notification', '2', {'foo', {old = 'baz', new = 'bar'}}}, next_msg())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user