To: vim_dev@googlegroups.com Subject: Patch 7.4.1836 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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. Files: src/structs.h, src/eval.c, src/testdir/test_partial.vim, runtime/doc/eval.txt *** ../vim-7.4.1835/src/structs.h 2016-05-09 17:57:59.810722519 +0200 --- src/structs.h 2016-05-24 13:13:50.267420387 +0200 *************** *** 1261,1266 **** --- 1261,1268 ---- { 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" */ *** ../vim-7.4.1835/src/eval.c 2016-05-15 18:00:11.510811069 +0200 --- src/eval.c 2016-05-24 15:37:25.719301875 +0200 *************** *** 9069,9082 **** 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; ! } if (error == ERROR_NONE && partial->pt_argc > 0) { for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear) --- 9069,9080 ---- if (partial != 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 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,12341 **** --- 12328,12343 ---- * 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,22286 **** } } ! if ((rettv->v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL) ! && selfdict != NULL) { char_u *fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string : rettv->vval.v_partial->pt_name; --- 22281,22294 ---- } } ! /* 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,22300 **** 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)); --- 22302,22307 ---- *************** *** 22303,22308 **** --- 22310,22316 ---- { pt->pt_refcount = 1; pt->pt_dict = selfdict; + pt->pt_auto = TRUE; selfdict = NULL; if (rettv->v_type == VAR_FUNC) { *** ../vim-7.4.1835/src/testdir/test_partial.vim 2016-04-08 17:25:15.198702510 +0200 --- src/testdir/test_partial.vim 2016-05-24 15:41:07.923298818 +0200 *************** *** 257,259 **** --- 257,281 ---- 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 *** ../vim-7.4.1835/runtime/doc/eval.txt 2016-04-14 13:51:16.211410903 +0200 --- runtime/doc/eval.txt 2016-05-24 12:46:44.923442745 +0200 *************** *** 61,72 **** Funcref A reference to a function |Funcref|. Example: function("strlen") Special |v:false|, |v:true|, |v:none| and |v:null|. *Special* ! Job Used for a job, see |job_start()|. *Job* ! Channel Used for a channel, see |ch_open()|. *Channel* The Number and String types are converted automatically, depending on how they are used. --- 59,73 ---- 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* ! Job Used for a job, see |job_start()|. *Job* *Jobs* ! Channel Used for a channel, see |ch_open()|. *Channel* *Channels* The Number and String types are converted automatically, depending on how they are used. *************** *** 150,159 **** You can use |call()| to invoke a Funcref and use a list variable for the arguments: > :let r = call(Fn, mylist) 1.3 Lists ~ ! *List* *Lists* *E686* A List is an ordered sequence of items. An item can be of any type. Items can be accessed by their index number. Items can be added and removed at any position in the sequence. --- 153,199 ---- 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 ~ ! *list* *List* *Lists* *E686* A List is an ordered sequence of items. An item can be of any type. Items can be accessed by their index number. Items can be added and removed at any position in the sequence. *** ../vim-7.4.1835/src/version.c 2016-05-24 11:31:10.523505120 +0200 --- src/version.c 2016-05-24 15:41:19.031298666 +0200 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 1836, /**/ -- It's totally unfair to suggest - as many have - that engineers are socially inept. Engineers simply have different objectives when it comes to social interaction. (Scott Adams - The Dilbert principle) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///