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 ~
|
||||
*Funcref* *E695* *E718*
|
||||
A Funcref variable is obtained with the |function()| function. It can be used
|
||||
in an expression in the place of a function name, before the parenthesis
|
||||
around the arguments, to invoke the function it refers to. Example: >
|
||||
A Funcref variable is obtained with the |function()| function or created with
|
||||
the lambda expression |expr-lambda|. It can be used in an expression in the
|
||||
place of a function name, before the parenthesis around the arguments, to
|
||||
invoke the function it refers to. Example: >
|
||||
|
||||
:let Fn = function("MyFunc")
|
||||
:echo Fn()
|
||||
@ -667,6 +668,7 @@ Expression syntax summary, from least to most significant:
|
||||
@r contents of register 'r'
|
||||
function(expr1, ...) function call
|
||||
func{ti}on(expr1, ...) function call with curly braces
|
||||
{args -> expr1} lambda expression
|
||||
|
||||
|
||||
".." indicates that the operations in this level can be concatenated.
|
||||
@ -1174,6 +1176,62 @@ function(expr1, ...) function call
|
||||
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*
|
||||
|
||||
@ -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
|
||||
as a String (e.g. in |expr5| with string concatenation
|
||||
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 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
|
||||
used as a String (e.g. in |expr5| with string concatenation
|
||||
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 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.
|
||||
{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 Full filename of the last loaded or saved session file. See
|
||||
|: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
|
||||
as a String (e.g. in |expr5| with string concatenation
|
||||
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 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
|
||||
foldtextresult({lnum}) String text for closed fold at {lnum}
|
||||
foreground() Number bring the Vim window to the foreground
|
||||
function({name} [, {arglist}] [, {dict}])
|
||||
funcref({name} [, {arglist}] [, {dict}])
|
||||
Funcref reference to function {name}
|
||||
function({name} [, {arglist}] [, {dict}])
|
||||
Funcref named reference to function {name}
|
||||
garbagecollect([{atexit}]) none free memory, breaking cyclic references
|
||||
get({list}, {idx} [, {def}]) any get item {idx} from {list} 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}
|
||||
tanh({expr}) Float hyperbolic tangent of {expr}
|
||||
tempname() String name for a temporary file
|
||||
test_garbagecollect_now() none free memory right now for testing
|
||||
timer_start({time}, {callback} [, {options}])
|
||||
Number create 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.
|
||||
|
||||
|
||||
filter({expr}, {string}) *filter()*
|
||||
{expr} must be a |List| or a |Dictionary|.
|
||||
For each item in {expr} evaluate {string} and when the result
|
||||
filter({expr1}, {expr2}) *filter()*
|
||||
{expr1} must be a |List| or a |Dictionary|.
|
||||
For each item in {expr1} evaluate {expr2} and when the result
|
||||
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.
|
||||
Examples: >
|
||||
:call filter(mylist, 'v:val !~ "OLD"')
|
||||
call filter(mylist, 'v:val !~ "OLD"')
|
||||
< 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. >
|
||||
:call filter(var, 0)
|
||||
call filter(var, 0)
|
||||
< 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
|
||||
|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
|
||||
|Dictionary| to remain unmodified make a copy first: >
|
||||
:let l = filter(copy(mylist), 'v:val =~ "KEEP"')
|
||||
|
||||
< Returns {expr}, the |List| or |Dictionary| that was filtered.
|
||||
When an error is encountered while evaluating {string} no
|
||||
further items in {expr} are processed.
|
||||
< Returns {expr1}, the |List| or |Dictionary| that was filtered.
|
||||
When an error is encountered while evaluating {expr2} no
|
||||
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()*
|
||||
@ -3545,12 +3625,31 @@ foreground() Move the Vim window to the foreground. Useful when sent from
|
||||
|remote_foreground()| instead.
|
||||
{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({name} [, {arglist}] [, {dict}])
|
||||
Return a |Funcref| variable that refers to function {name}.
|
||||
{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.
|
||||
That mans the argument list and/or the dictionary is stored in
|
||||
the Funcref and will be used when the Funcref is called.
|
||||
@ -3589,18 +3688,25 @@ function({name} [, {arglist}] [, {dict}])
|
||||
|
||||
garbagecollect([{atexit}]) *garbagecollect()*
|
||||
Cleanup unused |Lists| and |Dictionaries| that have circular
|
||||
references. There is hardly ever a need to invoke this
|
||||
function, as it is automatically done when Vim runs out of
|
||||
memory or is waiting for the user to press a key after
|
||||
'updatetime'. Items without circular references are always
|
||||
freed when they become unused.
|
||||
references.
|
||||
|
||||
There is hardly ever a need to invoke this function, as it is
|
||||
automatically done when Vim runs out of memory or is waiting
|
||||
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
|
||||
|Dictionary| with circular references in a script that runs
|
||||
for a long time.
|
||||
|
||||
When the optional {atexit} argument is one, garbage
|
||||
collection will also be done when exiting Vim, if it wasn't
|
||||
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 item {idx} from |List| {list}. When this item is not
|
||||
available return {default}. Return zero when {default} is
|
||||
@ -4937,29 +5043,43 @@ log10({expr}) *log10()*
|
||||
< -2.0
|
||||
|
||||
|
||||
map({expr}, {string}) *map()*
|
||||
{expr} must be a |List| or a |Dictionary|.
|
||||
Replace each item in {expr} with the result of evaluating
|
||||
{string}.
|
||||
Inside {string} |v:val| has the value 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.
|
||||
map({expr1}, {expr2}) *map()*
|
||||
{expr1} must be a |List| or a |Dictionary|.
|
||||
Replace each item in {expr1} with the result of evaluating
|
||||
{expr2}. {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 and for a |List| |v:key| has the index of
|
||||
the current item.
|
||||
Example: >
|
||||
:call map(mylist, '"> " . v:val . " <"')
|
||||
< 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
|
||||
|literal-string| to avoid having to double backslashes. You
|
||||
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
|
||||
|Dictionary| to remain unmodified make a copy first: >
|
||||
:let tlist = map(copy(mylist), ' v:val . "\t"')
|
||||
|
||||
< Returns {expr}, the |List| or |Dictionary| that was filtered.
|
||||
When an error is encountered while evaluating {string} no
|
||||
further items in {expr} are processed.
|
||||
< Returns {expr1}, the |List| or |Dictionary| that was filtered.
|
||||
When an error is encountered while evaluating {expr2} no
|
||||
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()*
|
||||
@ -6003,6 +6123,7 @@ screenrow() *screenrow()*
|
||||
The result is a Number, which is the current screen row of the
|
||||
cursor. The top line has number one.
|
||||
This function is mainly used for testing.
|
||||
Alternatively you can use |winline()|.
|
||||
|
||||
Note: Same restrictions as with |screencol()|.
|
||||
|
||||
@ -7029,6 +7150,14 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
|
||||
:echo substitute(s, '%\(\x\x\)',
|
||||
\ '\=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()*
|
||||
The result is a Number, which is the syntax ID at the position
|
||||
{lnum} and {col} in the current window.
|
||||
@ -7288,6 +7417,12 @@ termopen({cmd}[, {opts}]) {Nvim} *termopen()*
|
||||
|
||||
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()*
|
||||
Return the tangent of {expr}, measured in radians, as a |Float|
|
||||
in the range [-inf, inf].
|
||||
@ -7379,16 +7514,18 @@ trunc({expr}) *trunc()*
|
||||
< 4.0
|
||||
|
||||
type({expr}) *type()*
|
||||
The result is a Number, depending on the type of {expr}:
|
||||
Number: 0
|
||||
String: 1
|
||||
Funcref: 2
|
||||
List: 3
|
||||
Dictionary: 4
|
||||
Float: 5
|
||||
The result is a Number representing the type of {expr}.
|
||||
Instead of using the number directly, it is better to use the
|
||||
v:t_ variable that has the value:
|
||||
Number: 0 (|v:t_number|)
|
||||
String: 1 (|v:t_string|)
|
||||
Funcref: 2 (|v:t_func|)
|
||||
List: 3 (|v:t_list|)
|
||||
Dictionary: 4 (|v:t_dict|)
|
||||
Float: 5 (|v:t_float|)
|
||||
Boolean: 6 (|v:true| and |v:false|)
|
||||
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("")
|
||||
: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
|
||||
for |v:null| directly as it is the only value of this type: >
|
||||
:if myvar is v:null
|
||||
< To check if the v:t_ variables exist use this: >
|
||||
:if exists('v:t_number')
|
||||
|
||||
undofile({name}) *undofile()*
|
||||
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.
|
||||
jumplist Compiled with |jumplist| support.
|
||||
keymap Compiled with 'keymap' support.
|
||||
lambda Compiled with |lambda| support.
|
||||
langmap Compiled with 'langmap' support.
|
||||
libcall Compiled with |libcall()| support.
|
||||
linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and
|
||||
@ -7957,7 +8097,7 @@ last defined. Example: >
|
||||
See |:verbose-cmd| for more information.
|
||||
|
||||
*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
|
||||
must be made of alphanumeric characters and '_', and
|
||||
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
|
||||
local variable "self" will then be set to the
|
||||
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*
|
||||
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
|
||||
by its own, without other commands.
|
||||
|
||||
*:delf* *:delfunction* *E130* *E131*
|
||||
*:delf* *:delfunction* *E130* *E131* *E933*
|
||||
:delf[unction] {name} Delete function {name}.
|
||||
{name} can also be a |Dictionary| entry that is a
|
||||
|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]".
|
||||
*E742*
|
||||
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.
|
||||
Thus you can pass a |List| to a function and have the function add an item to
|
||||
it. If you want to make sure the function cannot change a |List| or
|
||||
|Dictionary| use |:lockvar|.
|
||||
However, if a composite type is used, such as |List| or |Dictionary| , you can
|
||||
change their contents. Thus you can pass a |List| to a function and have the
|
||||
function add an item to it. If you want to make sure the function cannot
|
||||
change a |List| or |Dictionary| use |:lockvar|.
|
||||
|
||||
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
|
||||
@ -8062,9 +8224,8 @@ until the matching |:endfunction|. It is allowed to define another function
|
||||
inside a function body.
|
||||
|
||||
*local-variables*
|
||||
Inside a function variables can be used. These are local variables, which
|
||||
will disappear when the function returns. Global variables need to be
|
||||
accessed with "g:".
|
||||
Inside a function local variables can be used. These will disappear when the
|
||||
function returns. Global variables need to be accessed with "g:".
|
||||
|
||||
Example: >
|
||||
:function Table(title, ...)
|
||||
|
@ -221,11 +221,10 @@ Object nvim_call_function(String fname, Array args, Error *err)
|
||||
// Call the function
|
||||
typval_T rettv;
|
||||
int dummy;
|
||||
int r = call_func((char_u *) fname.data, (int) fname.size,
|
||||
&rettv, (int) args.size, vim_args,
|
||||
int r = call_func((char_u *)fname.data, (int)fname.size,
|
||||
&rettv, (int)args.size, vim_args, NULL,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
|
||||
true,
|
||||
NULL, NULL);
|
||||
true, NULL, NULL);
|
||||
if (r == FAIL) {
|
||||
api_set_error(err, Exception, _("Error calling function."));
|
||||
}
|
||||
|
@ -31,8 +31,6 @@ typedef struct {
|
||||
#include "nvim/hashtab.h"
|
||||
// for dict_T
|
||||
#include "nvim/eval_defs.h"
|
||||
// for proftime_T
|
||||
#include "nvim/profile.h"
|
||||
// for String
|
||||
#include "nvim/api/private/defs.h"
|
||||
// for Map(K, V)
|
||||
@ -90,7 +88,6 @@ typedef struct {
|
||||
typedef struct window_S win_T;
|
||||
typedef struct wininfo_S wininfo_T;
|
||||
typedef struct frame_S frame_T;
|
||||
typedef int scid_T; /* script ID */
|
||||
|
||||
// for struct memline (it needs memfile_T)
|
||||
#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.
|
||||
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.
|
||||
EXTERN ufunc_T dumuf;
|
||||
#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_DICT, // Dictionary with NULL value. For test purposes only.
|
||||
VV_VIM_DID_ENTER,
|
||||
VV_TESTING,
|
||||
VV_TYPE_NUMBER,
|
||||
VV_TYPE_STRING,
|
||||
VV_TYPE_FUNC,
|
||||
@ -156,8 +124,8 @@ extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
|
||||
|
||||
#undef LAST_MSGPACK_TYPE
|
||||
|
||||
/// Maximum number of function arguments
|
||||
#define MAX_FUNC_ARGS 20
|
||||
typedef int (*ArgvFunc)(int current_argcount, typval_T *argv,
|
||||
int called_func_argcount);
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "eval.h.generated.h"
|
||||
|
@ -103,6 +103,7 @@ return {
|
||||
foldtext={},
|
||||
foldtextresult={args=1},
|
||||
foreground={},
|
||||
funcref={args={1, 3}},
|
||||
['function']={args={1, 3}},
|
||||
garbagecollect={args={0, 1}},
|
||||
get={args={2, 3}},
|
||||
@ -303,6 +304,7 @@ return {
|
||||
tanh={args=1, func="float_op_wrapper", data="&tanh"},
|
||||
tempname={},
|
||||
termopen={args={1, 2}},
|
||||
test_garbagecollect_now={},
|
||||
timer_start={args={2,3}},
|
||||
timer_stop={args=1},
|
||||
tolower={args=1},
|
||||
|
@ -344,7 +344,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
||||
case VAR_PARTIAL: {
|
||||
partial_T *const pt = tv->vval.v_partial;
|
||||
(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) {
|
||||
.type = kMPConvPartial,
|
||||
.tv = tv,
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#include "nvim/hashtab.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 double float_T;
|
||||
@ -104,15 +107,19 @@ struct listvar_S {
|
||||
list_T *lv_used_prev; /* previous list in used lists list */
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
// Static list with 10 items. Use init_static_list() to initialize.
|
||||
typedef struct {
|
||||
list_T sl_list; // must be first
|
||||
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 {
|
||||
typval_T di_tv; /* type and value of the variable */
|
||||
char_u di_flags; /* flags (only used for variable) */
|
||||
char_u di_key[1]; /* key (actually longer!) */
|
||||
typval_T di_tv; ///< type and value of the variable
|
||||
char_u di_flags; ///< flags (only used for variable)
|
||||
char_u di_key[1]; ///< key (actually longer!)
|
||||
};
|
||||
|
||||
typedef struct dictitem_S dictitem_T;
|
||||
@ -147,9 +154,88 @@ struct dictvar_S {
|
||||
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 {
|
||||
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
|
||||
///< dict.member in handle_subscript().
|
||||
int pt_argc; ///< Number of arguments.
|
||||
|
@ -1327,8 +1327,9 @@ int using_script(void)
|
||||
void before_blocking(void)
|
||||
{
|
||||
updatescript(0);
|
||||
if (may_garbage_collect)
|
||||
garbage_collect();
|
||||
if (may_garbage_collect) {
|
||||
garbage_collect(false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1366,10 +1367,11 @@ int vgetc(void)
|
||||
char_u buf[MB_MAXBYTES + 1];
|
||||
int i;
|
||||
|
||||
/* Do garbage collection when garbagecollect() was called previously and
|
||||
* we are now at the toplevel. */
|
||||
if (may_garbage_collect && want_garbage_collect)
|
||||
garbage_collect();
|
||||
// Do garbage collection when garbagecollect() was called previously and
|
||||
// we are now at the toplevel.
|
||||
if (may_garbage_collect && want_garbage_collect) {
|
||||
garbage_collect(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
// 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
|
||||
EXTERN bool embedded_mode INIT(= false);
|
||||
|
||||
|
@ -624,8 +624,9 @@ void getout(int exitval)
|
||||
iconv_end();
|
||||
#endif
|
||||
cs_end();
|
||||
if (garbage_collect_at_exit)
|
||||
garbage_collect();
|
||||
if (garbage_collect_at_exit) {
|
||||
garbage_collect(false);
|
||||
}
|
||||
|
||||
mch_exit(exitval);
|
||||
}
|
||||
|
@ -2460,11 +2460,11 @@ do_mouse (
|
||||
};
|
||||
typval_T rettv;
|
||||
int doesrange;
|
||||
(void) call_func((char_u *) tab_page_click_defs[mouse_col].func,
|
||||
(int) strlen(tab_page_click_defs[mouse_col].func),
|
||||
&rettv, ARRAY_SIZE(argv), argv,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&doesrange, true, NULL, NULL);
|
||||
(void)call_func((char_u *)tab_page_click_defs[mouse_col].func,
|
||||
(int)strlen(tab_page_click_defs[mouse_col].func),
|
||||
&rettv, ARRAY_SIZE(argv), argv, NULL,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&doesrange, true, NULL, NULL);
|
||||
clear_tv(&rettv);
|
||||
break;
|
||||
}
|
||||
|
@ -6442,32 +6442,72 @@ static linenr_T submatch_firstlnum;
|
||||
static linenr_T submatch_maxline;
|
||||
static int submatch_line_lbr;
|
||||
|
||||
/*
|
||||
* 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, char_u *dest, int copy, int magic, int backslash)
|
||||
/// Put the submatches in "argv[0]" which is a list passed into call_func() by
|
||||
/// vim_regsub_both().
|
||||
static int fill_submatch_list(int argc, typval_T *argv, int argcount)
|
||||
{
|
||||
listitem_T *li;
|
||||
int i;
|
||||
char_u *s;
|
||||
|
||||
if (argcount == 0) {
|
||||
// called function doesn't take an argument
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Relies on sl_list to be the first item in staticList10_T.
|
||||
init_static_list((staticList10_T *)(argv->vval.v_list));
|
||||
|
||||
// There are always 10 list items in staticList10_T.
|
||||
li = argv->vval.v_list->lv_first;
|
||||
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_mmatch = NULL;
|
||||
reg_maxline = 0;
|
||||
reg_buf = curbuf;
|
||||
reg_line_lbr = TRUE;
|
||||
return vim_regsub_both(source, dest, copy, magic, backslash);
|
||||
reg_line_lbr = true;
|
||||
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)
|
||||
@ -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_firstlnum = lnum;
|
||||
reg_maxline = curbuf->b_ml.ml_line_count - lnum;
|
||||
reg_line_lbr = FALSE;
|
||||
return vim_regsub_both(source, dest, copy, magic, backslash);
|
||||
reg_line_lbr = false;
|
||||
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 *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 */
|
||||
static char_u *eval_result = NULL;
|
||||
|
||||
/* Be paranoid... */
|
||||
if (source == NULL || dest == NULL) {
|
||||
// Be paranoid...
|
||||
if ((source == NULL && expr == NULL) || dest == NULL) {
|
||||
EMSG(_(e_null));
|
||||
return 0;
|
||||
}
|
||||
@ -6505,16 +6546,13 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
|
||||
src = source;
|
||||
dst = dest;
|
||||
|
||||
/*
|
||||
* When the substitute part starts with "\=" evaluate it as an expression.
|
||||
*/
|
||||
if (source[0] == '\\' && source[1] == '='
|
||||
&& !can_f_submatch /* can't do this recursively */
|
||||
) {
|
||||
/* To make sure that the length doesn't change between checking the
|
||||
* 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. */
|
||||
// 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
|
||||
// To make sure that the length doesn't change between checking the
|
||||
// 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 (eval_result != NULL) {
|
||||
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 {
|
||||
win_T *save_reg_win;
|
||||
int save_ireg_ic;
|
||||
bool prev_can_f_submatch = can_f_submatch;
|
||||
|
||||
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;
|
||||
save_reg_win = reg_win;
|
||||
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) {
|
||||
int had_backslash = FALSE;
|
||||
|
||||
|
@ -34,12 +34,14 @@ NEW_TESTS ?= \
|
||||
test_cscope.res \
|
||||
test_digraph.res \
|
||||
test_diffmode.res \
|
||||
test_filter_map.res \
|
||||
test_gn.res \
|
||||
test_hardcopy.res \
|
||||
test_help_tagjump.res \
|
||||
test_history.res \
|
||||
test_increment.res \
|
||||
test_increment_dbcs.res \
|
||||
test_lambda.res \
|
||||
test_langmap.res \
|
||||
test_match.res \
|
||||
test_matchadd_conceal.res \
|
||||
|
@ -62,6 +62,12 @@ lang mess C
|
||||
" Always use forward slashes.
|
||||
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.
|
||||
set directory^=.
|
||||
set nohidden
|
||||
|
@ -9,8 +9,10 @@ source test_ex_undo.vim
|
||||
source test_expr.vim
|
||||
source test_expr_utf8.vim
|
||||
source test_feedkeys.vim
|
||||
source test_filter_map.vim
|
||||
source test_goto.vim
|
||||
source test_jumps.vim
|
||||
source test_lambda.vim
|
||||
source test_match.vim
|
||||
source test_matchadd_conceal_utf8.vim
|
||||
source test_menu.vim
|
||||
|
@ -85,6 +85,11 @@ func Test_getcompletion()
|
||||
let l = getcompletion('paint', 'function')
|
||||
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')
|
||||
call assert_true(index(l, 'runtest.vim') >= 0)
|
||||
let l = getcompletion('walk', 'file')
|
||||
|
@ -106,3 +106,68 @@ func Test_setmatches()
|
||||
call setmatches(set)
|
||||
call assert_equal(exp, getmatches())
|
||||
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
|
||||
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()
|
||||
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))
|
||||
let Sort = function('MySort', [0])
|
||||
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
|
||||
|
||||
func MyDictFunc(arg1, arg2) dict
|
||||
@ -59,6 +77,9 @@ func Test_partial_dict()
|
||||
call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy"))
|
||||
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'])}
|
||||
call assert_equal("Hello", dict.tr())
|
||||
endfunc
|
||||
|
@ -205,9 +205,9 @@ static int included_patches[] = {
|
||||
// 2238 NA
|
||||
2237,
|
||||
// 2236,
|
||||
// 2235,
|
||||
2235,
|
||||
// 2234 NA
|
||||
// 2233,
|
||||
2233,
|
||||
// 2232 NA
|
||||
// 2231,
|
||||
// 2230,
|
||||
@ -243,7 +243,7 @@ static int included_patches[] = {
|
||||
// 2200,
|
||||
// 2199 NA
|
||||
// 2198 NA
|
||||
// 2197,
|
||||
2197,
|
||||
// 2196,
|
||||
// 2195 NA
|
||||
2194,
|
||||
@ -297,16 +297,16 @@ static int included_patches[] = {
|
||||
2146,
|
||||
// 2145 NA
|
||||
// 2144,
|
||||
// 2143,
|
||||
// 2142,
|
||||
// 2141,
|
||||
2143,
|
||||
2142,
|
||||
2141,
|
||||
// 2140 NA
|
||||
// 2139,
|
||||
2139,
|
||||
// 2138 NA
|
||||
// 2137,
|
||||
// 2136,
|
||||
2137,
|
||||
2136,
|
||||
// 2135,
|
||||
// 2134,
|
||||
2134,
|
||||
// 2133 NA
|
||||
// 2132 NA
|
||||
// 2131 NA
|
||||
@ -319,9 +319,9 @@ static int included_patches[] = {
|
||||
2124,
|
||||
2123,
|
||||
// 2122 NA
|
||||
// 2121,
|
||||
// 2120,
|
||||
// 2119,
|
||||
2121,
|
||||
2120,
|
||||
2119,
|
||||
// 2118 NA
|
||||
2117,
|
||||
// 2116 NA
|
||||
@ -344,13 +344,13 @@ static int included_patches[] = {
|
||||
2099,
|
||||
// 2098,
|
||||
// 2097,
|
||||
// 2096,
|
||||
2096,
|
||||
// 2095,
|
||||
// 2094 NA
|
||||
// 2093 NA
|
||||
// 2092 NA
|
||||
// 2091 NA
|
||||
// 2090,
|
||||
2090,
|
||||
// 2089 NA
|
||||
2088,
|
||||
2087,
|
||||
@ -364,11 +364,11 @@ static int included_patches[] = {
|
||||
// 2079 NA
|
||||
// 2078 NA
|
||||
2077,
|
||||
// 2076,
|
||||
2076,
|
||||
2075,
|
||||
2074,
|
||||
// 2073 NA
|
||||
// 2072,
|
||||
2072,
|
||||
2071,
|
||||
// 2070 NA
|
||||
// 2069,
|
||||
@ -396,7 +396,7 @@ static int included_patches[] = {
|
||||
// 2047,
|
||||
// 2046,
|
||||
// 2045 NA
|
||||
// 2044,
|
||||
2044,
|
||||
2043,
|
||||
// 2042 NA
|
||||
// 2041 NA
|
||||
@ -438,7 +438,7 @@ static int included_patches[] = {
|
||||
2005,
|
||||
// 2004 NA
|
||||
// 2003 NA
|
||||
// 2002,
|
||||
2002,
|
||||
// 2001 NA
|
||||
2000,
|
||||
1999,
|
||||
@ -451,7 +451,7 @@ static int included_patches[] = {
|
||||
// 1992,
|
||||
// 1991,
|
||||
1990,
|
||||
// 1989,
|
||||
1989,
|
||||
// 1988 NA
|
||||
// 1987 NA
|
||||
// 1986,
|
||||
@ -714,7 +714,7 @@ static int included_patches[] = {
|
||||
1730,
|
||||
// 1729 NA
|
||||
1728,
|
||||
// 1727 NA
|
||||
1727,
|
||||
// 1726 NA
|
||||
// 1725 NA
|
||||
// 1724 NA
|
||||
|
@ -391,6 +391,27 @@ describe('jobs', function()
|
||||
eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg())
|
||||
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()
|
||||
it('returns a list of status codes', function()
|
||||
source([[
|
||||
|
@ -254,23 +254,33 @@ describe('dictionary change notifications', function()
|
||||
command('call g:ReplaceWatcher2()')
|
||||
command('let g:key = "value"')
|
||||
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
|
||||
|
||||
end)
|
||||
|
||||
it('does not crash when freeing a watched dictionary', function()
|
||||
source([[
|
||||
function! Watcher(dict, key, value)
|
||||
echo a:key string(a:value)
|
||||
endfunction
|
||||
function! Watcher(dict, key, value)
|
||||
echo a:key string(a:value)
|
||||
endfunction
|
||||
|
||||
function! MakeWatch()
|
||||
let d = {'foo': 'bar'}
|
||||
call dictwatcheradd(d, 'foo', function('Watcher'))
|
||||
endfunction
|
||||
function! MakeWatch()
|
||||
let d = {'foo': 'bar'}
|
||||
call dictwatcheradd(d, 'foo', function('Watcher'))
|
||||
endfunction
|
||||
]])
|
||||
|
||||
command('call MakeWatch()')
|
||||
eq(2, eval('1+1')) -- Still alive?
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user