# HG changeset patch # User Christian Brabandt # Date 1457993705 -3600 # Node ID c337c813c64dc3ed64ec6b038816434ede27a2d3 # Parent cc20abebafa38f8a6dec97770452eacfc1095bef commit https://github.com/vim/vim/commit/1735bc988c546cc962c5f94792815b4d7cb79710 Author: Bram Moolenaar Date: Mon Mar 14 23:05:14 2016 +0100 patch 7.4.1559 Problem: Passing cookie to a callback is clumsy. Solution: Change function() to take arguments and return a partial. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Mar 13 +*eval.txt* For Vim version 7.4. Last change: 2016 Mar 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1895,7 +1895,8 @@ foldlevel( {lnum}) Number fold level at foldtext() String line displayed for closed fold foldtextresult( {lnum}) String text for closed fold at {lnum} foreground() Number bring the Vim window to the foreground -function( {name}) Funcref reference to function {name} +function({name} [, {arglist}] [, {dict}]) + Funcref reference to function {name} garbagecollect( [{atexit}]) none free memory, breaking cyclic references get( {list}, {idx} [, {def}]) any get item {idx} from {list} or {def} get( {dict}, {key} [, {def}]) any get item {key} from {dict} or {def} @@ -3568,10 +3569,46 @@ foreground() Move the Vim window to the Win32 console version} -function({name}) *function()* *E700* + *function()* *E700* *E922* *E923* +function({name} [, {arglist}] [, {dict}]) Return a |Funcref| variable that refers to function {name}. {name} can be a user defined function or an internal function. + When {arglist} or {dict} is present this creates a partial. + That mans the argument list and/or the dictionary is stored in + the Funcref and will be used when the Funcref is called. + + The arguments are passed to the function in front of other + arguments. Example: > + func Callback(arg1, arg2, name) + ... + let Func = function('Callback', ['one', 'two']) + ... + call Func('name') +< Invokes the function as with: > + call Callback('one', 'two', 'name') + +< The Dictionary is only useful when calling a "dict" function. + In that case the {dict} is passed in as "self". Example: > + function Callback() dict + echo "called for " . self.name + endfunction + ... + let context = {"name": "example"} + let Func = function('Callback', context) + ... + call Func() " will echo: called for example + +< The argument list and the Dictionary can be combined: > + function Callback(arg1, count) dict + ... + let context = {"name": "example"} + let Func = function('Callback', ['one'], context) + ... + call Func(500) +< Invokes the function as with: > + call context.Callback('one', 500) + garbagecollect([{atexit}]) *garbagecollect()* Cleanup unused |Lists| and |Dictionaries| that have circular diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -1025,8 +1025,9 @@ find_buffer(char_u *name, int err) void channel_set_options(channel_T *channel, jobopt_T *opt) { - int part; - char_u **cbp; + int part; + char_u **cbp; + partial_T **pp; if (opt->jo_set & JO_MODE) for (part = PART_SOCK; part <= PART_IN; ++part) @@ -1049,38 +1050,58 @@ channel_set_options(channel_T *channel, if (opt->jo_set & JO_CALLBACK) { cbp = &channel->ch_callback; + pp = &channel->ch_partial; vim_free(*cbp); + partial_unref(*pp); if (opt->jo_callback != NULL && *opt->jo_callback != NUL) *cbp = vim_strsave(opt->jo_callback); else *cbp = NULL; + *pp = opt->jo_partial; + if (*pp != NULL) + ++(*pp)->pt_refcount; } if (opt->jo_set & JO_OUT_CALLBACK) { cbp = &channel->ch_part[PART_OUT].ch_callback; + pp = &channel->ch_part[PART_OUT].ch_partial; vim_free(*cbp); + partial_unref(*pp); if (opt->jo_out_cb != NULL && *opt->jo_out_cb != NUL) *cbp = vim_strsave(opt->jo_out_cb); else *cbp = NULL; + *pp = opt->jo_out_partial; + if (*pp != NULL) + ++(*pp)->pt_refcount; } if (opt->jo_set & JO_ERR_CALLBACK) { cbp = &channel->ch_part[PART_ERR].ch_callback; + pp = &channel->ch_part[PART_ERR].ch_partial; vim_free(*cbp); + partial_unref(*pp); if (opt->jo_err_cb != NULL && *opt->jo_err_cb != NUL) *cbp = vim_strsave(opt->jo_err_cb); else *cbp = NULL; + *pp = opt->jo_err_partial; + if (*pp != NULL) + ++(*pp)->pt_refcount; } if (opt->jo_set & JO_CLOSE_CALLBACK) { cbp = &channel->ch_close_cb; + pp = &channel->ch_close_partial; vim_free(*cbp); + partial_unref(*pp); if (opt->jo_close_cb != NULL && *opt->jo_close_cb != NUL) *cbp = vim_strsave(opt->jo_close_cb); else *cbp = NULL; + *pp = opt->jo_err_partial; + if (*pp != NULL) + ++(*pp)->pt_refcount; } if ((opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER) @@ -1124,10 +1145,11 @@ channel_set_options(channel_T *channel, */ void channel_set_req_callback( - channel_T *channel, - int part, - char_u *callback, - int id) + channel_T *channel, + int part, + char_u *callback, + partial_T *partial, + int id) { cbq_T *head = &channel->ch_part[part].ch_cb_head; cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T)); @@ -1135,6 +1157,9 @@ channel_set_req_callback( if (item != NULL) { item->cq_callback = vim_strsave(callback); + item->cq_partial = partial; + if (partial != NULL) + ++partial->pt_refcount; item->cq_seq_nr = id; item->cq_prev = head->cq_prev; head->cq_prev = item; @@ -1247,7 +1272,8 @@ channel_write_new_lines(buf_T *buf) * Invoke the "callback" on channel "channel". */ static void -invoke_callback(channel_T *channel, char_u *callback, typval_T *argv) +invoke_callback(channel_T *channel, char_u *callback, partial_T *partial, + typval_T *argv) { typval_T rettv; int dummy; @@ -1256,7 +1282,7 @@ invoke_callback(channel_T *channel, char argv[0].vval.v_channel = channel; call_func(callback, (int)STRLEN(callback), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); + &rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL); clear_tv(&rettv); /* If an echo command was used the cursor needs to be put back where @@ -1629,7 +1655,7 @@ channel_exe_cmd(channel_T *channel, int ++emsg_skip; if (!is_call) tv = eval_expr(arg, NULL); - else if (func_call(arg, &argv[2], NULL, &res_tv) == OK) + else if (func_call(arg, &argv[2], NULL, NULL, &res_tv) == OK) tv = &res_tv; else tv = NULL; @@ -1685,8 +1711,9 @@ invoke_one_time_callback( /* Remove the item from the list first, if the callback * invokes ch_close() the list will be cleared. */ remove_cb_node(cbhead, item); - invoke_callback(channel, item->cq_callback, argv); + invoke_callback(channel, item->cq_callback, item->cq_partial, argv); vim_free(item->cq_callback); + partial_unref(item->cq_partial); vim_free(item); } @@ -1775,6 +1802,7 @@ may_invoke_callback(channel_T *channel, cbq_T *cbhead = &channel->ch_part[part].ch_cb_head; cbq_T *cbitem; char_u *callback = NULL; + partial_T *partial = NULL; buf_T *buffer = NULL; if (channel->ch_nb_close_cb != NULL) @@ -1786,11 +1814,20 @@ may_invoke_callback(channel_T *channel, if (cbitem->cq_seq_nr == 0) break; if (cbitem != NULL) + { callback = cbitem->cq_callback; + partial = cbitem->cq_partial; + } else if (channel->ch_part[part].ch_callback != NULL) + { callback = channel->ch_part[part].ch_callback; + partial = channel->ch_part[part].ch_partial; + } else + { callback = channel->ch_callback; + partial = channel->ch_partial; + } buffer = channel->ch_part[part].ch_buffer; if (buffer != NULL && !buf_valid(buffer)) @@ -1936,7 +1973,7 @@ may_invoke_callback(channel_T *channel, /* invoke the channel callback */ ch_logs(channel, "Invoking channel callback %s", (char *)callback); - invoke_callback(channel, callback, argv); + invoke_callback(channel, callback, partial, argv); } } } @@ -2024,13 +2061,16 @@ channel_close(channel_T *channel, int in argv[0].vval.v_channel = channel; ++channel->ch_refcount; call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb), - &rettv, 1, argv, 0L, 0L, &dummy, TRUE, NULL); + &rettv, 1, argv, 0L, 0L, &dummy, TRUE, + channel->ch_close_partial, NULL); clear_tv(&rettv); --channel->ch_refcount; /* the callback is only called once */ vim_free(channel->ch_close_cb); channel->ch_close_cb = NULL; + partial_unref(channel->ch_close_partial); + channel->ch_close_partial = NULL; } channel->ch_nb_close_cb = NULL; @@ -2068,6 +2108,7 @@ channel_clear_one(channel_T *channel, in remove_cb_node(cb_head, node); vim_free(node->cq_callback); + partial_unref(node->cq_partial); vim_free(node); } @@ -2079,6 +2120,8 @@ channel_clear_one(channel_T *channel, in vim_free(channel->ch_part[part].ch_callback); channel->ch_part[part].ch_callback = NULL; + partial_unref(channel->ch_part[part].ch_partial); + channel->ch_part[part].ch_partial = NULL; } /* @@ -2093,8 +2136,12 @@ channel_clear(channel_T *channel) channel_clear_one(channel, PART_ERR); vim_free(channel->ch_callback); channel->ch_callback = NULL; + partial_unref(channel->ch_partial); + channel->ch_partial = NULL; vim_free(channel->ch_close_cb); channel->ch_close_cb = NULL; + partial_unref(channel->ch_close_partial); + channel->ch_close_partial = NULL; } #if defined(EXITFREE) || defined(PROTO) @@ -2592,7 +2639,8 @@ send_common( EMSG2(_("E917: Cannot use a callback with %s()"), fun); return NULL; } - channel_set_req_callback(channel, part_send, opt->jo_callback, id); + channel_set_req_callback(channel, part_send, + opt->jo_callback, opt->jo_partial, id); } if (channel_send(channel, part_send, text, fun) == OK @@ -2982,13 +3030,19 @@ channel_get_timeout(channel_T *channel, * Return NULL for an invalid argument. */ static char_u * -get_callback(typval_T *arg) +get_callback(typval_T *arg, partial_T **pp) { + if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) + { + *pp = arg->vval.v_partial; + return (*pp)->pt_name; + } + *pp = NULL; if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) return arg->vval.v_string; if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) return (char_u *)""; - EMSG(_("E999: Invalid callback argument")); + EMSG(_("E921: Invalid callback argument")); return NULL; } @@ -3201,7 +3255,7 @@ get_job_options(typval_T *tv, jobopt_T * if (!(supported & JO_CALLBACK)) break; opt->jo_set |= JO_CALLBACK; - opt->jo_callback = get_callback(item); + opt->jo_callback = get_callback(item, &opt->jo_partial); if (opt->jo_callback == NULL) { EMSG2(_(e_invarg2), "callback"); @@ -3213,7 +3267,7 @@ get_job_options(typval_T *tv, jobopt_T * if (!(supported & JO_OUT_CALLBACK)) break; opt->jo_set |= JO_OUT_CALLBACK; - opt->jo_out_cb = get_callback(item); + opt->jo_out_cb = get_callback(item, &opt->jo_out_partial); if (opt->jo_out_cb == NULL) { EMSG2(_(e_invarg2), "out-cb"); @@ -3225,7 +3279,7 @@ get_job_options(typval_T *tv, jobopt_T * if (!(supported & JO_ERR_CALLBACK)) break; opt->jo_set |= JO_ERR_CALLBACK; - opt->jo_err_cb = get_callback(item); + opt->jo_err_cb = get_callback(item, &opt->jo_err_partial); if (opt->jo_err_cb == NULL) { EMSG2(_(e_invarg2), "err-cb"); @@ -3237,7 +3291,7 @@ get_job_options(typval_T *tv, jobopt_T * if (!(supported & JO_CLOSE_CALLBACK)) break; opt->jo_set |= JO_CLOSE_CALLBACK; - opt->jo_close_cb = get_callback(item); + opt->jo_close_cb = get_callback(item, &opt->jo_close_partial); if (opt->jo_close_cb == NULL) { EMSG2(_(e_invarg2), "close-cb"); @@ -3311,7 +3365,14 @@ get_job_options(typval_T *tv, jobopt_T * if (!(supported & JO_EXIT_CB)) break; opt->jo_set |= JO_EXIT_CB; - opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf); + if (item->v_type == VAR_PARTIAL && item->vval.v_partial != NULL) + { + opt->jo_exit_partial = item->vval.v_partial; + opt->jo_exit_cb = item->vval.v_partial->pt_name; + } + else + opt->jo_exit_cb = get_tv_string_buf_chk( + item, opt->jo_ecb_buf); if (opt->jo_exit_cb == NULL) { EMSG2(_(e_invarg2), "exit-cb"); @@ -3390,6 +3451,7 @@ job_free(job_T *job) vim_free(job->jv_stoponexit); vim_free(job->jv_exit_cb); + partial_unref(job->jv_exit_partial); vim_free(job); } @@ -3454,10 +3516,19 @@ job_set_options(job_T *job, jobopt_T *op if (opt->jo_set & JO_EXIT_CB) { vim_free(job->jv_exit_cb); + partial_unref(job->jv_exit_partial); if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL) + { job->jv_exit_cb = NULL; + job->jv_exit_partial = NULL; + } else + { job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); + job->jv_exit_partial = opt->jo_exit_partial; + if (job->jv_exit_partial != NULL) + ++job->jv_exit_partial->pt_refcount; + } } } @@ -3721,7 +3792,8 @@ job_status(job_T *job) argv[1].v_type = VAR_NUMBER; argv[1].vval.v_number = job->jv_exitval; call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); + &rettv, 2, argv, 0L, 0L, &dummy, TRUE, + job->jv_exit_partial, NULL); clear_tv(&rettv); --job->jv_refcount; } diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -452,8 +452,8 @@ static char_u *tv2string(typval_T *tv, c static char_u *string_quote(char_u *str, int function); static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); static int find_internal_func(char_u *name); -static char_u *deref_func_name(char_u *name, int *lenp, int no_autoload); -static int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict); +static char_u *deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload); +static int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict); static void emsg_funcname(char *ermsg, char_u *name); static int non_zero_arg(typval_T *argvars); @@ -1675,7 +1675,7 @@ call_vim_function( rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */ ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, TRUE, NULL); + &doesrange, TRUE, NULL, NULL); if (safe) { --sandbox; @@ -3091,6 +3091,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char case VAR_UNKNOWN: case VAR_DICT: case VAR_FUNC: + case VAR_PARTIAL: case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: @@ -3456,6 +3457,7 @@ ex_call(exarg_T *eap) int doesrange; int failed = FALSE; funcdict_T fudi; + partial_T *partial; if (eap->skip) { @@ -3486,7 +3488,7 @@ ex_call(exarg_T *eap) /* If it is the name of a variable of type VAR_FUNC use its contents. */ len = (int)STRLEN(tofree); - name = deref_func_name(tofree, &len, FALSE); + name = deref_func_name(tofree, &len, &partial, FALSE); /* Skip white space to allow ":call func ()". Not good, but required for * backward compatibility. */ @@ -3525,7 +3527,7 @@ ex_call(exarg_T *eap) arg = startarg; if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg, eap->line1, eap->line2, &doesrange, - !eap->skip, fudi.fd_dict) == FAIL) + !eap->skip, partial, fudi.fd_dict) == FAIL) { failed = TRUE; break; @@ -3870,6 +3872,7 @@ item_lock(typval_T *tv, int deep, int lo case VAR_NUMBER: case VAR_STRING: case VAR_FUNC: + case VAR_PARTIAL: case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: @@ -4542,7 +4545,8 @@ eval4(char_u **arg, typval_T *rettv, int } } - else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC) + else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC + || rettv->v_type == VAR_PARTIAL || var2.v_type == VAR_PARTIAL) { if (rettv->v_type != var2.v_type || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) @@ -4555,6 +4559,12 @@ eval4(char_u **arg, typval_T *rettv, int clear_tv(&var2); return FAIL; } + else if (rettv->v_type == VAR_PARTIAL) + { + /* Partials are only equal when identical. */ + n1 = rettv->vval.v_partial != NULL + && rettv->vval.v_partial == var2.vval.v_partial; + } else { /* Compare two Funcrefs for being equal or unequal. */ @@ -4564,9 +4574,9 @@ eval4(char_u **arg, typval_T *rettv, int else n1 = STRCMP(rettv->vval.v_string, var2.vval.v_string) == 0; - if (type == TYPE_NEQUAL) - n1 = !n1; - } + } + if (type == TYPE_NEQUAL) + n1 = !n1; } #ifdef FEAT_FLOAT @@ -5230,14 +5240,16 @@ eval7( { if (**arg == '(') /* recursive! */ { + partial_T *partial; + /* If "s" is the name of a variable of type VAR_FUNC * use its contents. */ - s = deref_func_name(s, &len, !evaluate); + s = deref_func_name(s, &len, &partial, !evaluate); /* Invoke the function. */ ret = get_func_tv(s, len, rettv, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, NULL); + &len, evaluate, partial, NULL); /* If evaluate is FALSE rettv->v_type was not set in * get_func_tv, but it's needed in handle_subscript() to parse @@ -5359,6 +5371,7 @@ eval_index( switch (rettv->v_type) { case VAR_FUNC: + case VAR_PARTIAL: if (verbose) EMSG(_("E695: Cannot index a Funcref")); return FAIL; @@ -5480,6 +5493,7 @@ eval_index( { case VAR_UNKNOWN: case VAR_FUNC: + case VAR_PARTIAL: case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: @@ -6218,6 +6232,10 @@ tv_equal( && tv2->vval.v_string != NULL && STRCMP(tv1->vval.v_string, tv2->vval.v_string) == 0); + case VAR_PARTIAL: + return tv1->vval.v_partial != NULL + && tv1->vval.v_partial == tv2->vval.v_partial; + case VAR_NUMBER: return tv1->vval.v_number == tv2->vval.v_number; @@ -7793,6 +7811,12 @@ echo_string( r = tv->vval.v_string; break; + case VAR_PARTIAL: + *tofree = NULL; + /* TODO: arguments */ + r = tv->vval.v_partial == NULL ? NULL : tv->vval.v_partial->pt_name; + break; + case VAR_LIST: if (tv->vval.v_list == NULL) { @@ -7878,6 +7902,10 @@ tv2string( case VAR_FUNC: *tofree = string_quote(tv->vval.v_string, TRUE); return *tofree; + case VAR_PARTIAL: + *tofree = string_quote(tv->vval.v_partial == NULL ? NULL + : tv->vval.v_partial->pt_name, TRUE); + return *tofree; case VAR_STRING: *tofree = string_quote(tv->vval.v_string, FALSE); return *tofree; @@ -8146,7 +8174,7 @@ static struct fst {"foldtext", 0, 0, f_foldtext}, {"foldtextresult", 1, 1, f_foldtextresult}, {"foreground", 0, 0, f_foreground}, - {"function", 1, 1, f_function}, + {"function", 1, 3, f_function}, {"garbagecollect", 0, 1, f_garbagecollect}, {"get", 2, 3, f_get}, {"getbufline", 2, 3, f_getbufline}, @@ -8524,13 +8552,16 @@ find_internal_func( /* * Check if "name" is a variable of type VAR_FUNC. If so, return the function * name it contains, otherwise return "name". + * If "name" is of type VAR_PARTIAL also return "partial" */ static char_u * -deref_func_name(char_u *name, int *lenp, int no_autoload) +deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload) { dictitem_T *v; int cc; + *partial = NULL; + cc = name[*lenp]; name[*lenp] = NUL; v = find_var(name, NULL, no_autoload); @@ -8546,6 +8577,18 @@ deref_func_name(char_u *name, int *lenp, return v->di_tv.vval.v_string; } + if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) + { + *partial = v->di_tv.vval.v_partial; + if (*partial == NULL) + { + *lenp = 0; + return (char_u *)""; /* just in case */ + } + *lenp = (int)STRLEN((*partial)->pt_name); + return (*partial)->pt_name; + } + return name; } @@ -8563,6 +8606,7 @@ get_func_tv( linenr_T lastline, /* last line of range */ int *doesrange, /* return: function handled range */ int evaluate, + partial_T *partial, /* for extra arguments */ dict_T *selfdict) /* Dictionary for "self" */ { char_u *argp; @@ -8574,7 +8618,7 @@ get_func_tv( * Get the arguments. */ argp = *arg; - while (argcount < MAX_FUNC_ARGS) + while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { argp = skipwhite(argp + 1); /* skip the '(' or ',' */ if (*argp == ')' || *argp == ',' || *argp == NUL) @@ -8595,7 +8639,7 @@ get_func_tv( if (ret == OK) ret = call_func(name, len, rettv, argcount, argvars, - firstline, lastline, doesrange, evaluate, selfdict); + firstline, lastline, doesrange, evaluate, partial, selfdict); else if (!aborting()) { if (argcount == MAX_FUNC_ARGS) @@ -8622,14 +8666,15 @@ call_func( char_u *funcname, /* name of the function */ int len, /* length of "name" */ typval_T *rettv, /* return value goes here */ - int argcount, /* number of "argvars" */ - typval_T *argvars, /* vars for arguments, must have "argcount" + int argcount_in, /* number of "argvars" */ + typval_T *argvars_in, /* vars for arguments, must have "argcount" PLUS ONE elements! */ linenr_T firstline, /* first line of range */ linenr_T lastline, /* last line of range */ int *doesrange, /* return: function handled range */ int evaluate, - dict_T *selfdict) /* Dictionary for "self" */ + partial_T *partial, /* optional, can be NULL */ + dict_T *selfdict_in) /* Dictionary for "self" */ { int ret = FAIL; #define ERROR_UNKNOWN 0 @@ -8639,6 +8684,7 @@ call_func( #define ERROR_DICT 4 #define ERROR_NONE 5 #define ERROR_OTHER 6 +#define ERROR_BOTH 7 int error = ERROR_NONE; int i; int llen; @@ -8647,6 +8693,11 @@ call_func( char_u fname_buf[FLEN_FIXED + 1]; char_u *fname; char_u *name; + int argcount = argcount_in; + typval_T *argvars = argvars_in; + dict_T *selfdict = selfdict_in; + typval_T argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */ + int argv_clear = 0; /* Make a copy of the name, if it comes from a funcref variable it could * be changed or deleted in the called function. */ @@ -8698,6 +8749,27 @@ call_func( *doesrange = FALSE; + if (partial != NULL) + { + if (partial->pt_dict != NULL) + { + if (selfdict_in != NULL) + error = ERROR_BOTH; + selfdict = partial->pt_dict; + } + if (error == ERROR_NONE && partial->pt_argc > 0) + { + int i; + + for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear) + copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]); + for (i = 0; i < argcount_in; ++i) + argv[i + argv_clear] = argvars_in[i]; + argvars = argv; + argcount = partial->pt_argc + argcount_in; + } + } + /* execute the function if no errors detected and executing */ if (evaluate && error == ERROR_NONE) @@ -8841,9 +8913,15 @@ call_func( emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), name); break; - } - } - + case ERROR_BOTH: + emsg_funcname(N_("E924: can't have both a \"self\" dict and a partial: %s"), + name); + break; + } + } + + while (argv_clear > 0) + clear_tv(&argv[--argv_clear]); if (fname != name && fname != fname_buf) vim_free(fname); vim_free(name); @@ -9737,6 +9815,7 @@ f_byteidxcomp(typval_T *argvars, typval_ func_call( char_u *name, typval_T *args, + partial_T *partial, dict_T *selfdict, typval_T *rettv) { @@ -9749,7 +9828,7 @@ func_call( for (item = args->vval.v_list->lv_first; item != NULL; item = item->li_next) { - if (argc == MAX_FUNC_ARGS) + if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { EMSG(_("E699: Too many arguments")); break; @@ -9763,7 +9842,7 @@ func_call( if (item == NULL) r = call_func(name, (int)STRLEN(name), rettv, argc, argv, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, TRUE, selfdict); + &dummy, TRUE, partial, selfdict); /* Free the arguments. */ while (argc > 0) @@ -9773,12 +9852,13 @@ func_call( } /* - * "call(func, arglist)" function + * "call(func, arglist [, dict])" function */ static void f_call(typval_T *argvars, typval_T *rettv) { char_u *func; + partial_T *partial = NULL; dict_T *selfdict = NULL; if (argvars[1].v_type != VAR_LIST) @@ -9791,6 +9871,11 @@ f_call(typval_T *argvars, typval_T *rett if (argvars[0].v_type == VAR_FUNC) func = argvars[0].vval.v_string; + else if (argvars[0].v_type == VAR_PARTIAL) + { + partial = argvars[0].vval.v_partial; + func = partial->pt_name; + } else func = get_tv_string(&argvars[0]); if (*func == NUL) @@ -9806,7 +9891,7 @@ f_call(typval_T *argvars, typval_T *rett selfdict = argvars[2].vval.v_dict; } - (void)func_call(func, &argvars[1], selfdict, rettv); + (void)func_call(func, &argvars[1], partial, selfdict, rettv); } #ifdef FEAT_FLOAT @@ -10627,6 +10712,9 @@ f_empty(typval_T *argvars, typval_T *ret n = argvars[0].vval.v_string == NULL || *argvars[0].vval.v_string == NUL; break; + case VAR_PARTIAL: + n = FALSE; + break; case VAR_NUMBER: n = argvars[0].vval.v_number == 0; break; @@ -11688,6 +11776,7 @@ f_foreground(typval_T *argvars UNUSED, t f_function(typval_T *argvars, typval_T *rettv) { char_u *s; + char_u *name; s = get_tv_string(&argvars[0]); if (s == NULL || *s == NUL || VIM_ISDIGIT(*s)) @@ -11707,18 +11796,118 @@ f_function(typval_T *argvars, typval_T * * would also work, but some plugins depend on the name being * printable text. */ sprintf(sid_buf, "%ld_", (long)current_SID); - rettv->vval.v_string = - alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1)); - if (rettv->vval.v_string != NULL) - { - STRCPY(rettv->vval.v_string, sid_buf); - STRCAT(rettv->vval.v_string, s + off); - } - } - else - rettv->vval.v_string = vim_strsave(s); - rettv->v_type = VAR_FUNC; - } + name = alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1)); + if (name != NULL) + { + STRCPY(name, sid_buf); + STRCAT(name, s + off); + } + } + else + name = vim_strsave(s); + + if (argvars[1].v_type != VAR_UNKNOWN) + { + partial_T *pt; + int dict_idx = 0; + int arg_idx = 0; + + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* function(name, [args], dict) */ + arg_idx = 1; + dict_idx = 2; + } + else if (argvars[1].v_type == VAR_DICT) + /* function(name, dict) */ + dict_idx = 1; + else + /* function(name, [args]) */ + arg_idx = 1; + if (dict_idx > 0 && (argvars[dict_idx].v_type != VAR_DICT + || argvars[dict_idx].vval.v_dict == NULL)) + { + EMSG(_("E922: expected a dict")); + vim_free(name); + return; + } + if (arg_idx > 0 && (argvars[arg_idx].v_type != VAR_LIST + || argvars[arg_idx].vval.v_list == NULL)) + { + EMSG(_("E923: Second argument of function() must be a list or a dict")); + vim_free(name); + return; + } + + pt = (partial_T *)alloc_clear(sizeof(partial_T)); + if (pt != NULL) + { + if (arg_idx > 0) + { + list_T *list = argvars[arg_idx].vval.v_list; + listitem_T *li; + int i = 0; + + pt->pt_argv = (typval_T *)alloc( + sizeof(typval_T) * list->lv_len); + if (pt->pt_argv == NULL) + { + vim_free(pt); + vim_free(name); + return; + } + else + { + pt->pt_argc = list->lv_len; + for (li = list->lv_first; li != NULL; li = li->li_next) + copy_tv(&li->li_tv, &pt->pt_argv[i++]); + } + } + + if (dict_idx > 0) + { + pt->pt_dict = argvars[dict_idx].vval.v_dict; + ++pt->pt_dict->dv_refcount; + } + + pt->pt_refcount = 1; + pt->pt_name = name; + func_ref(pt->pt_name); + } + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } + else + { + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = name; + func_ref(name); + } + } +} + + static void +partial_free(partial_T *pt) +{ + int i; + + for (i = 0; i < pt->pt_argc; ++i) + clear_tv(&pt->pt_argv[i]); + vim_free(pt->pt_argv); + func_unref(pt->pt_name); + vim_free(pt->pt_name); + vim_free(pt); +} + +/* + * Unreference a closure: decrement the reference count and free it when it + * becomes zero. + */ + void +partial_unref(partial_T *pt) +{ + if (pt != NULL && --pt->pt_refcount <= 0) + partial_free(pt); } /* @@ -14598,6 +14787,7 @@ f_len(typval_T *argvars, typval_T *rettv case VAR_SPECIAL: case VAR_FLOAT: case VAR_FUNC: + case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: EMSG(_("E701: Invalid type for len()")); @@ -18169,6 +18359,7 @@ typedef struct int item_compare_float; #endif char_u *item_compare_func; + partial_T *item_compare_partial; dict_T *item_compare_selfdict; int item_compare_func_err; int item_compare_keep_zero; @@ -18278,6 +18469,8 @@ item_compare2(const void *s1, const void typval_T rettv; typval_T argv[3]; int dummy; + char_u *func_name; + partial_T *partial = sortinfo->item_compare_partial; /* shortcut after failure in previous call; compare all items equal */ if (sortinfo->item_compare_func_err) @@ -18286,16 +18479,20 @@ item_compare2(const void *s1, const void si1 = (sortItem_T *)s1; si2 = (sortItem_T *)s2; + if (partial == NULL) + func_name = sortinfo->item_compare_func; + else + func_name = partial->pt_name; + /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED * in the copy without changing the original list items. */ copy_tv(&si1->item->li_tv, &argv[0]); copy_tv(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - res = call_func(sortinfo->item_compare_func, - (int)STRLEN(sortinfo->item_compare_func), + res = call_func(func_name, (int)STRLEN(func_name), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, - sortinfo->item_compare_selfdict); + partial, sortinfo->item_compare_selfdict); clear_tv(&argv[0]); clear_tv(&argv[1]); @@ -18358,12 +18555,15 @@ do_sort_uniq(typval_T *argvars, typval_T info.item_compare_float = FALSE; #endif info.item_compare_func = NULL; + info.item_compare_partial = NULL; info.item_compare_selfdict = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { /* optional second argument: {func} */ if (argvars[1].v_type == VAR_FUNC) info.item_compare_func = argvars[1].vval.v_string; + else if (argvars[1].v_type == VAR_PARTIAL) + info.item_compare_partial = argvars[1].vval.v_partial; else { int error = FALSE; @@ -18443,7 +18643,8 @@ do_sort_uniq(typval_T *argvars, typval_T info.item_compare_func_err = FALSE; info.item_compare_keep_zero = FALSE; /* test the compare function */ - if (info.item_compare_func != NULL + if ((info.item_compare_func != NULL + || info.item_compare_partial != NULL) && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) == ITEM_COMPARE_FAIL) EMSG(_("E702: Sort compare function failed")); @@ -18452,6 +18653,7 @@ do_sort_uniq(typval_T *argvars, typval_T /* Sort the array with item pointers. */ qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), info.item_compare_func == NULL + && info.item_compare_partial == NULL ? item_compare : item_compare2); if (!info.item_compare_func_err) @@ -18471,7 +18673,8 @@ do_sort_uniq(typval_T *argvars, typval_T /* f_uniq(): ptrs will be a stack of items to remove */ info.item_compare_func_err = FALSE; info.item_compare_keep_zero = TRUE; - item_compare_func_ptr = info.item_compare_func + item_compare_func_ptr = info.item_compare_func != NULL + || info.item_compare_partial != NULL ? item_compare2 : item_compare; for (li = l->lv_first; li != NULL && li->li_next != NULL; @@ -20045,6 +20248,7 @@ f_type(typval_T *argvars, typval_T *rett break; case VAR_JOB: n = 8; break; case VAR_CHANNEL: n = 9; break; + case VAR_PARTIAL: n = 10; break; case VAR_UNKNOWN: EMSG2(_(e_intern2), "f_type(UNKNOWN)"); n = -1; @@ -21295,11 +21499,13 @@ handle_subscript( char_u *s; int len; typval_T functv; + partial_T *pt = NULL; while (ret == OK && (**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC))) + || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC + || rettv->v_type == VAR_PARTIAL))) && !vim_iswhite(*(*arg - 1))) { if (**arg == '(') @@ -21311,13 +21517,19 @@ handle_subscript( rettv->v_type = VAR_UNKNOWN; /* Invoke the function. Recursive! */ - s = functv.vval.v_string; + if (rettv->v_type == VAR_PARTIAL) + { + pt = functv.vval.v_partial; + s = pt->pt_name; + } + else + s = functv.vval.v_string; } else s = (char_u *)""; ret = get_func_tv(s, (int)STRLEN(s), rettv, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, selfdict); + &len, evaluate, pt, selfdict); /* Clear the funcref afterwards, so that deleting it while * evaluating the arguments is possible (see test55). */ @@ -21405,6 +21617,9 @@ free_tv(typval_T *varp) case VAR_STRING: vim_free(varp->vval.v_string); break; + case VAR_PARTIAL: + partial_unref(varp->vval.v_partial); + break; case VAR_LIST: list_unref(varp->vval.v_list); break; @@ -21448,6 +21663,10 @@ clear_tv(typval_T *varp) vim_free(varp->vval.v_string); varp->vval.v_string = NULL; break; + case VAR_PARTIAL: + partial_unref(varp->vval.v_partial); + varp->vval.v_partial = NULL; + break; case VAR_LIST: list_unref(varp->vval.v_list); varp->vval.v_list = NULL; @@ -21524,6 +21743,7 @@ get_tv_number_chk(typval_T *varp, int *d break; #endif case VAR_FUNC: + case VAR_PARTIAL: EMSG(_("E703: Using a Funcref as a Number")); break; case VAR_STRING: @@ -21572,6 +21792,7 @@ get_tv_float(typval_T *varp) case VAR_FLOAT: return varp->vval.v_float; case VAR_FUNC: + case VAR_PARTIAL: EMSG(_("E891: Using a Funcref as a Float")); break; case VAR_STRING: @@ -21688,6 +21909,7 @@ get_tv_string_buf_chk(typval_T *varp, ch sprintf((char *)buf, "%ld", (long)varp->vval.v_number); return buf; case VAR_FUNC: + case VAR_PARTIAL: EMSG(_("E729: using Funcref as a String")); break; case VAR_LIST: @@ -22087,7 +22309,7 @@ list_one_var_a( msg_advance(22); if (type == VAR_NUMBER) msg_putchar('#'); - else if (type == VAR_FUNC) + else if (type == VAR_FUNC || type == VAR_PARTIAL) msg_putchar('*'); else if (type == VAR_LIST) { @@ -22106,7 +22328,7 @@ list_one_var_a( msg_outtrans(string); - if (type == VAR_FUNC) + if (type == VAR_FUNC || type == VAR_PARTIAL) msg_puts((char_u *)"()"); if (*first) { @@ -22138,7 +22360,8 @@ set_var( } v = find_var_in_ht(ht, 0, varname, TRUE); - if (tv->v_type == VAR_FUNC && var_check_func_name(name, v == NULL)) + if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) + && var_check_func_name(name, v == NULL)) return; if (v != NULL) @@ -22383,6 +22606,15 @@ copy_tv(typval_T *from, typval_T *to) func_ref(to->vval.v_string); } break; + case VAR_PARTIAL: + if (from->vval.v_partial == NULL) + to->vval.v_partial = NULL; + else + { + to->vval.v_partial = from->vval.v_partial; + ++to->vval.v_partial->pt_refcount; + } + break; case VAR_LIST: if (from->vval.v_list == NULL) to->vval.v_list = NULL; @@ -22437,6 +22669,7 @@ item_copy( case VAR_FLOAT: case VAR_STRING: case VAR_FUNC: + case VAR_PARTIAL: case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: @@ -23415,6 +23648,7 @@ trans_function_name( char_u sid_buf[20]; int len; lval_T lv; + partial_T *partial; if (fdp != NULL) vim_memset(fdp, 0, sizeof(funcdict_T)); @@ -23499,14 +23733,15 @@ trans_function_name( if (lv.ll_exp_name != NULL) { len = (int)STRLEN(lv.ll_exp_name); - name = deref_func_name(lv.ll_exp_name, &len, flags & TFN_NO_AUTOLOAD); + name = deref_func_name(lv.ll_exp_name, &len, &partial, + flags & TFN_NO_AUTOLOAD); if (name == lv.ll_exp_name) name = NULL; } else { len = (int)(end - *pp); - name = deref_func_name(*pp, &len, flags & TFN_NO_AUTOLOAD); + name = deref_func_name(*pp, &len, &partial, flags & TFN_NO_AUTOLOAD); if (name == *pp) name = NULL; } @@ -25111,6 +25346,7 @@ write_viminfo_varlist(FILE *fp) case VAR_UNKNOWN: case VAR_FUNC: + case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: continue; diff --git a/src/if_py_both.h b/src/if_py_both.h --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -2944,7 +2944,7 @@ FunctionCall(FunctionObject *self, PyObj Python_Lock_Vim(); VimTryStart(); - error = func_call(name, &args, selfdict, &rettv); + error = func_call(name, &args, NULL, selfdict, &rettv); Python_Release_Vim(); Py_END_ALLOW_THREADS diff --git a/src/if_python.c b/src/if_python.c --- a/src/if_python.c +++ b/src/if_python.c @@ -1561,6 +1561,7 @@ do_pyeval (char_u *str, typval_T *rettv) case VAR_DICT: ++rettv->vval.v_dict->dv_refcount; break; case VAR_LIST: ++rettv->vval.v_list->lv_refcount; break; case VAR_FUNC: func_ref(rettv->vval.v_string); break; + case VAR_PARTIAL: ++rettv->vval.v_partial->pt_refcount; break; case VAR_UNKNOWN: rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; diff --git a/src/if_python3.c b/src/if_python3.c --- a/src/if_python3.c +++ b/src/if_python3.c @@ -1654,6 +1654,7 @@ do_py3eval (char_u *str, typval_T *rettv case VAR_DICT: ++rettv->vval.v_dict->dv_refcount; break; case VAR_LIST: ++rettv->vval.v_list->lv_refcount; break; case VAR_FUNC: func_ref(rettv->vval.v_string); break; + case VAR_PARTIAL: ++rettv->vval.v_partial->pt_refcount; break; case VAR_UNKNOWN: rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; diff --git a/src/json.c b/src/json.c --- a/src/json.c +++ b/src/json.c @@ -212,6 +212,7 @@ json_encode_item(garray_T *gap, typval_T break; case VAR_FUNC: + case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: /* no JSON equivalent TODO: better error */ diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -83,10 +83,11 @@ long get_dict_number(dict_T *d, char_u * int string2float(char_u *text, float_T *value); char_u *get_function_name(expand_T *xp, int idx); char_u *get_expr_name(expand_T *xp, int idx); -int call_func(char_u *funcname, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict); +int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in); buf_T *buflist_find_by_name(char_u *name, int curtab_only); -int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv); +int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); +void partial_unref(partial_T *pt); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1110,6 +1110,7 @@ typedef double float_T; typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; +typedef struct partial_S partial_T; typedef struct jobvar_S job_T; typedef struct readq_S readq_T; @@ -1123,6 +1124,7 @@ typedef enum VAR_NUMBER, /* "v_number" is used */ VAR_STRING, /* "v_string" is used */ VAR_FUNC, /* "v_string" is function name */ + VAR_PARTIAL, /* "v_partial" is used */ VAR_LIST, /* "v_list" is used */ VAR_DICT, /* "v_dict" is used */ VAR_FLOAT, /* "v_float" is used */ @@ -1147,6 +1149,7 @@ typedef struct char_u *v_string; /* string value (can be NULL!) */ list_T *v_list; /* list value (can be NULL!) */ dict_T *v_dict; /* dict value (can be NULL!) */ + partial_T *v_partial; /* closure: function with args */ #ifdef FEAT_JOB_CHANNEL job_T *v_job; /* job value (can be NULL!) */ channel_T *v_channel; /* channel value (can be NULL!) */ @@ -1239,6 +1242,15 @@ struct dictvar_S dict_T *dv_used_prev; /* previous dict in used dicts list */ }; +struct partial_S +{ + int pt_refcount; /* reference count */ + char_u *pt_name; /* function name */ + int pt_argc; /* number of arguments */ + typval_T *pt_argv; /* arguments in allocated array */ + dict_T *pt_dict; /* dict for "self" */ +}; + typedef enum { JOB_FAILED, @@ -1264,6 +1276,7 @@ struct jobvar_S char_u *jv_stoponexit; /* allocated */ int jv_exitval; char_u *jv_exit_cb; /* allocated */ + partial_T *jv_exit_partial; buf_T *jv_in_buf; /* buffer from "in-name" */ @@ -1291,6 +1304,7 @@ struct jsonq_S struct cbq_S { char_u *cq_callback; + partial_T *cq_partial; int cq_seq_nr; cbq_T *cq_next; cbq_T *cq_prev; @@ -1346,6 +1360,7 @@ typedef struct { cbq_T ch_cb_head; /* dummy node for per-request callbacks */ char_u *ch_callback; /* call when a msg is not handled */ + partial_T *ch_partial; buf_T *ch_buffer; /* buffer to read from or write to */ linenr_T ch_buf_top; /* next line to send */ @@ -1371,7 +1386,9 @@ struct channel_S { * closed */ char_u *ch_callback; /* call when any msg is not handled */ + partial_T *ch_partial; char_u *ch_close_cb; /* call when channel is closed */ + partial_T *ch_close_partial; job_T *ch_job; /* Job that uses this channel; this does not * count as a reference to avoid a circular @@ -1447,9 +1464,15 @@ typedef struct linenr_T jo_in_bot; char_u *jo_callback; /* not allocated! */ + partial_T *jo_partial; /* not referenced! */ char_u *jo_out_cb; /* not allocated! */ + partial_T *jo_out_partial; /* not referenced! */ char_u *jo_err_cb; /* not allocated! */ + partial_T *jo_err_partial; /* not referenced! */ char_u *jo_close_cb; /* not allocated! */ + partial_T *jo_close_partial; /* not referenced! */ + char_u *jo_exit_cb; /* not allocated! */ + partial_T *jo_exit_partial; /* not referenced! */ int jo_waittime; int jo_timeout; int jo_out_timeout; @@ -1459,7 +1482,6 @@ typedef struct char_u jo_soe_buf[NUMBUFLEN]; char_u *jo_stoponexit; char_u jo_ecb_buf[NUMBUFLEN]; - char_u *jo_exit_cb; } jobopt_T; diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -12,6 +12,7 @@ source test_glob2regpat.vim source test_join.vim source test_lispwords.vim source test_menu.vim +source test_partial.vim source test_reltime.vim source test_searchpos.vim source test_set.vim diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_partial.vim @@ -0,0 +1,43 @@ +" Test binding arguments to a Funcref. + +func MyFunc(arg1, arg2, arg3) + return a:arg1 . '/' . a:arg2 . '/' . a:arg3 +endfunc + +func MySort(up, one, two) + if a:one == a:two + return 0 + endif + if a:up + return a:one > a:two + endif + return a:one < a:two +endfunc + +func Test_partial_args() + let Cb = function('MyFunc', ["foo", "bar"]) + call assert_equal("foo/bar/xxx", Cb("xxx")) + call assert_equal("foo/bar/yyy", call(Cb, ["yyy"])) + + let Sort = function('MySort', [1]) + call assert_equal([1, 2, 3], sort([3, 1, 2], Sort)) + let Sort = function('MySort', [0]) + call assert_equal([3, 2, 1], sort([3, 1, 2], Sort)) +endfunc + +func MyDictFunc(arg1, arg2) dict + return self.name . '/' . a:arg1 . '/' . a:arg2 +endfunc + +func Test_partial_dict() + let dict = {'name': 'hello'} + let Cb = function('MyDictFunc', ["foo", "bar"], dict) + call assert_equal("hello/foo/bar", Cb()) + call assert_fails('Cb("xxx")', 'E492:') + let Cb = function('MyDictFunc', ["foo"], dict) + call assert_equal("hello/foo/xxx", Cb("xxx")) + call assert_fails('Cb()', 'E492:') + let Cb = function('MyDictFunc', dict) + call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy")) + call assert_fails('Cb()', 'E492:') +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -744,6 +744,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1559, +/**/ 1558, /**/ 1557,