Patch 7.4.1559

50 views
Skip to first unread message

Bram Moolenaar

unread,
Mar 14, 2016, 6:05:50 PM3/14/16
to vim...@googlegroups.com

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, "<SNR>%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, "<SNR>%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 -- Br...@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 ///

Tony Mechelynck

unread,
Mar 14, 2016, 6:43:14 PM3/14/16
to vim_dev
On Mon, Mar 14, 2016 at 11:05 PM, Bram Moolenaar <Br...@moolenaar.net> wrote:
>
> 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

With this patch, compile error in Huge but not in Tiny:

gcc -c -I. -Iproto -DHAVE_CONFIG_H -DFEAT_GUI_GTK -pthread
-I/usr/include/gtk-2.0 -I/usr/lib64/gtk-2.0/include
-I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/cairo
-I/usr/include/pixman-1 -I/usr/include/libdrm -I/usr/include/libpng16
-I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libpng16
-I/usr/include/pango-1.0 -I/usr/include/harfbuzz
-I/usr/include/pango-1.0 -I/usr/include/glib-2.0
-I/usr/lib64/glib-2.0/include -I/usr/include/freetype2 -D_REENTRANT
-DORBIT2=1 -pthread -I/usr/include/libgnomeui-2.0
-I/usr/include/gnome-keyring-1 -I/usr/include/libbonoboui-2.0
-I/usr/include/libxml2 -I/usr/include/libgnome-2.0
-I/usr/include/libbonobo-2.0 -I/usr/include/bonobo-activation-2.0
-I/usr/include/orbit-2.0 -I/usr/include/libgnomecanvas-2.0
-I/usr/include/gail-1.0 -I/usr/include/libart-2.0
-I/usr/include/gtk-2.0 -I/usr/lib64/gtk-2.0/include
-I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/cairo
-I/usr/include/pixman-1 -I/usr/include/libdrm -I/usr/include/libpng16
-I/usr/include/pango-1.0 -I/usr/include/harfbuzz
-I/usr/include/pango-1.0 -I/usr/include/freetype2
-I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libpng16
-I/usr/include/gnome-vfs-2.0 -I/usr/lib64/gnome-vfs-2.0/include
-I/usr/include/gconf/2 -I/usr/include/dbus-1.0
-I/usr/lib64/dbus-1.0/include -I/usr/include/glib-2.0
-I/usr/lib64/glib-2.0/include -O2 -fno-strength-reduce -Wall
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -o objects/channel.o
channel.c
channel.c:1147:1: error: conflicting types for ‘channel_set_req_callback’
channel_set_req_callback(
^
In file included from proto.h:197:0,
from vim.h:1948,
from channel.c:13:
proto/channel.pro:15:6: note: previous declaration of
‘channel_set_req_callback’ was here
void channel_set_req_callback(channel_T *channel, int part, char_u
*callback, int id);
^
Makefile:2955: recipe for target 'objects/channel.o' failed
make: *** [objects/channel.o] Error 1
exit status 2

Tony Mechelynck

unread,
Mar 14, 2016, 6:50:34 PM3/14/16
to vim_dev
On Mon, Mar 14, 2016 at 11:43 PM, Tony Mechelynck
<antoine.m...@gmail.com> wrote:
> On Mon, Mar 14, 2016 at 11:05 PM, Bram Moolenaar <Br...@moolenaar.net> wrote:
>>
>> 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
>
> With this patch, compile error in Huge but not in Tiny:


Fixed in 7.4.1561, sorry.

Best regards,
Tony.
Reply all
Reply to author
Forward
0 new messages