Patch 8.1.1820
Problem:    Using expr->FuncRef() does not work.
Solution:   Make FuncRef work as a method.
Files:      src/eval.c, src/userfunc.c, src/testdir/test_method.vim


*** ../vim-8.1.1819/src/eval.c  2019-08-04 23:04:35.525945914 +0200
--- src/eval.c  2019-08-05 22:49:59.044167083 +0200
***************
*** 3655,3660 ****
--- 3655,3728 ----
  }
  
  /*
+  * Handle a name followed by "(".  Both for just "name(arg)" and for
+  * "expr->name(arg)".
+  * Returns OK or FAIL.
+  */
+     static int
+ eval_func(
+       char_u      **arg,      // points to "(", will be advanced
+       char_u      *name,
+       int         name_len,
+       typval_T    *rettv,
+       int         evaluate,
+       typval_T    *basetv)    // "expr" for "expr->name(arg)"
+ {
+     char_u    *s = name;
+     int               len = name_len;
+     partial_T *partial;
+     int               ret = OK;
+ 
+     if (!evaluate)
+       check_vars(s, len);
+ 
+     /* If "s" is the name of a variable of type VAR_FUNC
+      * use its contents. */
+     s = deref_func_name(s, &len, &partial, !evaluate);
+ 
+     /* Need to make a copy, in case evaluating the arguments makes
+      * the name invalid. */
+     s = vim_strsave(s);
+     if (s == NULL)
+       ret = FAIL;
+     else
+     {
+       funcexe_T funcexe;
+ 
+       // Invoke the function.
+       vim_memset(&funcexe, 0, sizeof(funcexe));
+       funcexe.firstline = curwin->w_cursor.lnum;
+       funcexe.lastline = curwin->w_cursor.lnum;
+       funcexe.doesrange = &len;
+       funcexe.evaluate = evaluate;
+       funcexe.partial = partial;
+       funcexe.basetv = basetv;
+       ret = get_func_tv(s, len, rettv, arg, &funcexe);
+     }
+     vim_free(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 = NULL;
+       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)
+           clear_tv(rettv);
+       ret = FAIL;
+     }
+     return ret;
+ }
+ 
+ /*
   * The "evaluate" argument: When FALSE, the argument is only parsed but not
   * executed.  The function may return OK, but the rettv will be of type
   * VAR_UNKNOWN.  The function still returns FAIL for a syntax error.
***************
*** 4671,4725 ****
        else
        {
            if (**arg == '(')           /* recursive! */
!           {
!               partial_T *partial;
! 
!               if (!evaluate)
!                   check_vars(s, len);
! 
!               /* If "s" is the name of a variable of type VAR_FUNC
!                * use its contents. */
!               s = deref_func_name(s, &len, &partial, !evaluate);
! 
!               /* Need to make a copy, in case evaluating the arguments makes
!                * the name invalid. */
!               s = vim_strsave(s);
!               if (s == NULL)
!                   ret = FAIL;
!               else
!               {
!                   funcexe_T funcexe;
! 
!                   // Invoke the function.
!                   vim_memset(&funcexe, 0, sizeof(funcexe));
!                   funcexe.firstline = curwin->w_cursor.lnum;
!                   funcexe.lastline = curwin->w_cursor.lnum;
!                   funcexe.doesrange = &len;
!                   funcexe.evaluate = evaluate;
!                   funcexe.partial = partial;
!                   ret = get_func_tv(s, len, rettv, arg, &funcexe);
!               }
!               vim_free(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 = NULL;
!                   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)
!                       clear_tv(rettv);
!                   ret = FAIL;
!               }
!           }
            else if (evaluate)
                ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE);
            else
--- 4739,4745 ----
        else
        {
            if (**arg == '(')           /* recursive! */
!               ret = eval_func(arg, s, len, rettv, evaluate, NULL);
            else if (evaluate)
                ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE);
            else
***************
*** 4815,4869 ****
  {
      char_u    *name;
      long      len;
!     funcexe_T funcexe;
!     int               ret = OK;
      typval_T  base = *rettv;
  
      // Skip over the ->.
      *arg += 2;
  
-     // Locate the method name.
      name = *arg;
!     for (len = 0; eval_isnamec(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)
!           semsg(_(e_missingparen), name);
!       return FAIL;
      }
-     *arg += len;
- 
-     // TODO: if "name" is a function reference, resolve it.
- 
-     vim_memset(&funcexe, 0, sizeof(funcexe));
-     funcexe.evaluate = evaluate;
-     funcexe.basetv = &base;
-     rettv->v_type = VAR_UNKNOWN;
-     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)
        clear_tv(&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)
-           clear_tv(rettv);
-       ret = FAIL;
-     }
      return ret;
  }
  
--- 4835,4876 ----
  {
      char_u    *name;
      long      len;
!     char_u    *alias;
      typval_T  base = *rettv;
+     int               ret;
  
      // Skip over the ->.
      *arg += 2;
+     rettv->v_type = VAR_UNKNOWN;
  
      name = *arg;
!     len = get_name_len(arg, &alias, evaluate, TRUE);
!     if (alias != NULL)
!       name = alias;
! 
!     if (len <= 0)
      {
        if (verbose)
            emsg(_("E260: Missing name after ->"));
!       ret = FAIL;
      }
!     else
      {
!       if (**arg != '(')
!       {
!           if (verbose)
!               semsg(_(e_missingparen), name);
!           ret = FAIL;
!       }
!       else
!           ret = eval_func(arg, name, len, rettv, evaluate, &base);
      }
  
      /* Clear the funcref afterwards, so that deleting it while
       * evaluating the arguments is possible (see test55). */
      if (evaluate)
        clear_tv(&base);
  
      return ret;
  }
  
*** ../vim-8.1.1819/src/userfunc.c      2019-08-04 23:04:35.529945897 +0200
--- src/userfunc.c      2019-08-05 22:58:05.585534120 +0200
***************
*** 1498,1503 ****
--- 1498,1504 ----
      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;
  
      // Make a copy of the name, if it comes from a funcref variable it could
***************
*** 1590,1598 ****
                    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;
--- 1591,1599 ----
                    mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount);
                    argv[0] = *funcexe->basetv;
                    argcount++;
+                   argvars = argv;
+                   argv_base = 1;
                }
  
                if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL)
                    *funcexe->doesrange = TRUE;
***************
*** 1621,1627 ****
                        did_save_redo = TRUE;
                    }
                    ++fp->uf_calls;
!                   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)
--- 1622,1628 ----
                        did_save_redo = TRUE;
                    }
                    ++fp->uf_calls;
!                   call_user_func(fp, argcount, argvars, rettv,
                                         funcexe->firstline, funcexe->lastline,
                                  (fp->uf_flags & FC_DICT) ? selfdict : NULL);
                    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
***************
*** 1698,1705 ****
        }
      }
  
      while (argv_clear > 0)
!       clear_tv(&argv[--argv_clear]);
      vim_free(tofree);
      vim_free(name);
  
--- 1699,1708 ----
        }
      }
  
+     // clear the copies made from the partial
      while (argv_clear > 0)
!       clear_tv(&argv[--argv_clear + argv_base]);
! 
      vim_free(tofree);
      vim_free(name);
  
*** ../vim-8.1.1819/src/testdir/test_method.vim 2019-08-04 17:35:49.331707782 
+0200
--- src/testdir/test_method.vim 2019-08-05 23:05:37.498972087 +0200
***************
*** 1,6 ****
  " Tests for ->method()
  
! 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)
--- 1,6 ----
  " Tests for ->method()
  
! 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,40 ****
    call assert_fails('eval l->values()', 'E715:')
  endfunc
  
! func Test_dict()
    let d = #{one: 1, two: 2, three: 3}
  
    call assert_equal(d, d->copy())
--- 34,40 ----
    call assert_fails('eval l->values()', 'E715:')
  endfunc
  
! func Test_dict_method()
    let d = #{one: 1, two: 2, three: 3}
  
    call assert_equal(d, d->copy())
***************
*** 66,72 ****
    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())
--- 66,72 ----
    call assert_equal([1, 2, 3], d->values())
  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())
***************
*** 76,82 ****
    call assert_equal('axc', 'abc'->substitute('b', 'x', ''))
  endfunc
  
! func Test_append()
    new
    eval ['one', 'two', 'three']->append(1)
    call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
--- 76,82 ----
    call assert_equal('axc', 'abc'->substitute('b', 'x', ''))
  endfunc
  
! func Test_method_append()
    new
    eval ['one', 'two', 'three']->append(1)
    call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
***************
*** 89,91 ****
--- 89,104 ----
  
    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-8.1.1819/src/version.c       2019-08-05 21:51:36.801568843 +0200
--- src/version.c       2019-08-05 22:36:18.051902871 +0200
***************
*** 775,776 ****
--- 775,778 ----
  {   /* Add new patch number below this line */
+ /**/
+     1820,
  /**/

-- 
How To Keep A Healthy Level Of Insanity:
13. Go to a poetry recital and ask why the poems don't rhyme.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/201908052110.x75LAh5V022604%40masaka.moolenaar.net.

Raspunde prin e-mail lui