diff options
author | Bram Moolenaar <Bram@vim.org> | 2016-05-24 15:44:17 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2016-05-24 15:44:17 +0200 |
commit | 1d429610bf9e99a6252be8abbc910d6667e4d1da (patch) | |
tree | 348de7a5f9706f94ebcf03796263d5c743d6d0e3 | |
parent | 991dea3ab185fb35e577ab0bdfd443cd4b43ccc6 (diff) | |
download | vim-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.txt | 42 | ||||
-rw-r--r-- | src/eval.c | 30 | ||||
-rw-r--r-- | src/structs.h | 2 | ||||
-rw-r--r-- | src/testdir/test_partial.vim | 22 | ||||
-rw-r--r-- | src/version.c | 2 |
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, |