mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
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:
@@ -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
|
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|.
|
Funcref A reference to a function |Funcref|.
|
||||||
Example: function("strlen")
|
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*
|
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
|
You can use |call()| to invoke a Funcref and use a list variable for the
|
||||||
arguments: >
|
arguments: >
|
||||||
:let r = call(Fn, mylist)
|
: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 ~
|
1.3 Lists ~
|
||||||
|
30
src/eval.c
30
src/eval.c
@@ -9069,14 +9069,12 @@ call_func(
|
|||||||
|
|
||||||
if (partial != NULL)
|
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.
|
||||||
/* When the function has a partial with a dict and there is a dict
|
* When the dict was bound explicitly use the one from the partial. */
|
||||||
* argument, use the dict argument. That is backwards compatible.
|
if (partial->pt_dict != NULL
|
||||||
*/
|
&& (selfdict_in == NULL || !partial->pt_auto))
|
||||||
if (selfdict_in == NULL)
|
selfdict = partial->pt_dict;
|
||||||
selfdict = partial->pt_dict;
|
|
||||||
}
|
|
||||||
if (error == ERROR_NONE && partial->pt_argc > 0)
|
if (error == ERROR_NONE && partial->pt_argc > 0)
|
||||||
{
|
{
|
||||||
for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear)
|
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. */
|
* use "dict". That is backwards compatible. */
|
||||||
if (dict_idx > 0)
|
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 = argvars[dict_idx].vval.v_dict;
|
||||||
++pt->pt_dict->dv_refcount;
|
++pt->pt_dict->dv_refcount;
|
||||||
}
|
}
|
||||||
else if (arg_pt != NULL)
|
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_dict = arg_pt->pt_dict;
|
||||||
|
pt->pt_auto = arg_pt->pt_auto;
|
||||||
if (pt->pt_dict != NULL)
|
if (pt->pt_dict != NULL)
|
||||||
++pt->pt_dict->dv_refcount;
|
++pt->pt_dict->dv_refcount;
|
||||||
}
|
}
|
||||||
@@ -22279,8 +22281,14 @@ handle_subscript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((rettv->v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL)
|
/* Turn "dict.Func" into a partial for "Func" bound to "dict".
|
||||||
&& selfdict != NULL)
|
* 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
|
char_u *fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string
|
||||||
: rettv->vval.v_partial->pt_name;
|
: rettv->vval.v_partial->pt_name;
|
||||||
@@ -22294,7 +22302,6 @@ handle_subscript(
|
|||||||
fp = find_func(fname);
|
fp = find_func(fname);
|
||||||
vim_free(tofree);
|
vim_free(tofree);
|
||||||
|
|
||||||
/* Turn "dict.Func" into a partial for "Func" with "dict". */
|
|
||||||
if (fp != NULL && (fp->uf_flags & FC_DICT))
|
if (fp != NULL && (fp->uf_flags & FC_DICT))
|
||||||
{
|
{
|
||||||
partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T));
|
partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T));
|
||||||
@@ -22303,6 +22310,7 @@ handle_subscript(
|
|||||||
{
|
{
|
||||||
pt->pt_refcount = 1;
|
pt->pt_refcount = 1;
|
||||||
pt->pt_dict = selfdict;
|
pt->pt_dict = selfdict;
|
||||||
|
pt->pt_auto = TRUE;
|
||||||
selfdict = NULL;
|
selfdict = NULL;
|
||||||
if (rettv->v_type == VAR_FUNC)
|
if (rettv->v_type == VAR_FUNC)
|
||||||
{
|
{
|
||||||
|
@@ -1261,6 +1261,8 @@ struct partial_S
|
|||||||
{
|
{
|
||||||
int pt_refcount; /* reference count */
|
int pt_refcount; /* reference count */
|
||||||
char_u *pt_name; /* function name */
|
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 */
|
int pt_argc; /* number of arguments */
|
||||||
typval_T *pt_argv; /* arguments in allocated array */
|
typval_T *pt_argv; /* arguments in allocated array */
|
||||||
dict_T *pt_dict; /* dict for "self" */
|
dict_T *pt_dict; /* dict for "self" */
|
||||||
|
@@ -257,3 +257,25 @@ func Test_ref_job_partial_dict()
|
|||||||
call job_setoptions(g:ref_job, {'exit_cb': function('string', [], d)})
|
call job_setoptions(g:ref_job, {'exit_cb': function('string', [], d)})
|
||||||
endif
|
endif
|
||||||
endfunc
|
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
|
||||||
|
@@ -753,6 +753,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1836,
|
||||||
/**/
|
/**/
|
||||||
1835,
|
1835,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user