]> granicus.if.org Git - vim/commitdiff
patch 7.4.1836 v7.4.1836
authorBram Moolenaar <Bram@vim.org>
Tue, 24 May 2016 13:44:17 +0000 (15:44 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 24 May 2016 13:44:17 +0000 (15:44 +0200)
Problem:    When using a partial on a dictionary it always gets bound to that
            dictionary.
Solution:   Make a difference between binding a function to a dictionary
            explicitly or automatically.

runtime/doc/eval.txt
src/eval.c
src/structs.h
src/testdir/test_partial.vim
src/version.c

index a1c1505cf9c4d4c5bbfb1c94651af761848d5ded..19957fc29fbf16352f8f2f0dd1b33c7230f7603d 100644 (file)
@@ -1,4 +1,4 @@
-*eval.txt*     For Vim version 7.4.  Last change: 2016 May 20
+*eval.txt*     For Vim version 7.4.  Last change: 2016 May 24
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -59,6 +59,9 @@ Dictionary    An associative, unordered array: Each entry has a key and a
 
 Funcref                A reference to a function |Funcref|.
                Example: function("strlen")
+               It can be bound to a dictionary and arguments, it then works
+               like a Partial.
+               Example: function("Callback", [arg], myDict)
 
 Special                |v:false|, |v:true|, |v:none| and |v:null|.  *Special*
 
@@ -150,6 +153,43 @@ The name of the referenced function can be obtained with |string()|. >
 You can use |call()| to invoke a Funcref and use a list variable for the
 arguments: >
        :let r = call(Fn, mylist)
+<
+                                                               *Partial*
+A Funcref optionally binds a Dictionary and/or arguments.  This is also called
+a Partial.  This is created by passing the Dictionary and/or arguments to
+function().  When calling the function the Dictionary and/or arguments will be
+passed to the function.  Example: >
+
+       let Cb = function('Callback', ['foo'], myDict)
+       call Cb()
+
+This will invoke the function as if using: >
+       call myDict.Callback('foo')
+
+This is very useful when passing a function around, e.g. in the arguments of
+|ch_open()|.
+
+Note that binding a function to a Dictionary also happens when the function is
+a member of the Dictionary: >
+
+       let myDict.myFunction = MyFunction
+       call myDict.myFunction()
+
+Here MyFunction() will get myDict passed as "self".  This happens when the
+"myFunction" member is accessed.  When making assigning "myFunction" to
+otherDict and calling it, it will be bound to otherDict: >
+
+       let otherDict.myFunction = myDict.myFunction
+       call otherDict.myFunction()
+
+Now "self" will be "otherDict".  But when the dictionary was bound explicitly
+this won't happen: >
+
+       let myDict.myFunction = function(MyFunction, myDict)
+       let otherDict.myFunction = myDict.myFunction
+       call otherDict.myFunction()
+
+Here "self" will be "myDict", because it was bound explitly.
 
 
 1.3 Lists ~
index 005485dd9c6e3b31878c14d93e4526c50e498aeb..ce83143cee96979a16ae3d6364d4df2b388b9576 100644 (file)
@@ -9069,14 +9069,12 @@ call_func(
 
     if (partial != NULL)
     {
-       if (partial->pt_dict != NULL)
-       {
-           /* When the function has a partial with a dict and there is a dict
-            * argument, use the dict argument.  That is backwards compatible.
-            */
-           if (selfdict_in == NULL)
-               selfdict = partial->pt_dict;
-       }
+       /* 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))
+           selfdict = partial->pt_dict;
        if (error == ERROR_NONE && partial->pt_argc > 0)
        {
            for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear)
@@ -12330,12 +12328,16 @@ f_function(typval_T *argvars, typval_T *rettv)
                 * use "dict".  That is backwards compatible. */
                if (dict_idx > 0)
                {
+                   /* The dict is bound explicitly, pt_auto is FALSE. */
                    pt->pt_dict = argvars[dict_idx].vval.v_dict;
                    ++pt->pt_dict->dv_refcount;
                }
                else if (arg_pt != NULL)
                {
+                   /* If the dict was bound automatically the result is also
+                    * bound automatically. */
                    pt->pt_dict = arg_pt->pt_dict;
+                   pt->pt_auto = arg_pt->pt_auto;
                    if (pt->pt_dict != NULL)
                        ++pt->pt_dict->dv_refcount;
                }
@@ -22279,8 +22281,14 @@ handle_subscript(
        }
     }
 
-    if ((rettv->v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL)
-                                                         && selfdict != NULL)
+    /* Turn "dict.Func" into a partial for "Func" bound to "dict".
+     * Don't do this when "Func" is already a partial that was bound
+     * explicitly (pt_auto is FALSE). */
+    if (selfdict != NULL
+           && (rettv->v_type == VAR_FUNC
+               || (rettv->v_type == VAR_PARTIAL
+                   && (rettv->vval.v_partial->pt_auto
+                       || rettv->vval.v_partial->pt_dict == NULL))))
     {
        char_u      *fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string
                                             : rettv->vval.v_partial->pt_name;
@@ -22294,7 +22302,6 @@ handle_subscript(
        fp = find_func(fname);
        vim_free(tofree);
 
-       /* Turn "dict.Func" into a partial for "Func" with "dict". */
        if (fp != NULL && (fp->uf_flags & FC_DICT))
        {
            partial_T   *pt = (partial_T *)alloc_clear(sizeof(partial_T));
@@ -22303,6 +22310,7 @@ handle_subscript(
            {
                pt->pt_refcount = 1;
                pt->pt_dict = selfdict;
+               pt->pt_auto = TRUE;
                selfdict = NULL;
                if (rettv->v_type == VAR_FUNC)
                {
index 24d819bf5dce01fe34646e295dfe769fca733b3d..12a8a438786ece73e3aefd8b034dc7342ef0f552 100644 (file)
@@ -1261,6 +1261,8 @@ struct partial_S
 {
     int                pt_refcount;    /* reference count */
     char_u     *pt_name;       /* function name */
+    int                pt_auto;        /* when TRUE the partial was created for using
+                                  dict.member in handle_subscript() */
     int                pt_argc;        /* number of arguments */
     typval_T   *pt_argv;       /* arguments in allocated array */
     dict_T     *pt_dict;       /* dict for "self" */
index 5f4a48faf81b98aaec3af2d2019a03c5ff09cb4a..8b11200d2b13042ff0638bc4f5a249cc269d4e85 100644 (file)
@@ -257,3 +257,25 @@ func Test_ref_job_partial_dict()
     call job_setoptions(g:ref_job, {'exit_cb': function('string', [], d)})
   endif
 endfunc
+
+func Test_auto_partial_rebind()
+  let dict1 = {'name': 'dict1'}
+  func! dict1.f1()
+    return self.name
+  endfunc
+  let dict1.f2 = function(dict1.f1, dict1)
+
+  call assert_equal('dict1', dict1.f1())
+  call assert_equal('dict1', dict1['f1']())
+  call assert_equal('dict1', dict1.f2())
+  call assert_equal('dict1', dict1['f2']())
+
+  let dict2 = {'name': 'dict2'}
+  let dict2.f1 = dict1.f1
+  let dict2.f2 = dict1.f2
+
+  call assert_equal('dict2', dict2.f1())
+  call assert_equal('dict2', dict2['f1']())
+  call assert_equal('dict1', dict2.f2())
+  call assert_equal('dict1', dict2['f2']())
+endfunc
index 166a9de507b846b7a8514262bcde95375866c04c..8b92714340d2bf3152218ac977fe157116625f84 100644 (file)
@@ -753,6 +753,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1836,
 /**/
     1835,
 /**/