changeset 9104:2242a5766417 v7.4.1836

commit https://github.com/vim/vim/commit/1d429610bf9e99a6252be8abbc910d6667e4d1da Author: Bram Moolenaar <Bram@vim.org> Date: Tue May 24 15:44:17 2016 +0200 patch 7.4.1836 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.
author Christian Brabandt <cb@256bit.org>
date Tue, 24 May 2016 15:45:06 +0200
parents b94f1b29ed35
children 06f8149137c9
files runtime/doc/eval.txt src/eval.c src/structs.h src/testdir/test_partial.vim src/version.c
diffstat 5 files changed, 86 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -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 arr
 
 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 
 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 ~
--- a/src/eval.c
+++ b/src/eval.c
@@ -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 *
 		 * 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)
 		{
--- a/src/structs.h
+++ b/src/structs.h
@@ -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" */
--- a/src/testdir/test_partial.vim
+++ b/src/testdir/test_partial.vim
@@ -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
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1836,
+/**/
     1835,
 /**/
     1834,