diff options
author | Sean Dewar <seandewar@users.noreply.github.com> | 2021-08-05 19:46:42 +0100 |
---|---|---|
committer | Sean Dewar <seandewar@users.noreply.github.com> | 2021-08-12 22:35:19 +0100 |
commit | e6be6c307a832d661d2a6269ad2d322e4bf5e9cc (patch) | |
tree | b2b4e54254af2af04763d4f650c43e481b36cd8b /src/nvim/eval | |
parent | 4042ae5a2bc4bbca608ebb196a3d54a78d6c100c (diff) | |
download | rneovim-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')
-rw-r--r-- | src/nvim/eval/funcs.c | 44 | ||||
-rw-r--r-- | src/nvim/eval/funcs.h | 1 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 24 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.h | 2 |
4 files changed, 57 insertions, 14 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 363640adc5..787d5aaf78 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -175,6 +175,50 @@ const VimLFuncDef *find_internal_func(const char *const name) 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. */ diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index a343290734..3ad0b8282a 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -14,6 +14,7 @@ typedef struct fst { char *name; ///< Name of the function. uint8_t min_argc; ///< Minimal 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. FunPtr data; ///< Userdata for function implementation. } VimLFuncDef; diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 516f7a1d53..ec43987b07 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1514,7 +1514,10 @@ call_func( } } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // 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); } @@ -1560,20 +1563,13 @@ call_func( 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 { // Find the function name in the table, call its implementation. - const VimLFuncDef *const fdef = find_internal_func((const char *)fname); - 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; - } - } + error = call_internal_func(fname, argcount, argvars, rettv); } /* * 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. if (*startarg != '(') { - EMSG2(_("E107: Missing parentheses: %s"), eap->arg); + EMSG2(_(e_missingparen), eap->arg); goto end; } diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 9af35e0411..513473449a 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -44,6 +44,7 @@ typedef struct { bool 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; #define FUNCEXE_INIT (funcexe_T) { \ @@ -54,6 +55,7 @@ typedef struct { .evaluate = false, \ .partial = NULL, \ .selfdict = NULL, \ + .basetv = NULL, \ } #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] |