diff options
author | Sean Dewar <seandewar@users.noreply.github.com> | 2021-08-06 18:58:27 +0100 |
---|---|---|
committer | Sean Dewar <seandewar@users.noreply.github.com> | 2021-08-12 22:35:21 +0100 |
commit | d41b87e070037786bf2cc4486b1839169805ee21 (patch) | |
tree | 7a0a68bd5fea74a8a3604efe2cf281c5c679d113 /src | |
parent | 8d1ca37d1f413b08f5ff9ceb502fa35ffd6034c7 (diff) | |
download | rneovim-d41b87e070037786bf2cc4486b1839169805ee21.tar.gz rneovim-d41b87e070037786bf2cc4486b1839169805ee21.tar.bz2 rneovim-d41b87e070037786bf2cc4486b1839169805ee21.zip |
vim-patch:8.1.1820: using expr->FuncRef() does not work
Problem: Using expr->FuncRef() does not work.
Solution: Make FuncRef work as a method.
https://github.com/vim/vim/commit/761fdf01c6e307c448cec2684f8b315ba6d1f454
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 152 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 11 | ||||
-rw-r--r-- | src/nvim/testdir/test_method.vim | 21 |
3 files changed, 104 insertions, 80 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 021a9a75b8..4f934fdc70 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3146,6 +3146,65 @@ static int pattern_match(char_u *pat, char_u *text, bool ic) return matches; } +/// Handle a name followed by "(". Both for just "name(arg)" and for +/// "expr->name(arg)". +// +/// @param arg Points to "(", will be advanced +/// @param basetv "expr" for "expr->name(arg)" +// +/// @return OK or FAIL. +static int eval_func(char_u **const arg, char_u *const name, const int name_len, + typval_T *const rettv, const bool evaluate, + typval_T *const basetv) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) +{ + char_u *s = name; + int len = name_len; + + if (!evaluate) { + check_vars((const char *)s, len); + } + + // If "s" is the name of a variable of type VAR_FUNC + // use its contents. + partial_T *partial; + s = deref_func_name((const char *)s, &len, &partial, !evaluate); + + // Need to make a copy, in case evaluating the arguments makes + // the name invalid. + s = xmemdupz(s, len); + + // Invoke the function. + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = partial; + funcexe.basetv = basetv; + int ret = get_func_tv(s, len, rettv, arg, &funcexe); + + xfree(s); + + // If evaluate is false rettv->v_type was not set in + // get_func_tv, but it's needed in handle_subscript() to parse + // what follows. So set it here. + if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { + rettv->vval.v_string = (char_u *)tv_empty_string; + rettv->v_type = VAR_FUNC; + } + + // Stop the expression evaluation when immediately + // aborting on error, or when an interrupt occurred or + // an exception was thrown but not caught. + if (evaluate && aborting()) { + if (ret == OK) { + tv_clear(rettv); + } + ret = FAIL; + } + return ret; +} + // TODO(ZyX-I): move to eval/expressions /* @@ -3977,47 +4036,7 @@ static int eval7( ret = FAIL; } else { if (**arg == '(') { // recursive! - partial_T *partial; - - if (!evaluate) { - check_vars((const char *)s, len); - } - - // If "s" is the name of a variable of type VAR_FUNC - // use its contents. - s = deref_func_name((const char *)s, &len, &partial, !evaluate); - - // Need to make a copy, in case evaluating the arguments makes - // the name invalid. - s = xmemdupz(s, len); - - // Invoke the function. - funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = evaluate; - funcexe.partial = partial; - ret = get_func_tv(s, len, rettv, arg, &funcexe); - - xfree(s); - - // If evaluate is false rettv->v_type was not set in - // get_func_tv, but it's needed in handle_subscript() to parse - // what follows. So set it here. - if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { - rettv->vval.v_string = (char_u *)tv_empty_string; - rettv->v_type = VAR_FUNC; - } - - // Stop the expression evaluation when immediately - // aborting on error, or when an interrupt occurred or - // an exception was thrown but not caught. - if (evaluate && aborting()) { - if (ret == OK) { - tv_clear(rettv); - } - ret = FAIL; - } + ret = eval_func(arg, s, len, rettv, evaluate, NULL); } else if (evaluate) { ret = get_var_tv((const char *)s, len, rettv, NULL, true, false); } else { @@ -4090,37 +4109,35 @@ static int eval_method(char_u **const arg, typval_T *const rettv, { // Skip over the ->. *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; // Locate the method name. - const char_u *const name = *arg; - size_t len; - for (len = 0; eval_isnamec(name[len]); 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; } - if (len == 0) { + int ret; + 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); + ret = FAIL; + } else { + if (**arg != '(') { + if (verbose) { + EMSG2(_(e_missingparen), name); + } + ret = FAIL; + } else { + ret = eval_func(arg, name, len, rettv, evaluate, &base); } - return FAIL; } - *arg += len; - - // TODO(seandewar): if "name" is a function reference, resolve it. - - 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). @@ -4128,15 +4145,6 @@ static int eval_method(char_u **const arg, typval_T *const rettv, 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; } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 3c0c0091f2..9ecde327de 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1450,6 +1450,7 @@ call_func( typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or // "funcexe->basetv" is not NULL int argv_clear = 0; + int argv_base = 0; partial_T *partial = funcexe->partial; // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) @@ -1550,8 +1551,8 @@ call_func( memmove(&argv[1], argvars, sizeof(typval_T) * argcount); argv[0] = *funcexe->basetv; argcount++; - } else { - memcpy(argv, argvars, sizeof(typval_T) * argcount); + argvars = argv; + argv_base = 1; } if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { @@ -1565,7 +1566,7 @@ call_func( error = ERROR_DICT; } else { // Call the user function. - call_user_func(fp, argcount, argv, rettv, funcexe->firstline, + call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; @@ -1602,9 +1603,11 @@ theend: user_func_error(error, (name != NULL) ? name : funcname); } + // clear the copies made from the partial while (argv_clear > 0) { - tv_clear(&argv[--argv_clear]); + tv_clear(&argv[--argv_clear + argv_base]); } + xfree(tofree); xfree(name); diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index ef87773924..a1fbe7af28 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -1,6 +1,6 @@ " Tests for ->method() -func Test_list() +func Test_list_method() let l = [1, 2, 3] call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) eval l->assert_equal(l) @@ -34,7 +34,7 @@ func Test_list() call assert_fails('eval l->values()', 'E715:') endfunc -func Test_dict() +func Test_dict_method() let d = #{one: 1, two: 2, three: 3} call assert_equal(d, d->copy()) @@ -71,7 +71,7 @@ func Test_dict() call assert_equal([1, 2, 3], d->values()) endfunc -func Test_string() +func Test_string_method() call assert_equal(['1', '2', '3'], '1 2 3'->split()) call assert_equal([1, 2, 3], '1 2 3'->split()->map({i, v -> str2nr(v)})) call assert_equal([65, 66, 67], 'ABC'->str2list()) @@ -81,7 +81,7 @@ func Test_string() call assert_equal('axc', 'abc'->substitute('b', 'x', '')) endfunc -func Test_append() +func Test_method_append() new eval ['one', 'two', 'three']->append(1) call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) @@ -95,4 +95,17 @@ func Test_append() exe 'bwipe! ' .. bnr endfunc +func Test_method_funcref() + func Concat(one, two, three) + return a:one .. a:two .. a:three + endfunc + let FuncRef = function('Concat') + eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail') + + let Partial = function('Concat', ['two']) + eval 'one'->Partial('three')->assert_equal('onetwothree') + + delfunc Concat +endfunc + " vim: shiftwidth=2 sts=2 expandtab |