To: vim_dev@googlegroups.com Subject: Patch 7.4.1559 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1559 Problem: Passing cookie to a callback is clumsy. Solution: Change function() to take arguments and return a partial. Files: src/structs.h, src/channel.c, src/eval.c, src/if_python.c, src/if_python3.c, src/if_py_both.h, src/json.c, src/proto/eval.pro, src/testdir/test_partial.vim, src/testdir/test_alot.vim, runtime/doc/eval.txt *** ../vim-7.4.1558/src/structs.h 2016-03-13 18:06:59.520803811 +0100 --- src/structs.h 2016-03-14 22:45:33.611073843 +0100 *************** *** 1110,1115 **** --- 1110,1116 ---- 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,1128 **** --- 1124,1130 ---- 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,1152 **** --- 1149,1155 ---- 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,1244 **** --- 1242,1256 ---- 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,1269 **** --- 1276,1282 ---- 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,1296 **** --- 1304,1310 ---- struct cbq_S { char_u *cq_callback; + partial_T *cq_partial; int cq_seq_nr; cbq_T *cq_next; cbq_T *cq_prev; *************** *** 1346,1351 **** --- 1360,1366 ---- 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,1377 **** --- 1386,1394 ---- * 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,1455 **** --- 1464,1478 ---- 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,1465 **** char_u jo_soe_buf[NUMBUFLEN]; char_u *jo_stoponexit; char_u jo_ecb_buf[NUMBUFLEN]; - char_u *jo_exit_cb; } jobopt_T; --- 1482,1487 ---- *** ../vim-7.4.1558/src/channel.c 2016-03-12 15:58:30.020231560 +0100 --- src/channel.c 2016-03-14 22:50:20.228063983 +0100 *************** *** 1025,1032 **** void channel_set_options(channel_T *channel, jobopt_T *opt) { ! int part; ! char_u **cbp; if (opt->jo_set & JO_MODE) for (part = PART_SOCK; part <= PART_IN; ++part) --- 1025,1033 ---- void channel_set_options(channel_T *channel, jobopt_T *opt) { ! int part; ! char_u **cbp; ! partial_T **pp; if (opt->jo_set & JO_MODE) for (part = PART_SOCK; part <= PART_IN; ++part) *************** *** 1049,1086 **** --- 1050,1107 ---- 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,1133 **** */ void channel_set_req_callback( ! channel_T *channel, ! int part, ! char_u *callback, ! int id) { cbq_T *head = &channel->ch_part[part].ch_cb_head; cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T)); --- 1145,1155 ---- */ void channel_set_req_callback( ! 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,1140 **** --- 1157,1165 ---- 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,1253 **** * Invoke the "callback" on channel "channel". */ static void ! invoke_callback(channel_T *channel, char_u *callback, typval_T *argv) { typval_T rettv; int dummy; --- 1272,1279 ---- * Invoke the "callback" on channel "channel". */ static void ! invoke_callback(channel_T *channel, char_u *callback, partial_T *partial, ! typval_T *argv) { typval_T rettv; int dummy; *************** *** 1256,1262 **** argv[0].vval.v_channel = channel; call_func(callback, (int)STRLEN(callback), ! &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); clear_tv(&rettv); /* If an echo command was used the cursor needs to be put back where --- 1282,1288 ---- argv[0].vval.v_channel = channel; call_func(callback, (int)STRLEN(callback), ! &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,1635 **** ++emsg_skip; if (!is_call) tv = eval_expr(arg, NULL); ! else if (func_call(arg, &argv[2], NULL, &res_tv) == OK) tv = &res_tv; else tv = NULL; --- 1655,1661 ---- ++emsg_skip; if (!is_call) tv = eval_expr(arg, NULL); ! else if (func_call(arg, &argv[2], NULL, NULL, &res_tv) == OK) tv = &res_tv; else tv = NULL; *************** *** 1685,1692 **** /* 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); vim_free(item->cq_callback); vim_free(item); } --- 1711,1719 ---- /* 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, item->cq_partial, argv); vim_free(item->cq_callback); + partial_unref(item->cq_partial); vim_free(item); } *************** *** 1775,1780 **** --- 1802,1808 ---- 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,1796 **** --- 1814,1833 ---- 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,1942 **** /* invoke the channel callback */ ch_logs(channel, "Invoking channel callback %s", (char *)callback); ! invoke_callback(channel, callback, argv); } } } --- 1973,1979 ---- /* invoke the channel callback */ ch_logs(channel, "Invoking channel callback %s", (char *)callback); ! invoke_callback(channel, callback, partial, argv); } } } *************** *** 2024,2036 **** 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); clear_tv(&rettv); --channel->ch_refcount; /* the callback is only called once */ vim_free(channel->ch_close_cb); channel->ch_close_cb = NULL; } channel->ch_nb_close_cb = NULL; --- 2061,2076 ---- 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, ! 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,2073 **** --- 2108,2114 ---- remove_cb_node(cb_head, node); vim_free(node->cq_callback); + partial_unref(node->cq_partial); vim_free(node); } *************** *** 2079,2084 **** --- 2120,2127 ---- 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,2100 **** --- 2136,2147 ---- 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,2598 **** EMSG2(_("E917: Cannot use a callback with %s()"), fun); return NULL; } ! channel_set_req_callback(channel, part_send, opt->jo_callback, id); } if (channel_send(channel, part_send, text, fun) == OK --- 2639,2646 ---- EMSG2(_("E917: Cannot use a callback with %s()"), fun); return NULL; } ! channel_set_req_callback(channel, part_send, ! opt->jo_callback, opt->jo_partial, id); } if (channel_send(channel, part_send, text, fun) == OK *************** *** 2982,2994 **** * Return NULL for an invalid argument. */ static char_u * ! get_callback(typval_T *arg) { 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")); return NULL; } --- 3030,3048 ---- * Return NULL for an invalid argument. */ static char_u * ! 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(_("E921: Invalid callback argument")); return NULL; } *************** *** 3201,3207 **** if (!(supported & JO_CALLBACK)) break; opt->jo_set |= JO_CALLBACK; ! opt->jo_callback = get_callback(item); if (opt->jo_callback == NULL) { EMSG2(_(e_invarg2), "callback"); --- 3255,3261 ---- if (!(supported & JO_CALLBACK)) break; opt->jo_set |= JO_CALLBACK; ! opt->jo_callback = get_callback(item, &opt->jo_partial); if (opt->jo_callback == NULL) { EMSG2(_(e_invarg2), "callback"); *************** *** 3213,3219 **** if (!(supported & JO_OUT_CALLBACK)) break; opt->jo_set |= JO_OUT_CALLBACK; ! opt->jo_out_cb = get_callback(item); if (opt->jo_out_cb == NULL) { EMSG2(_(e_invarg2), "out-cb"); --- 3267,3273 ---- if (!(supported & JO_OUT_CALLBACK)) break; opt->jo_set |= JO_OUT_CALLBACK; ! opt->jo_out_cb = get_callback(item, &opt->jo_out_partial); if (opt->jo_out_cb == NULL) { EMSG2(_(e_invarg2), "out-cb"); *************** *** 3225,3231 **** if (!(supported & JO_ERR_CALLBACK)) break; opt->jo_set |= JO_ERR_CALLBACK; ! opt->jo_err_cb = get_callback(item); if (opt->jo_err_cb == NULL) { EMSG2(_(e_invarg2), "err-cb"); --- 3279,3285 ---- if (!(supported & JO_ERR_CALLBACK)) break; opt->jo_set |= JO_ERR_CALLBACK; ! opt->jo_err_cb = get_callback(item, &opt->jo_err_partial); if (opt->jo_err_cb == NULL) { EMSG2(_(e_invarg2), "err-cb"); *************** *** 3237,3243 **** if (!(supported & JO_CLOSE_CALLBACK)) break; opt->jo_set |= JO_CLOSE_CALLBACK; ! opt->jo_close_cb = get_callback(item); if (opt->jo_close_cb == NULL) { EMSG2(_(e_invarg2), "close-cb"); --- 3291,3297 ---- if (!(supported & JO_CLOSE_CALLBACK)) break; opt->jo_set |= JO_CLOSE_CALLBACK; ! opt->jo_close_cb = get_callback(item, &opt->jo_close_partial); if (opt->jo_close_cb == NULL) { EMSG2(_(e_invarg2), "close-cb"); *************** *** 3311,3317 **** 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 (opt->jo_exit_cb == NULL) { EMSG2(_(e_invarg2), "exit-cb"); --- 3365,3378 ---- if (!(supported & JO_EXIT_CB)) break; opt->jo_set |= JO_EXIT_CB; ! 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,3395 **** --- 3451,3457 ---- vim_free(job->jv_stoponexit); vim_free(job->jv_exit_cb); + partial_unref(job->jv_exit_partial); vim_free(job); } *************** *** 3454,3463 **** --- 3516,3534 ---- 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,3727 **** 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); clear_tv(&rettv); --job->jv_refcount; } --- 3792,3799 ---- 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, ! job->jv_exit_partial, NULL); clear_tv(&rettv); --job->jv_refcount; } *** ../vim-7.4.1558/src/eval.c 2016-03-13 19:04:45.381224860 +0100 --- src/eval.c 2016-03-14 22:44:48.439548238 +0100 *************** *** 452,459 **** 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 void emsg_funcname(char *ermsg, char_u *name); static int non_zero_arg(typval_T *argvars); --- 452,459 ---- 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, 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,1681 **** 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); if (safe) { --sandbox; --- 1675,1681 ---- 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, NULL); if (safe) { --sandbox; *************** *** 3091,3096 **** --- 3091,3097 ---- case VAR_UNKNOWN: case VAR_DICT: case VAR_FUNC: + case VAR_PARTIAL: case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: *************** *** 3456,3461 **** --- 3457,3463 ---- int doesrange; int failed = FALSE; funcdict_T fudi; + partial_T *partial; if (eap->skip) { *************** *** 3486,3492 **** /* 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); /* Skip white space to allow ":call func ()". Not good, but required for * backward compatibility. */ --- 3488,3494 ---- /* 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, &partial, FALSE); /* Skip white space to allow ":call func ()". Not good, but required for * backward compatibility. */ *************** *** 3525,3531 **** arg = startarg; if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg, eap->line1, eap->line2, &doesrange, ! !eap->skip, fudi.fd_dict) == FAIL) { failed = TRUE; break; --- 3527,3533 ---- arg = startarg; if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg, eap->line1, eap->line2, &doesrange, ! !eap->skip, partial, fudi.fd_dict) == FAIL) { failed = TRUE; break; *************** *** 3870,3875 **** --- 3872,3878 ---- case VAR_NUMBER: case VAR_STRING: case VAR_FUNC: + case VAR_PARTIAL: case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: *************** *** 4542,4548 **** } } ! else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC) { if (rettv->v_type != var2.v_type || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) --- 4545,4552 ---- } } ! 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,4560 **** --- 4559,4570 ---- 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,4572 **** else n1 = STRCMP(rettv->vval.v_string, var2.vval.v_string) == 0; - if (type == TYPE_NEQUAL) - n1 = !n1; } } #ifdef FEAT_FLOAT --- 4574,4582 ---- else n1 = STRCMP(rettv->vval.v_string, var2.vval.v_string) == 0; } + if (type == TYPE_NEQUAL) + n1 = !n1; } #ifdef FEAT_FLOAT *************** *** 5230,5243 **** { if (**arg == '(') /* recursive! */ { /* If "s" is the name of a variable of type VAR_FUNC * use its contents. */ ! s = deref_func_name(s, &len, !evaluate); /* Invoke the function. */ ret = get_func_tv(s, len, rettv, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, ! &len, evaluate, NULL); /* If evaluate is FALSE rettv->v_type was not set in * get_func_tv, but it's needed in handle_subscript() to parse --- 5240,5255 ---- { 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, &partial, !evaluate); /* Invoke the function. */ ret = get_func_tv(s, len, rettv, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, ! &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,5364 **** --- 5371,5377 ---- switch (rettv->v_type) { case VAR_FUNC: + case VAR_PARTIAL: if (verbose) EMSG(_("E695: Cannot index a Funcref")); return FAIL; *************** *** 5480,5485 **** --- 5493,5499 ---- { case VAR_UNKNOWN: case VAR_FUNC: + case VAR_PARTIAL: case VAR_FLOAT: case VAR_SPECIAL: case VAR_JOB: *************** *** 6218,6223 **** --- 6232,6241 ---- && 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,7798 **** --- 7811,7822 ---- 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,7883 **** --- 7902,7911 ---- 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,8152 **** {"foldtext", 0, 0, f_foldtext}, {"foldtextresult", 1, 1, f_foldtextresult}, {"foreground", 0, 0, f_foreground}, ! {"function", 1, 1, f_function}, {"garbagecollect", 0, 1, f_garbagecollect}, {"get", 2, 3, f_get}, {"getbufline", 2, 3, f_getbufline}, --- 8174,8180 ---- {"foldtext", 0, 0, f_foldtext}, {"foldtextresult", 1, 1, f_foldtextresult}, {"foreground", 0, 0, f_foreground}, ! {"function", 1, 3, f_function}, {"garbagecollect", 0, 1, f_garbagecollect}, {"get", 2, 3, f_get}, {"getbufline", 2, 3, f_getbufline}, *************** *** 8524,8536 **** /* * Check if "name" is a variable of type VAR_FUNC. If so, return the function * name it contains, otherwise return "name". */ static char_u * ! deref_func_name(char_u *name, int *lenp, int no_autoload) { dictitem_T *v; int cc; cc = name[*lenp]; name[*lenp] = NUL; v = find_var(name, NULL, no_autoload); --- 8552,8567 ---- /* * 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, 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,8551 **** --- 8577,8594 ---- 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,8568 **** --- 8606,8612 ---- 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,8580 **** * Get the arguments. */ argp = *arg; ! while (argcount < MAX_FUNC_ARGS) { argp = skipwhite(argp + 1); /* skip the '(' or ',' */ if (*argp == ')' || *argp == ',' || *argp == NUL) --- 8618,8624 ---- * Get the arguments. */ argp = *arg; ! while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { argp = skipwhite(argp + 1); /* skip the '(' or ',' */ if (*argp == ')' || *argp == ',' || *argp == NUL) *************** *** 8595,8601 **** if (ret == OK) ret = call_func(name, len, rettv, argcount, argvars, ! firstline, lastline, doesrange, evaluate, selfdict); else if (!aborting()) { if (argcount == MAX_FUNC_ARGS) --- 8639,8645 ---- if (ret == OK) ret = call_func(name, len, rettv, argcount, argvars, ! firstline, lastline, doesrange, evaluate, partial, selfdict); else if (!aborting()) { if (argcount == MAX_FUNC_ARGS) *************** *** 8622,8635 **** 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" 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" */ { int ret = FAIL; #define ERROR_UNKNOWN 0 --- 8666,8680 ---- char_u *funcname, /* name of the function */ int len, /* length of "name" */ typval_T *rettv, /* return value goes here */ ! 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, ! partial_T *partial, /* optional, can be NULL */ ! dict_T *selfdict_in) /* Dictionary for "self" */ { int ret = FAIL; #define ERROR_UNKNOWN 0 *************** *** 8639,8644 **** --- 8684,8690 ---- #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,8652 **** --- 8693,8703 ---- 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,8703 **** --- 8749,8775 ---- *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,8849 **** --- 8913,8927 ---- 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,9742 **** --- 9815,9821 ---- func_call( char_u *name, typval_T *args, + partial_T *partial, dict_T *selfdict, typval_T *rettv) { *************** *** 9749,9755 **** for (item = args->vval.v_list->lv_first; item != NULL; item = item->li_next) { ! if (argc == MAX_FUNC_ARGS) { EMSG(_("E699: Too many arguments")); break; --- 9828,9834 ---- for (item = args->vval.v_list->lv_first; item != NULL; item = item->li_next) { ! if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { EMSG(_("E699: Too many arguments")); break; *************** *** 9763,9769 **** if (item == NULL) r = call_func(name, (int)STRLEN(name), rettv, argc, argv, curwin->w_cursor.lnum, curwin->w_cursor.lnum, ! &dummy, TRUE, selfdict); /* Free the arguments. */ while (argc > 0) --- 9842,9848 ---- if (item == NULL) r = call_func(name, (int)STRLEN(name), rettv, argc, argv, curwin->w_cursor.lnum, curwin->w_cursor.lnum, ! &dummy, TRUE, partial, selfdict); /* Free the arguments. */ while (argc > 0) *************** *** 9773,9784 **** } /* ! * "call(func, arglist)" function */ static void f_call(typval_T *argvars, typval_T *rettv) { char_u *func; dict_T *selfdict = NULL; if (argvars[1].v_type != VAR_LIST) --- 9852,9864 ---- } /* ! * "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,9796 **** --- 9871,9881 ---- 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,9812 **** selfdict = argvars[2].vval.v_dict; } ! (void)func_call(func, &argvars[1], selfdict, rettv); } #ifdef FEAT_FLOAT --- 9891,9897 ---- selfdict = argvars[2].vval.v_dict; } ! (void)func_call(func, &argvars[1], partial, selfdict, rettv); } #ifdef FEAT_FLOAT *************** *** 10627,10632 **** --- 10712,10720 ---- 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,11693 **** --- 11776,11782 ---- 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,11726 **** * 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; } } /* * "garbagecollect()" function */ --- 11796,11915 ---- * would also work, but some plugins depend on the name being * printable text. */ sprintf(sid_buf, "%ld_", (long)current_SID); ! 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); + } + /* * "garbagecollect()" function */ *************** *** 14598,14603 **** --- 14787,14793 ---- 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,18174 **** --- 18359,18365 ---- 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,18283 **** --- 18469,18476 ---- 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,18301 **** si1 = (sortItem_T *)s1; si2 = (sortItem_T *)s2; /* 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), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, ! sortinfo->item_compare_selfdict); clear_tv(&argv[0]); clear_tv(&argv[1]); --- 18479,18498 ---- 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(func_name, (int)STRLEN(func_name), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, ! partial, sortinfo->item_compare_selfdict); clear_tv(&argv[0]); clear_tv(&argv[1]); *************** *** 18358,18369 **** --- 18555,18569 ---- 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,18449 **** info.item_compare_func_err = FALSE; info.item_compare_keep_zero = FALSE; /* test the compare function */ ! if (info.item_compare_func != NULL && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) == ITEM_COMPARE_FAIL) EMSG(_("E702: Sort compare function failed")); --- 18643,18650 ---- info.item_compare_func_err = FALSE; info.item_compare_keep_zero = FALSE; /* test the compare function */ ! 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,18457 **** --- 18653,18659 ---- /* 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,18477 **** /* 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_compare2 : item_compare; for (li = l->lv_first; li != NULL && li->li_next != NULL; --- 18673,18680 ---- /* 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 != NULL ! || info.item_compare_partial != NULL ? item_compare2 : item_compare; for (li = l->lv_first; li != NULL && li->li_next != NULL; *************** *** 20045,20050 **** --- 20248,20254 ---- 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,21305 **** char_u *s; int len; typval_T functv; while (ret == OK && (**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) ! || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC))) && !vim_iswhite(*(*arg - 1))) { if (**arg == '(') --- 21499,21511 ---- 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 ! || rettv->v_type == VAR_PARTIAL))) && !vim_iswhite(*(*arg - 1))) { if (**arg == '(') *************** *** 21311,21323 **** rettv->v_type = VAR_UNKNOWN; /* Invoke the function. Recursive! */ ! 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); /* Clear the funcref afterwards, so that deleting it while * evaluating the arguments is possible (see test55). */ --- 21517,21535 ---- rettv->v_type = VAR_UNKNOWN; /* Invoke the function. Recursive! */ ! 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, pt, selfdict); /* Clear the funcref afterwards, so that deleting it while * evaluating the arguments is possible (see test55). */ *************** *** 21405,21410 **** --- 21617,21625 ---- 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,21453 **** --- 21663,21672 ---- 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,21529 **** --- 21743,21749 ---- break; #endif case VAR_FUNC: + case VAR_PARTIAL: EMSG(_("E703: Using a Funcref as a Number")); break; case VAR_STRING: *************** *** 21572,21577 **** --- 21792,21798 ---- 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,21693 **** --- 21909,21915 ---- 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,22093 **** msg_advance(22); if (type == VAR_NUMBER) msg_putchar('#'); ! else if (type == VAR_FUNC) msg_putchar('*'); else if (type == VAR_LIST) { --- 22309,22315 ---- msg_advance(22); if (type == VAR_NUMBER) msg_putchar('#'); ! else if (type == VAR_FUNC || type == VAR_PARTIAL) msg_putchar('*'); else if (type == VAR_LIST) { *************** *** 22106,22112 **** msg_outtrans(string); ! if (type == VAR_FUNC) msg_puts((char_u *)"()"); if (*first) { --- 22328,22334 ---- msg_outtrans(string); ! if (type == VAR_FUNC || type == VAR_PARTIAL) msg_puts((char_u *)"()"); if (*first) { *************** *** 22138,22144 **** } v = find_var_in_ht(ht, 0, varname, TRUE); ! if (tv->v_type == VAR_FUNC && var_check_func_name(name, v == NULL)) return; if (v != NULL) --- 22360,22367 ---- } v = find_var_in_ht(ht, 0, varname, TRUE); ! if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) ! && var_check_func_name(name, v == NULL)) return; if (v != NULL) *************** *** 22383,22388 **** --- 22606,22620 ---- 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,22442 **** --- 22669,22675 ---- case VAR_FLOAT: case VAR_STRING: case VAR_FUNC: + case VAR_PARTIAL: case VAR_SPECIAL: case VAR_JOB: case VAR_CHANNEL: *************** *** 23415,23420 **** --- 23648,23654 ---- char_u sid_buf[20]; int len; lval_T lv; + partial_T *partial; if (fdp != NULL) vim_memset(fdp, 0, sizeof(funcdict_T)); *************** *** 23499,23512 **** 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); if (name == lv.ll_exp_name) name = NULL; } else { len = (int)(end - *pp); ! name = deref_func_name(*pp, &len, flags & TFN_NO_AUTOLOAD); if (name == *pp) name = NULL; } --- 23733,23747 ---- if (lv.ll_exp_name != NULL) { len = (int)STRLEN(lv.ll_exp_name); ! 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, &partial, flags & TFN_NO_AUTOLOAD); if (name == *pp) name = NULL; } *************** *** 25111,25116 **** --- 25346,25352 ---- case VAR_UNKNOWN: case VAR_FUNC: + case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: continue; *** ../vim-7.4.1558/src/if_python.c 2016-02-16 15:06:54.661635316 +0100 --- src/if_python.c 2016-03-13 21:58:43.707962531 +0100 *************** *** 1561,1566 **** --- 1561,1567 ---- 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; *** ../vim-7.4.1558/src/if_python3.c 2016-02-16 15:06:54.661635316 +0100 --- src/if_python3.c 2016-03-13 21:59:10.571680867 +0100 *************** *** 1654,1659 **** --- 1654,1660 ---- 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; *** ../vim-7.4.1558/src/if_py_both.h 2016-03-12 22:11:34.243300238 +0100 --- src/if_py_both.h 2016-03-14 21:13:05.965595807 +0100 *************** *** 2944,2950 **** Python_Lock_Vim(); VimTryStart(); ! error = func_call(name, &args, selfdict, &rettv); Python_Release_Vim(); Py_END_ALLOW_THREADS --- 2944,2950 ---- Python_Lock_Vim(); VimTryStart(); ! error = func_call(name, &args, NULL, selfdict, &rettv); Python_Release_Vim(); Py_END_ALLOW_THREADS *** ../vim-7.4.1558/src/json.c 2016-03-05 23:22:57.781396509 +0100 --- src/json.c 2016-03-13 21:59:47.967288790 +0100 *************** *** 212,217 **** --- 212,218 ---- break; case VAR_FUNC: + case VAR_PARTIAL: case VAR_JOB: case VAR_CHANNEL: /* no JSON equivalent TODO: better error */ *** ../vim-7.4.1558/src/proto/eval.pro 2016-03-13 18:06:59.528803729 +0100 --- src/proto/eval.pro 2016-03-14 22:44:54.475484846 +0100 *************** *** 83,92 **** 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); 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); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); 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); --- 83,93 ---- 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_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, 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); *** ../vim-7.4.1558/src/testdir/test_partial.vim 2016-03-14 23:01:21.345113210 +0100 --- src/testdir/test_partial.vim 2016-03-14 22:59:27.918305643 +0100 *************** *** 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 *** ../vim-7.4.1558/src/testdir/test_alot.vim 2016-03-12 19:22:43.781293285 +0100 --- src/testdir/test_alot.vim 2016-03-13 22:05:31.331689410 +0100 *************** *** 12,17 **** --- 12,18 ---- 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 *** ../vim-7.4.1558/runtime/doc/eval.txt 2016-03-13 19:04:45.381224860 +0100 --- runtime/doc/eval.txt 2016-03-14 23:03:33.367725396 +0100 *************** *** 1876,1885 **** foldclosed( {lnum}) Number first line of fold at {lnum} if closed foldclosedend( {lnum}) Number last line of fold at {lnum} if closed foldlevel( {lnum}) Number fold level at {lnum} ! 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} 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} --- 1892,1902 ---- foldclosed( {lnum}) Number first line of fold at {lnum} if closed foldclosedend( {lnum}) Number last line of fold at {lnum} if closed foldlevel( {lnum}) Number fold level at {lnum} ! 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} [, {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} *************** *** 3521,3530 **** Win32 console version} ! function({name}) *function()* *E700* Return a |Funcref| variable that refers to function {name}. {name} can be a user defined function or an internal function. garbagecollect([{atexit}]) *garbagecollect()* Cleanup unused |Lists| and |Dictionaries| that have circular --- 3569,3614 ---- Win32 console version} ! *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 *** ../vim-7.4.1558/src/version.c 2016-03-13 19:04:45.385224819 +0100 --- src/version.c 2016-03-14 23:00:45.813486736 +0100 *************** *** 745,746 **** --- 745,748 ---- { /* Add new patch number below this line */ + /**/ + 1559, /**/ -- The users that I support would double-click on a landmine to find out what happens. -- A system administrator /// 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 ///