]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.1.1816: cannot use a user defined function as a method v8.1.1816
authorBram Moolenaar <Bram@vim.org>
Sun, 4 Aug 2019 21:04:39 +0000 (23:04 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 4 Aug 2019 21:04:39 +0000 (23:04 +0200)
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)

src/eval.c
src/testdir/sautest/autoload/foo.vim
src/testdir/test_autoload.vim
src/testdir/test_user_func.vim
src/userfunc.c
src/version.c

index 7875edeafe6687fbce386336fa30c8f45e04e7ed..16ced406034e61d69b3c176246b656b34ec12f47 100644 (file)
@@ -4734,7 +4734,7 @@ 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(arg, rettv, evaluate, TRUE);
 
@@ -4824,7 +4824,7 @@ eval_method(
 
     // Locate the method name.
     name = *arg;
-    for (len = 0; ASCII_ISALNUM(name[len]) || name[len] == '_'; ++len)
+    for (len = 0; eval_isnamec(name[len]); ++len)
        ;
     if (len == 0)
     {
@@ -4842,6 +4842,8 @@ eval_method(
     }
     *arg += len;
 
+    // TODO: if "name" is a function reference, resolve it.
+
     vim_memset(&funcexe, 0, sizeof(funcexe));
     funcexe.evaluate = evaluate;
     funcexe.basetv = &base;
index d7dcd5ce3df876afced901a67cc1d0624c66181a..298e7275d880732fe26540c614d073e3db7b428f 100644 (file)
@@ -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
index 7396c227c96a0a6e37544a863d2955f4224a06d7..b8c4fa251f6734af68687f0778e3a05d3041f93d 100644 (file)
@@ -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()
index 666c06c13d0e63c7f6ccd75b54fa6e3f213da506..4b4a2766f168423b9478b16dfc051a817eb6ecad 100644 (file)
@@ -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 <expr> ( ListItem()
   inoremap <expr> [ 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
@@ -144,3 +152,11 @@ func Test_default_arg()
        \ .. "   endfunction",
        \ 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
index 8e834a3e55db9edb71cc854b94d305aa915a9d89..a609196185b9c59a8d8ba69e9509825c76a0de3c 100644 (file)
@@ -1495,7 +1495,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;
 
@@ -1554,10 +1555,7 @@ call_func(
            /*
             * User defined function.
             */
-           if (funcexe->basetv != NULL)
-               // TODO: support User function: base->Method()
-               fp = NULL;
-           else if (partial != NULL && partial->pt_func != NULL)
+           if (partial != NULL && partial->pt_func != NULL)
                fp = partial->pt_func;
            else
                fp = find_func(rfname);
@@ -1586,6 +1584,16 @@ call_func(
                    argcount = funcexe->argv_func(argcount, argvars,
                                                           fp->uf_args.ga_len);
 
+               if (funcexe->basetv != NULL)
+               {
+                   // Method call: base->Method()
+                   mch_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;
                if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len)
@@ -1613,7 +1621,7 @@ call_func(
                        did_save_redo = TRUE;
                    }
                    ++fp->uf_calls;
-                   call_user_func(fp, argcount, argvars, rettv,
+                   call_user_func(fp, argcount, argv, rettv,
                                         funcexe->firstline, funcexe->lastline,
                                  (fp->uf_flags & FC_DICT) ? selfdict : NULL);
                    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
@@ -1630,7 +1638,8 @@ call_func(
        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);
index 43e692a2b994c203b3b23478b46aa5be51d33adc..5c806a8556740388eb2ae42e9e0e738604d1c4b9 100644 (file)
@@ -773,6 +773,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1816,
 /**/
     1815,
 /**/