summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-05-24 15:44:17 +0200
committerBram Moolenaar <Bram@vim.org>2016-05-24 15:44:17 +0200
commit1d429610bf9e99a6252be8abbc910d6667e4d1da (patch)
tree348de7a5f9706f94ebcf03796263d5c743d6d0e3
parent991dea3ab185fb35e577ab0bdfd443cd4b43ccc6 (diff)
downloadvim-1d429610bf9e99a6252be8abbc910d6667e4d1da.zip
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.
-rw-r--r--runtime/doc/eval.txt42
-rw-r--r--src/eval.c30
-rw-r--r--src/structs.h2
-rw-r--r--src/testdir/test_partial.vim22
-rw-r--r--src/version.c2
5 files changed, 86 insertions, 12 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index a1c1505cf..19957fc29 100644
--- 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 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 ~
diff --git a/src/eval.c b/src/eval.c
index 005485dd9..ce83143ce 100644
--- 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 *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)
{
diff --git a/src/structs.h b/src/structs.h
index 24d819bf5..12a8a4387 100644
--- 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" */
diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim
index 5f4a48faf..8b11200d2 100644
--- 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
diff --git a/src/version.c b/src/version.c
index 166a9de50..8b9271434 100644
--- 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,