forked from aniani/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.
This commit is contained in:
parent
b2129068a5
commit
ac92e25a33
@ -1114,6 +1114,8 @@ in any order. E.g., these are all possible:
|
||||
expr9[expr1].name
|
||||
expr9.name[expr1]
|
||||
expr9(expr1, ...)[expr1].name
|
||||
expr9->(expr1, ...)[expr1]
|
||||
Evaluation is always from left to right.
|
||||
|
||||
|
||||
expr8[expr1] item of String or |List| *expr-[]* *E111*
|
||||
@ -1213,6 +1215,11 @@ expr8(expr1, ...) |Funcref| function call
|
||||
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*
|
||||
number
|
||||
@ -2877,6 +2884,8 @@ add({object}, {expr}) *add()*
|
||||
item. Use |extend()| to concatenate |Lists|.
|
||||
When {object} is a |Blob| then {expr} must be a number.
|
||||
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()*
|
||||
@ -3512,6 +3521,8 @@ copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
|
||||
changing an item changes the contents of both |Lists|.
|
||||
A |Dictionary| is copied in a similar way as a |List|.
|
||||
Also see |deepcopy()|.
|
||||
Can also be used as a |method|: >
|
||||
mylist->copy()
|
||||
|
||||
cos({expr}) *cos()*
|
||||
Return the cosine of {expr}, measured in radians, as a |Float|.
|
||||
@ -3548,6 +3559,8 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()*
|
||||
When {comp} is a string then the number of not overlapping
|
||||
occurrences of {expr} is returned. Zero is returned when
|
||||
{expr} is an empty string.
|
||||
Can also be used as a |method|: >
|
||||
mylist->count(val)
|
||||
|
||||
*cscope_connection()*
|
||||
cscope_connection([{num} , {dbpath} [, {prepend}]])
|
||||
@ -3731,6 +3744,8 @@ empty({expr}) *empty()*
|
||||
|
||||
For a long |List| this is much faster than comparing the
|
||||
length with zero.
|
||||
Can also be used as a |method|: >
|
||||
mylist->empty()
|
||||
|
||||
escape({string}, {chars}) *escape()*
|
||||
Escape the characters in {chars} that occur in {string} with a
|
||||
@ -4041,6 +4056,9 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()*
|
||||
fails.
|
||||
Returns {expr1}.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->extend(otherlist)
|
||||
|
||||
|
||||
feedkeys({string} [, {mode}]) *feedkeys()*
|
||||
Characters in {string} are queued for processing as if they
|
||||
@ -4154,6 +4172,8 @@ filter({expr1}, {expr2}) *filter()*
|
||||
Funcref errors inside a function are ignored, unless it was
|
||||
defined with the "abort" flag.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->filter(expr2)
|
||||
|
||||
finddir({name} [, {path} [, {count}]]) *finddir()*
|
||||
Find directory {name} in {path}. Supports both downwards and
|
||||
@ -4416,6 +4436,8 @@ get({list}, {idx} [, {default}]) *get()*
|
||||
Get item {idx} from |List| {list}. When this item is not
|
||||
available return {default}. Return zero when {default} is
|
||||
omitted.
|
||||
Can also be used as a |method|: >
|
||||
mylist->get(idx)
|
||||
get({blob}, {idx} [, {default}])
|
||||
Get byte {idx} from |Blob| {blob}. When this byte is not
|
||||
available return {default}. Return -1 when {default} is
|
||||
@ -5689,6 +5711,9 @@ insert({object}, {item} [, {idx}]) *insert()*
|
||||
Note that when {item} is a |List| it is inserted as a single
|
||||
item. Use |extend()| to concatenate |Lists|.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->insert(item)
|
||||
|
||||
invert({expr}) *invert()*
|
||||
Bitwise invert. The argument is converted to a number. A
|
||||
List, Dict or Float argument causes an error. Example: >
|
||||
@ -5740,6 +5765,8 @@ items({dict}) *items()*
|
||||
echo key . ': ' . value
|
||||
endfor
|
||||
|
||||
< Can also be used as a |method|: >
|
||||
mydict->items()
|
||||
|
||||
job_ functions are documented here: |job-functions-details|
|
||||
|
||||
@ -5755,6 +5782,9 @@ join({list} [, {sep}]) *join()*
|
||||
converted into a string like with |string()|.
|
||||
The opposite function is |split()|.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->join()
|
||||
|
||||
js_decode({string}) *js_decode()*
|
||||
This is similar to |json_decode()| with these differences:
|
||||
- Object key names do not have to be in quotes.
|
||||
@ -5840,7 +5870,10 @@ keys({dict}) *keys()*
|
||||
Return a |List| with all the keys of {dict}. The |List| is in
|
||||
arbitrary order. Also see |items()| and |values()|.
|
||||
|
||||
*len()* *E701*
|
||||
Can also be used as a |method|: >
|
||||
mydict->keys()
|
||||
|
||||
< *len()* *E701*
|
||||
len({expr}) The result is a Number, which is the length of the argument.
|
||||
When {expr} is a String or a Number the length in bytes is
|
||||
used, as with |strlen()|.
|
||||
@ -5851,7 +5884,10 @@ len({expr}) The result is a Number, which is the length of the argument.
|
||||
|Dictionary| is returned.
|
||||
Otherwise an error is given.
|
||||
|
||||
*libcall()* *E364* *E368*
|
||||
Can also be used as a |method|: >
|
||||
mylist->len()
|
||||
|
||||
< *libcall()* *E364* *E368*
|
||||
libcall({libname}, {funcname}, {argument})
|
||||
Call function {funcname} in the run-time library {libname}
|
||||
with single argument {argument}.
|
||||
@ -6136,6 +6172,8 @@ map({expr1}, {expr2}) *map()*
|
||||
Funcref errors inside a function are ignored, unless it was
|
||||
defined with the "abort" flag.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->map(expr2)
|
||||
|
||||
maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
|
||||
When {dict} is omitted or zero: Return the rhs of mapping
|
||||
@ -6462,7 +6500,10 @@ max({expr}) Return the maximum value of all items in {expr}.
|
||||
items in {expr} cannot be used as a Number this results in
|
||||
an error. An empty |List| or |Dictionary| results in zero.
|
||||
|
||||
*min()*
|
||||
Can also be used as a |method|: >
|
||||
mylist->max()
|
||||
|
||||
< *min()*
|
||||
min({expr}) Return the minimum value of all items in {expr}.
|
||||
{expr} can be a list or a dictionary. For a dictionary,
|
||||
it returns the minimum of all values in the dictionary.
|
||||
@ -6470,7 +6511,10 @@ min({expr}) Return the minimum value of all items in {expr}.
|
||||
items in {expr} cannot be used as a Number this results in
|
||||
an error. An empty |List| or |Dictionary| results in zero.
|
||||
|
||||
*mkdir()* *E739*
|
||||
Can also be used as a |method|: >
|
||||
mylist->min()
|
||||
|
||||
< *mkdir()* *E739*
|
||||
mkdir({name} [, {path} [, {prot}]])
|
||||
Create directory {name}.
|
||||
|
||||
@ -7154,6 +7198,9 @@ remove({list}, {idx} [, {end}]) *remove()*
|
||||
<
|
||||
Use |delete()| to remove a file.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->remove(idx)
|
||||
|
||||
remove({blob}, {idx} [, {end}])
|
||||
Without {end}: Remove the byte at {idx} from |Blob| {blob} and
|
||||
return the byte.
|
||||
@ -7189,6 +7236,8 @@ repeat({expr}, {count}) *repeat()*
|
||||
:let longlist = repeat(['a', 'b'], 3)
|
||||
< Results in ['a', 'b', 'a', 'b', 'a', 'b'].
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->repeat(count)
|
||||
|
||||
resolve({filename}) *resolve()* *E655*
|
||||
On MS-Windows, when {filename} is a shortcut (a .lnk file),
|
||||
@ -7206,13 +7255,15 @@ resolve({filename}) *resolve()* *E655*
|
||||
current directory (provided the result is still a relative
|
||||
path name) and also keeps a trailing path separator.
|
||||
|
||||
*reverse()*
|
||||
reverse({object})
|
||||
|
||||
reverse({object}) *reverse()*
|
||||
Reverse the order of items in {object} in-place.
|
||||
{object} can be a |List| or a |Blob|.
|
||||
Returns {object}.
|
||||
If you want an object to remain unmodified make a copy first: >
|
||||
:let revlist = reverse(copy(mylist))
|
||||
< Can also be used as a |method|: >
|
||||
mylist->reverse()
|
||||
|
||||
round({expr}) *round()*
|
||||
Round off {expr} to the nearest integral value and return it
|
||||
@ -8070,7 +8121,10 @@ sort({list} [, {func} [, {dict}]]) *sort()* *E702*
|
||||
on numbers, text strings will sort next to each other, in the
|
||||
same order as they were originally.
|
||||
|
||||
Also see |uniq()|.
|
||||
Can also be used as a |method|: >
|
||||
mylist->sort()
|
||||
|
||||
< Also see |uniq()|.
|
||||
|
||||
Example: >
|
||||
func MyCompare(i1, i2)
|
||||
@ -8378,7 +8432,10 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number,
|
||||
replaced by "[...]" or "{...}". Using eval() on the result
|
||||
will then fail.
|
||||
|
||||
Also see |strtrans()|.
|
||||
Can also be used as a |method|: >
|
||||
mylist->string()
|
||||
|
||||
< Also see |strtrans()|.
|
||||
|
||||
*strlen()*
|
||||
strlen({expr}) The result is a Number, which is the length of the String
|
||||
@ -9000,6 +9057,9 @@ type({expr}) The result is a Number representing the type of {expr}.
|
||||
< To check if the v:t_ variables exist use this: >
|
||||
:if exists('v:t_number')
|
||||
|
||||
< Can also be used as a |method|: >
|
||||
mylist->type()
|
||||
|
||||
undofile({name}) *undofile()*
|
||||
Return the name of the undo file that would be used for a file
|
||||
with name {name} when writing. This uses the 'undodir'
|
||||
@ -9064,10 +9124,15 @@ uniq({list} [, {func} [, {dict}]]) *uniq()* *E882*
|
||||
< The default compare function uses the string representation of
|
||||
each item. For the use of {func} and {dict} see |sort()|.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mylist->uniq()
|
||||
|
||||
values({dict}) *values()*
|
||||
Return a |List| with all the values of {dict}. The |List| is
|
||||
in arbitrary order. Also see |items()| and |keys()|.
|
||||
|
||||
Can also be used as a |method|: >
|
||||
mydict->values()
|
||||
|
||||
virtcol({expr}) *virtcol()*
|
||||
The result is a Number, which is the screen column of the file
|
||||
|
98
src/eval.c
98
src/eval.c
@ -4412,6 +4412,7 @@ eval6(
|
||||
* + in front unary plus (ignored)
|
||||
* trailing [] subscript in String or List
|
||||
* trailing .name entry in Dictionary
|
||||
* trailing ->name() method call
|
||||
*
|
||||
* "arg" must point to the first non-white of the expression.
|
||||
* "arg" is advanced to the next non-white after the recognized expression.
|
||||
@ -4690,13 +4691,12 @@ eval7(
|
||||
funcexe_T funcexe;
|
||||
|
||||
// Invoke the function.
|
||||
funcexe.argv_func = NULL;
|
||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.doesrange = &len;
|
||||
funcexe.evaluate = evaluate;
|
||||
funcexe.partial = partial;
|
||||
funcexe.selfdict = NULL;
|
||||
ret = get_func_tv(s, len, rettv, arg, &funcexe);
|
||||
}
|
||||
vim_free(s);
|
||||
@ -4801,6 +4801,70 @@ eval7(
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate "->method()".
|
||||
* "*arg" points to the '-'.
|
||||
* Returns FAIL or OK. "*arg" is advanced to after the ')'.
|
||||
*/
|
||||
static int
|
||||
eval_method(
|
||||
char_u **arg,
|
||||
typval_T *rettv,
|
||||
int evaluate,
|
||||
int verbose) /* give error messages */
|
||||
{
|
||||
char_u *name;
|
||||
long len;
|
||||
funcexe_T funcexe;
|
||||
int ret = OK;
|
||||
typval_T base = *rettv;
|
||||
|
||||
// Skip over the ->.
|
||||
*arg += 2;
|
||||
|
||||
// Locate the method name.
|
||||
name = *arg;
|
||||
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)
|
||||
semsg(_(e_missingparen), name);
|
||||
return FAIL;
|
||||
}
|
||||
*arg += len;
|
||||
|
||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||
funcexe.evaluate = evaluate;
|
||||
funcexe.basetv = &base;
|
||||
rettv->v_type = VAR_UNKNOWN;
|
||||
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)
|
||||
clear_tv(&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)
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key".
|
||||
* "*arg" points to the '[' or '.'.
|
||||
@ -7359,9 +7423,13 @@ check_vars(char_u *name, int len)
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle expr[expr], expr[expr:expr] subscript and .name lookup.
|
||||
* Also handle function call with Funcref variable: func(expr)
|
||||
* Can all be combined: dict.func(expr)[idx]['func'](expr)
|
||||
* Handle:
|
||||
* - expr[expr], expr[expr:expr] subscript
|
||||
* - ".name" lookup
|
||||
* - function call with Funcref variable: func(expr)
|
||||
* - method call: var->method()
|
||||
*
|
||||
* Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len()
|
||||
*/
|
||||
int
|
||||
handle_subscript(
|
||||
@ -7378,14 +7446,15 @@ handle_subscript(
|
||||
// "." is ".name" lookup when we found a dict or when evaluating and
|
||||
// scriptversion is at least 2, where string concatenation is "..".
|
||||
while (ret == OK
|
||||
&& (**arg == '['
|
||||
|| (**arg == '.' && (rettv->v_type == VAR_DICT
|
||||
&& (((**arg == '['
|
||||
|| (**arg == '.' && (rettv->v_type == VAR_DICT
|
||||
|| (!evaluate
|
||||
&& (*arg)[1] != '.'
|
||||
&& current_sctx.sc_version >= 2)))
|
||||
|| (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
|
||||
|| (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
|
||||
|| rettv->v_type == VAR_PARTIAL)))
|
||||
&& !VIM_ISWHITE(*(*arg - 1)))
|
||||
&& !VIM_ISWHITE(*(*arg - 1)))
|
||||
|| (**arg == '-' && (*arg)[1] == '>')))
|
||||
{
|
||||
if (**arg == '(')
|
||||
{
|
||||
@ -7410,10 +7479,9 @@ handle_subscript(
|
||||
else
|
||||
s = (char_u *)"";
|
||||
|
||||
funcexe.argv_func = NULL;
|
||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.doesrange = NULL;
|
||||
funcexe.evaluate = evaluate;
|
||||
funcexe.partial = pt;
|
||||
funcexe.selfdict = selfdict;
|
||||
@ -7436,6 +7504,14 @@ handle_subscript(
|
||||
dict_unref(selfdict);
|
||||
selfdict = NULL;
|
||||
}
|
||||
else if (**arg == '-')
|
||||
{
|
||||
if (eval_method(arg, rettv, evaluate, verbose) == FAIL)
|
||||
{
|
||||
clear_tv(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
}
|
||||
else /* **arg == '[' || **arg == '.' */
|
||||
{
|
||||
dict_unref(selfdict);
|
||||
|
107
src/evalfunc.c
107
src/evalfunc.c
@ -412,14 +412,16 @@ static void f_xor(typval_T *argvars, typval_T *rettv);
|
||||
* Array with names and number of arguments of all internal functions
|
||||
* MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH!
|
||||
*/
|
||||
static struct fst
|
||||
typedef struct
|
||||
{
|
||||
char *f_name; /* function name */
|
||||
char f_min_argc; /* minimal number of arguments */
|
||||
char f_max_argc; /* maximal number of arguments */
|
||||
void (*f_func)(typval_T *args, typval_T *rvar);
|
||||
/* implementation of function */
|
||||
} functions[] =
|
||||
} funcentry_T;
|
||||
|
||||
static funcentry_T global_functions[] =
|
||||
{
|
||||
#ifdef FEAT_FLOAT
|
||||
{"abs", 1, 1, f_abs},
|
||||
@ -987,6 +989,37 @@ static struct fst
|
||||
{"xor", 2, 2, f_xor},
|
||||
};
|
||||
|
||||
/*
|
||||
* Methods that call the internal function with the base as the first argument.
|
||||
*/
|
||||
static funcentry_T base_methods[] =
|
||||
{
|
||||
{"add", 1, 1, f_add},
|
||||
{"copy", 0, 0, f_copy},
|
||||
{"count", 1, 3, f_count},
|
||||
{"empty", 0, 0, f_empty},
|
||||
{"extend", 1, 2, f_extend},
|
||||
{"filter", 1, 1, f_filter},
|
||||
{"get", 1, 2, f_get},
|
||||
{"index", 1, 3, f_index},
|
||||
{"insert", 1, 2, f_insert},
|
||||
{"items", 0, 0, f_items},
|
||||
{"join", 0, 1, f_join},
|
||||
{"keys", 0, 0, f_keys},
|
||||
{"len", 0, 0, f_len},
|
||||
{"map", 1, 1, f_map},
|
||||
{"max", 0, 0, f_max},
|
||||
{"min", 0, 0, f_min},
|
||||
{"remove", 1, 2, f_remove},
|
||||
{"repeat", 1, 1, f_repeat},
|
||||
{"reverse", 0, 0, f_reverse},
|
||||
{"sort", 0, 2, f_sort},
|
||||
{"string", 0, 0, f_string},
|
||||
{"type", 0, 0, f_type},
|
||||
{"uniq", 0, 2, f_uniq},
|
||||
{"values", 0, 0, f_values},
|
||||
};
|
||||
|
||||
#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
|
||||
|
||||
/*
|
||||
@ -1007,11 +1040,11 @@ get_function_name(expand_T *xp, int idx)
|
||||
if (name != NULL)
|
||||
return name;
|
||||
}
|
||||
if (++intidx < (int)(sizeof(functions) / sizeof(struct fst)))
|
||||
if (++intidx < (int)(sizeof(global_functions) / sizeof(funcentry_T)))
|
||||
{
|
||||
STRCPY(IObuff, functions[intidx].f_name);
|
||||
STRCPY(IObuff, global_functions[intidx].f_name);
|
||||
STRCAT(IObuff, "(");
|
||||
if (functions[intidx].f_max_argc == 0)
|
||||
if (global_functions[intidx].f_max_argc == 0)
|
||||
STRCAT(IObuff, ")");
|
||||
return IObuff;
|
||||
}
|
||||
@ -1043,21 +1076,25 @@ get_expr_name(expand_T *xp, int idx)
|
||||
#endif /* FEAT_CMDL_COMPL */
|
||||
|
||||
/*
|
||||
* Find internal function in table above.
|
||||
* Find internal function in table "functions".
|
||||
* Return index, or -1 if not found
|
||||
*/
|
||||
int
|
||||
static int
|
||||
find_internal_func(
|
||||
char_u *name) /* name of the function */
|
||||
char_u *name, // name of the function
|
||||
funcentry_T *functions) // functions table to use
|
||||
{
|
||||
int first = 0;
|
||||
int last = (int)(sizeof(functions) / sizeof(struct fst)) - 1;
|
||||
int last;
|
||||
int cmp;
|
||||
int x;
|
||||
|
||||
/*
|
||||
* Find the function name in the table. Binary search.
|
||||
*/
|
||||
if (functions == global_functions)
|
||||
last = (int)(sizeof(global_functions) / sizeof(funcentry_T)) - 1;
|
||||
else
|
||||
last = (int)(sizeof(base_methods) / sizeof(funcentry_T)) - 1;
|
||||
|
||||
// Find the function name in the table. Binary search.
|
||||
while (first <= last)
|
||||
{
|
||||
x = first + ((unsigned)(last - first) >> 1);
|
||||
@ -1072,6 +1109,12 @@ find_internal_func(
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
has_internal_func(char_u *name)
|
||||
{
|
||||
return find_internal_func(name, global_functions) >= 0;
|
||||
}
|
||||
|
||||
int
|
||||
call_internal_func(
|
||||
char_u *name,
|
||||
@ -1081,15 +1124,47 @@ call_internal_func(
|
||||
{
|
||||
int i;
|
||||
|
||||
i = find_internal_func(name);
|
||||
i = find_internal_func(name, global_functions);
|
||||
if (i < 0)
|
||||
return ERROR_UNKNOWN;
|
||||
if (argcount < functions[i].f_min_argc)
|
||||
if (argcount < global_functions[i].f_min_argc)
|
||||
return ERROR_TOOFEW;
|
||||
if (argcount > functions[i].f_max_argc)
|
||||
if (argcount > global_functions[i].f_max_argc)
|
||||
return ERROR_TOOMANY;
|
||||
argvars[argcount].v_type = VAR_UNKNOWN;
|
||||
functions[i].f_func(argvars, rettv);
|
||||
global_functions[i].f_func(argvars, rettv);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke a method for base->method().
|
||||
*/
|
||||
int
|
||||
call_internal_method(
|
||||
char_u *name,
|
||||
int argcount,
|
||||
typval_T *argvars,
|
||||
typval_T *rettv,
|
||||
typval_T *basetv)
|
||||
{
|
||||
int i;
|
||||
int fi;
|
||||
typval_T argv[MAX_FUNC_ARGS + 1];
|
||||
|
||||
fi = find_internal_func(name, base_methods);
|
||||
if (fi < 0)
|
||||
return ERROR_UNKNOWN;
|
||||
if (argcount < base_methods[fi].f_min_argc)
|
||||
return ERROR_TOOFEW;
|
||||
if (argcount > base_methods[fi].f_max_argc)
|
||||
return ERROR_TOOMANY;
|
||||
|
||||
argv[0] = *basetv;
|
||||
for (i = 0; i < argcount; ++i)
|
||||
argv[i + 1] = argvars[i];
|
||||
argv[argcount + 1].v_type = VAR_UNKNOWN;
|
||||
|
||||
base_methods[fi].f_func(argv, rettv);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
|
@ -1592,6 +1592,7 @@ EXTERN char e_write[] INIT(= N_("E80: Error while writing"));
|
||||
EXTERN char e_zerocount[] INIT(= N_("E939: Positive count required"));
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN char e_usingsid[] INIT(= N_("E81: Using <SID> not in a script context"));
|
||||
EXTERN char e_missingparen[] INIT(= N_("E107: Missing parentheses: %s"));
|
||||
#endif
|
||||
#ifdef FEAT_CLIENTSERVER
|
||||
EXTERN char e_invexprmsg[] INIT(= N_("E449: Invalid expression received"));
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* evalfunc.c */
|
||||
char_u *get_function_name(expand_T *xp, int idx);
|
||||
char_u *get_expr_name(expand_T *xp, int idx);
|
||||
int find_internal_func(char_u *name);
|
||||
int has_internal_func(char_u *name);
|
||||
int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
|
||||
int call_internal_method(char_u *name, int argcount, typval_T *argvars, typval_T *rettv, typval_T *basetv);
|
||||
linenr_T tv_get_lnum(typval_T *argvars);
|
||||
buf_T *buflist_find_by_name(char_u *name, int curtab_only);
|
||||
buf_T *tv_get_buf(typval_T *tv, int curtab_only);
|
||||
|
@ -1619,6 +1619,7 @@ typedef struct {
|
||||
int evaluate; // actually evaluate expressions
|
||||
partial_T *partial; // for extra arguments
|
||||
dict_T *selfdict; // Dictionary for "self"
|
||||
typval_T *basetv; // base for base->method()
|
||||
} funcexe_T;
|
||||
|
||||
struct partial_S
|
||||
|
@ -180,6 +180,7 @@ NEW_TESTS = \
|
||||
test_matchadd_conceal \
|
||||
test_matchadd_conceal_utf8 \
|
||||
test_memory_usage \
|
||||
test_method \
|
||||
test_menu \
|
||||
test_messages \
|
||||
test_mksession \
|
||||
@ -373,6 +374,7 @@ NEW_TESTS_RES = \
|
||||
test_marks.res \
|
||||
test_matchadd_conceal.res \
|
||||
test_memory_usage.res \
|
||||
test_method.res \
|
||||
test_mksession.res \
|
||||
test_nested_function.res \
|
||||
test_netbeans.res \
|
||||
|
61
src/testdir/test_method.vim
Normal file
61
src/testdir/test_method.vim
Normal file
@ -0,0 +1,61 @@
|
||||
" 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'))
|
||||
call assert_fails("let x = d->index(2)", 'E897:')
|
||||
call assert_fails("let x = d->insert(0)", 'E899:')
|
||||
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:')
|
||||
call assert_fails('let x = d->reverse()', 'E899:')
|
||||
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
|
@ -1431,10 +1431,9 @@ func_call(
|
||||
{
|
||||
funcexe_T funcexe;
|
||||
|
||||
funcexe.argv_func = NULL;
|
||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||
funcexe.firstline = curwin->w_cursor.lnum;
|
||||
funcexe.lastline = curwin->w_cursor.lnum;
|
||||
funcexe.doesrange = NULL;
|
||||
funcexe.evaluate = TRUE;
|
||||
funcexe.partial = partial;
|
||||
funcexe.selfdict = selfdict;
|
||||
@ -1555,7 +1554,10 @@ call_func(
|
||||
/*
|
||||
* User defined function.
|
||||
*/
|
||||
if (partial != NULL && partial->pt_func != NULL)
|
||||
if (funcexe->basetv != NULL)
|
||||
// TODO: support User function: base->Method()
|
||||
fp = NULL;
|
||||
else if (partial != NULL && partial->pt_func != NULL)
|
||||
fp = partial->pt_func;
|
||||
else
|
||||
fp = find_func(rfname);
|
||||
@ -1625,6 +1627,14 @@ call_func(
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
/*
|
||||
@ -2715,7 +2725,7 @@ eval_fname_script(char_u *p)
|
||||
translated_function_exists(char_u *name)
|
||||
{
|
||||
if (builtin_function(name, -1))
|
||||
return find_internal_func(name) >= 0;
|
||||
return has_internal_func(name);
|
||||
return find_func(name) != NULL;
|
||||
}
|
||||
|
||||
@ -3084,7 +3094,7 @@ ex_call(exarg_T *eap)
|
||||
|
||||
if (*startarg != '(')
|
||||
{
|
||||
semsg(_("E107: Missing parentheses: %s"), eap->arg);
|
||||
semsg(_(e_missingparen), eap->arg);
|
||||
goto end;
|
||||
}
|
||||
|
||||
@ -3120,7 +3130,7 @@ ex_call(exarg_T *eap)
|
||||
}
|
||||
arg = startarg;
|
||||
|
||||
funcexe.argv_func = NULL;
|
||||
vim_memset(&funcexe, 0, sizeof(funcexe));
|
||||
funcexe.firstline = eap->line1;
|
||||
funcexe.lastline = eap->line2;
|
||||
funcexe.doesrange = &doesrange;
|
||||
|
@ -773,6 +773,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1803,
|
||||
/**/
|
||||
1802,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user