diff options
author | Sean Dewar <seandewar@users.noreply.github.com> | 2021-08-11 13:47:33 +0100 |
---|---|---|
committer | Sean Dewar <seandewar@users.noreply.github.com> | 2021-08-13 01:11:36 +0100 |
commit | b2994e35c9357a8144beaf27e1a8ea4dd133f5d4 (patch) | |
tree | 8c8f5b17fc75ae51c55aae8d247462e49426678c /src | |
parent | da9005af792f7a7eaae98ee9f6499af9a97fd095 (diff) | |
download | rneovim-b2994e35c9357a8144beaf27e1a8ea4dd133f5d4.tar.gz rneovim-b2994e35c9357a8144beaf27e1a8ea4dd133f5d4.tar.bz2 rneovim-b2994e35c9357a8144beaf27e1a8ea4dd133f5d4.zip |
feat(v:lua): support calling v:lua as a method
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 50 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 27 |
2 files changed, 58 insertions, 19 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a6becb4765..472b43a387 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4212,7 +4212,7 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, return ret; } -/// Evaluate "->method()". +/// Evaluate "->method()" or "->v:lua.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, @@ -4225,19 +4225,30 @@ static int eval_method(char_u **const arg, typval_T *const rettv, rettv->v_type = VAR_UNKNOWN; // Locate the method name. + int len; char_u *name = *arg; - char_u *alias; - - const int len - = get_name_len((const char **)arg, (char **)&alias, evaluate, true); - if (alias != NULL) { - name = alias; + char_u *lua_funcname = NULL; + if (STRNCMP(name, "v:lua.", 6) == 0) { + lua_funcname = name + 6; + *arg = (char_u *)skip_luafunc_name((const char *)lua_funcname); + *arg = skipwhite(*arg); // to detect trailing whitespace later + len = *arg - lua_funcname; + } else { + char_u *alias; + len = get_name_len((const char **)arg, (char **)&alias, evaluate, true); + if (alias != NULL) { + name = alias; + } } int ret; if (len <= 0) { if (verbose) { - EMSG(_("E260: Missing name after ->")); + if (lua_funcname == NULL) { + EMSG(_("E260: Missing name after ->")); + } else { + EMSG2(_(e_invexpr2), name); + } } ret = FAIL; } else { @@ -4251,6 +4262,13 @@ static int eval_method(char_u **const arg, typval_T *const rettv, EMSG(_(e_nowhitespace)); } ret = FAIL; + } else if (lua_funcname != NULL) { + if (evaluate) { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = vvlua_partial; + rettv->vval.v_partial->pt_refcount++; + } + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname); } else { ret = eval_func(arg, name, len, rettv, evaluate, &base); } @@ -8591,13 +8609,23 @@ static bool tv_is_luafunc(typval_T *tv) return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial); } -/// check the function name after "v:lua." -int check_luafunc_name(const char *str, bool paren) +/// Skips one character past the end of the name of a v:lua function. +/// @param p Pointer to the char AFTER the "v:lua." prefix. +/// @return Pointer to the char one past the end of the function's name. +const char *skip_luafunc_name(const char *p) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - const char *p = str; while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') { p++; } + return p; +} + +/// check the function name after "v:lua." +int check_luafunc_name(const char *const str, const bool paren) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const p = skip_luafunc_name(str); if (*p != (paren ? '(' : NUL)) { return 0; } else { diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 5ffd578891..4184e4d922 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1423,6 +1423,23 @@ static void user_func_error(int error, const char_u *name) } } +/// Used by call_func to add a method base (if any) to a function argument list +/// as the first argument. @see call_func +static void argv_add_base(typval_T *const basetv, typval_T **const argvars, + int *const argcount, typval_T *const new_argvars, + int *const argv_base) + FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) +{ + if (basetv != NULL) { + // Method call: base->Method() + memmove(&new_argvars[1], *argvars, sizeof(typval_T) * (*argcount)); + new_argvars[0] = *basetv; + (*argcount)++; + *argvars = new_argvars; + *argv_base = 1; + } +} + /// Call a function with its resolved parameters /// /// @return FAIL if function cannot be called, else OK (even if an error @@ -1515,6 +1532,7 @@ call_func( if (is_luafunc(partial)) { if (len > 0) { error = ERROR_NONE; + argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv); } else { // v:lua was called directly; show its name in the emsg @@ -1553,14 +1571,7 @@ call_func( fp->uf_args.ga_len); } - if (funcexe->basetv != NULL) { - // Method call: base->Method() - memmove(&argv[1], argvars, sizeof(typval_T) * argcount); - argv[0] = *funcexe->basetv; - argcount++; - argvars = argv; - argv_base = 1; - } + argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { *funcexe->doesrange = true; |