aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2021-08-05 19:46:42 +0100
committerSean Dewar <seandewar@users.noreply.github.com>2021-08-12 22:35:19 +0100
commite6be6c307a832d661d2a6269ad2d322e4bf5e9cc (patch)
treeb2b4e54254af2af04763d4f650c43e481b36cd8b /src/nvim/eval.c
parent4042ae5a2bc4bbca608ebb196a3d54a78d6c100c (diff)
downloadrneovim-e6be6c307a832d661d2a6269ad2d322e4bf5e9cc.tar.gz
rneovim-e6be6c307a832d661d2a6269ad2d322e4bf5e9cc.tar.bz2
rneovim-e6be6c307a832d661d2a6269ad2d322e4bf5e9cc.zip
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. https://github.com/vim/vim/commit/ac92e25a33c37ec5becbfffeccda136c73b761ac - 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.
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c82
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) {