mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:8.1.1803: all builtin functions are global
Problem: All builtin functions are global.
Solution: Add the method call operator ->. Implemented for a limited number
of functions.
ac92e25a33
- Note that to *exactly* port hunk @@ -7376,18 +7444,19 from
handle_subscript(), we need the :scriptversion patches (I have an open
PR for those, but this patch works fine without them anyway).
- Port call_internal_func() from v7.4.2058.
- Adjust some error messages in tests, as they rely on the Blob patches.
- Add a modeline to test_method.vim.
Ignore the global_functions and base_method tables and prefer the
current GPerf implementation. Instead, add an extra base_arg field to
VimLFuncDef that holds the number of the argument to use as the base
(1-indexed, so that 0 may be used to refer to functions that cannot be
used as methods).
This also means we support using any argument as a base from the get-go,
rather than just the first (Vim includes this ability in future patches,
however).
To mark a function as usable as a method, use the "base" key as
described in eval.lua.
This commit is contained in:
parent
4042ae5a2b
commit
e6be6c307a
@ -942,6 +942,8 @@ in any order. E.g., these are all possible:
|
|||||||
expr9[expr1].name
|
expr9[expr1].name
|
||||||
expr9.name[expr1]
|
expr9.name[expr1]
|
||||||
expr9(expr1, ...)[expr1].name
|
expr9(expr1, ...)[expr1].name
|
||||||
|
expr9->(expr1, ...)[expr1]
|
||||||
|
Evaluation is always from left to right.
|
||||||
|
|
||||||
|
|
||||||
expr8[expr1] item of String or |List| *expr-[]* *E111*
|
expr8[expr1] item of String or |List| *expr-[]* *E111*
|
||||||
@ -1043,6 +1045,11 @@ expr8(expr1, ...) |Funcref| function call
|
|||||||
When expr8 is a |Funcref| type variable, invoke the function it refers to.
|
When expr8 is a |Funcref| type variable, invoke the function it refers to.
|
||||||
|
|
||||||
|
|
||||||
|
expr8->name([args]) method call *method*
|
||||||
|
|
||||||
|
For global methods this is the same as: >
|
||||||
|
name(expr8 [, args])
|
||||||
|
There can also be methods specifically for the type of "expr8".
|
||||||
|
|
||||||
*expr9*
|
*expr9*
|
||||||
number
|
number
|
||||||
@ -2603,6 +2610,8 @@ add({list}, {expr}) *add()*
|
|||||||
< Note that when {expr} is a |List| it is appended as a single
|
< Note that when {expr} is a |List| it is appended as a single
|
||||||
item. Use |extend()| to concatenate |Lists|.
|
item. Use |extend()| to concatenate |Lists|.
|
||||||
Use |insert()| to add an item at another position.
|
Use |insert()| to add an item at another position.
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->add(val1)->add(val2)
|
||||||
|
|
||||||
|
|
||||||
and({expr}, {expr}) *and()*
|
and({expr}, {expr}) *and()*
|
||||||
@ -3215,6 +3224,8 @@ copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
|
|||||||
changing an item changes the contents of both |Lists|.
|
changing an item changes the contents of both |Lists|.
|
||||||
A |Dictionary| is copied in a similar way as a |List|.
|
A |Dictionary| is copied in a similar way as a |List|.
|
||||||
Also see |deepcopy()|.
|
Also see |deepcopy()|.
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->copy()
|
||||||
|
|
||||||
cos({expr}) *cos()*
|
cos({expr}) *cos()*
|
||||||
Return the cosine of {expr}, measured in radians, as a |Float|.
|
Return the cosine of {expr}, measured in radians, as a |Float|.
|
||||||
@ -3249,6 +3260,8 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()*
|
|||||||
When {comp} is a string then the number of not overlapping
|
When {comp} is a string then the number of not overlapping
|
||||||
occurrences of {expr} is returned. Zero is returned when
|
occurrences of {expr} is returned. Zero is returned when
|
||||||
{expr} is an empty string.
|
{expr} is an empty string.
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->count(val)
|
||||||
|
|
||||||
*cscope_connection()*
|
*cscope_connection()*
|
||||||
cscope_connection([{num} , {dbpath} [, {prepend}]])
|
cscope_connection([{num} , {dbpath} [, {prepend}]])
|
||||||
@ -3479,6 +3492,8 @@ empty({expr}) *empty()*
|
|||||||
A |List| or |Dictionary| is empty when it does not have any
|
A |List| or |Dictionary| is empty when it does not have any
|
||||||
items. A Number is empty when its value is zero. Special
|
items. A Number is empty when its value is zero. Special
|
||||||
variable is empty when it is |v:false| or |v:null|.
|
variable is empty when it is |v:false| or |v:null|.
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->empty()
|
||||||
|
|
||||||
environ() *environ()*
|
environ() *environ()*
|
||||||
Return all of environment variables as dictionary. You can
|
Return all of environment variables as dictionary. You can
|
||||||
@ -3794,6 +3809,8 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()*
|
|||||||
fails.
|
fails.
|
||||||
Returns {expr1}.
|
Returns {expr1}.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->extend(otherlist)
|
||||||
|
|
||||||
feedkeys({string} [, {mode}]) *feedkeys()*
|
feedkeys({string} [, {mode}]) *feedkeys()*
|
||||||
Characters in {string} are queued for processing as if they
|
Characters in {string} are queued for processing as if they
|
||||||
@ -3903,6 +3920,8 @@ filter({expr1}, {expr2}) *filter()*
|
|||||||
Funcref errors inside a function are ignored, unless it was
|
Funcref errors inside a function are ignored, unless it was
|
||||||
defined with the "abort" flag.
|
defined with the "abort" flag.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->filter(expr2)
|
||||||
|
|
||||||
finddir({name} [, {path} [, {count}]]) *finddir()*
|
finddir({name} [, {path} [, {count}]]) *finddir()*
|
||||||
Find directory {name} in {path}. Supports both downwards and
|
Find directory {name} in {path}. Supports both downwards and
|
||||||
@ -4159,6 +4178,8 @@ get({list}, {idx} [, {default}]) *get()*
|
|||||||
Get item {idx} from |List| {list}. When this item is not
|
Get item {idx} from |List| {list}. When this item is not
|
||||||
available return {default}. Return zero when {default} is
|
available return {default}. Return zero when {default} is
|
||||||
omitted.
|
omitted.
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->get(idx)
|
||||||
get({dict}, {key} [, {default}])
|
get({dict}, {key} [, {default}])
|
||||||
Get item with key {key} from |Dictionary| {dict}. When this
|
Get item with key {key} from |Dictionary| {dict}. When this
|
||||||
item is not available return {default}. Return zero when
|
item is not available return {default}. Return zero when
|
||||||
@ -5513,6 +5534,9 @@ insert({list}, {item} [, {idx}]) *insert()*
|
|||||||
Note that when {item} is a |List| it is inserted as a single
|
Note that when {item} is a |List| it is inserted as a single
|
||||||
item. Use |extend()| to concatenate |Lists|.
|
item. Use |extend()| to concatenate |Lists|.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->insert(item)
|
||||||
|
|
||||||
interrupt() *interrupt()*
|
interrupt() *interrupt()*
|
||||||
Interrupt script execution. It works more or less like the
|
Interrupt script execution. It works more or less like the
|
||||||
user typing CTRL-C, most commands won't execute and control
|
user typing CTRL-C, most commands won't execute and control
|
||||||
@ -5580,6 +5604,8 @@ items({dict}) *items()*
|
|||||||
|List| item is a list with two items: the key of a {dict}
|
|List| item is a list with two items: the key of a {dict}
|
||||||
entry and the value of this entry. The |List| is in arbitrary
|
entry and the value of this entry. The |List| is in arbitrary
|
||||||
order.
|
order.
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mydict->items()
|
||||||
|
|
||||||
isnan({expr}) *isnan()*
|
isnan({expr}) *isnan()*
|
||||||
Return |TRUE| if {expr} is a float with value NaN. >
|
Return |TRUE| if {expr} is a float with value NaN. >
|
||||||
@ -5713,6 +5739,9 @@ join({list} [, {sep}]) *join()*
|
|||||||
converted into a string like with |string()|.
|
converted into a string like with |string()|.
|
||||||
The opposite function is |split()|.
|
The opposite function is |split()|.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->join()
|
||||||
|
|
||||||
json_decode({expr}) *json_decode()*
|
json_decode({expr}) *json_decode()*
|
||||||
Convert {expr} from JSON object. Accepts |readfile()|-style
|
Convert {expr} from JSON object. Accepts |readfile()|-style
|
||||||
list as the input, as well as regular string. May output any
|
list as the input, as well as regular string. May output any
|
||||||
@ -5743,8 +5772,10 @@ json_encode({expr}) *json_encode()*
|
|||||||
keys({dict}) *keys()*
|
keys({dict}) *keys()*
|
||||||
Return a |List| with all the keys of {dict}. The |List| is in
|
Return a |List| with all the keys of {dict}. The |List| is in
|
||||||
arbitrary order.
|
arbitrary order.
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mydict->keys()
|
||||||
|
|
||||||
*len()* *E701*
|
< *len()* *E701*
|
||||||
len({expr}) The result is a Number, which is the length of the argument.
|
len({expr}) The result is a Number, which is the length of the argument.
|
||||||
When {expr} is a String or a Number the length in bytes is
|
When {expr} is a String or a Number the length in bytes is
|
||||||
used, as with |strlen()|.
|
used, as with |strlen()|.
|
||||||
@ -5755,7 +5786,10 @@ len({expr}) The result is a Number, which is the length of the argument.
|
|||||||
|Dictionary| is returned.
|
|Dictionary| is returned.
|
||||||
Otherwise an error is given.
|
Otherwise an error is given.
|
||||||
|
|
||||||
*libcall()* *E364* *E368*
|
Can also be used as a |method|: >
|
||||||
|
mylist->len()
|
||||||
|
|
||||||
|
< *libcall()* *E364* *E368*
|
||||||
libcall({libname}, {funcname}, {argument})
|
libcall({libname}, {funcname}, {argument})
|
||||||
Call function {funcname} in the run-time library {libname}
|
Call function {funcname} in the run-time library {libname}
|
||||||
with single argument {argument}.
|
with single argument {argument}.
|
||||||
@ -5938,6 +5972,8 @@ map({expr1}, {expr2}) *map()*
|
|||||||
Funcref errors inside a function are ignored, unless it was
|
Funcref errors inside a function are ignored, unless it was
|
||||||
defined with the "abort" flag.
|
defined with the "abort" flag.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->map(expr2)
|
||||||
|
|
||||||
maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
|
maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
|
||||||
When {dict} is omitted or zero: Return the rhs of mapping
|
When {dict} is omitted or zero: Return the rhs of mapping
|
||||||
@ -6273,6 +6309,9 @@ max({expr}) Return the maximum value of all items in {expr}.
|
|||||||
items in {expr} cannot be used as a Number this results in
|
items in {expr} cannot be used as a Number this results in
|
||||||
an error. An empty |List| or |Dictionary| results in zero.
|
an error. An empty |List| or |Dictionary| results in zero.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->max()
|
||||||
|
|
||||||
menu_get({path}, {modes}) *menu_get()*
|
menu_get({path}, {modes}) *menu_get()*
|
||||||
Returns a |List| of |Dictionaries| describing |menus| (defined
|
Returns a |List| of |Dictionaries| describing |menus| (defined
|
||||||
by |:menu|, |:amenu|, …), including |hidden-menus|.
|
by |:menu|, |:amenu|, …), including |hidden-menus|.
|
||||||
@ -6327,7 +6366,10 @@ min({expr}) Return the minimum value of all items in {expr}.
|
|||||||
items in {expr} cannot be used as a Number this results in
|
items in {expr} cannot be used as a Number this results in
|
||||||
an error. An empty |List| or |Dictionary| results in zero.
|
an error. An empty |List| or |Dictionary| results in zero.
|
||||||
|
|
||||||
*mkdir()* *E739*
|
Can also be used as a |method|: >
|
||||||
|
mylist->min()
|
||||||
|
|
||||||
|
< *mkdir()* *E739*
|
||||||
mkdir({name} [, {path} [, {prot}]])
|
mkdir({name} [, {path} [, {prot}]])
|
||||||
Create directory {name}.
|
Create directory {name}.
|
||||||
If {path} is "p" then intermediate directories are created as
|
If {path} is "p" then intermediate directories are created as
|
||||||
@ -7085,6 +7127,10 @@ remove({list}, {idx} [, {end}]) *remove()*
|
|||||||
Example: >
|
Example: >
|
||||||
:echo "last item: " . remove(mylist, -1)
|
:echo "last item: " . remove(mylist, -1)
|
||||||
:call remove(mylist, 0, 9)
|
:call remove(mylist, 0, 9)
|
||||||
|
|
||||||
|
< Can also be used as a |method|: >
|
||||||
|
mylist->remove(idx)
|
||||||
|
|
||||||
remove({dict}, {key})
|
remove({dict}, {key})
|
||||||
Remove the entry from {dict} with key {key} and return it.
|
Remove the entry from {dict} with key {key} and return it.
|
||||||
Example: >
|
Example: >
|
||||||
@ -7111,6 +7157,8 @@ repeat({expr}, {count}) *repeat()*
|
|||||||
:let longlist = repeat(['a', 'b'], 3)
|
:let longlist = repeat(['a', 'b'], 3)
|
||||||
< Results in ['a', 'b', 'a', 'b', 'a', 'b'].
|
< Results in ['a', 'b', 'a', 'b', 'a', 'b'].
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->repeat(count)
|
||||||
|
|
||||||
resolve({filename}) *resolve()* *E655*
|
resolve({filename}) *resolve()* *E655*
|
||||||
On MS-Windows, when {filename} is a shortcut (a .lnk file),
|
On MS-Windows, when {filename} is a shortcut (a .lnk file),
|
||||||
@ -7130,6 +7178,8 @@ reverse({list}) Reverse the order of items in {list} in-place. Returns
|
|||||||
{list}.
|
{list}.
|
||||||
If you want a list to remain unmodified make a copy first: >
|
If you want a list to remain unmodified make a copy first: >
|
||||||
:let revlist = reverse(copy(mylist))
|
:let revlist = reverse(copy(mylist))
|
||||||
|
< Can also be used as a |method|: >
|
||||||
|
mylist->reverse()
|
||||||
|
|
||||||
round({expr}) *round()*
|
round({expr}) *round()*
|
||||||
Round off {expr} to the nearest integral value and return it
|
Round off {expr} to the nearest integral value and return it
|
||||||
@ -8209,7 +8259,10 @@ sort({list} [, {func} [, {dict}]]) *sort()* *E702*
|
|||||||
on numbers, text strings will sort next to each other, in the
|
on numbers, text strings will sort next to each other, in the
|
||||||
same order as they were originally.
|
same order as they were originally.
|
||||||
|
|
||||||
Also see |uniq()|.
|
Can also be used as a |method|: >
|
||||||
|
mylist->sort()
|
||||||
|
|
||||||
|
< Also see |uniq()|.
|
||||||
|
|
||||||
Example: >
|
Example: >
|
||||||
func MyCompare(i1, i2)
|
func MyCompare(i1, i2)
|
||||||
@ -8504,6 +8557,9 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number,
|
|||||||
method, use |msgpackdump()| or |json_encode()| if you need to
|
method, use |msgpackdump()| or |json_encode()| if you need to
|
||||||
share data with other application.
|
share data with other application.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->string()
|
||||||
|
|
||||||
*strlen()*
|
*strlen()*
|
||||||
strlen({expr}) The result is a Number, which is the length of the String
|
strlen({expr}) The result is a Number, which is the length of the String
|
||||||
{expr} in bytes.
|
{expr} in bytes.
|
||||||
@ -9149,6 +9205,9 @@ type({expr}) *type()*
|
|||||||
< To check if the v:t_ variables exist use this: >
|
< To check if the v:t_ variables exist use this: >
|
||||||
:if exists('v:t_number')
|
:if exists('v:t_number')
|
||||||
|
|
||||||
|
< Can also be used as a |method|: >
|
||||||
|
mylist->type()
|
||||||
|
|
||||||
undofile({name}) *undofile()*
|
undofile({name}) *undofile()*
|
||||||
Return the name of the undo file that would be used for a file
|
Return the name of the undo file that would be used for a file
|
||||||
with name {name} when writing. This uses the 'undodir'
|
with name {name} when writing. This uses the 'undodir'
|
||||||
@ -9211,10 +9270,15 @@ uniq({list} [, {func} [, {dict}]]) *uniq()* *E882*
|
|||||||
< The default compare function uses the string representation of
|
< The default compare function uses the string representation of
|
||||||
each item. For the use of {func} and {dict} see |sort()|.
|
each item. For the use of {func} and {dict} see |sort()|.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mylist->uniq()
|
||||||
|
|
||||||
values({dict}) *values()*
|
values({dict}) *values()*
|
||||||
Return a |List| with all the values of {dict}. The |List| is
|
Return a |List| with all the values of {dict}. The |List| is
|
||||||
in arbitrary order.
|
in arbitrary order.
|
||||||
|
|
||||||
|
Can also be used as a |method|: >
|
||||||
|
mydict->values()
|
||||||
|
|
||||||
virtcol({expr}) *virtcol()*
|
virtcol({expr}) *virtcol()*
|
||||||
The result is a Number, which is the screen column of the file
|
The result is a Number, which is the screen column of the file
|
||||||
|
@ -3809,6 +3809,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
|
|||||||
// + in front unary plus (ignored)
|
// + in front unary plus (ignored)
|
||||||
// trailing [] subscript in String or List
|
// trailing [] subscript in String or List
|
||||||
// trailing .name entry in Dictionary
|
// trailing .name entry in Dictionary
|
||||||
|
// trailing ->name() method call
|
||||||
//
|
//
|
||||||
// "arg" must point to the first non-white of the expression.
|
// "arg" must point to the first non-white of the expression.
|
||||||
// "arg" is advanced to the next non-white after the recognized expression.
|
// "arg" is advanced to the next non-white after the recognized expression.
|
||||||
@ -4080,6 +4081,63 @@ static int eval7(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate "->method()".
|
||||||
|
/// @note "*arg" points to the '-'.
|
||||||
|
/// @return FAIL or OK. "*arg" is advanced to after the ')'.
|
||||||
|
static int eval_method(char_u **const arg, typval_T *const rettv,
|
||||||
|
const bool evaluate, const bool verbose)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
// Skip over the ->.
|
||||||
|
*arg += 2;
|
||||||
|
|
||||||
|
// Locate the method name.
|
||||||
|
const char_u *const name = *arg;
|
||||||
|
size_t len;
|
||||||
|
for (len = 0; ASCII_ISALNUM(name[len]) || name[len] == '_'; len++) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
if (verbose) {
|
||||||
|
EMSG(_("E260: Missing name after ->"));
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the "(". Skip over white space after it.
|
||||||
|
if (name[len] != '(') {
|
||||||
|
if (verbose) {
|
||||||
|
EMSG2(_(e_missingparen), name);
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
*arg += len;
|
||||||
|
|
||||||
|
typval_T base = *rettv;
|
||||||
|
funcexe_T funcexe = FUNCEXE_INIT;
|
||||||
|
funcexe.evaluate = evaluate;
|
||||||
|
funcexe.basetv = &base;
|
||||||
|
rettv->v_type = VAR_UNKNOWN;
|
||||||
|
int ret = get_func_tv(name, len, rettv, arg, &funcexe);
|
||||||
|
|
||||||
|
// Clear the funcref afterwards, so that deleting it while
|
||||||
|
// evaluating the arguments is possible (see test55).
|
||||||
|
if (evaluate) {
|
||||||
|
tv_clear(&base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the expression evaluation when immediately aborting on
|
||||||
|
// error, or when an interrupt occurred or an exception was thrown
|
||||||
|
// but not caught.
|
||||||
|
if (aborting()) {
|
||||||
|
if (ret == OK) {
|
||||||
|
tv_clear(rettv);
|
||||||
|
}
|
||||||
|
ret = FAIL;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(ZyX-I): move to eval/expressions
|
// TODO(ZyX-I): move to eval/expressions
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -8420,9 +8478,13 @@ int check_luafunc_name(const char *str, bool paren)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle expr[expr], expr[expr:expr] subscript and .name lookup.
|
/// Handle:
|
||||||
/// Also handle function call with Funcref variable: func(expr)
|
/// - expr[expr], expr[expr:expr] subscript
|
||||||
/// Can all be combined: dict.func(expr)[idx]['func'](expr)
|
/// - ".name" lookup
|
||||||
|
/// - function call with Funcref variable: func(expr)
|
||||||
|
/// - method call: var->method()
|
||||||
|
///
|
||||||
|
/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len()
|
||||||
int
|
int
|
||||||
handle_subscript(
|
handle_subscript(
|
||||||
const char **const arg,
|
const char **const arg,
|
||||||
@ -8456,12 +8518,11 @@ handle_subscript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
while (ret == OK
|
while (ret == OK
|
||||||
&& (**arg == '['
|
&& (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT)
|
||||||
|| (**arg == '.' && rettv->v_type == VAR_DICT)
|
|| (**arg == '(' && (!evaluate || tv_is_func(*rettv))))
|
||||||
|| (**arg == '(' && (!evaluate || tv_is_func(*rettv))))
|
&& !ascii_iswhite(*(*arg - 1)))
|
||||||
&& !ascii_iswhite(*(*arg - 1))) {
|
|| (**arg == '-' && (*arg)[1] == '>'))) {
|
||||||
if (**arg == '(') {
|
if (**arg == '(') {
|
||||||
partial_T *pt = NULL;
|
partial_T *pt = NULL;
|
||||||
// need to copy the funcref so that we can clear rettv
|
// need to copy the funcref so that we can clear rettv
|
||||||
@ -8507,6 +8568,11 @@ handle_subscript(
|
|||||||
}
|
}
|
||||||
tv_dict_unref(selfdict);
|
tv_dict_unref(selfdict);
|
||||||
selfdict = NULL;
|
selfdict = NULL;
|
||||||
|
} else if (**arg == '-') {
|
||||||
|
if (eval_method((char_u **)arg, rettv, evaluate, verbose) == FAIL) {
|
||||||
|
tv_clear(rettv);
|
||||||
|
ret = FAIL;
|
||||||
|
}
|
||||||
} else { // **arg == '[' || **arg == '.'
|
} else { // **arg == '[' || **arg == '.'
|
||||||
tv_dict_unref(selfdict);
|
tv_dict_unref(selfdict);
|
||||||
if (rettv->v_type == VAR_DICT) {
|
if (rettv->v_type == VAR_DICT) {
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
-- args Number of arguments, list with maximum and minimum number of arguments
|
-- args Number of arguments, list with maximum and minimum number of arguments
|
||||||
-- or list with a minimum number of arguments only. Defaults to zero
|
-- or list with a minimum number of arguments only. Defaults to zero
|
||||||
-- arguments.
|
-- arguments.
|
||||||
|
-- base For methods: the argument to use as the base argument (1-indexed):
|
||||||
|
-- base->method()
|
||||||
|
-- Defaults to zero (function cannot be used as a method).
|
||||||
-- func Name of the C function which implements the VimL function. Defaults to
|
-- func Name of the C function which implements the VimL function. Defaults to
|
||||||
-- `f_{funcname}`.
|
-- `f_{funcname}`.
|
||||||
|
|
||||||
@ -16,7 +19,7 @@ return {
|
|||||||
funcs={
|
funcs={
|
||||||
abs={args=1},
|
abs={args=1},
|
||||||
acos={args=1, func="float_op_wrapper", data="&acos"}, -- WJMc
|
acos={args=1, func="float_op_wrapper", data="&acos"}, -- WJMc
|
||||||
add={args=2},
|
add={args=2, base=1},
|
||||||
['and']={args=2},
|
['and']={args=2},
|
||||||
api_info={},
|
api_info={},
|
||||||
append={args=2},
|
append={args=2},
|
||||||
@ -73,10 +76,10 @@ return {
|
|||||||
complete_check={},
|
complete_check={},
|
||||||
complete_info={args={0, 1}},
|
complete_info={args={0, 1}},
|
||||||
confirm={args={1, 4}},
|
confirm={args={1, 4}},
|
||||||
copy={args=1},
|
copy={args=1, base=1},
|
||||||
cos={args=1, func="float_op_wrapper", data="&cos"},
|
cos={args=1, func="float_op_wrapper", data="&cos"},
|
||||||
cosh={args=1, func="float_op_wrapper", data="&cosh"},
|
cosh={args=1, func="float_op_wrapper", data="&cosh"},
|
||||||
count={args={2, 4}},
|
count={args={2, 4}, base=1},
|
||||||
cscope_connection={args={0, 3}},
|
cscope_connection={args={0, 3}},
|
||||||
ctxget={args={0, 1}},
|
ctxget={args={0, 1}},
|
||||||
ctxpop={},
|
ctxpop={},
|
||||||
@ -93,7 +96,7 @@ return {
|
|||||||
did_filetype={},
|
did_filetype={},
|
||||||
diff_filler={args=1},
|
diff_filler={args=1},
|
||||||
diff_hlID={args=2},
|
diff_hlID={args=2},
|
||||||
empty={args=1},
|
empty={args=1, base=1},
|
||||||
environ={},
|
environ={},
|
||||||
escape={args=2},
|
escape={args=2},
|
||||||
eval={args=1},
|
eval={args=1},
|
||||||
@ -105,12 +108,12 @@ return {
|
|||||||
exp={args=1, func="float_op_wrapper", data="&exp"},
|
exp={args=1, func="float_op_wrapper", data="&exp"},
|
||||||
expand={args={1, 3}},
|
expand={args={1, 3}},
|
||||||
expandcmd={args=1},
|
expandcmd={args=1},
|
||||||
extend={args={2, 3}},
|
extend={args={2, 3}, base=1},
|
||||||
feedkeys={args={1, 2}},
|
feedkeys={args={1, 2}},
|
||||||
file_readable={args=1, func='f_filereadable'}, -- obsolete
|
file_readable={args=1, func='f_filereadable'}, -- obsolete
|
||||||
filereadable={args=1},
|
filereadable={args=1},
|
||||||
filewritable={args=1},
|
filewritable={args=1},
|
||||||
filter={args=2},
|
filter={args=2, base=1},
|
||||||
finddir={args={1, 3}},
|
finddir={args={1, 3}},
|
||||||
findfile={args={1, 3}},
|
findfile={args={1, 3}},
|
||||||
flatten={args={1, 2}},
|
flatten={args={1, 2}},
|
||||||
@ -128,7 +131,7 @@ return {
|
|||||||
funcref={args={1, 3}},
|
funcref={args={1, 3}},
|
||||||
['function']={args={1, 3}},
|
['function']={args={1, 3}},
|
||||||
garbagecollect={args={0, 1}},
|
garbagecollect={args={0, 1}},
|
||||||
get={args={2, 3}},
|
get={args={2, 3}, base=1},
|
||||||
getbufinfo={args={0, 1}},
|
getbufinfo={args={0, 1}},
|
||||||
getbufline={args={2, 3}},
|
getbufline={args={2, 3}},
|
||||||
getbufvar={args={2, 3}},
|
getbufvar={args={2, 3}},
|
||||||
@ -187,14 +190,14 @@ return {
|
|||||||
hostname={},
|
hostname={},
|
||||||
iconv={args=3},
|
iconv={args=3},
|
||||||
indent={args=1},
|
indent={args=1},
|
||||||
index={args={2, 4}},
|
index={args={2, 4}, base=1},
|
||||||
input={args={1, 3}},
|
input={args={1, 3}},
|
||||||
inputdialog={args={1, 3}},
|
inputdialog={args={1, 3}},
|
||||||
inputlist={args=1},
|
inputlist={args=1},
|
||||||
inputrestore={},
|
inputrestore={},
|
||||||
inputsave={},
|
inputsave={},
|
||||||
inputsecret={args={1, 2}},
|
inputsecret={args={1, 2}},
|
||||||
insert={args={2, 3}},
|
insert={args={2, 3}, base=1},
|
||||||
interrupt={args=0},
|
interrupt={args=0},
|
||||||
invert={args=1},
|
invert={args=1},
|
||||||
isdirectory={args=1},
|
isdirectory={args=1},
|
||||||
@ -202,7 +205,7 @@ return {
|
|||||||
islocked={args=1},
|
islocked={args=1},
|
||||||
isnan={args=1},
|
isnan={args=1},
|
||||||
id={args=1},
|
id={args=1},
|
||||||
items={args=1},
|
items={args=1, base=1},
|
||||||
jobclose={args={1, 2}, func="f_chanclose"},
|
jobclose={args={1, 2}, func="f_chanclose"},
|
||||||
jobpid={args=1},
|
jobpid={args=1},
|
||||||
jobresize={args=3},
|
jobresize={args=3},
|
||||||
@ -210,12 +213,12 @@ return {
|
|||||||
jobstart={args={1, 2}},
|
jobstart={args={1, 2}},
|
||||||
jobstop={args=1},
|
jobstop={args=1},
|
||||||
jobwait={args={1, 2}},
|
jobwait={args={1, 2}},
|
||||||
join={args={1, 2}},
|
join={args={1, 2}, base=1},
|
||||||
json_decode={args=1},
|
json_decode={args=1},
|
||||||
json_encode={args=1},
|
json_encode={args=1},
|
||||||
keys={args=1},
|
keys={args=1, base=1},
|
||||||
last_buffer_nr={}, -- obsolete
|
last_buffer_nr={}, -- obsolete
|
||||||
len={args=1},
|
len={args=1, base=1},
|
||||||
libcall={args=3},
|
libcall={args=3},
|
||||||
libcallnr={args=3},
|
libcallnr={args=3},
|
||||||
line={args={1, 2}},
|
line={args={1, 2}},
|
||||||
@ -226,7 +229,7 @@ return {
|
|||||||
log={args=1, func="float_op_wrapper", data="&log"},
|
log={args=1, func="float_op_wrapper", data="&log"},
|
||||||
log10={args=1, func="float_op_wrapper", data="&log10"},
|
log10={args=1, func="float_op_wrapper", data="&log10"},
|
||||||
luaeval={args={1, 2}},
|
luaeval={args={1, 2}},
|
||||||
map={args=2},
|
map={args=2, base=1},
|
||||||
maparg={args={1, 4}},
|
maparg={args={1, 4}},
|
||||||
mapcheck={args={1, 3}},
|
mapcheck={args={1, 3}},
|
||||||
match={args={2, 4}},
|
match={args={2, 4}},
|
||||||
@ -238,9 +241,9 @@ return {
|
|||||||
matchlist={args={2, 4}},
|
matchlist={args={2, 4}},
|
||||||
matchstr={args={2, 4}},
|
matchstr={args={2, 4}},
|
||||||
matchstrpos={args={2,4}},
|
matchstrpos={args={2,4}},
|
||||||
max={args=1},
|
max={args=1, base=1},
|
||||||
menu_get={args={1, 2}},
|
menu_get={args={1, 2}},
|
||||||
min={args=1},
|
min={args=1, base=1},
|
||||||
mkdir={args={1, 3}},
|
mkdir={args={1, 3}},
|
||||||
mode={args={0, 1}},
|
mode={args={0, 1}},
|
||||||
msgpackdump={args=1},
|
msgpackdump={args=1},
|
||||||
@ -270,11 +273,11 @@ return {
|
|||||||
reltime={args={0, 2}},
|
reltime={args={0, 2}},
|
||||||
reltimefloat={args=1},
|
reltimefloat={args=1},
|
||||||
reltimestr={args=1},
|
reltimestr={args=1},
|
||||||
remove={args={2, 3}},
|
remove={args={2, 3}, base=1},
|
||||||
rename={args=2},
|
rename={args=2},
|
||||||
['repeat']={args=2},
|
['repeat']={args=2, base=1},
|
||||||
resolve={args=1},
|
resolve={args=1},
|
||||||
reverse={args=1},
|
reverse={args=1, base=1},
|
||||||
round={args=1, func="float_op_wrapper", data="&round"},
|
round={args=1, func="float_op_wrapper", data="&round"},
|
||||||
rpcnotify={args=varargs(2)},
|
rpcnotify={args=varargs(2)},
|
||||||
rpcrequest={args=varargs(2)},
|
rpcrequest={args=varargs(2)},
|
||||||
@ -327,7 +330,7 @@ return {
|
|||||||
sin={args=1, func="float_op_wrapper", data="&sin"},
|
sin={args=1, func="float_op_wrapper", data="&sin"},
|
||||||
sinh={args=1, func="float_op_wrapper", data="&sinh"},
|
sinh={args=1, func="float_op_wrapper", data="&sinh"},
|
||||||
sockconnect={args={2,3}},
|
sockconnect={args={2,3}},
|
||||||
sort={args={1, 3}},
|
sort={args={1, 3}, base=1},
|
||||||
soundfold={args=1},
|
soundfold={args=1},
|
||||||
stdioopen={args=1},
|
stdioopen={args=1},
|
||||||
spellbadword={args={0, 1}},
|
spellbadword={args={0, 1}},
|
||||||
@ -344,7 +347,7 @@ return {
|
|||||||
strftime={args={1, 2}},
|
strftime={args={1, 2}},
|
||||||
strgetchar={args={2, 2}},
|
strgetchar={args={2, 2}},
|
||||||
stridx={args={2, 3}},
|
stridx={args={2, 3}},
|
||||||
string={args=1},
|
string={args=1, base=1},
|
||||||
strlen={args=1},
|
strlen={args=1},
|
||||||
strpart={args={2, 4}},
|
strpart={args={2, 4}},
|
||||||
strptime={args=2},
|
strptime={args=2},
|
||||||
@ -383,11 +386,11 @@ return {
|
|||||||
tr={args=3},
|
tr={args=3},
|
||||||
trim={args={1,3}},
|
trim={args={1,3}},
|
||||||
trunc={args=1, func="float_op_wrapper", data="&trunc"},
|
trunc={args=1, func="float_op_wrapper", data="&trunc"},
|
||||||
type={args=1},
|
type={args=1, base=1},
|
||||||
undofile={args=1},
|
undofile={args=1},
|
||||||
undotree={},
|
undotree={},
|
||||||
uniq={args={1, 3}},
|
uniq={args={1, 3}, base=1},
|
||||||
values={args=1},
|
values={args=1, base=1},
|
||||||
virtcol={args=1},
|
virtcol={args=1},
|
||||||
visualmode={args={0, 1}},
|
visualmode={args={0, 1}},
|
||||||
wait={args={2,3}},
|
wait={args={2,3}},
|
||||||
|
@ -175,6 +175,50 @@ const VimLFuncDef *find_internal_func(const char *const name)
|
|||||||
return find_internal_func_gperf(name, len);
|
return find_internal_func_gperf(name, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int call_internal_func(const char_u *const fname, const int argcount,
|
||||||
|
typval_T *const argvars, typval_T *const rettv)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
|
||||||
|
if (fdef == NULL) {
|
||||||
|
return ERROR_UNKNOWN;
|
||||||
|
} else if (argcount < fdef->min_argc) {
|
||||||
|
return ERROR_TOOFEW;
|
||||||
|
} else if (argcount > fdef->max_argc) {
|
||||||
|
return ERROR_TOOMANY;
|
||||||
|
}
|
||||||
|
argvars[argcount].v_type = VAR_UNKNOWN;
|
||||||
|
fdef->func(argvars, rettv, fdef->data);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoke a method for base->method().
|
||||||
|
int call_internal_method(const char_u *const fname, const int argcount,
|
||||||
|
typval_T *const argvars, typval_T *const rettv,
|
||||||
|
typval_T *const basetv)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
|
||||||
|
if (fdef == NULL || fdef->base_arg == 0) {
|
||||||
|
return ERROR_UNKNOWN;
|
||||||
|
} else if (argcount + 1 < fdef->min_argc) {
|
||||||
|
return ERROR_TOOFEW;
|
||||||
|
} else if (argcount + 1 > fdef->max_argc) {
|
||||||
|
return ERROR_TOOMANY;
|
||||||
|
}
|
||||||
|
|
||||||
|
typval_T argv[MAX_FUNC_ARGS + 1];
|
||||||
|
const ptrdiff_t base_index = fdef->base_arg - 1;
|
||||||
|
memcpy(argv, argvars, base_index * sizeof(typval_T));
|
||||||
|
argv[base_index] = *basetv;
|
||||||
|
memcpy(argv + base_index + 1, argvars + base_index,
|
||||||
|
(argcount - base_index) * sizeof(typval_T));
|
||||||
|
argv[argcount + 1].v_type = VAR_UNKNOWN;
|
||||||
|
|
||||||
|
fdef->func(argv, rettv, fdef->data);
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return TRUE for a non-zero Number and a non-empty String.
|
* Return TRUE for a non-zero Number and a non-empty String.
|
||||||
*/
|
*/
|
||||||
|
@ -14,6 +14,7 @@ typedef struct fst {
|
|||||||
char *name; ///< Name of the function.
|
char *name; ///< Name of the function.
|
||||||
uint8_t min_argc; ///< Minimal number of arguments.
|
uint8_t min_argc; ///< Minimal number of arguments.
|
||||||
uint8_t max_argc; ///< Maximal number of arguments.
|
uint8_t max_argc; ///< Maximal number of arguments.
|
||||||
|
uint8_t base_arg; ///< Method base arg # (1-indexed), or 0 if not a method.
|
||||||
VimLFunc func; ///< Function implementation.
|
VimLFunc func; ///< Function implementation.
|
||||||
FunPtr data; ///< Userdata for function implementation.
|
FunPtr data; ///< Userdata for function implementation.
|
||||||
} VimLFuncDef;
|
} VimLFuncDef;
|
||||||
|
@ -1514,7 +1514,10 @@ call_func(
|
|||||||
}
|
}
|
||||||
} else if (fp != NULL || !builtin_function((const char *)rfname, -1)) {
|
} else if (fp != NULL || !builtin_function((const char *)rfname, -1)) {
|
||||||
// User defined function.
|
// User defined function.
|
||||||
if (fp == NULL) {
|
if (funcexe->basetv != NULL) {
|
||||||
|
// TODO(seandewar): support User function: base->Method()
|
||||||
|
fp = NULL;
|
||||||
|
} else if (fp == NULL) {
|
||||||
fp = find_func(rfname);
|
fp = find_func(rfname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1560,20 +1563,13 @@ call_func(
|
|||||||
error = ERROR_NONE;
|
error = ERROR_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (funcexe->basetv != NULL) {
|
||||||
|
// Find the method name in the table, call its implementation.
|
||||||
|
error = call_internal_method(fname, argcount, argvars, rettv,
|
||||||
|
funcexe->basetv);
|
||||||
} else {
|
} else {
|
||||||
// Find the function name in the table, call its implementation.
|
// Find the function name in the table, call its implementation.
|
||||||
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
|
error = call_internal_func(fname, argcount, argvars, rettv);
|
||||||
if (fdef != NULL) {
|
|
||||||
if (argcount < fdef->min_argc) {
|
|
||||||
error = ERROR_TOOFEW;
|
|
||||||
} else if (argcount > fdef->max_argc) {
|
|
||||||
error = ERROR_TOOMANY;
|
|
||||||
} else {
|
|
||||||
argvars[argcount].v_type = VAR_UNKNOWN;
|
|
||||||
fdef->func(argvars, rettv, fdef->data);
|
|
||||||
error = ERROR_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* The function call (or "FuncUndefined" autocommand sequence) might
|
* The function call (or "FuncUndefined" autocommand sequence) might
|
||||||
@ -2937,7 +2933,7 @@ void ex_call(exarg_T *eap)
|
|||||||
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
|
rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this.
|
||||||
|
|
||||||
if (*startarg != '(') {
|
if (*startarg != '(') {
|
||||||
EMSG2(_("E107: Missing parentheses: %s"), eap->arg);
|
EMSG2(_(e_missingparen), eap->arg);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ typedef struct {
|
|||||||
bool evaluate; ///< actually evaluate expressions
|
bool evaluate; ///< actually evaluate expressions
|
||||||
partial_T *partial; ///< for extra arguments
|
partial_T *partial; ///< for extra arguments
|
||||||
dict_T *selfdict; ///< Dictionary for "self"
|
dict_T *selfdict; ///< Dictionary for "self"
|
||||||
|
typval_T *basetv; ///< base for base->method()
|
||||||
} funcexe_T;
|
} funcexe_T;
|
||||||
|
|
||||||
#define FUNCEXE_INIT (funcexe_T) { \
|
#define FUNCEXE_INIT (funcexe_T) { \
|
||||||
@ -54,6 +55,7 @@ typedef struct {
|
|||||||
.evaluate = false, \
|
.evaluate = false, \
|
||||||
.partial = NULL, \
|
.partial = NULL, \
|
||||||
.selfdict = NULL, \
|
.selfdict = NULL, \
|
||||||
|
.basetv = NULL, \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
|
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
|
||||||
|
@ -42,7 +42,7 @@ gperfpipe:write([[
|
|||||||
%language=ANSI-C
|
%language=ANSI-C
|
||||||
%global-table
|
%global-table
|
||||||
%readonly-tables
|
%readonly-tables
|
||||||
%define initializer-suffix ,0,0,NULL,NULL
|
%define initializer-suffix ,0,0,0,NULL,NULL
|
||||||
%define word-array-name functions
|
%define word-array-name functions
|
||||||
%define hash-function-name hash_internal_func_gperf
|
%define hash-function-name hash_internal_func_gperf
|
||||||
%define lookup-function-name find_internal_func_gperf
|
%define lookup-function-name find_internal_func_gperf
|
||||||
@ -59,9 +59,10 @@ for name, def in pairs(funcs) do
|
|||||||
elseif #args == 1 then
|
elseif #args == 1 then
|
||||||
args[2] = 'MAX_FUNC_ARGS'
|
args[2] = 'MAX_FUNC_ARGS'
|
||||||
end
|
end
|
||||||
|
local base = def.base or 0
|
||||||
local func = def.func or ('f_' .. name)
|
local func = def.func or ('f_' .. name)
|
||||||
local data = def.data or "NULL"
|
local data = def.data or "NULL"
|
||||||
gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n')
|
gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n')
|
||||||
:format(name, args[1], args[2], func, data))
|
:format(name, args[1], args[2], base, func, data))
|
||||||
end
|
end
|
||||||
gperfpipe:close()
|
gperfpipe:close()
|
||||||
|
@ -972,6 +972,7 @@ EXTERN char_u e_write[] INIT(= N_("E80: Error while writing"));
|
|||||||
EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required"));
|
EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required"));
|
||||||
EXTERN char_u e_usingsid[] INIT(= N_(
|
EXTERN char_u e_usingsid[] INIT(= N_(
|
||||||
"E81: Using <SID> not in a script context"));
|
"E81: Using <SID> not in a script context"));
|
||||||
|
EXTERN char_u e_missingparen[] INIT(= N_("E107: Missing parentheses: %s"));
|
||||||
EXTERN char_u e_maxmempat[] INIT(= N_(
|
EXTERN char_u e_maxmempat[] INIT(= N_(
|
||||||
"E363: pattern uses more memory than 'maxmempattern'"));
|
"E363: pattern uses more memory than 'maxmempattern'"));
|
||||||
EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer"));
|
EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer"));
|
||||||
|
68
src/nvim/testdir/test_method.vim
Normal file
68
src/nvim/testdir/test_method.vim
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
" Tests for ->method()
|
||||||
|
|
||||||
|
func Test_list()
|
||||||
|
let l = [1, 2, 3]
|
||||||
|
call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4))
|
||||||
|
call assert_equal(l, l->copy())
|
||||||
|
call assert_equal(1, l->count(2))
|
||||||
|
call assert_false(l->empty())
|
||||||
|
call assert_true([]->empty())
|
||||||
|
call assert_equal([1, 2, 3, 4, 5], [1, 2, 3]->extend([4, 5]))
|
||||||
|
call assert_equal([1, 3], [1, 2, 3]->filter('v:val != 2'))
|
||||||
|
call assert_equal(2, l->get(1))
|
||||||
|
call assert_equal(1, l->index(2))
|
||||||
|
call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0))
|
||||||
|
call assert_fails('let x = l->items()', 'E715:')
|
||||||
|
call assert_equal('1 2 3', l->join())
|
||||||
|
call assert_fails('let x = l->keys()', 'E715:')
|
||||||
|
call assert_equal(3, l->len())
|
||||||
|
call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1'))
|
||||||
|
call assert_equal(3, l->max())
|
||||||
|
call assert_equal(1, l->min())
|
||||||
|
call assert_equal(2, [1, 2, 3]->remove(1))
|
||||||
|
call assert_equal([1, 2, 3, 1, 2, 3], l->repeat(2))
|
||||||
|
call assert_equal([3, 2, 1], [1, 2, 3]->reverse())
|
||||||
|
call assert_equal([1, 2, 3, 4], [4, 2, 3, 1]->sort())
|
||||||
|
call assert_equal('[1, 2, 3]', l->string())
|
||||||
|
call assert_equal(v:t_list, l->type())
|
||||||
|
call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq())
|
||||||
|
call assert_fails('let x = l->values()', 'E715:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_dict()
|
||||||
|
let d = #{one: 1, two: 2, three: 3}
|
||||||
|
|
||||||
|
call assert_equal(d, d->copy())
|
||||||
|
call assert_equal(1, d->count(2))
|
||||||
|
call assert_false(d->empty())
|
||||||
|
call assert_true({}->empty())
|
||||||
|
call assert_equal(#{one: 1, two: 2, three: 3, four: 4}, d->extend(#{four: 4}))
|
||||||
|
call assert_equal(#{one: 1, two: 2, three: 3}, d->filter('v:val != 4'))
|
||||||
|
call assert_equal(2, d->get('two'))
|
||||||
|
" Nvim doesn't support Blobs yet; expect a different emsg
|
||||||
|
" call assert_fails("let x = d->index(2)", 'E897:')
|
||||||
|
" call assert_fails("let x = d->insert(0)", 'E899:')
|
||||||
|
call assert_fails("let x = d->index(2)", 'E714:')
|
||||||
|
call assert_fails("let x = d->insert(0)", 'E686:')
|
||||||
|
call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items())
|
||||||
|
call assert_fails("let x = d->join()", 'E714:')
|
||||||
|
call assert_equal(['one', 'two', 'three'], d->keys())
|
||||||
|
call assert_equal(3, d->len())
|
||||||
|
call assert_equal(#{one: 2, two: 3, three: 4}, d->map('v:val + 1'))
|
||||||
|
call assert_equal(#{one: 1, two: 2, three: 3}, d->map('v:val - 1'))
|
||||||
|
call assert_equal(3, d->max())
|
||||||
|
call assert_equal(1, d->min())
|
||||||
|
call assert_equal(2, d->remove("two"))
|
||||||
|
let d.two = 2
|
||||||
|
call assert_fails('let x = d->repeat(2)', 'E731:')
|
||||||
|
" Nvim doesn't support Blobs yet; expect a different emsg
|
||||||
|
" call assert_fails('let x = d->reverse()', 'E899:')
|
||||||
|
call assert_fails('let x = d->reverse()', 'E686:')
|
||||||
|
call assert_fails('let x = d->sort()', 'E686:')
|
||||||
|
call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string())
|
||||||
|
call assert_equal(v:t_dict, d->type())
|
||||||
|
call assert_fails('let x = d->uniq()', 'E686:')
|
||||||
|
call assert_equal([1, 2, 3], d->values())
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" vim: shiftwidth=2 sts=2 expandtab
|
Loading…
Reference in New Issue
Block a user