From e0bdc9b89c336a15e77639eef1c9087808292f36 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 21 May 2021 22:15:31 +0100 Subject: fix(eval): cherry-pick emsg changes from v8.1.0736 v8.1.0736 made some changes for making some emsgs more specific. Includes the change for Test_lambda_fails() in test_lambda.vim. Adjust relevant functionaltests to expect the new emsgs. This patch has been fully ported in my Blob port PR, but it hasn't been merged yet, so just use what we need from it for now. Required for v8.1.1821. --- src/nvim/eval.c | 12 +++++++++--- src/nvim/testdir/test_lambda.vim | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 789936fdff..5634eafdf2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3162,6 +3162,8 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; char_u *p; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; p = skipwhite(arg); ret = eval1(&p, rettv, evaluate); @@ -3171,8 +3173,10 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) } // Report the invalid expression unless the expression evaluation has // been cancelled due to an aborting error, an interrupt, or an - // exception. - if (!aborting()) { + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { emsgf(_(e_invexpr2), arg); } ret = FAIL; @@ -9275,6 +9279,7 @@ void ex_echo(exarg_T *eap) bool atstart = true; bool need_clear = true; const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; if (eap->skip) ++emsg_skip; @@ -9289,7 +9294,8 @@ void ex_echo(exarg_T *eap) // Report the invalid expression unless the expression evaluation // has been cancelled due to an aborting error, an interrupt, or an // exception. - if (!aborting() && did_emsg == did_emsg_before) { + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { EMSG2(_(e_invexpr2), p); } need_clr_eos = false; diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index f026c8a55f..63bb4ae1ef 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -61,7 +61,7 @@ endfunction function Test_lambda_fails() call assert_equal(3, {a, b -> a + b}(1, 2)) - call assert_fails('echo {a, a -> a + a}(1, 2)', 'E15:') + call assert_fails('echo {a, a -> a + a}(1, 2)', 'E853:') call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:') endfunc -- cgit From 1918638c673e5c081bf063617ae69facd82c30d9 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 21:17:13 +0100 Subject: test(oldtest): cherry-pick needed test functions from v8.1.1336 Cherry-pick Test_call() and Test_cindent_func(). Required for v8.1.1911 and v8.1.1915. --- src/nvim/testdir/test_cindent.vim | 10 ++++++++++ src/nvim/testdir/test_functions.vim | 12 ++++++++++++ 2 files changed, 22 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index b6c2d1467e..c8b7d90fbb 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -118,6 +118,16 @@ b = something(); bw! endfunc +func Test_cindent_func() + new + setlocal cindent + call setline(1, ['int main(void)', '{', 'return 0;', '}']) + call assert_equal(cindent(0), -1) + call assert_equal(cindent(3), &sw) + call assert_equal(cindent(line('$')+1), -1) + bwipe! +endfunc + " this was going beyond the end of the line. func Test_cindent_case() new diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 48f97be96b..612b72cb62 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1473,6 +1473,18 @@ func Test_readdir() call delete('Xdir', 'rf') endfunc +func Test_call() + call assert_equal(3, call('len', [123])) + call assert_fails("call call('len', 123)", 'E714:') + call assert_equal(0, call('', [])) + + function Mylen() dict + return len(self.data) + endfunction + let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")} + call assert_fails("call call('Mylen', [], 0)", 'E715:') +endfunc + " Test for the eval() function func Test_eval() call assert_fails("call eval('5 a')", 'E488:') -- cgit From 60dd76c7e29e75dee641f93483eb6e5c2afb28e4 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 17:33:07 +0100 Subject: vim-patch:8.1.1638: running tests leaves some files behind Problem: Running tests leaves some files behind. Solution: Delete the files. (Ozaki Kiichi, closes vim/vim#4617) https://github.com/vim/vim/commit/3940ec6d41a07f9abbfba7d4db6b49d3d8b43a9a Required for v8.1.1888. N/A patches for version.c: vim-patch:8.1.1912: more functions can be used as methods Problem: More functions can be used as methods. Solution: Make channel and job functions usable as a method. vim/vim@570497a --- src/nvim/testdir/test_functions.vim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 612b72cb62..2493cc2e27 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1413,10 +1413,10 @@ func Test_bufadd_bufload() call assert_equal([''], getbufline(buf, 1, '$')) let curbuf = bufnr('') - call writefile(['some', 'text'], 'otherName') - let buf = bufadd('otherName') + call writefile(['some', 'text'], 'XotherName') + let buf = bufadd('XotherName') call assert_notequal(0, buf) - call assert_equal(1, bufexists('otherName')) + call assert_equal(1, bufexists('XotherName')) call assert_equal(0, getbufvar(buf, '&buflisted')) call assert_equal(0, bufloaded(buf)) call bufload(buf) @@ -1439,8 +1439,9 @@ func Test_bufadd_bufload() call assert_equal(0, bufexists(buf2)) bwipe someName - bwipe otherName + bwipe XotherName call assert_equal(0, bufexists('someName')) + call delete('XotherName') endfunc func Test_readdir() -- cgit From 4042ae5a2bc4bbca608ebb196a3d54a78d6c100c Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 21 May 2021 15:12:35 +0100 Subject: vim-patch:8.1.1800: function call functions have too many arguments Problem: Function call functions have too many arguments. Solution: Pass values in a funcexe_T struct. https://github.com/vim/vim/commit/c6538bcc1cdd1fb83732f22fdc69bd9bb66f968a Use FUNCEXE_INIT to initialize funcexe_T instances. call_callback() and other Vim listener related stuff is N/A. --- src/nvim/api/vim.c | 9 ++++-- src/nvim/eval.c | 70 ++++++++++++++++++++++-------------------- src/nvim/eval/funcs.c | 10 +++--- src/nvim/eval/userfunc.c | 80 +++++++++++++++++++++++------------------------- src/nvim/eval/userfunc.h | 23 ++++++++++++++ src/nvim/lua/executor.c | 10 +++--- src/nvim/normal.c | 12 ++++---- src/nvim/regexp.c | 14 ++++----- 8 files changed, 126 insertions(+), 102 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 400dbb126c..ec13bdd0e2 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -614,12 +614,15 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) recursive++; try_start(); typval_T rettv; - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.selfdict = self; // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (see above) to capture abort-causing non-exception errors. (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size, - vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, NULL, self); + vim_args, &funcexe); if (!try_end(err)) { rv = vim_to_object(&rettv); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5634eafdf2..5a6dbf7ff4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -737,15 +737,15 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(1, 2, 4) { - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; if (expr->v_type == VAR_FUNC) { const char_u *const s = expr->vval.v_string; if (s == NULL || *s == NUL) { return FAIL; } - if (call_func(s, -1, rettv, argc, argv, NULL, - 0L, 0L, &dummy, true, NULL, NULL) == FAIL) { + funcexe.evaluate = true; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } } else if (expr->v_type == VAR_PARTIAL) { @@ -754,8 +754,9 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, if (s == NULL || *s == NUL) { return FAIL; } - if (call_func(s, -1, rettv, argc, argv, NULL, - 0L, 0L, &dummy, true, partial, NULL) == FAIL) { + funcexe.evaluate = true; + funcexe.partial = partial; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } } else { @@ -1051,7 +1052,6 @@ int call_vim_function( ) FUNC_ATTR_NONNULL_ALL { - int doesrange; int ret; int len = (int)STRLEN(func); partial_T *pt = NULL; @@ -1067,9 +1067,12 @@ int call_vim_function( } rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. - ret = call_func(func, len, rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, true, pt, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = pt; + ret = call_func(func, len, rettv, argc, argv, &funcexe); fail: if (ret == FAIL) { @@ -3988,9 +3991,12 @@ static int eval7( s = xmemdupz(s, len); // Invoke the function. - ret = get_func_tv(s, len, rettv, arg, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, partial, NULL); + 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); @@ -7260,10 +7266,12 @@ bool callback_call(Callback *const callback, const int argcount_in, abort(); } - int dummy; - return call_func(name, -1, rettv, argcount_in, argvars_in, - NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, - true, partial, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); } static bool set_ref_in_callback(Callback *callback, int copyID, @@ -8426,7 +8434,6 @@ handle_subscript( int ret = OK; dict_T *selfdict = NULL; const char_u *s; - int len; typval_T functv; int slen = 0; bool lua = false; @@ -8474,9 +8481,14 @@ handle_subscript( } else { s = (char_u *)""; } - ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, pt, selfdict); + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = pt; + funcexe.selfdict = selfdict; + ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg, &funcexe); // Clear the funcref afterwards, so that deleting it while // evaluating the arguments is possible (see test55). @@ -10416,19 +10428,11 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; tv_list_ref(arguments); - int dummy; - (void)call_func((const char_u *)func, - name_len, - &rettv, - 2, - argvars, - NULL, - curwin->w_cursor.lnum, - curwin->w_cursor.lnum, - &dummy, - true, - NULL, - NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + (void)call_func((const char_u *)func, name_len, &rettv, 2, argvars, &funcexe); tv_list_unref(arguments); // Restore caller scope information diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4e409cca50..363640adc5 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9420,7 +9420,6 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) int res; typval_T rettv; typval_T argv[3]; - int dummy; const char *func_name; partial_T *partial = sortinfo->item_compare_partial; @@ -9444,10 +9443,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - res = call_func((const char_u *)func_name, - -1, - &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, - partial, sortinfo->item_compare_selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = sortinfo->item_compare_selfdict; + res = call_func((const char_u *)func_name, -1, &rettv, 2, argv, &funcexe); tv_clear(&argv[0]); tv_clear(&argv[1]); diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index deddec413b..516f7a1d53 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -414,12 +414,7 @@ get_func_tv( int len, // length of "name" or -1 to use strlen() typval_T *rettv, char_u **arg, // argument, pointing to the '(' - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - int *doesrange, // return: function handled range - int evaluate, - partial_T *partial, // for extra arguments - dict_T *selfdict // Dictionary for "self" + funcexe_T *funcexe // various values ) { char_u *argp; @@ -431,12 +426,13 @@ get_func_tv( * Get the arguments. */ argp = *arg; - while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { + while (argcount < MAX_FUNC_ARGS + - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) { argp = skipwhite(argp + 1); // skip the '(' or ',' if (*argp == ')' || *argp == ',' || *argp == NUL) { break; } - if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) { + if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) { ret = FAIL; break; } @@ -463,9 +459,7 @@ get_func_tv( ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i]; } } - ret = call_func(name, len, rettv, argcount, argvars, NULL, - firstline, lastline, doesrange, evaluate, - partial, selfdict); + ret = call_func(name, len, rettv, argcount, argvars, funcexe); funcargs.ga_len -= i; } else if (!aborting()) { @@ -1367,7 +1361,6 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, { typval_T argv[MAX_FUNC_ARGS + 1]; int argc = 0; - int dummy; int r = 0; TV_LIST_ITER(args->vval.v_list, item, { @@ -1380,9 +1373,13 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); }); - r = call_func(name, -1, rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, partial, selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = selfdict; + r = call_func(name, -1, rettv, argc, argv, &funcexe); func_call_skip_call: // Free the arguments. @@ -1425,10 +1422,6 @@ static void user_func_error(int error, const char_u *name) /// Call a function with its resolved parameters /// -/// "argv_func", when not NULL, can be used to fill in arguments only when the -/// invoked function uses them. It is called like this: -/// new_argcount = argv_func(current_argcount, argv, called_func_argcount) -/// /// @return FAIL if function cannot be called, else OK (even if an error /// occurred while executing the function! Set `msg_list` to capture /// the error, see do_cmdline()). @@ -1440,15 +1433,9 @@ call_func( int argcount_in, // number of "argvars" typval_T *argvars_in, // vars for arguments, must have "argcount" // PLUS ONE elements! - ArgvFunc argv_func, // function to fill in argvars - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - int *doesrange, // [out] function handled range - bool evaluate, - partial_T *partial, // optional, can be NULL - dict_T *selfdict_in // Dictionary for "self" + funcexe_T *funcexe // more arguments ) - FUNC_ATTR_NONNULL_ARG(1, 3, 5, 9) + FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6) { int ret = FAIL; int error = ERROR_NONE; @@ -1459,9 +1446,10 @@ call_func( char_u *name = NULL; int argcount = argcount_in; typval_T *argvars = argvars_in; - dict_T *selfdict = selfdict_in; + dict_T *selfdict = funcexe->selfdict; typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" is not NULL int argv_clear = 0; + partial_T *partial = funcexe->partial; // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) // even when call_func() returns FAIL. @@ -1480,14 +1468,15 @@ call_func( fname = fname_trans_sid(name, fname_buf, &tofree, &error); } - *doesrange = false; + if (funcexe->doesrange != NULL) { + *funcexe->doesrange = false; + } if (partial != NULL) { // When the function has a partial with a dict and there is a dict // argument, use the dict argument. That is backwards compatible. // When the dict was bound explicitly use the one from the partial. - if (partial->pt_dict != NULL - && (selfdict_in == NULL || !partial->pt_auto)) { + if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) { selfdict = partial->pt_dict; } if (error == ERROR_NONE && partial->pt_argc > 0) { @@ -1506,7 +1495,7 @@ call_func( } } - if (error == ERROR_NONE && evaluate) { + if (error == ERROR_NONE && funcexe->evaluate) { char_u *rfname = fname; // Ignore "g:" before a function name. @@ -1549,13 +1538,13 @@ call_func( cfunc_T cb = fp->uf_cb; error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state); } else if (fp != NULL) { - if (argv_func != NULL) { + if (funcexe->argv_func != NULL) { // postponed filling in the arguments, do it now - argcount = argv_func(argcount, argvars, argv_clear, - fp->uf_args.ga_len); + argcount = funcexe->argv_func(argcount, argvars, argv_clear, + fp->uf_args.ga_len); } - if (fp->uf_flags & FC_RANGE) { - *doesrange = true; + if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { + *funcexe->doesrange = true; } if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) { error = ERROR_TOOFEW; @@ -1565,7 +1554,8 @@ call_func( error = ERROR_DICT; } else { // Call the user function. - call_user_func(fp, argcount, argvars, rettv, firstline, lastline, + call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, + funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; } @@ -2901,7 +2891,7 @@ void ex_call(exarg_T *eap) int len; typval_T rettv; linenr_T lnum; - int doesrange; + bool doesrange; bool failed = false; funcdict_T fudi; partial_T *partial = NULL; @@ -2965,9 +2955,15 @@ void ex_call(exarg_T *eap) curwin->w_cursor.coladd = 0; } arg = startarg; - if (get_func_tv(name, -1, &rettv, &arg, - eap->line1, eap->line2, &doesrange, - true, partial, fudi.fd_dict) == FAIL) { + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = eap->line1; + funcexe.lastline = eap->line2; + funcexe.doesrange = &doesrange; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = fudi.fd_dict; + if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) { failed = true; break; } diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index e8ad0bf1da..9af35e0411 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -30,9 +30,32 @@ typedef enum { ERROR_DELETED, } FnameTransError; +/// Used in funcexe_T. Returns the new argcount. typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip, int called_func_argcount); +/// Structure passed between functions dealing with function call execution. +typedef struct { + ArgvFunc argv_func; ///< when not NULL, can be used to fill in arguments only + ///< when the invoked function uses them + linenr_T firstline; ///< first line of range + linenr_T lastline; ///< last line of range + bool *doesrange; ///< [out] if not NULL: function handled range + bool evaluate; ///< actually evaluate expressions + partial_T *partial; ///< for extra arguments + dict_T *selfdict; ///< Dictionary for "self" +} funcexe_T; + +#define FUNCEXE_INIT (funcexe_T) { \ + .argv_func = NULL, \ + .firstline = 0, \ + .lastline = 0, \ + .doesrange = NULL, \ + .evaluate = false, \ + .partial = NULL, \ + .selfdict = NULL, \ +} + #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5799c3ee98..cb3cb6e73e 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -780,13 +780,13 @@ int nlua_call(lua_State *lstate) try_start(); typval_T rettv; - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (TRY_WRAP) to capture abort-causing non-exception errors. - (void)call_func(name, (int)name_len, &rettv, nargs, - vim_args, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, NULL, NULL); + (void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe); if (!try_end(&err)) { nlua_push_typval(lstate, &rettv, false); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2a530db934..e42ec92a4c 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2531,12 +2531,12 @@ do_mouse ( } }; typval_T rettv; - int doesrange; - (void)call_func((char_u *)tab_page_click_defs[mouse_col].func, - -1, - &rettv, ARRAY_SIZE(argv), argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, true, NULL, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + (void)call_func((char_u *)tab_page_click_defs[mouse_col].func, -1, + &rettv, ARRAY_SIZE(argv), argv, &funcexe); tv_clear(&rettv); break; } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 6379174938..98a46cf781 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -6726,26 +6726,24 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (expr != NULL) { typval_T argv[2]; - int dummy; typval_T rettv; staticList10_T matchList = TV_LIST_STATIC10_INIT; - rettv.v_type = VAR_STRING; rettv.vval.v_string = NULL; argv[0].v_type = VAR_LIST; argv[0].vval.v_list = &matchList.sl_list; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.argv_func = fill_submatch_list; + funcexe.evaluate = true; if (expr->v_type == VAR_FUNC) { s = expr->vval.v_string; - call_func(s, -1, &rettv, 1, argv, - fill_submatch_list, 0L, 0L, &dummy, - true, NULL, NULL); + call_func(s, -1, &rettv, 1, argv, &funcexe); } else if (expr->v_type == VAR_PARTIAL) { partial_T *partial = expr->vval.v_partial; s = partial_name(partial); - call_func(s, -1, &rettv, 1, argv, - fill_submatch_list, 0L, 0L, &dummy, - true, partial, NULL); + funcexe.partial = partial; + call_func(s, -1, &rettv, 1, argv, &funcexe); } if (tv_list_len(&matchList.sl_list) > 0) { // fill_submatch_list() was called. -- cgit From e6be6c307a832d661d2a6269ad2d322e4bf5e9cc Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 5 Aug 2021 19:46:42 +0100 Subject: 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. --- src/nvim/eval.c | 82 ++++++++++++++++++++++++++++++++++++---- src/nvim/eval.lua | 51 +++++++++++++------------ src/nvim/eval/funcs.c | 44 +++++++++++++++++++++ src/nvim/eval/funcs.h | 1 + src/nvim/eval/userfunc.c | 24 +++++------- src/nvim/eval/userfunc.h | 2 + src/nvim/generators/gen_eval.lua | 7 ++-- src/nvim/globals.h | 1 + src/nvim/testdir/test_method.vim | 68 +++++++++++++++++++++++++++++++++ 9 files changed, 231 insertions(+), 49 deletions(-) create mode 100644 src/nvim/testdir/test_method.vim (limited to 'src') 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) { diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index eb20cd1bc8..f94ecffed9 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -5,6 +5,9 @@ -- args Number of arguments, list with maximum and minimum number of arguments -- or list with a minimum number of arguments only. Defaults to zero -- arguments. +-- base For methods: the argument to use as the base argument (1-indexed): +-- base->method() +-- Defaults to zero (function cannot be used as a method). -- func Name of the C function which implements the VimL function. Defaults to -- `f_{funcname}`. @@ -16,7 +19,7 @@ return { funcs={ abs={args=1}, acos={args=1, func="float_op_wrapper", data="&acos"}, -- WJMc - add={args=2}, + add={args=2, base=1}, ['and']={args=2}, api_info={}, append={args=2}, @@ -73,10 +76,10 @@ return { complete_check={}, complete_info={args={0, 1}}, confirm={args={1, 4}}, - copy={args=1}, + copy={args=1, base=1}, cos={args=1, func="float_op_wrapper", data="&cos"}, cosh={args=1, func="float_op_wrapper", data="&cosh"}, - count={args={2, 4}}, + count={args={2, 4}, base=1}, cscope_connection={args={0, 3}}, ctxget={args={0, 1}}, ctxpop={}, @@ -93,7 +96,7 @@ return { did_filetype={}, diff_filler={args=1}, diff_hlID={args=2}, - empty={args=1}, + empty={args=1, base=1}, environ={}, escape={args=2}, eval={args=1}, @@ -105,12 +108,12 @@ return { exp={args=1, func="float_op_wrapper", data="&exp"}, expand={args={1, 3}}, expandcmd={args=1}, - extend={args={2, 3}}, + extend={args={2, 3}, base=1}, feedkeys={args={1, 2}}, file_readable={args=1, func='f_filereadable'}, -- obsolete filereadable={args=1}, filewritable={args=1}, - filter={args=2}, + filter={args=2, base=1}, finddir={args={1, 3}}, findfile={args={1, 3}}, flatten={args={1, 2}}, @@ -128,7 +131,7 @@ return { funcref={args={1, 3}}, ['function']={args={1, 3}}, garbagecollect={args={0, 1}}, - get={args={2, 3}}, + get={args={2, 3}, base=1}, getbufinfo={args={0, 1}}, getbufline={args={2, 3}}, getbufvar={args={2, 3}}, @@ -187,14 +190,14 @@ return { hostname={}, iconv={args=3}, indent={args=1}, - index={args={2, 4}}, + index={args={2, 4}, base=1}, input={args={1, 3}}, inputdialog={args={1, 3}}, inputlist={args=1}, inputrestore={}, inputsave={}, inputsecret={args={1, 2}}, - insert={args={2, 3}}, + insert={args={2, 3}, base=1}, interrupt={args=0}, invert={args=1}, isdirectory={args=1}, @@ -202,7 +205,7 @@ return { islocked={args=1}, isnan={args=1}, id={args=1}, - items={args=1}, + items={args=1, base=1}, jobclose={args={1, 2}, func="f_chanclose"}, jobpid={args=1}, jobresize={args=3}, @@ -210,12 +213,12 @@ return { jobstart={args={1, 2}}, jobstop={args=1}, jobwait={args={1, 2}}, - join={args={1, 2}}, + join={args={1, 2}, base=1}, json_decode={args=1}, json_encode={args=1}, - keys={args=1}, + keys={args=1, base=1}, last_buffer_nr={}, -- obsolete - len={args=1}, + len={args=1, base=1}, libcall={args=3}, libcallnr={args=3}, line={args={1, 2}}, @@ -226,7 +229,7 @@ return { log={args=1, func="float_op_wrapper", data="&log"}, log10={args=1, func="float_op_wrapper", data="&log10"}, luaeval={args={1, 2}}, - map={args=2}, + map={args=2, base=1}, maparg={args={1, 4}}, mapcheck={args={1, 3}}, match={args={2, 4}}, @@ -238,9 +241,9 @@ return { matchlist={args={2, 4}}, matchstr={args={2, 4}}, matchstrpos={args={2,4}}, - max={args=1}, + max={args=1, base=1}, menu_get={args={1, 2}}, - min={args=1}, + min={args=1, base=1}, mkdir={args={1, 3}}, mode={args={0, 1}}, msgpackdump={args=1}, @@ -270,11 +273,11 @@ return { reltime={args={0, 2}}, reltimefloat={args=1}, reltimestr={args=1}, - remove={args={2, 3}}, + remove={args={2, 3}, base=1}, rename={args=2}, - ['repeat']={args=2}, + ['repeat']={args=2, base=1}, resolve={args=1}, - reverse={args=1}, + reverse={args=1, base=1}, round={args=1, func="float_op_wrapper", data="&round"}, rpcnotify={args=varargs(2)}, rpcrequest={args=varargs(2)}, @@ -327,7 +330,7 @@ return { sin={args=1, func="float_op_wrapper", data="&sin"}, sinh={args=1, func="float_op_wrapper", data="&sinh"}, sockconnect={args={2,3}}, - sort={args={1, 3}}, + sort={args={1, 3}, base=1}, soundfold={args=1}, stdioopen={args=1}, spellbadword={args={0, 1}}, @@ -344,7 +347,7 @@ return { strftime={args={1, 2}}, strgetchar={args={2, 2}}, stridx={args={2, 3}}, - string={args=1}, + string={args=1, base=1}, strlen={args=1}, strpart={args={2, 4}}, strptime={args=2}, @@ -383,11 +386,11 @@ return { tr={args=3}, trim={args={1,3}}, trunc={args=1, func="float_op_wrapper", data="&trunc"}, - type={args=1}, + type={args=1, base=1}, undofile={args=1}, undotree={}, - uniq={args={1, 3}}, - values={args=1}, + uniq={args={1, 3}, base=1}, + values={args=1, base=1}, virtcol={args=1}, visualmode={args={0, 1}}, wait={args={2,3}}, 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] diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua index 679895421a..aa2274e0a2 100644 --- a/src/nvim/generators/gen_eval.lua +++ b/src/nvim/generators/gen_eval.lua @@ -42,7 +42,7 @@ gperfpipe:write([[ %language=ANSI-C %global-table %readonly-tables -%define initializer-suffix ,0,0,NULL,NULL +%define initializer-suffix ,0,0,0,NULL,NULL %define word-array-name functions %define hash-function-name hash_internal_func_gperf %define lookup-function-name find_internal_func_gperf @@ -59,9 +59,10 @@ for name, def in pairs(funcs) do elseif #args == 1 then args[2] = 'MAX_FUNC_ARGS' end + local base = def.base or 0 local func = def.func or ('f_' .. name) local data = def.data or "NULL" - gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n') - :format(name, args[1], args[2], func, data)) + gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n') + :format(name, args[1], args[2], base, func, data)) end gperfpipe:close() diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 96acca4ac7..2a72dbcd09 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -972,6 +972,7 @@ EXTERN char_u e_write[] INIT(= N_("E80: Error while writing")); EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required")); EXTERN char_u e_usingsid[] INIT(= N_( "E81: Using not in a script context")); +EXTERN char_u e_missingparen[] INIT(= N_("E107: Missing parentheses: %s")); EXTERN char_u e_maxmempat[] INIT(= N_( "E363: pattern uses more memory than 'maxmempattern'")); EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer")); diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim new file mode 100644 index 0000000000..1c0de01ac5 --- /dev/null +++ b/src/nvim/testdir/test_method.vim @@ -0,0 +1,68 @@ +" Tests for ->method() + +func Test_list() + let l = [1, 2, 3] + call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) + call assert_equal(l, l->copy()) + call assert_equal(1, l->count(2)) + call assert_false(l->empty()) + call assert_true([]->empty()) + call assert_equal([1, 2, 3, 4, 5], [1, 2, 3]->extend([4, 5])) + call assert_equal([1, 3], [1, 2, 3]->filter('v:val != 2')) + call assert_equal(2, l->get(1)) + call assert_equal(1, l->index(2)) + call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0)) + call assert_fails('let x = l->items()', 'E715:') + call assert_equal('1 2 3', l->join()) + call assert_fails('let x = l->keys()', 'E715:') + call assert_equal(3, l->len()) + call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1')) + call assert_equal(3, l->max()) + call assert_equal(1, l->min()) + call assert_equal(2, [1, 2, 3]->remove(1)) + call assert_equal([1, 2, 3, 1, 2, 3], l->repeat(2)) + call assert_equal([3, 2, 1], [1, 2, 3]->reverse()) + call assert_equal([1, 2, 3, 4], [4, 2, 3, 1]->sort()) + call assert_equal('[1, 2, 3]', l->string()) + call assert_equal(v:t_list, l->type()) + call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq()) + call assert_fails('let x = l->values()', 'E715:') +endfunc + +func Test_dict() + let d = #{one: 1, two: 2, three: 3} + + call assert_equal(d, d->copy()) + call assert_equal(1, d->count(2)) + call assert_false(d->empty()) + call assert_true({}->empty()) + call assert_equal(#{one: 1, two: 2, three: 3, four: 4}, d->extend(#{four: 4})) + call assert_equal(#{one: 1, two: 2, three: 3}, d->filter('v:val != 4')) + call assert_equal(2, d->get('two')) + " Nvim doesn't support Blobs yet; expect a different emsg + " call assert_fails("let x = d->index(2)", 'E897:') + " call assert_fails("let x = d->insert(0)", 'E899:') + call assert_fails("let x = d->index(2)", 'E714:') + call assert_fails("let x = d->insert(0)", 'E686:') + call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items()) + call assert_fails("let x = d->join()", 'E714:') + call assert_equal(['one', 'two', 'three'], d->keys()) + call assert_equal(3, d->len()) + call assert_equal(#{one: 2, two: 3, three: 4}, d->map('v:val + 1')) + call assert_equal(#{one: 1, two: 2, three: 3}, d->map('v:val - 1')) + call assert_equal(3, d->max()) + call assert_equal(1, d->min()) + call assert_equal(2, d->remove("two")) + let d.two = 2 + call assert_fails('let x = d->repeat(2)', 'E731:') + " Nvim doesn't support Blobs yet; expect a different emsg + " call assert_fails('let x = d->reverse()', 'E899:') + call assert_fails('let x = d->reverse()', 'E686:') + call assert_fails('let x = d->sort()', 'E686:') + call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string()) + call assert_equal(v:t_dict, d->type()) + call assert_fails('let x = d->uniq()', 'E686:') + call assert_equal([1, 2, 3], d->values()) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 003c8acc8a9863932430bfb51bee8403b964c19b Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 17:09:47 +0100 Subject: vim-patch:8.1.1807: more functions can be used as a method Problem: More functions can be used as a method. Solution: Add append(), appendbufline(), assert_equal(), etc. Also add the :eval command. https://github.com/vim/vim/commit/25e42231d3ee27feec2568fa4be2aa2bfba82ae5 :eval is already ported. --- src/nvim/eval.lua | 16 ++++++++++------ src/nvim/eval/funcs.c | 5 +++-- src/nvim/eval/funcs.h | 6 +++++- src/nvim/generators/gen_eval.lua | 4 ++-- src/nvim/testdir/test_method.vim | 25 ++++++++++++++++++++++--- 5 files changed, 42 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index f94ecffed9..506368a3b2 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -7,7 +7,7 @@ -- arguments. -- base For methods: the argument to use as the base argument (1-indexed): -- base->method() --- Defaults to zero (function cannot be used as a method). +-- Defaults to BASE_NONE (function cannot be used as a method). -- func Name of the C function which implements the VimL function. Defaults to -- `f_{funcname}`. @@ -15,6 +15,10 @@ local varargs = function(nr) return {nr} end +-- Usable with the base key: use the last function argument as the method base. +-- Value is from funcs.h file. "BASE_" prefix is omitted. +local LAST = "BASE_LAST" + return { funcs={ abs={args=1}, @@ -22,15 +26,15 @@ return { add={args=2, base=1}, ['and']={args=2}, api_info={}, - append={args=2}, - appendbufline={args=3}, + append={args=2, base=LAST}, + appendbufline={args=3, base=LAST}, argc={args={0, 1}}, argidx={}, arglistid={args={0, 2}}, argv={args={0, 2}}, asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc assert_beeps={args={1}}, - assert_equal={args={2, 3}}, + assert_equal={args={2, 3}, base=2}, assert_equalfile={args={2, 3}}, assert_exception={args={1, 2}}, assert_fails={args={1, 3}}, @@ -38,7 +42,7 @@ return { assert_inrange={args={3, 4}}, assert_match={args={2, 3}}, assert_nobeep={args={1}}, - assert_notequal={args={2, 3}}, + assert_notequal={args={2, 3}, base=2}, assert_notmatch={args={2, 3}}, assert_report={args=1}, assert_true={args={1, 2}}, @@ -99,7 +103,7 @@ return { empty={args=1, base=1}, environ={}, escape={args=2}, - eval={args=1}, + eval={args=1, base=1}, eventhandler={}, executable={args=1}, execute={args={1, 2}}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 787d5aaf78..e090f3b37f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -199,7 +199,7 @@ int call_internal_method(const char_u *const fname, const int argcount, FUNC_ATTR_NONNULL_ALL { const VimLFuncDef *const fdef = find_internal_func((const char *)fname); - if (fdef == NULL || fdef->base_arg == 0) { + if (fdef == NULL || fdef->base_arg == BASE_NONE) { return ERROR_UNKNOWN; } else if (argcount + 1 < fdef->min_argc) { return ERROR_TOOFEW; @@ -208,7 +208,8 @@ int call_internal_method(const char_u *const fname, const int argcount, } typval_T argv[MAX_FUNC_ARGS + 1]; - const ptrdiff_t base_index = fdef->base_arg - 1; + const ptrdiff_t base_index + = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; memcpy(argv, argvars, base_index * sizeof(typval_T)); argv[base_index] = *basetv; memcpy(argv + base_index + 1, argvars + base_index, diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 3ad0b8282a..c6a0cb959e 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -9,12 +9,16 @@ typedef void (*FunPtr)(void); /// Prototype of C function that implements VimL function typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); +/// Special flags for base_arg @see VimLFuncDef +#define BASE_NONE 0 ///< Not a method (no base argument). +#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base. + /// Structure holding VimL function definition 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. + uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST. VimLFunc func; ///< Function implementation. FunPtr data; ///< Userdata for function implementation. } VimLFuncDef; diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua index aa2274e0a2..945fa5099f 100644 --- a/src/nvim/generators/gen_eval.lua +++ b/src/nvim/generators/gen_eval.lua @@ -42,7 +42,7 @@ gperfpipe:write([[ %language=ANSI-C %global-table %readonly-tables -%define initializer-suffix ,0,0,0,NULL,NULL +%define initializer-suffix ,0,0,BASE_NONE,NULL,NULL %define word-array-name functions %define hash-function-name hash_internal_func_gperf %define lookup-function-name find_internal_func_gperf @@ -59,7 +59,7 @@ for name, def in pairs(funcs) do elseif #args == 1 then args[2] = 'MAX_FUNC_ARGS' end - local base = def.base or 0 + local base = def.base or "BASE_NONE" local func = def.func or ('f_' .. name) local data = def.data or "NULL" gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n') diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index 1c0de01ac5..43ed830aba 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -3,18 +3,23 @@ func Test_list() let l = [1, 2, 3] call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) + eval l->assert_equal(l) + eval l->assert_equal(l, 'wrong') + eval l->assert_notequal([3, 2, 1]) + eval l->assert_notequal([3, 2, 1], 'wrong') call assert_equal(l, l->copy()) call assert_equal(1, l->count(2)) call assert_false(l->empty()) call assert_true([]->empty()) + call assert_equal(579, ['123', '+', '456']->join()->eval()) call assert_equal([1, 2, 3, 4, 5], [1, 2, 3]->extend([4, 5])) call assert_equal([1, 3], [1, 2, 3]->filter('v:val != 2')) call assert_equal(2, l->get(1)) call assert_equal(1, l->index(2)) call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0)) - call assert_fails('let x = l->items()', 'E715:') + call assert_fails('eval l->items()', 'E715:') call assert_equal('1 2 3', l->join()) - call assert_fails('let x = l->keys()', 'E715:') + call assert_fails('eval l->keys()', 'E715:') call assert_equal(3, l->len()) call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1')) call assert_equal(3, l->max()) @@ -26,7 +31,7 @@ func Test_list() call assert_equal('[1, 2, 3]', l->string()) call assert_equal(v:t_list, l->type()) call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq()) - call assert_fails('let x = l->values()', 'E715:') + call assert_fails('eval l->values()', 'E715:') endfunc func Test_dict() @@ -65,4 +70,18 @@ func Test_dict() call assert_equal([1, 2, 3], d->values()) endfunc +func Test_append() + new + eval ['one', 'two', 'three']->append(1) + call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) + + %del + let bnr = bufnr('') + wincmd w + eval ['one', 'two', 'three']->appendbufline(bnr, 1) + call assert_equal(['', 'one', 'two', 'three'], getbufline(bnr, 1, '$')) + + exe 'bwipe! ' .. bnr +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From aa2dc8b7b4e0e86c9102f2df6b670317c5693657 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 18:10:30 +0100 Subject: vim-patch:8.1.1809: more functions can be used as a method Problem: More functions can be used as a method. Solution: Add has_key(), split(), str2list(), etc. https://github.com/vim/vim/commit/a74e4946de074d2916e3d6004f7fa1810d12dda9 --- src/nvim/eval.lua | 22 +++++++++++----------- src/nvim/testdir/test_diffmode.vim | 12 ++++++------ src/nvim/testdir/test_method.vim | 11 +++++++++++ src/nvim/testdir/test_syntax.vim | 4 ++-- src/nvim/testdir/test_system.vim | 4 ++-- 5 files changed, 32 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 506368a3b2..50b6eaadfd 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -180,7 +180,7 @@ return { glob2regpat={args=1}, globpath={args={2, 5}}, has={args=1}, - has_key={args=2}, + has_key={args=2, base=1}, haslocaldir={args={0,2}}, hasmapto={args={1, 3}}, highlightID={args=1, func='f_hlID'}, -- obsolete @@ -339,11 +339,11 @@ return { stdioopen={args=1}, spellbadword={args={0, 1}}, spellsuggest={args={1, 3}}, - split={args={1, 3}}, + split={args={1, 3}, base=1}, sqrt={args=1, func="float_op_wrapper", data="&sqrt"}, stdpath={args=1}, str2float={args=1}, - str2list={args={1, 2}}, + str2list={args={1, 2}, base=1}, str2nr={args={1, 2}}, strcharpart={args={2, 3}}, strchars={args={1,2}}, @@ -352,23 +352,23 @@ return { strgetchar={args={2, 2}}, stridx={args={2, 3}}, string={args=1, base=1}, - strlen={args=1}, + strlen={args=1, base=1}, strpart={args={2, 4}}, strptime={args=2}, strridx={args={2, 3}}, - strtrans={args=1}, - strwidth={args=1}, + strtrans={args=1, base=1}, + strwidth={args=1, base=1}, submatch={args={1, 2}}, - substitute={args=4}, + substitute={args=4, base=1}, swapinfo={args={1}}, swapname={args={1}}, synID={args=3}, - synIDattr={args={2, 3}}, - synIDtrans={args=1}, + synIDattr={args={2, 3}, base=1}, + synIDtrans={args=1, base=1}, synconcealed={args=2}, synstack={args=2}, - system={args={1, 2}}, - systemlist={args={1, 3}}, + system={args={1, 2}, base=1}, + systemlist={args={1, 3}, base=1}, tabpagebuflist={args={0, 1}}, tabpagenr={args={0, 1}}, tabpagewinnr={args={1, 2}}, diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 8592f48af7..1552bc4f78 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -741,16 +741,16 @@ func Test_diff_hlID() diffthis redraw - call assert_equal(synIDattr(diff_hlID(-1, 1), "name"), "") + call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("") call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) - call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange") + call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange") call assert_equal(diff_hlID(1, 2), hlID("DiffText")) - call assert_equal(synIDattr(diff_hlID(1, 2), "name"), "DiffText") - call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "") + call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(2, 1)->synIDattr("name")->assert_equal("") call assert_equal(diff_hlID(3, 1), hlID("DiffAdd")) - call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "DiffAdd") - call assert_equal(synIDattr(diff_hlID(4, 1), "name"), "") + call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd") + call diff_hlID(4, 1)->synIDattr("name")->assert_equal("") wincmd w call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index 43ed830aba..ef87773924 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -49,6 +49,7 @@ func Test_dict() " call assert_fails("let x = d->insert(0)", 'E899:') call assert_fails("let x = d->index(2)", 'E714:') call assert_fails("let x = d->insert(0)", 'E686:') + call assert_true(d->has_key('two')) call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items()) call assert_fails("let x = d->join()", 'E714:') call assert_equal(['one', 'two', 'three'], d->keys()) @@ -70,6 +71,16 @@ func Test_dict() call assert_equal([1, 2, 3], d->values()) endfunc +func Test_string() + 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()) + call assert_equal(3, 'ABC'->strlen()) + call assert_equal('a^Mb^[c', "a\rb\ec"->strtrans()) + call assert_equal(4, "aあb"->strwidth()) + call assert_equal('axc', 'abc'->substitute('b', 'x', '')) +endfunc + func Test_append() new eval ['one', 'two', 'three']->append(1) diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 875e23894f..2344bac498 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -546,8 +546,8 @@ func Test_synstack_synIDtrans() call assert_equal([], synstack(1, 1)) norm f/ - call assert_equal(['cComment', 'cCommentStart'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) - call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")')) + eval synstack(line("."), col("."))->map('synIDattr(v:val, "name")')->assert_equal(['cComment', 'cCommentStart']) + eval synstack(line("."), col("."))->map('synIDattr(synIDtrans(v:val), "name")')->assert_equal(['Comment', 'Comment']) norm fA call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 6bbe714d19..7b8ee778cc 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -7,10 +7,10 @@ func Test_System() if !executable('echo') || !executable('cat') || !executable('wc') return endif - let out = system('echo 123') + let out = 'echo 123'->system() call assert_equal("123\n", out) - let out = systemlist('echo 123') + let out = 'echo 123'->systemlist() if &shell =~# 'cmd.exe$' call assert_equal(["123\r"], out) else -- cgit From 8d1ca37d1f413b08f5ff9ceb502fa35ffd6034c7 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 18:31:27 +0100 Subject: vim-patch:8.1.1816: cannot use a user defined function as a method Problem: Cannot use a user defined function as a method. Solution: Pass the base as the first argument to the user defined function after "->". (partly by FUJIWARA Takuya) https://github.com/vim/vim/commit/fcfe1a9b8950b8b211ab3b24d84b17c6847ea43f --- src/nvim/eval.c | 6 ++++-- src/nvim/eval/userfunc.c | 23 ++++++++++++++++------- src/nvim/testdir/sautest/autoload/foo.vim | 4 ++++ src/nvim/testdir/test_autoload.vim | 2 ++ src/nvim/testdir/test_user_func.vim | 18 +++++++++++++++++- 5 files changed, 43 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 39e8686580..021a9a75b8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4031,7 +4031,7 @@ static int eval7( *arg = skipwhite(*arg); // Handle following '[', '(' and '.' for expr[expr], expr.name, - // expr(expr). + // expr(expr), expr->name(expr) if (ret == OK) { ret = handle_subscript((const char **)arg, rettv, evaluate, true); } @@ -4094,7 +4094,7 @@ static int eval_method(char_u **const arg, typval_T *const rettv, // Locate the method name. const char_u *const name = *arg; size_t len; - for (len = 0; ASCII_ISALNUM(name[len]) || name[len] == '_'; len++) { + for (len = 0; eval_isnamec(name[len]); len++) { } if (len == 0) { @@ -4113,6 +4113,8 @@ static int eval_method(char_u **const arg, typval_T *const rettv, } *arg += len; + // TODO(seandewar): if "name" is a function reference, resolve it. + typval_T base = *rettv; funcexe_T funcexe = FUNCEXE_INIT; funcexe.evaluate = evaluate; diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index ec43987b07..3c0c0091f2 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1447,7 +1447,8 @@ call_func( int argcount = argcount_in; typval_T *argvars = argvars_in; dict_T *selfdict = funcexe->selfdict; - typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" is not NULL + typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or + // "funcexe->basetv" is not NULL int argv_clear = 0; partial_T *partial = funcexe->partial; @@ -1514,10 +1515,7 @@ call_func( } } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // User defined function. - if (funcexe->basetv != NULL) { - // TODO(seandewar): support User function: base->Method() - fp = NULL; - } else if (fp == NULL) { + if (fp == NULL) { fp = find_func(rfname); } @@ -1546,6 +1544,16 @@ call_func( argcount = funcexe->argv_func(argcount, argvars, argv_clear, 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++; + } else { + memcpy(argv, argvars, sizeof(typval_T) * argcount); + } + if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { *funcexe->doesrange = true; } @@ -1557,14 +1565,15 @@ call_func( error = ERROR_DICT; } else { // Call the user function. - call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, + call_user_func(fp, argcount, argv, rettv, funcexe->firstline, funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; } } } else if (funcexe->basetv != NULL) { - // Find the method name in the table, call its implementation. + // expr->method(): Find the method name in the table, call its + // implementation with the base as one of the arguments. error = call_internal_method(fname, argcount, argvars, rettv, funcexe->basetv); } else { diff --git a/src/nvim/testdir/sautest/autoload/foo.vim b/src/nvim/testdir/sautest/autoload/foo.vim index d7dcd5ce3d..298e7275d8 100644 --- a/src/nvim/testdir/sautest/autoload/foo.vim +++ b/src/nvim/testdir/sautest/autoload/foo.vim @@ -5,3 +5,7 @@ let foo#bar = {} func foo#bar.echo() let g:called_foo_bar_echo += 1 endfunc + +func foo#addFoo(head) + return a:head .. 'foo' +endfunc diff --git a/src/nvim/testdir/test_autoload.vim b/src/nvim/testdir/test_autoload.vim index 7396c227c9..b8c4fa251f 100644 --- a/src/nvim/testdir/test_autoload.vim +++ b/src/nvim/testdir/test_autoload.vim @@ -8,6 +8,8 @@ func Test_autoload_dict_func() call g:foo#bar.echo() call assert_equal(1, g:loaded_foo_vim) call assert_equal(1, g:called_foo_bar_echo) + + eval 'bar'->g:foo#addFoo()->assert_equal('barfoo') endfunc func Test_source_autoload() diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 7dcd92a54b..5231ef7b4f 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -47,7 +47,7 @@ func FuncWithRef(a) endfunc func Test_user_func() - let g:FuncRef=function("FuncWithRef") + let g:FuncRef = function("FuncWithRef") let g:counter = 0 inoremap ( ListItem() inoremap [ ListReset() @@ -62,6 +62,14 @@ func Test_user_func() call assert_equal(9, g:retval) call assert_equal(333, g:FuncRef(333)) + let g:retval = "nop" + call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf")) + call assert_equal('fail', 45->Compute(0, "retval")) + call assert_equal('nop', g:retval) + call assert_equal('ok', 45->Compute(5, "retval")) + call assert_equal(9, g:retval) + " call assert_equal(333, 333->g:FuncRef()) + enew normal oXX+-XX @@ -150,6 +158,14 @@ func Test_default_arg() \ execute('func Args2')) endfunc +func s:addFoo(lead) + return a:lead .. 'foo' +endfunc + +func Test_user_method() + eval 'bar'->s:addFoo()->assert_equal('barfoo') +endfunc + func Test_failed_call_in_try() try | call UnknownFunc() | catch | endtry endfunc -- cgit From d41b87e070037786bf2cc4486b1839169805ee21 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 18:58:27 +0100 Subject: 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 --- src/nvim/eval.c | 152 ++++++++++++++++++++------------------- src/nvim/eval/userfunc.c | 11 +-- src/nvim/testdir/test_method.vim | 21 ++++-- 3 files changed, 104 insertions(+), 80 deletions(-) (limited to 'src') 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 -- cgit From daedbd93124e3334a1024b5d2c60e91b7aeb8fc5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 20:39:47 +0100 Subject: vim-patch:8.1.1821: no test for wrong number of method arguments Problem: No test for wrong number of method arguments. Solution: Add a test. https://github.com/vim/vim/commit/f97d46f816e84edb6899a903a1c334a50a6d31bb --- src/nvim/testdir/test_method.vim | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index a1fbe7af28..142b259e78 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -102,9 +102,19 @@ func Test_method_funcref() let FuncRef = function('Concat') eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail') + " not enough arguments + call assert_fails("eval 'foo'->FuncRef('bar')", 'E119:') + " too many arguments + call assert_fails("eval 'foo'->FuncRef('bar', 'tail', 'four')", 'E118:') + let Partial = function('Concat', ['two']) eval 'one'->Partial('three')->assert_equal('onetwothree') + " not enough arguments + call assert_fails("eval 'one'->Partial()", 'E119:') + " too many arguments + call assert_fails("eval 'one'->Partial('three', 'four')", 'E118:') + delfunc Concat endfunc -- cgit From 32589341a41f49a11e68d5b080271115787f2dc5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 21:04:17 +0100 Subject: vim-patch:8.1.1828: not strict enough checking syntax of method invocation Problem: Not strict enough checking syntax of method invocation. Solution: Check there is no white space inside ->method(. https://github.com/vim/vim/commit/5184132ec015f5889a3195d911e609d214f06bed --- src/nvim/eval.c | 5 +++++ src/nvim/testdir/test_method.vim | 10 ++++++++++ 2 files changed, 15 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4f934fdc70..23a0ef8506 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4134,6 +4134,11 @@ static int eval_method(char_u **const arg, typval_T *const rettv, EMSG2(_(e_missingparen), name); } ret = FAIL; + } else if (ascii_iswhite((*arg)[-1])) { + if (verbose) { + EMSG(_("E274: No white space allowed before parenthesis")); + } + ret = FAIL; } else { ret = eval_func(arg, name, len, rettv, evaluate, &base); } diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index 142b259e78..58e86131dc 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -118,4 +118,14 @@ func Test_method_funcref() delfunc Concat endfunc +func Test_method_syntax() + eval [1, 2, 3] ->sort( ) + eval [1, 2, 3] + \ ->sort( + \ ) + call assert_fails('eval [1, 2, 3]-> sort()', 'E260:') + call assert_fails('eval [1, 2, 3]->sort ()', 'E274:') + call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 5811390f82c51a4bb15b2e8901e6b5a1d453480a Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 21:08:20 +0100 Subject: vim-patch:8.1.1834: cannot use a lambda as a method Problem: Cannot use a lambda as a method. Solution: Implement ->{lambda}(). (closes vim/vim#4768) https://github.com/vim/vim/commit/22a0c0c4ecd23b6c43f79ba9b92899ca0b426e29 Add an additional lua_funcname argument to call_func_rettv() to maintain support for v:lua. A memory leak was introduced with this patch that was fixed in v8.1.2107. --- src/nvim/eval.c | 153 ++++++++++++++++++++++++++------------- src/nvim/testdir/test_method.vim | 10 +++ 2 files changed, 114 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 23a0ef8506..3646222b91 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -65,6 +65,8 @@ static char *e_missbrac = N_("E111: Missing ']'"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_cannot_mod = N_("E995: Cannot modify existing variable"); +static char *e_nowhitespace + = N_("E274: No white space allowed before parenthesis"); static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); @@ -4100,6 +4102,90 @@ static int eval7( return ret; } +/// Call the function referred to in "rettv". +/// @param lua_funcname If `rettv` refers to a v:lua function, this must point +/// to the name of the Lua function to call (after the +/// "v:lua." prefix). +/// @return OK on success, FAIL on failure. +static int call_func_rettv(char_u **const arg, + typval_T *const rettv, + const bool evaluate, + dict_T *const selfdict, + typval_T *const basetv, + const char_u *const lua_funcname) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + partial_T *pt = NULL; + typval_T functv; + const char_u *funcname; + bool is_lua = false; + + // need to copy the funcref so that we can clear rettv + if (evaluate) { + functv = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Invoke the function. Recursive! + if (functv.v_type == VAR_PARTIAL) { + pt = functv.vval.v_partial; + is_lua = is_luafunc(pt); + funcname = is_lua ? lua_funcname : partial_name(pt); + } else { + funcname = functv.vval.v_string; + } + } else { + funcname = (char_u *)""; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = pt; + funcexe.selfdict = selfdict; + funcexe.basetv = basetv; + const int ret = get_func_tv(funcname, is_lua ? *arg - funcname : -1, rettv, + (char_u **)arg, &funcexe); + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } + + return ret; +} + +/// Evaluate "->method()". +/// @param verbose if true, give error messages. +/// @note "*arg" points to the '-'. +/// @return FAIL or OK. @note "*arg" is advanced to after the ')'. +static int eval_lambda(char_u **const arg, typval_T *const rettv, + const bool evaluate, const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + const int ret = get_lambda_tv(arg, rettv, evaluate); + if (ret == NOTDONE) { + return FAIL; + } else if (**arg != '(') { + if (verbose) { + if (*skipwhite(*arg) == '(') { + EMSG(_(e_nowhitespace)); + } else { + EMSG2(_(e_missingparen), "lambda"); + } + } + tv_clear(rettv); + return FAIL; + } + return call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); +} + /// Evaluate "->method()". /// @note "*arg" points to the '-'. /// @return FAIL or OK. "*arg" is advanced to after the ')'. @@ -4136,7 +4222,7 @@ static int eval_method(char_u **const arg, typval_T *const rettv, ret = FAIL; } else if (ascii_iswhite((*arg)[-1])) { if (verbose) { - EMSG(_("E274: No white space allowed before parenthesis")); + EMSG(_(e_nowhitespace)); } ret = FAIL; } else { @@ -8509,11 +8595,8 @@ handle_subscript( ) { int ret = OK; - dict_T *selfdict = NULL; - const char_u *s; - typval_T functv; - int slen = 0; - bool lua = false; + dict_T *selfdict = NULL; + const char_u *lua_funcname = NULL; if (tv_is_luafunc(rettv)) { if (**arg != '.') { @@ -8522,14 +8605,13 @@ handle_subscript( } else { (*arg)++; - lua = true; - s = (char_u *)(*arg); - slen = check_luafunc_name(*arg, true); - if (slen == 0) { + lua_funcname = (char_u *)(*arg); + const int len = check_luafunc_name(*arg, true); + if (len == 0) { tv_clear(rettv); ret = FAIL; } - (*arg) += slen; + (*arg) += len; } } @@ -8539,42 +8621,12 @@ handle_subscript( && !ascii_iswhite(*(*arg - 1))) || (**arg == '-' && (*arg)[1] == '>'))) { if (**arg == '(') { - partial_T *pt = NULL; - // need to copy the funcref so that we can clear rettv - if (evaluate) { - functv = *rettv; - rettv->v_type = VAR_UNKNOWN; - - // Invoke the function. Recursive! - if (functv.v_type == VAR_PARTIAL) { - pt = functv.vval.v_partial; - if (!lua) { - s = partial_name(pt); - } - } else { - s = functv.vval.v_string; - } - } else { - s = (char_u *)""; - } + ret = call_func_rettv((char_u **)arg, rettv, evaluate, selfdict, NULL, + lua_funcname); - funcexe_T funcexe = FUNCEXE_INIT; - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.evaluate = evaluate; - funcexe.partial = pt; - funcexe.selfdict = selfdict; - ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg, &funcexe); - - // Clear the funcref afterwards, so that deleting it while - // evaluating the arguments is possible (see test55). - if (evaluate) { - tv_clear(&functv); - } - - /* Stop the expression evaluation when immediately aborting on - * error, or when an interrupt occurred or an exception was thrown - * but not caught. */ + // 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); @@ -8584,9 +8636,12 @@ 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; + if ((*arg)[2] == '{') { + // expr->{lambda}() + ret = eval_lambda((char_u **)arg, rettv, evaluate, verbose); + } else { + // expr->name() + ret = eval_method((char_u **)arg, rettv, evaluate, verbose); } } else { // **arg == '[' || **arg == '.' tv_dict_unref(selfdict); diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index 58e86131dc..4788f211e8 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -128,4 +128,14 @@ func Test_method_syntax() call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:') endfunc +func Test_method_lambda() + eval "text"->{x -> x .. " extended"}()->assert_equal('text extended') + eval "text"->{x, y -> x .. " extended " .. y}('more')->assert_equal('text extended more') + + call assert_fails('eval "text"->{x -> x .. " extended"} ()', 'E274:') + + " todo: lambda accepts more arguments than it consumes + " call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From f03dd22f0d37af88f40a27c694c3265026178e80 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 01:08:22 +0100 Subject: vim-patch:8.1.1835: cannot use printf() as a method Problem: Cannot use printf() as a method. Solution: Pass the base as the second argument to printf(). https://github.com/vim/vim/commit/fd8ca21b3ff207e44891aef922935d4adcd140cf --- src/nvim/eval.lua | 2 +- src/nvim/testdir/test_method.vim | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 50b6eaadfd..1821ae51d7 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -258,7 +258,7 @@ return { pathshorten={args=1}, pow={args=2}, prevnonblank={args=1}, - printf={args=varargs(1)}, + printf={args=varargs(1), base=2}, prompt_getprompt={args=1}, prompt_setcallback={args={2, 2}}, prompt_setinterrupt={args={2, 2}}, diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index 4788f211e8..eeb90cd16a 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -72,13 +72,15 @@ func Test_dict_method() endfunc 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()) - call assert_equal(3, 'ABC'->strlen()) - call assert_equal('a^Mb^[c', "a\rb\ec"->strtrans()) - call assert_equal(4, "aあb"->strwidth()) - call assert_equal('axc', 'abc'->substitute('b', 'x', '')) + eval '1 2 3'->split()->assert_equal(['1', '2', '3']) + eval '1 2 3'->split()->map({i, v -> str2nr(v)})->assert_equal([1, 2, 3]) + eval 'ABC'->str2list()->assert_equal([65, 66, 67]) + eval 'ABC'->strlen()->assert_equal(3) + eval "a\rb\ec"->strtrans()->assert_equal('a^Mb^[c') + eval "aあb"->strwidth()->assert_equal(4) + eval 'abc'->substitute('b', 'x', '')->assert_equal('axc') + + eval 'abc'->printf('the %s arg')->assert_equal('the abc arg') endfunc func Test_method_append() -- cgit From 287a77ef51a7a12813d3fa6e1c49c595e5951ba5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 01:28:52 +0100 Subject: vim-patch:8.1.1861: only some assert functions can be used as a method Problem: Only some assert functions can be used as a method. Solution: Allow using most assert functions as a method. https://github.com/vim/vim/commit/24278d2407dfbc8d93eb36593cdd006ff5d86f94 Port tests to assert_spec.lua. --- src/nvim/eval.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 1821ae51d7..a629fd9889 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -33,19 +33,19 @@ return { arglistid={args={0, 2}}, argv={args={0, 2}}, asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc - assert_beeps={args={1}}, + assert_beeps={args={1}, base=1}, assert_equal={args={2, 3}, base=2}, assert_equalfile={args={2, 3}}, assert_exception={args={1, 2}}, - assert_fails={args={1, 3}}, - assert_false={args={1, 2}}, - assert_inrange={args={3, 4}}, - assert_match={args={2, 3}}, + assert_fails={args={1, 3}, base=1}, + assert_false={args={1, 2}, base=1}, + assert_inrange={args={3, 4}, base=3}, + assert_match={args={2, 3}, base=2}, assert_nobeep={args={1}}, assert_notequal={args={2, 3}, base=2}, - assert_notmatch={args={2, 3}}, + assert_notmatch={args={2, 3}, base=2}, assert_report={args=1}, - assert_true={args={1, 2}}, + assert_true={args={1, 2}, base=1}, atan={args=1, func="float_op_wrapper", data="&atan"}, atan2={args=2}, browse={args=4}, -- cgit From 98dfe4adc48d83e05b2ee039869853760de9c096 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 15:10:28 +0100 Subject: vim-patch:8.1.1863: confusing error when using a builtin function as method Problem: Confusing error when using a builtin function as method while it does not support that. Solution: Add a specific error message. https://github.com/vim/vim/commit/9174639a82799011cfa0013cbc4c4709b3833bf0 --- src/nvim/eval/funcs.c | 4 +++- src/nvim/eval/userfunc.c | 3 +++ src/nvim/eval/userfunc.h | 1 + src/nvim/testdir/test_method.vim | 4 ++++ 4 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e090f3b37f..8a1258efd4 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -199,8 +199,10 @@ int call_internal_method(const char_u *const fname, const int argcount, FUNC_ATTR_NONNULL_ALL { const VimLFuncDef *const fdef = find_internal_func((const char *)fname); - if (fdef == NULL || fdef->base_arg == BASE_NONE) { + if (fdef == NULL) { return ERROR_UNKNOWN; + } else if (fdef->base_arg == BASE_NONE) { + return ERROR_NOTMETHOD; } else if (argcount + 1 < fdef->min_argc) { return ERROR_TOOFEW; } else if (argcount + 1 > fdef->max_argc) { diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 9ecde327de..59f07ff486 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1399,6 +1399,9 @@ static void user_func_error(int error, const char_u *name) case ERROR_UNKNOWN: emsg_funcname(N_("E117: Unknown function: %s"), name); break; + case ERROR_NOTMETHOD: + emsg_funcname(N_("E276: Cannot use function as a method: %s"), name); + break; case ERROR_DELETED: emsg_funcname(N_("E933: Function was deleted: %s"), name); break; diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 513473449a..3f111343d2 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -28,6 +28,7 @@ typedef enum { ERROR_OTHER, ERROR_BOTH, ERROR_DELETED, + ERROR_NOTMETHOD, } FnameTransError; /// Used in funcexe_T. Returns the new argcount. diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index eeb90cd16a..f08ca4e7f1 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -140,4 +140,8 @@ func Test_method_lambda() " call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:') endfunc +func Test_method_not_supported() + call assert_fails('eval 123->changenr()', 'E276:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 7474db98afcae3a47732d1ba99971b500a519cb2 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 15:18:04 +0100 Subject: vim-patch:8.1.1878: negative float before method not parsed correctly Problem: Negative float before method not parsed correctly. Solution: Apply "!" and "-" in front of expression before using ->. https://github.com/vim/vim/commit/9cfe8f6e68de4bfb5942d84f4465de914a747b3f --- src/nvim/eval.c | 122 ++++++++++++++++++++++++--------------- src/nvim/eval/userfunc.c | 3 +- src/nvim/testdir/test_method.vim | 5 ++ 3 files changed, 82 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3646222b91..5c8f90c490 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1730,7 +1730,9 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) } else { // handle d.key, l[idx], f(expr) const char *const arg_subsc = arg; - if (handle_subscript(&arg, &tv, true, true) == FAIL) { + if (handle_subscript(&arg, &tv, true, true, (const char_u *)name, + (const char_u **)&name) + == FAIL) { error = true; } else { if (arg == arg_subsc && len == 2 && name[1] == ':') { @@ -3885,10 +3887,10 @@ static int eval7( { varnumber_T n; int len; - char_u *s; - char_u *start_leader, *end_leader; + char_u *s; + const char_u *start_leader, *end_leader; int ret = OK; - char_u *alias; + char_u *alias; // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. @@ -4054,51 +4056,66 @@ static int eval7( // Handle following '[', '(' and '.' for expr[expr], expr.name, // expr(expr), expr->name(expr) if (ret == OK) { - ret = handle_subscript((const char **)arg, rettv, evaluate, true); + ret = handle_subscript((const char **)arg, rettv, evaluate, true, + start_leader, &end_leader); } // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - bool error = false; - varnumber_T val = 0; - float_T f = 0.0; + ret = eval7_leader(rettv, start_leader, &end_leader); + } + return ret; +} - if (rettv->v_type == VAR_FLOAT) { - f = rettv->vval.v_float; - } else { - val = tv_get_number_chk(rettv, &error); +/// Apply the leading "!" and "-" before an eval7 expression to "rettv". +/// Adjusts "end_leaderp" until it is at "start_leader". +/// @return OK on success, FAIL on failure. +static int eval7_leader(typval_T *const rettv, const char_u *const start_leader, + const char_u **const end_leaderp) + FUNC_ATTR_NONNULL_ALL +{ + const char_u *end_leader = *end_leaderp; + int ret = OK; + bool error = false; + varnumber_T val = 0; + float_T f = 0.0; + + if (rettv->v_type == VAR_FLOAT) { + f = rettv->vval.v_float; + } else { + val = tv_get_number_chk(rettv, &error); + } + if (error) { + tv_clear(rettv); + ret = FAIL; + } else { + while (end_leader > start_leader) { + end_leader--; + if (*end_leader == '!') { + if (rettv->v_type == VAR_FLOAT) { + f = !f; + } else { + val = !val; + } + } else if (*end_leader == '-') { + if (rettv->v_type == VAR_FLOAT) { + f = -f; + } else { + val = -val; + } + } } - if (error) { + if (rettv->v_type == VAR_FLOAT) { tv_clear(rettv); - ret = FAIL; + rettv->vval.v_float = f; } else { - while (end_leader > start_leader) { - --end_leader; - if (*end_leader == '!') { - if (rettv->v_type == VAR_FLOAT) { - f = !f; - } else { - val = !val; - } - } else if (*end_leader == '-') { - if (rettv->v_type == VAR_FLOAT) { - f = -f; - } else { - val = -val; - } - } - } - if (rettv->v_type == VAR_FLOAT) { - tv_clear(rettv); - rettv->vval.v_float = f; - } else { - tv_clear(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = val; - } + tv_clear(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = val; } } + *end_leaderp = end_leader; return ret; } @@ -8590,8 +8607,10 @@ int handle_subscript( const char **const arg, typval_T *rettv, - int evaluate, // do more than finding the end - int verbose // give error messages + int evaluate, // do more than finding the end + int verbose, // give error messages + const char_u *const start_leader, // start of '!' and '-' prefixes + const char_u **const end_leaderp // end of '!' and '-' prefixes ) { int ret = OK; @@ -8636,12 +8655,19 @@ handle_subscript( tv_dict_unref(selfdict); selfdict = NULL; } else if (**arg == '-') { - if ((*arg)[2] == '{') { - // expr->{lambda}() - ret = eval_lambda((char_u **)arg, rettv, evaluate, verbose); - } else { - // expr->name() - ret = eval_method((char_u **)arg, rettv, evaluate, verbose); + // Expression "-1.0->method()" applies the leader "-" before + // applying ->. + if (evaluate && *end_leaderp > start_leader) { + ret = eval7_leader(rettv, start_leader, end_leaderp); + } + if (ret == OK) { + if ((*arg)[2] == '{') { + // expr->{lambda}() + ret = eval_lambda((char_u **)arg, rettv, evaluate, verbose); + } else { + // expr->name() + ret = eval_method((char_u **)arg, rettv, evaluate, verbose); + } } } else { // **arg == '[' || **arg == '.' tv_dict_unref(selfdict); @@ -10926,7 +10952,9 @@ bool var_exists(const char *var) n = get_var_tv(name, len, &tv, NULL, false, true) == OK; if (n) { // Handle d.key, l[idx], f(expr). - n = handle_subscript(&var, &tv, true, false) == OK; + n = handle_subscript(&var, &tv, true, false, (const char_u *)name, + (const char_u **)&name) + == OK; if (n) { tv_clear(&tv); } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 59f07ff486..4f10d31615 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2980,7 +2980,8 @@ void ex_call(exarg_T *eap) } // Handle a function returning a Funcref, Dictionary or List. - if (handle_subscript((const char **)&arg, &rettv, true, true) + if (handle_subscript((const char **)&arg, &rettv, true, true, + (const char_u *)name, (const char_u **)&name) == FAIL) { failed = true; break; diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index f08ca4e7f1..7cb4eec400 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -120,6 +120,11 @@ func Test_method_funcref() delfunc Concat endfunc +func Test_method_float() + eval 1.234->string()->assert_equal('1.234') + eval -1.234->string()->assert_equal('-1.234') +endfunc + func Test_method_syntax() eval [1, 2, 3] ->sort( ) eval [1, 2, 3] -- cgit From 41dbd3a2e05f5f4d59e076f8b8c82ad974d2bbd5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 16:27:23 +0100 Subject: vim-patch:8.1.1879: more functions can be used as methods Problem: More functions can be used as methods. Solution: Make float functions usable as a method. https://github.com/vim/vim/commit/93cf85f9ef02931de3f8c8e536a137da0b48b7dc Fix atan2() doc typo (patch referred to it as atan()). Adjust Test_fmod() method test to expect "str2float('nan')". --- src/nvim/eval.lua | 50 ++++++++++++++++++------------------ src/nvim/testdir/test_float_func.vim | 27 +++++++++++++++++++ 2 files changed, 52 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index a629fd9889..1cac57a8ff 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -21,8 +21,8 @@ local LAST = "BASE_LAST" return { funcs={ - abs={args=1}, - acos={args=1, func="float_op_wrapper", data="&acos"}, -- WJMc + abs={args=1, base=1}, + acos={args=1, base=1, func="float_op_wrapper", data="&acos"}, -- WJMc add={args=2, base=1}, ['and']={args=2}, api_info={}, @@ -32,7 +32,7 @@ return { argidx={}, arglistid={args={0, 2}}, argv={args={0, 2}}, - asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc + asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc assert_beeps={args={1}, base=1}, assert_equal={args={2, 3}, base=2}, assert_equalfile={args={2, 3}}, @@ -46,8 +46,8 @@ return { assert_notmatch={args={2, 3}, base=2}, assert_report={args=1}, assert_true={args={1, 2}, base=1}, - atan={args=1, func="float_op_wrapper", data="&atan"}, - atan2={args=2}, + atan={args=1, base=1, func="float_op_wrapper", data="&atan"}, + atan2={args=2, base=1}, browse={args=4}, browsedir={args=2}, bufadd={args=1}, @@ -66,7 +66,7 @@ return { byteidx={args=2}, byteidxcomp={args=2}, call={args={2, 3}}, - ceil={args=1, func="float_op_wrapper", data="&ceil"}, + ceil={args=1, base=1, func="float_op_wrapper", data="&ceil"}, changenr={}, chanclose={args={1, 2}}, chansend={args=2}, @@ -81,8 +81,8 @@ return { complete_info={args={0, 1}}, confirm={args={1, 4}}, copy={args=1, base=1}, - cos={args=1, func="float_op_wrapper", data="&cos"}, - cosh={args=1, func="float_op_wrapper", data="&cosh"}, + cos={args=1, base=1, func="float_op_wrapper", data="&cos"}, + cosh={args=1, base=1, func="float_op_wrapper", data="&cosh"}, count={args={2, 4}, base=1}, cscope_connection={args={0, 3}}, ctxget={args={0, 1}}, @@ -109,7 +109,7 @@ return { execute={args={1, 2}}, exepath={args=1}, exists={args=1}, - exp={args=1, func="float_op_wrapper", data="&exp"}, + exp={args=1, base=1, func="float_op_wrapper", data="&exp"}, expand={args={1, 3}}, expandcmd={args=1}, extend={args={2, 3}, base=1}, @@ -121,9 +121,9 @@ return { finddir={args={1, 3}}, findfile={args={1, 3}}, flatten={args={1, 2}}, - float2nr={args=1}, - floor={args=1, func="float_op_wrapper", data="&floor"}, - fmod={args=2}, + float2nr={args=1, base=1}, + floor={args=1, base=1, func="float_op_wrapper", data="&floor"}, + fmod={args=2, base=1}, fnameescape={args=1}, fnamemodify={args=2}, foldclosed={args=1}, @@ -205,9 +205,9 @@ return { interrupt={args=0}, invert={args=1}, isdirectory={args=1}, - isinf={args=1}, + isinf={args=1, base=1}, islocked={args=1}, - isnan={args=1}, + isnan={args=1, base=1}, id={args=1}, items={args=1, base=1}, jobclose={args={1, 2}, func="f_chanclose"}, @@ -230,8 +230,8 @@ return { lispindent={args=1}, list2str={args={1, 2}}, localtime={}, - log={args=1, func="float_op_wrapper", data="&log"}, - log10={args=1, func="float_op_wrapper", data="&log10"}, + log={args=1, base=1, func="float_op_wrapper", data="&log"}, + log10={args=1, base=1, func="float_op_wrapper", data="&log10"}, luaeval={args={1, 2}}, map={args=2, base=1}, maparg={args={1, 4}}, @@ -256,7 +256,7 @@ return { nr2char={args={1, 2}}, ['or']={args=2}, pathshorten={args=1}, - pow={args=2}, + pow={args=2, base=1}, prevnonblank={args=1}, printf={args=varargs(1), base=2}, prompt_getprompt={args=1}, @@ -282,7 +282,7 @@ return { ['repeat']={args=2, base=1}, resolve={args=1}, reverse={args=1, base=1}, - round={args=1, func="float_op_wrapper", data="&round"}, + round={args=1, base=1, func="float_op_wrapper", data="&round"}, rpcnotify={args=varargs(2)}, rpcrequest={args=varargs(2)}, rpcstart={args={1, 2}}, @@ -331,8 +331,8 @@ return { sign_unplace={args={1, 2}}, sign_unplacelist={args={1}}, simplify={args=1}, - sin={args=1, func="float_op_wrapper", data="&sin"}, - sinh={args=1, func="float_op_wrapper", data="&sinh"}, + sin={args=1, base=1, func="float_op_wrapper", data="&sin"}, + sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"}, sockconnect={args={2,3}}, sort={args={1, 3}, base=1}, soundfold={args=1}, @@ -340,9 +340,9 @@ return { spellbadword={args={0, 1}}, spellsuggest={args={1, 3}}, split={args={1, 3}, base=1}, - sqrt={args=1, func="float_op_wrapper", data="&sqrt"}, + sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"}, stdpath={args=1}, - str2float={args=1}, + str2float={args=1, base=1}, str2list={args={1, 2}, base=1}, str2nr={args={1, 2}}, strcharpart={args={2, 3}}, @@ -374,8 +374,8 @@ return { tabpagewinnr={args={1, 2}}, tagfiles={}, taglist={args={1, 2}}, - tan={args=1, func="float_op_wrapper", data="&tan"}, - tanh={args=1, func="float_op_wrapper", data="&tanh"}, + tan={args=1, base=1, func="float_op_wrapper", data="&tan"}, + tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"}, tempname={}, termopen={args={1, 2}}, test_garbagecollect_now={}, @@ -389,7 +389,7 @@ return { toupper={args=1}, tr={args=3}, trim={args={1,3}}, - trunc={args=1, func="float_op_wrapper", data="&trunc"}, + trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"}, type={args=1, base=1}, undofile={args=1}, undotree={}, diff --git a/src/nvim/testdir/test_float_func.vim b/src/nvim/testdir/test_float_func.vim index 154ef570e0..78675d7016 100644 --- a/src/nvim/testdir/test_float_func.vim +++ b/src/nvim/testdir/test_float_func.vim @@ -7,6 +7,8 @@ end func Test_abs() call assert_equal('1.23', string(abs(1.23))) call assert_equal('1.23', string(abs(-1.23))) + eval -1.23->abs()->string()->assert_equal('1.23') + call assert_equal('0.0', string(abs(0.0))) call assert_equal('0.0', string(abs(1.0/(1.0/0.0)))) call assert_equal('0.0', string(abs(-1.0/(1.0/0.0)))) @@ -22,6 +24,7 @@ endfunc func Test_sqrt() call assert_equal('0.0', string(sqrt(0.0))) call assert_equal('1.414214', string(sqrt(2.0))) + eval 2.0->sqrt()->string()->assert_equal('1.414214') call assert_equal("str2float('inf')", string(sqrt(1.0/0.0))) call assert_equal("str2float('nan')", string(sqrt(-1.0))) call assert_equal("str2float('nan')", string(sqrt(0.0/0.0))) @@ -31,6 +34,7 @@ endfunc func Test_log() call assert_equal('0.0', string(log(1.0))) call assert_equal('-0.693147', string(log(0.5))) + eval 0.5->log()->string()->assert_equal('-0.693147') call assert_equal("-str2float('inf')", string(log(0.0))) call assert_equal("str2float('nan')", string(log(-1.0))) call assert_equal("str2float('inf')", string(log(1.0/0.0))) @@ -42,6 +46,7 @@ func Test_log10() call assert_equal('0.0', string(log10(1.0))) call assert_equal('2.0', string(log10(100.0))) call assert_equal('2.079181', string(log10(120.0))) + eval 120.0->log10()->string()->assert_equal('2.079181') call assert_equal("-str2float('inf')", string(log10(0.0))) call assert_equal("str2float('nan')", string(log10(-1.0))) call assert_equal("str2float('inf')", string(log10(1.0/0.0))) @@ -53,6 +58,7 @@ func Test_exp() call assert_equal('1.0', string(exp(0.0))) call assert_equal('7.389056', string(exp(2.0))) call assert_equal('0.367879', string(exp(-1.0))) + eval -1.0->exp()->string()->assert_equal('0.367879') call assert_equal("str2float('inf')", string(exp(1.0/0.0))) call assert_equal('0.0', string(exp(-1.0/0.0))) call assert_equal("str2float('nan')", string(exp(0.0/0.0))) @@ -63,6 +69,7 @@ func Test_sin() call assert_equal('0.0', string(sin(0.0))) call assert_equal('0.841471', string(sin(1.0))) call assert_equal('-0.479426', string(sin(-0.5))) + eval -0.5->sin()->string()->assert_equal('-0.479426') call assert_equal("str2float('nan')", string(sin(0.0/0.0))) call assert_equal("str2float('nan')", string(sin(1.0/0.0))) call assert_equal('0.0', string(sin(1.0/(1.0/0.0)))) @@ -73,6 +80,8 @@ endfunc func Test_asin() call assert_equal('0.0', string(asin(0.0))) call assert_equal('1.570796', string(asin(1.0))) + eval 1.0->asin()->string()->assert_equal('1.570796') + call assert_equal('-0.523599', string(asin(-0.5))) call assert_equal("str2float('nan')", string(asin(1.1))) call assert_equal("str2float('nan')", string(asin(1.0/0.0))) @@ -84,6 +93,7 @@ func Test_sinh() call assert_equal('0.0', string(sinh(0.0))) call assert_equal('0.521095', string(sinh(0.5))) call assert_equal('-1.026517', string(sinh(-0.9))) + eval -0.9->sinh()->string()->assert_equal('-1.026517') call assert_equal("str2float('inf')", string(sinh(1.0/0.0))) call assert_equal("-str2float('inf')", string(sinh(-1.0/0.0))) call assert_equal("str2float('nan')", string(sinh(0.0/0.0))) @@ -94,6 +104,7 @@ func Test_cos() call assert_equal('1.0', string(cos(0.0))) call assert_equal('0.540302', string(cos(1.0))) call assert_equal('0.877583', string(cos(-0.5))) + eval -0.5->cos()->string()->assert_equal('0.877583') call assert_equal("str2float('nan')", string(cos(0.0/0.0))) call assert_equal("str2float('nan')", string(cos(1.0/0.0))) call assert_fails('call cos("")', 'E808:') @@ -103,6 +114,7 @@ func Test_acos() call assert_equal('1.570796', string(acos(0.0))) call assert_equal('0.0', string(acos(1.0))) call assert_equal('3.141593', string(acos(-1.0))) + eval -1.0->acos()->string()->assert_equal('3.141593') call assert_equal('2.094395', string(acos(-0.5))) call assert_equal("str2float('nan')", string(acos(1.1))) call assert_equal("str2float('nan')", string(acos(1.0/0.0))) @@ -113,6 +125,7 @@ endfunc func Test_cosh() call assert_equal('1.0', string(cosh(0.0))) call assert_equal('1.127626', string(cosh(0.5))) + eval 0.5->cosh()->string()->assert_equal('1.127626') call assert_equal("str2float('inf')", string(cosh(1.0/0.0))) call assert_equal("str2float('inf')", string(cosh(-1.0/0.0))) call assert_equal("str2float('nan')", string(cosh(0.0/0.0))) @@ -123,6 +136,7 @@ func Test_tan() call assert_equal('0.0', string(tan(0.0))) call assert_equal('0.546302', string(tan(0.5))) call assert_equal('-0.546302', string(tan(-0.5))) + eval -0.5->tan()->string()->assert_equal('-0.546302') call assert_equal("str2float('nan')", string(tan(1.0/0.0))) call assert_equal("str2float('nan')", string(cos(0.0/0.0))) call assert_equal('0.0', string(tan(1.0/(1.0/0.0)))) @@ -134,6 +148,7 @@ func Test_atan() call assert_equal('0.0', string(atan(0.0))) call assert_equal('0.463648', string(atan(0.5))) call assert_equal('-0.785398', string(atan(-1.0))) + eval -1.0->atan()->string()->assert_equal('-0.785398') call assert_equal('1.570796', string(atan(1.0/0.0))) call assert_equal('-1.570796', string(atan(-1.0/0.0))) call assert_equal("str2float('nan')", string(atan(0.0/0.0))) @@ -144,6 +159,7 @@ func Test_atan2() call assert_equal('-2.356194', string(atan2(-1, -1))) call assert_equal('2.356194', string(atan2(1, -1))) call assert_equal('0.0', string(atan2(1.0, 1.0/0.0))) + eval 1.0->atan2(1.0/0.0)->string()->assert_equal('0.0') call assert_equal('1.570796', string(atan2(1.0/0.0, 1.0))) call assert_equal("str2float('nan')", string(atan2(0.0/0.0, 1.0))) call assert_fails('call atan2("", -1)', 'E808:') @@ -154,6 +170,7 @@ func Test_tanh() call assert_equal('0.0', string(tanh(0.0))) call assert_equal('0.462117', string(tanh(0.5))) call assert_equal('-0.761594', string(tanh(-1.0))) + eval -1.0->tanh()->string()->assert_equal('-0.761594') call assert_equal('1.0', string(tanh(1.0/0.0))) call assert_equal('-1.0', string(tanh(-1.0/0.0))) call assert_equal("str2float('nan')", string(tanh(0.0/0.0))) @@ -164,6 +181,7 @@ func Test_fmod() call assert_equal('0.13', string(fmod(12.33, 1.22))) call assert_equal('-0.13', string(fmod(-12.33, 1.22))) call assert_equal("str2float('nan')", string(fmod(1.0/0.0, 1.0))) + eval (1.0/0.0)->fmod(1.0)->string()->assert_equal("str2float('nan')") " On Windows we get "nan" instead of 1.0, accept both. let res = string(fmod(1.0, 1.0/0.0)) if res != "str2float('nan')" @@ -177,6 +195,7 @@ endfunc func Test_pow() call assert_equal('1.0', string(pow(0.0, 0.0))) call assert_equal('8.0', string(pow(2.0, 3.0))) + eval 2.0->pow(3.0)->string()->assert_equal('8.0') call assert_equal("str2float('nan')", string(pow(2.0, 0.0/0.0))) call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0))) call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0))) @@ -192,6 +211,7 @@ func Test_str2float() call assert_equal('1.0', string(str2float(' 1.0 '))) call assert_equal('1.23', string(str2float('1.23'))) call assert_equal('1.23', string(str2float('1.23abc'))) + eval '1.23abc'->str2float()->string()->assert_equal('1.23') call assert_equal('1.0e40', string(str2float('1e40'))) call assert_equal('-1.23', string(str2float('-1.23'))) call assert_equal('1.23', string(str2float(' + 1.23 '))) @@ -228,6 +248,7 @@ func Test_float2nr() call assert_equal(1, float2nr(1.234)) call assert_equal(123, float2nr(1.234e2)) call assert_equal(12, float2nr(123.4e-1)) + eval 123.4e-1->float2nr()->assert_equal(12) let max_number = 1/0 let min_number = -max_number call assert_equal(max_number/2+1, float2nr(pow(2, 62))) @@ -242,6 +263,7 @@ func Test_floor() call assert_equal('2.0', string(floor(2.0))) call assert_equal('2.0', string(floor(2.11))) call assert_equal('2.0', string(floor(2.99))) + eval 2.99->floor()->string()->assert_equal('2.0') call assert_equal('-3.0', string(floor(-2.11))) call assert_equal('-3.0', string(floor(-2.99))) call assert_equal("str2float('nan')", string(floor(0.0/0.0))) @@ -255,6 +277,7 @@ func Test_ceil() call assert_equal('3.0', string(ceil(2.11))) call assert_equal('3.0', string(ceil(2.99))) call assert_equal('-2.0', string(ceil(-2.11))) + eval -2.11->ceil()->string()->assert_equal('-2.0') call assert_equal('-2.0', string(ceil(-2.99))) call assert_equal("str2float('nan')", string(ceil(0.0/0.0))) call assert_equal("str2float('inf')", string(ceil(1.0/0.0))) @@ -266,6 +289,7 @@ func Test_round() call assert_equal('2.0', string(round(2.1))) call assert_equal('3.0', string(round(2.5))) call assert_equal('3.0', string(round(2.9))) + eval 2.9->round()->string()->assert_equal('3.0') call assert_equal('-2.0', string(round(-2.1))) call assert_equal('-3.0', string(round(-2.5))) call assert_equal('-3.0', string(round(-2.9))) @@ -279,6 +303,7 @@ func Test_trunc() call assert_equal('2.0', string(trunc(2.1))) call assert_equal('2.0', string(trunc(2.5))) call assert_equal('2.0', string(trunc(2.9))) + eval 2.9->trunc()->string()->assert_equal('2.0') call assert_equal('-2.0', string(trunc(-2.1))) call assert_equal('-2.0', string(trunc(-2.5))) call assert_equal('-2.0', string(trunc(-2.9))) @@ -291,6 +316,7 @@ endfunc func Test_isinf() call assert_equal(1, isinf(1.0/0.0)) call assert_equal(-1, isinf(-1.0/0.0)) + eval (-1.0/0.0)->isinf()->assert_equal(-1) call assert_false(isinf(1.0)) call assert_false(isinf(0.0/0.0)) call assert_false(isinf('a')) @@ -302,6 +328,7 @@ func Test_isnan() call assert_true(isnan(0.0/0.0)) call assert_false(isnan(1.0)) call assert_false(isnan(1.0/0.0)) + eval (1.0/0.0)->isnan()->assert_false() call assert_false(isnan(-1.0/0.0)) call assert_false(isnan('a')) call assert_false(isnan([])) -- cgit From 5fbc1a49c719c0c93fa73277ac30d17f797d096d Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 17:06:08 +0100 Subject: vim-patch:8.1.1888: more functions can be used as methods Problem: More functions can be used as methods. Solution: Make various functions usable as a method. https://github.com/vim/vim/commit/073e4b92e613d22ce7b16e0fbf5c0e40cb5f9b2c test_popup.vim already has the changes from this patch (they're N/A anyway). --- src/nvim/eval.lua | 24 ++++++++++++------------ src/nvim/testdir/test_arglist.vim | 4 ++-- src/nvim/testdir/test_functions.vim | 6 +++--- src/nvim/testdir/test_hide.vim | 2 +- src/nvim/testdir/test_vimscript.vim | 4 ++++ 5 files changed, 22 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 1cac57a8ff..6f8645c806 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -24,7 +24,7 @@ return { abs={args=1, base=1}, acos={args=1, base=1, func="float_op_wrapper", data="&acos"}, -- WJMc add={args=2, base=1}, - ['and']={args=2}, + ['and']={args=2, base=1}, api_info={}, append={args=2, base=LAST}, appendbufline={args=3, base=LAST}, @@ -50,16 +50,16 @@ return { atan2={args=2, base=1}, browse={args=4}, browsedir={args=2}, - bufadd={args=1}, - bufexists={args=1}, - buffer_exists={args=1, func='f_bufexists'}, -- obsolete + bufadd={args=1, base=1}, + bufexists={args=1, base=1}, + buffer_exists={args=1, base=1, func='f_bufexists'}, -- obsolete buffer_name={args={0, 1}, func='f_bufname'}, -- obsolete buffer_number={args={0, 1}, func='f_bufnr'}, -- obsolete - buflisted={args=1}, - bufload={args=1}, - bufloaded={args=1}, - bufname={args={0, 1}}, - bufnr={args={0, 2}}, + buflisted={args=1, base=1}, + bufload={args=1, base=1}, + bufloaded={args=1, base=1}, + bufname={args={0, 1}, base=1}, + bufnr={args={0, 2}, base=1}, bufwinid={args=1}, bufwinnr={args=1}, byte2line={args=1}, @@ -203,7 +203,7 @@ return { inputsecret={args={1, 2}}, insert={args={2, 3}, base=1}, interrupt={args=0}, - invert={args=1}, + invert={args=1, base=1}, isdirectory={args=1}, isinf={args=1, base=1}, islocked={args=1}, @@ -254,7 +254,7 @@ return { msgpackparse={args=1}, nextnonblank={args=1}, nr2char={args={1, 2}}, - ['or']={args=2}, + ['or']={args=2, base=1}, pathshorten={args=1}, pow={args=2, base=1}, prevnonblank={args=1}, @@ -421,6 +421,6 @@ return { winwidth={args=1}, wordcount={}, writefile={args={2, 3}}, - xor={args=2}, + xor={args=2, base=1}, }, } diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index a1ef8325ec..01d8f32893 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -90,8 +90,8 @@ func Test_argadd_empty_curbuf() call assert_equal('', bufname('%')) call assert_equal(1, line('$')) rew - call assert_notequal(curbuf, bufnr('%')) - call assert_equal('Xargadd', bufname('%')) + call assert_notequal(curbuf, '%'->bufnr()) + call assert_equal('Xargadd', '%'->bufname()) call assert_equal(2, line('$')) %argd diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 2493cc2e27..1c56e936a8 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1414,12 +1414,12 @@ func Test_bufadd_bufload() let curbuf = bufnr('') call writefile(['some', 'text'], 'XotherName') - let buf = bufadd('XotherName') + let buf = 'XotherName'->bufadd() call assert_notequal(0, buf) - call assert_equal(1, bufexists('XotherName')) + eval 'XotherName'->bufexists()->assert_equal(1) call assert_equal(0, getbufvar(buf, '&buflisted')) call assert_equal(0, bufloaded(buf)) - call bufload(buf) + eval buf->bufload() call assert_equal(1, bufloaded(buf)) call assert_equal(['some', 'text'], getbufline(buf, 1, '$')) call assert_equal(curbuf, bufnr('')) diff --git a/src/nvim/testdir/test_hide.vim b/src/nvim/testdir/test_hide.vim index 128b8ff945..41b1a4ad7c 100644 --- a/src/nvim/testdir/test_hide.vim +++ b/src/nvim/testdir/test_hide.vim @@ -37,7 +37,7 @@ function Test_hide() " :hide as a command hide call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')]) - call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + call assert_equal([1, 1], ['Xf1'->buflisted(), 'Xf1'->bufloaded()]) bwipeout! Xf1 new Xf1 diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 5922660ef9..d5837e88c9 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1372,6 +1372,7 @@ func Test_bitwise_functions() " and call assert_equal(127, and(127, 127)) call assert_equal(16, and(127, 16)) + eval 127->and(16)->assert_equal(16) call assert_equal(0, and(127, 128)) call assert_fails("call and(1.0, 1)", 'E805:') call assert_fails("call and([], 1)", 'E745:') @@ -1382,6 +1383,7 @@ func Test_bitwise_functions() " or call assert_equal(23, or(16, 7)) call assert_equal(15, or(8, 7)) + eval 8->or(7)->assert_equal(15) call assert_equal(123, or(0, 123)) call assert_fails("call or(1.0, 1)", 'E805:') call assert_fails("call or([], 1)", 'E745:') @@ -1392,6 +1394,7 @@ func Test_bitwise_functions() " xor call assert_equal(0, xor(127, 127)) call assert_equal(111, xor(127, 16)) + eval 127->xor(16)->assert_equal(111) call assert_equal(255, xor(127, 128)) call assert_fails("call xor(1.0, 1)", 'E805:') call assert_fails("call xor([], 1)", 'E745:') @@ -1401,6 +1404,7 @@ func Test_bitwise_functions() call assert_fails("call xor(1, {})", 'E728:') " invert call assert_equal(65408, and(invert(127), 65535)) + eval 127->invert()->and(65535)->assert_equal(65408) call assert_equal(65519, and(invert(16), 65535)) call assert_equal(65407, and(invert(128), 65535)) call assert_fails("call invert(1.0)", 'E805:') -- cgit From 7925f0b6330c18c9391d02f844cd17b41fbb3d2e Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 17:53:43 +0100 Subject: vim-patch:8.1.1909: more functions can be used as methods Problem: More functions can be used as methods. Solution: Make a few more functions usable as a method. https://github.com/vim/vim/commit/e49fbff384e45dd17fed72321c26937edf6de16b --- src/nvim/eval.lua | 10 +++++----- src/nvim/testdir/test_assert.vim | 2 +- src/nvim/testdir/test_bufline.vim | 6 +++--- src/nvim/testdir/test_bufwintabinfo.vim | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 6f8645c806..722f413d82 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -35,7 +35,7 @@ return { asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc assert_beeps={args={1}, base=1}, assert_equal={args={2, 3}, base=2}, - assert_equalfile={args={2, 3}}, + assert_equalfile={args={2, 3}, base=1}, assert_exception={args={1, 2}}, assert_fails={args={1, 3}, base=1}, assert_false={args={1, 2}, base=1}, @@ -44,7 +44,7 @@ return { assert_nobeep={args={1}}, assert_notequal={args={2, 3}, base=2}, assert_notmatch={args={2, 3}, base=2}, - assert_report={args=1}, + assert_report={args=1, base=1}, assert_true={args={1, 2}, base=1}, atan={args=1, base=1, func="float_op_wrapper", data="&atan"}, atan2={args=2, base=1}, @@ -60,8 +60,8 @@ return { bufloaded={args=1, base=1}, bufname={args={0, 1}, base=1}, bufnr={args={0, 2}, base=1}, - bufwinid={args=1}, - bufwinnr={args=1}, + bufwinid={args=1, base=1}, + bufwinnr={args=1, base=1}, byte2line={args=1}, byteidx={args=2}, byteidxcomp={args=2}, @@ -408,7 +408,7 @@ return { win_id2win={args=1}, win_screenpos={args=1}, win_splitmove={args={2, 3}}, - winbufnr={args=1}, + winbufnr={args=1, base=1}, wincol={}, windowsversion={}, winheight={args=1}, diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index 1d114221dc..52f243aaea 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -7,7 +7,7 @@ func Test_assert_equalfile() let goodtext = ["one", "two", "three"] call writefile(goodtext, 'Xone') - call assert_equal(1, assert_equalfile('Xone', 'xyzxyz')) + call assert_equal(1, 'Xone'->assert_equalfile('xyzxyz')) call assert_match("E485: Can't read file xyzxyz", v:errors[0]) call remove(v:errors, 0) diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index e038bce08e..2a8c6915e2 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -131,11 +131,11 @@ func Test_appendbufline_redraw() endif let lines =<< trim END new foo - let winnr=bufwinnr('foo') - let buf=bufnr('foo') + let winnr = 'foo'->bufwinnr() + let buf = bufnr('foo') wincmd p call appendbufline(buf, '$', range(1,200)) - exe winnr. 'wincmd w' + exe winnr .. 'wincmd w' norm! G wincmd p call deletebufline(buf, 1, '$') diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim index cb7ab44798..4b5b55e6bf 100644 --- a/src/nvim/testdir/test_bufwintabinfo.vim +++ b/src/nvim/testdir/test_bufwintabinfo.vim @@ -18,7 +18,7 @@ function Test_getbufwintabinfo() let l = getbufinfo('%') call assert_equal(bufnr('%'), l[0].bufnr) call assert_equal('vim', l[0].variables.editor) - call assert_notequal(-1, index(l[0].windows, bufwinid('%'))) + call assert_notequal(-1, index(l[0].windows, '%'->bufwinid())) " Test for getbufinfo() with 'bufmodified' call assert_equal(0, len(getbufinfo({'bufmodified' : 1}))) -- cgit From 56b56a76e8294a319d2db32581f82421d4a4d446 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 18:09:39 +0100 Subject: vim-patch:8.1.1911: more functions can be used as methods Problem: More functions can be used as methods. Solution: Make a few more functions usable as a method. https://github.com/vim/vim/commit/64b4d73524b9a2304d89b87529cd8d3cef14b856 Note that the old-style version of Test_byteidx() was already translated to a Lua test in 069_multibyte_formatting_spec.lua. Keep both versions, using Test_byteidx() to mainly test the method call syntax for byteidx() and byteidxcomp(). --- src/nvim/eval.lua | 8 ++++---- src/nvim/testdir/test_functions.vim | 32 +++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 722f413d82..24f4b5fc49 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -62,10 +62,10 @@ return { bufnr={args={0, 2}, base=1}, bufwinid={args=1, base=1}, bufwinnr={args=1, base=1}, - byte2line={args=1}, - byteidx={args=2}, - byteidxcomp={args=2}, - call={args={2, 3}}, + byte2line={args=1, base=1}, + byteidx={args=2, base=1}, + byteidxcomp={args=2, base=1}, + call={args={2, 3}, base=1}, ceil={args=1, base=1, func="float_op_wrapper", data="&ceil"}, changenr={}, chanclose={args={1, 2}}, diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 1c56e936a8..64cd8147dc 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -852,7 +852,7 @@ func Test_byte2line_line2byte() set fileformat=mac call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1], - \ map(range(-1, 8), 'byte2line(v:val)')) + \ map(range(-1, 8), 'v:val->byte2line()')) call assert_equal([-1, -1, 1, 3, 6, 8, -1], \ map(range(-1, 5), 'line2byte(v:val)')) @@ -875,6 +875,34 @@ func Test_byte2line_line2byte() bw! endfunc +func Test_byteidx() + let a = '.é.' " one char of two bytes + call assert_equal(0, byteidx(a, 0)) + call assert_equal(0, byteidxcomp(a, 0)) + call assert_equal(1, byteidx(a, 1)) + call assert_equal(1, byteidxcomp(a, 1)) + call assert_equal(3, byteidx(a, 2)) + call assert_equal(3, byteidxcomp(a, 2)) + call assert_equal(4, byteidx(a, 3)) + call assert_equal(4, byteidxcomp(a, 3)) + call assert_equal(-1, byteidx(a, 4)) + call assert_equal(-1, byteidxcomp(a, 4)) + + let b = '.é.' " normal e with composing char + call assert_equal(0, b->byteidx(0)) + call assert_equal(1, b->byteidx(1)) + call assert_equal(4, b->byteidx(2)) + call assert_equal(5, b->byteidx(3)) + call assert_equal(-1, b->byteidx(4)) + + call assert_equal(0, b->byteidxcomp(0)) + call assert_equal(1, b->byteidxcomp(1)) + call assert_equal(2, b->byteidxcomp(2)) + call assert_equal(4, b->byteidxcomp(3)) + call assert_equal(5, b->byteidxcomp(4)) + call assert_equal(-1, b->byteidxcomp(5)) +endfunc + " Test for charidx() func Test_charidx() let a = 'xáb́y' @@ -1476,6 +1504,7 @@ endfunc func Test_call() call assert_equal(3, call('len', [123])) + call assert_equal(3, 'len'->call([123])) call assert_fails("call call('len', 123)", 'E714:') call assert_equal(0, call('', [])) @@ -1483,6 +1512,7 @@ func Test_call() return len(self.data) endfunction let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")} + eval mydict.len->call([], mydict)->assert_equal(4) call assert_fails("call call('Mylen', [], 0)", 'E715:') endfunc -- cgit From 5d883498179651c6da95b10959e83cf8707eaa4f Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 22:39:23 +0100 Subject: feat(eval): partially port v8.1.1915 Cannot be fully ported as chdir() hasn't been ported yet. --- src/nvim/eval.lua | 30 +++++++++++++++--------------- src/nvim/testdir/test_bufline.vim | 2 +- src/nvim/testdir/test_cindent.vim | 6 +++--- src/nvim/testdir/test_diffmode.vim | 2 +- src/nvim/testdir/test_functions.vim | 2 +- src/nvim/testdir/test_match.vim | 2 +- src/nvim/testdir/test_method.vim | 2 ++ src/nvim/testdir/test_popup.vim | 6 +++--- 8 files changed, 27 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 24f4b5fc49..faff29b268 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -70,16 +70,16 @@ return { changenr={}, chanclose={args={1, 2}}, chansend={args=2}, - char2nr={args={1, 2}}, + char2nr={args={1, 2}, base=1}, charidx={args={2, 3}}, - cindent={args=1}, - clearmatches={args={0, 1}}, - col={args=1}, - complete={args=2}, - complete_add={args=1}, + cindent={args=1, base=1}, + clearmatches={args={0, 1}, base=1}, + col={args=1, base=1}, + complete={args=2, base=2}, + complete_add={args=1, base=1}, complete_check={}, - complete_info={args={0, 1}}, - confirm={args={1, 4}}, + complete_info={args={0, 1}, base=1}, + confirm={args={1, 4}, base=1}, copy={args=1, base=1}, cos={args=1, base=1, func="float_op_wrapper", data="&cos"}, cosh={args=1, base=1, func="float_op_wrapper", data="&cosh"}, @@ -90,16 +90,16 @@ return { ctxpush={args={0, 1}}, ctxset={args={1, 2}}, ctxsize={}, - cursor={args={1, 3}}, - debugbreak={args={1, 1}}, - deepcopy={args={1, 2}}, - delete={args={1,2}}, - deletebufline={args={2,3}}, + cursor={args={1, 3}, base=1}, + debugbreak={args={1, 1}, base=1}, + deepcopy={args={1, 2}, base=1}, + delete={args={1,2}, base=1}, + deletebufline={args={2,3}, base=1}, dictwatcheradd={args=3}, dictwatcherdel={args=3}, did_filetype={}, - diff_filler={args=1}, - diff_hlID={args=2}, + diff_filler={args=1, base=1}, + diff_hlID={args=2, base=1}, empty={args=1, base=1}, environ={}, escape={args=2}, diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index 2a8c6915e2..b4e8a0bc71 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -102,7 +102,7 @@ func Test_deletebufline() call assert_equal(0, deletebufline(b, 2, 8)) call assert_equal(['aaa'], getbufline(b, 1, 2)) exe "bd!" b - call assert_equal(1, deletebufline(b, 1)) + call assert_equal(1, b->deletebufline(1)) split Xtest call setline(1, ['a', 'b', 'c']) diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index c8b7d90fbb..562867f548 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -122,9 +122,9 @@ func Test_cindent_func() new setlocal cindent call setline(1, ['int main(void)', '{', 'return 0;', '}']) - call assert_equal(cindent(0), -1) - call assert_equal(cindent(3), &sw) - call assert_equal(cindent(line('$')+1), -1) + call assert_equal(-1, cindent(0)) + call assert_equal(&sw, 3->cindent()) + call assert_equal(-1, cindent(line('$')+1)) bwipe! endfunc diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 1552bc4f78..8a709d3f0d 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -725,7 +725,7 @@ func Test_diff_filler() diffthis redraw - call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'diff_filler(v:val)')) + call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'v:val->diff_filler()')) wincmd w call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)')) diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 64cd8147dc..6cb3e24201 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1093,7 +1093,7 @@ func Test_col() call assert_equal(7, col('$')) call assert_equal(4, col("'x")) call assert_equal(6, col("'Y")) - call assert_equal(2, col([1, 2])) + call assert_equal(2, [1, 2]->col()) call assert_equal(7, col([1, '$'])) call assert_equal(0, col('')) diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index 09448ca71b..fed7240eb2 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -239,7 +239,7 @@ func Test_matchaddpos_otherwin() \] call assert_equal(expect, savematches) - call clearmatches(winid) + eval winid->clearmatches() call assert_equal([], getmatches(winid)) call setmatches(savematches, winid) diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index 7cb4eec400..5ad1d47e71 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -8,6 +8,7 @@ func Test_list_method() eval l->assert_notequal([3, 2, 1]) eval l->assert_notequal([3, 2, 1], 'wrong') call assert_equal(l, l->copy()) + call assert_equal(l, l->deepcopy()) call assert_equal(1, l->count(2)) call assert_false(l->empty()) call assert_true([]->empty()) @@ -38,6 +39,7 @@ func Test_dict_method() let d = #{one: 1, two: 2, three: 3} call assert_equal(d, d->copy()) + call assert_equal(d, d->deepcopy()) call assert_equal(1, d->count(2)) call assert_false(d->empty()) call assert_true({}->empty()) diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 06bdb1236a..710450293c 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -250,7 +250,7 @@ endfunc func Test_noinsert_complete() func! s:complTest1() abort - call complete(1, ['source', 'soundfold']) + eval ['source', 'soundfold']->complete(1) return '' endfunc @@ -403,7 +403,7 @@ func DummyCompleteFour(findstart, base) return 0 else call complete_add('four1') - call complete_add('four2') + eval 'four2'->complete_add() call complete_check() call complete_add('four3') call complete_add('four4') @@ -989,7 +989,7 @@ func GetCompleteInfo() if empty(g:compl_what) let g:compl_info = complete_info() else - let g:compl_info = complete_info(g:compl_what) + let g:compl_info = g:compl_what->complete_info() endif return '' endfunc -- cgit From 5503d8e28b1636f07bff4123a824b9ffdde4ca29 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 00:48:05 +0100 Subject: fix(eval_lambda): cherry-pick leak fix from v8.1.2107 That patch also includes a test using test_refcount() for lambdas, but such test functions are N/A for Nvim. --- src/nvim/eval.c | 15 ++++++++++++--- src/nvim/testdir/test_method.vim | 5 +++++ 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5c8f90c490..a6becb4765 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4186,7 +4186,7 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, typval_T base = *rettv; rettv->v_type = VAR_UNKNOWN; - const int ret = get_lambda_tv(arg, rettv, evaluate); + int ret = get_lambda_tv(arg, rettv, evaluate); if (ret == NOTDONE) { return FAIL; } else if (**arg != '(') { @@ -4198,9 +4198,18 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, } } tv_clear(rettv); - return FAIL; + ret = FAIL; + } else { + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); } - return call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); + } + + return ret; } /// Evaluate "->method()". diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim index 5ad1d47e71..7a6e6aa19d 100644 --- a/src/nvim/testdir/test_method.vim +++ b/src/nvim/testdir/test_method.vim @@ -145,6 +145,11 @@ func Test_method_lambda() " todo: lambda accepts more arguments than it consumes " call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:') + + " Nvim doesn't include test_refcount(). + " let l = [1, 2, 3] + " eval l->{x -> x}() + " call assert_equal(1, test_refcount(l)) endfunc func Test_method_not_supported() -- cgit From da9005af792f7a7eaae98ee9f6499af9a97fd095 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 12 Aug 2021 17:19:05 +0100 Subject: fix(v:lua): fix emsg when calling v:lua directly v:lua expressions are represented using vvlua_partial. As v:lua isn't intended to be called directly, it's given an empty pt_name. Because of this, calling v:lua directly like "v:lua()" will cause "E117: Unknown function: ", with an empty name. Instead, have call_func() show the name "v:lua" in the emsg. --- src/nvim/eval/userfunc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 4f10d31615..5ffd578891 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1516,6 +1516,10 @@ call_func( if (len > 0) { error = ERROR_NONE; nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv); + } else { + // v:lua was called directly; show its name in the emsg + XFREE_CLEAR(name); + funcname = (const char_u *)"v:lua"; } } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // User defined function. -- cgit From b2994e35c9357a8144beaf27e1a8ea4dd133f5d4 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 11 Aug 2021 13:47:33 +0100 Subject: feat(v:lua): support calling v:lua as a method --- src/nvim/eval.c | 50 +++++++++++++++++++++++++++++++++++++----------- src/nvim/eval/userfunc.c | 27 ++++++++++++++++++-------- 2 files changed, 58 insertions(+), 19 deletions(-) (limited to 'src') 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; -- cgit