diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 82 |
1 files changed, 74 insertions, 8 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5a6dbf7ff4..39e8686580 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3809,6 +3809,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string) // + in front unary plus (ignored) // trailing [] subscript in String or List // trailing .name entry in Dictionary +// trailing ->name() method call // // "arg" must point to the first non-white of the expression. // "arg" is advanced to the next non-white after the recognized expression. @@ -4080,6 +4081,63 @@ static int eval7( 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 /* @@ -8420,9 +8478,13 @@ int check_luafunc_name(const char *str, bool paren) } } -/// Handle expr[expr], expr[expr:expr] subscript and .name lookup. -/// Also handle function call with Funcref variable: func(expr) -/// Can all be combined: dict.func(expr)[idx]['func'](expr) +/// Handle: +/// - expr[expr], expr[expr:expr] subscript +/// - ".name" lookup +/// - function call with Funcref variable: func(expr) +/// - method call: var->method() +/// +/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len() int handle_subscript( const char **const arg, @@ -8456,12 +8518,11 @@ handle_subscript( } } - while (ret == OK - && (**arg == '[' - || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) - && !ascii_iswhite(*(*arg - 1))) { + && (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) + && !ascii_iswhite(*(*arg - 1))) + || (**arg == '-' && (*arg)[1] == '>'))) { if (**arg == '(') { partial_T *pt = NULL; // need to copy the funcref so that we can clear rettv @@ -8507,6 +8568,11 @@ handle_subscript( } tv_dict_unref(selfdict); selfdict = NULL; + } else if (**arg == '-') { + if (eval_method((char_u **)arg, rettv, evaluate, verbose) == FAIL) { + tv_clear(rettv); + ret = FAIL; + } } else { // **arg == '[' || **arg == '.' tv_dict_unref(selfdict); if (rettv->v_type == VAR_DICT) { |