1
0
forked from aniani/vim

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.
This commit is contained in:
Bram Moolenaar 2016-05-24 15:44:17 +02:00
parent 991dea3ab1
commit 1d429610bf
5 changed files with 86 additions and 12 deletions

View 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 ~

View 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)
{

View 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" */

View 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

View File

@ -753,6 +753,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1836,
/**/
1835,
/**/