To: vim_dev@googlegroups.com Subject: Patch 8.1.1437 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1437 Problem: Code to handle callbacks is duplicated. Solution: Add callback_T and functions to deal with it. Files: src/structs.h, src/evalfunc.c, src/proto/evalfunc.pro, src/change.c, src/channel.c, src/proto/channel.pro, src/buffer.c, src/userfunc.c, src/proto/userfunc.pro, src/eval.c, src/ex_cmds2.c, src/popupwin.c *** ../vim-8.1.1436/src/structs.h 2019-05-30 21:24:22.177201251 +0200 --- src/structs.h 2019-06-01 13:00:39.892747290 +0200 *************** *** 1237,1242 **** --- 1237,1253 ---- typedef struct partial_S partial_T; typedef struct blobvar_S blob_T; + // Struct that holds both a normal function name and a partial_T, as used for a + // callback argument. + // When used temporarily "cb_name" is not allocated. The refcounts to either + // the function or the partial are incremented and need to be decremented + // later with free_callback(). + typedef struct { + char_u *cb_name; + partial_T *cb_partial; + int cb_free_name; // cb_name was allocated + } callback_T; + typedef struct jobvar_S job_T; typedef struct readq_S readq_T; typedef struct writeq_S writeq_T; *************** *** 1566,1573 **** char_u *jv_tty_type; // allocated #endif int jv_exitval; ! char_u *jv_exit_cb; /* allocated */ ! partial_T *jv_exit_partial; buf_T *jv_in_buf; /* buffer from "in-name" */ --- 1577,1583 ---- char_u *jv_tty_type; // allocated #endif int jv_exitval; ! callback_T jv_exit_cb; buf_T *jv_in_buf; /* buffer from "in-name" */ *************** *** 1606,1613 **** struct cbq_S { ! char_u *cq_callback; ! partial_T *cq_partial; int cq_seq_nr; cbq_T *cq_next; cbq_T *cq_prev; --- 1616,1622 ---- struct cbq_S { ! callback_T cq_callback; int cq_seq_nr; cbq_T *cq_next; cbq_T *cq_prev; *************** *** 1689,1696 **** writeq_T ch_writeque; /* header for write queue */ 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; bufref_T ch_bufref; /* buffer to read from or write to */ int ch_nomodifiable; /* TRUE when buffer can be 'nomodifiable' */ --- 1698,1704 ---- writeq_T ch_writeque; /* header for write queue */ cbq_T ch_cb_head; /* dummy node for per-request callbacks */ ! callback_T ch_callback; /* call when a msg is not handled */ bufref_T ch_bufref; /* buffer to read from or write to */ int ch_nomodifiable; /* TRUE when buffer can be 'nomodifiable' */ *************** *** 1731,1740 **** #ifdef MSWIN int ch_named_pipe; /* using named pipe instead of pty */ #endif ! 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; int ch_drop_never; int ch_keep_open; /* do not close on read error */ int ch_nonblock; --- 1739,1746 ---- #ifdef MSWIN int ch_named_pipe; /* using named pipe instead of pty */ #endif ! callback_T ch_callback; /* call when any msg is not handled */ ! callback_T ch_close_cb; /* call when channel is closed */ int ch_drop_never; int ch_keep_open; /* do not close on read error */ int ch_nonblock; *************** *** 1833,1848 **** linenr_T jo_in_top; 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_drop_never; int jo_waittime; int jo_timeout; --- 1839,1849 ---- linenr_T jo_in_top; linenr_T jo_in_bot; ! callback_T jo_callback; ! callback_T jo_out_cb; ! callback_T jo_err_cb; ! callback_T jo_close_cb; ! callback_T jo_exit_cb; int jo_drop_never; int jo_waittime; int jo_timeout; *************** *** 1886,1893 **** { listener_T *lr_next; int lr_id; ! char_u *lr_callback; ! partial_T *lr_partial; }; #endif --- 1887,1893 ---- { listener_T *lr_next; int lr_id; ! callback_T lr_callback; }; #endif *************** *** 1950,1962 **** #ifdef FEAT_TIMERS timer_T *tr_next; timer_T *tr_prev; ! proftime_T tr_due; /* when the callback is to be invoked */ ! char tr_firing; /* when TRUE callback is being called */ ! char tr_paused; /* when TRUE callback is not invoked */ ! int tr_repeat; /* number of times to repeat, -1 forever */ ! long tr_interval; /* msec */ ! char_u *tr_callback; /* allocated */ ! partial_T *tr_partial; int tr_emsg_count; #endif }; --- 1950,1961 ---- #ifdef FEAT_TIMERS timer_T *tr_next; timer_T *tr_prev; ! proftime_T tr_due; // when the callback is to be invoked ! char tr_firing; // when TRUE callback is being called ! char tr_paused; // when TRUE callback is not invoked ! int tr_repeat; // number of times to repeat, -1 forever ! long tr_interval; // msec ! callback_T tr_callback; int tr_emsg_count; #endif }; *************** *** 2509,2521 **** int b_shortname; /* this file has an 8.3 file name */ #ifdef FEAT_JOB_CHANNEL ! char_u *b_prompt_text; // set by prompt_setprompt() ! char_u *b_prompt_callback; // set by prompt_setcallback() ! partial_T *b_prompt_partial; // set by prompt_setcallback() ! char_u *b_prompt_interrupt; // set by prompt_setinterrupt() ! partial_T *b_prompt_int_partial; // set by prompt_setinterrupt() ! int b_prompt_insert; // value for restart_edit when entering ! // a prompt buffer window. #endif #ifdef FEAT_MZSCHEME void *b_mzscheme_ref; /* The MzScheme reference to this buffer */ --- 2508,2518 ---- int b_shortname; /* this file has an 8.3 file name */ #ifdef FEAT_JOB_CHANNEL ! char_u *b_prompt_text; // set by prompt_setprompt() ! callback_T b_prompt_callback; // set by prompt_setcallback() ! callback_T b_prompt_interrupt; // set by prompt_setinterrupt() ! int b_prompt_insert; // value for restart_edit when entering ! // a prompt buffer window. #endif #ifdef FEAT_MZSCHEME void *b_mzscheme_ref; /* The MzScheme reference to this buffer */ *** ../vim-8.1.1436/src/evalfunc.c 2019-05-30 22:35:15.151191862 +0200 --- src/evalfunc.c 2019-06-01 13:13:24.669729965 +0200 *************** *** 9200,9207 **** f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED) { buf_T *buf; ! char_u *callback; ! partial_T *partial; if (check_secure()) return; --- 9200,9206 ---- f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED) { buf_T *buf; ! callback_T callback; if (check_secure()) return; *************** *** 9209,9225 **** if (buf == NULL) return; ! callback = get_callback(&argvars[1], &partial); ! if (callback == NULL) return; ! free_callback(buf->b_prompt_callback, buf->b_prompt_partial); ! if (partial == NULL) ! buf->b_prompt_callback = vim_strsave(callback); ! else ! /* pointer into the partial */ ! buf->b_prompt_callback = callback; ! buf->b_prompt_partial = partial; } /* --- 9208,9219 ---- if (buf == NULL) return; ! callback = get_callback(&argvars[1]); ! if (callback.cb_name == NULL) return; ! free_callback(&buf->b_prompt_callback); ! set_callback(&buf->b_prompt_callback, &callback); } /* *************** *** 9229,9236 **** f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv UNUSED) { buf_T *buf; ! char_u *callback; ! partial_T *partial; if (check_secure()) return; --- 9223,9229 ---- f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv UNUSED) { buf_T *buf; ! callback_T callback; if (check_secure()) return; *************** *** 9238,9254 **** if (buf == NULL) return; ! callback = get_callback(&argvars[1], &partial); ! if (callback == NULL) return; ! free_callback(buf->b_prompt_interrupt, buf->b_prompt_int_partial); ! if (partial == NULL) ! buf->b_prompt_interrupt = vim_strsave(callback); ! else ! /* pointer into the partial */ ! buf->b_prompt_interrupt = callback; ! buf->b_prompt_int_partial = partial; } /* --- 9231,9242 ---- if (buf == NULL) return; ! callback = get_callback(&argvars[1]); ! if (callback.cb_name == NULL) return; ! free_callback(&buf->b_prompt_interrupt); ! set_callback(&buf->b_prompt_interrupt, &callback); } /* *************** *** 14631,14674 **** /* * Get a callback from "arg". It can be a Funcref or a function name. * When "arg" is zero return an empty string. ! * Return NULL for an invalid argument. */ ! 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; ! ++(*pp)->pt_refcount; ! return partial_name(*pp); } ! *pp = NULL; ! if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) { ! func_ref(arg->vval.v_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; } /* ! * Unref/free "callback" and "partial" returned by get_callback(). */ void ! free_callback(char_u *callback, partial_T *partial) { ! if (partial != NULL) ! partial_unref(partial); ! else if (callback != NULL) { ! func_unref(callback); ! vim_free(callback); } } #ifdef FEAT_TIMERS /* * "timer_info([timer])" function --- 14619,14724 ---- /* * Get a callback from "arg". It can be a Funcref or a function name. * When "arg" is zero return an empty string. ! * "cb_name" is not allocated. ! * "cb_name" is set to NULL for an invalid argument. */ ! callback_T ! get_callback(typval_T *arg) { + callback_T res; + + res.cb_free_name = FALSE; if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) { ! res.cb_partial = arg->vval.v_partial; ! ++res.cb_partial->pt_refcount; ! res.cb_name = partial_name(res.cb_partial); } ! else { ! res.cb_partial = NULL; ! if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) ! { ! // Note that we don't make a copy of the string. ! res.cb_name = arg->vval.v_string; ! func_ref(res.cb_name); ! } ! else if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) ! { ! res.cb_name = (char_u *)""; ! } ! else ! { ! emsg(_("E921: Invalid callback argument")); ! res.cb_name = NULL; ! } } ! return res; } /* ! * Copy a callback into a typval_T. */ void ! put_callback(callback_T *cb, typval_T *tv) { ! if (cb->cb_partial != NULL) ! { ! tv->v_type = VAR_PARTIAL; ! tv->vval.v_partial = cb->cb_partial; ! ++tv->vval.v_partial->pt_refcount; ! } ! else { ! tv->v_type = VAR_FUNC; ! tv->vval.v_string = vim_strsave(cb->cb_name); ! func_ref(cb->cb_name); } } + /* + * Make a copy of "src" into "dest", allocating the function name if needed, + * without incrementing the refcount. + */ + void + set_callback(callback_T *dest, callback_T *src) + { + if (src->cb_partial == NULL) + { + // just a function name, make a copy + dest->cb_name = vim_strsave(src->cb_name); + dest->cb_free_name = TRUE; + } + else + { + // cb_name is a pointer into cb_partial + dest->cb_name = src->cb_name; + dest->cb_free_name = FALSE; + } + dest->cb_partial = src->cb_partial; + } + + /* + * Unref/free "callback" returned by get_callback() or set_callback(). + */ + void + free_callback(callback_T *callback) + { + if (callback->cb_partial != NULL) + { + partial_unref(callback->cb_partial); + callback->cb_partial = NULL; + } + else if (callback->cb_name != NULL) + func_unref(callback->cb_name); + if (callback->cb_free_name) + { + vim_free(callback->cb_name); + callback->cb_free_name = FALSE; + } + callback->cb_name = NULL; + } + #ifdef FEAT_TIMERS /* * "timer_info([timer])" function *************** *** 14723,14731 **** long msec = (long)tv_get_number(&argvars[0]); timer_T *timer; int repeat = 0; ! char_u *callback; dict_T *dict; - partial_T *partial; rettv->vval.v_number = -1; if (check_secure()) --- 14773,14780 ---- long msec = (long)tv_get_number(&argvars[0]); timer_T *timer; int repeat = 0; ! callback_T callback; dict_T *dict; rettv->vval.v_number = -1; if (check_secure()) *************** *** 14742,14762 **** repeat = dict_get_number(dict, (char_u *)"repeat"); } ! callback = get_callback(&argvars[1], &partial); ! if (callback == NULL) return; timer = create_timer(msec, repeat); if (timer == NULL) ! free_callback(callback, partial); else { ! if (partial == NULL) ! timer->tr_callback = vim_strsave(callback); ! else ! /* pointer into the partial */ ! timer->tr_callback = callback; ! timer->tr_partial = partial; rettv->vval.v_number = (varnumber_T)timer->tr_id; } } --- 14791,14806 ---- repeat = dict_get_number(dict, (char_u *)"repeat"); } ! callback = get_callback(&argvars[1]); ! if (callback.cb_name == NULL) return; timer = create_timer(msec, repeat); if (timer == NULL) ! free_callback(&callback); else { ! set_callback(&timer->tr_callback, &callback); rettv->vval.v_number = (varnumber_T)timer->tr_id; } } *** ../vim-8.1.1436/src/proto/evalfunc.pro 2019-05-11 21:14:02.336269566 +0200 --- src/proto/evalfunc.pro 2019-05-31 23:04:21.602577328 +0200 *************** *** 11,16 **** float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); void f_string(typval_T *argvars, typval_T *rettv); ! char_u *get_callback(typval_T *arg, partial_T **pp); ! void free_callback(char_u *callback, partial_T *partial); /* vim: set ft=c : */ --- 11,18 ---- float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); void f_string(typval_T *argvars, typval_T *rettv); ! callback_T get_callback(typval_T *arg); ! void put_callback(callback_T *cb, typval_T *tv); ! void set_callback(callback_T *dest, callback_T *src); ! void free_callback(callback_T *callback); /* vim: set ft=c : */ *** ../vim-8.1.1436/src/change.c 2019-05-29 22:28:25.759184826 +0200 --- src/change.c 2019-05-31 22:55:13.929346887 +0200 *************** *** 270,305 **** void f_listener_add(typval_T *argvars, typval_T *rettv) { ! char_u *callback; ! partial_T *partial; listener_T *lnr; buf_T *buf = curbuf; ! callback = get_callback(&argvars[0], &partial); ! if (callback == NULL) return; if (argvars[1].v_type != VAR_UNKNOWN) { buf = get_buf_arg(&argvars[1]); if (buf == NULL) return; } lnr = ALLOC_CLEAR_ONE(listener_T); if (lnr == NULL) { ! free_callback(callback, partial); return; } lnr->lr_next = buf->b_listener; buf->b_listener = lnr; ! if (partial == NULL) ! lnr->lr_callback = vim_strsave(callback); ! else ! lnr->lr_callback = callback; // pointer into the partial ! lnr->lr_partial = partial; lnr->lr_id = ++next_listener_id; rettv->vval.v_number = lnr->lr_id; --- 270,303 ---- void f_listener_add(typval_T *argvars, typval_T *rettv) { ! callback_T callback; listener_T *lnr; buf_T *buf = curbuf; ! callback = get_callback(&argvars[0]); ! if (callback.cb_name == NULL) return; if (argvars[1].v_type != VAR_UNKNOWN) { buf = get_buf_arg(&argvars[1]); if (buf == NULL) + { + free_callback(&callback); return; + } } lnr = ALLOC_CLEAR_ONE(listener_T); if (lnr == NULL) { ! free_callback(&callback); return; } lnr->lr_next = buf->b_listener; buf->b_listener = lnr; ! set_callback(&lnr->lr_callback, &callback); lnr->lr_id = ++next_listener_id; rettv->vval.v_number = lnr->lr_id; *************** *** 344,350 **** prev->lr_next = lnr->lr_next; else buf->b_listener = lnr->lr_next; ! free_callback(lnr->lr_callback, lnr->lr_partial); vim_free(lnr); } prev = lnr; --- 342,348 ---- prev->lr_next = lnr->lr_next; else buf->b_listener = lnr->lr_next; ! free_callback(&lnr->lr_callback); vim_free(lnr); } prev = lnr; *************** *** 418,425 **** for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next) { ! call_func(lnr->lr_callback, -1, &rettv, ! 5, argv, NULL, 0L, 0L, &dummy, TRUE, lnr->lr_partial, NULL); clear_tv(&rettv); } --- 416,423 ---- for (lnr = buf->b_listener; lnr != NULL; lnr = lnr->lr_next) { ! call_callback(&lnr->lr_callback, -1, &rettv, ! 5, argv, NULL, 0L, 0L, &dummy, TRUE, NULL); clear_tv(&rettv); } *** ../vim-8.1.1436/src/channel.c 2019-05-28 23:08:12.052648779 +0200 --- src/channel.c 2019-06-01 12:58:20.873575186 +0200 *************** *** 348,354 **** return FALSE; /* If there is a close callback it may still need to be invoked. */ ! if (channel->ch_close_cb != NULL) return TRUE; /* If reading from or a buffer it's still useful. */ --- 348,354 ---- return FALSE; /* If there is a close callback it may still need to be invoked. */ ! if (channel->ch_close_cb.cb_name != NULL) return TRUE; /* If reading from or a buffer it's still useful. */ *************** *** 366,377 **** has_err_msg = channel->ch_part[PART_ERR].ch_fd != INVALID_FD || channel->ch_part[PART_ERR].ch_head.rq_next != NULL || channel->ch_part[PART_ERR].ch_json_head.jq_next != NULL; ! return (channel->ch_callback != NULL && (has_sock_msg || has_out_msg || has_err_msg)) ! || ((channel->ch_part[PART_OUT].ch_callback != NULL || channel->ch_part[PART_OUT].ch_bufref.br_buf != NULL) && has_out_msg) ! || ((channel->ch_part[PART_ERR].ch_callback != NULL || channel->ch_part[PART_ERR].ch_bufref.br_buf != NULL) && has_err_msg); } --- 366,377 ---- has_err_msg = channel->ch_part[PART_ERR].ch_fd != INVALID_FD || channel->ch_part[PART_ERR].ch_head.rq_next != NULL || channel->ch_part[PART_ERR].ch_json_head.jq_next != NULL; ! return (channel->ch_callback.cb_name != NULL && (has_sock_msg || has_out_msg || has_err_msg)) ! || ((channel->ch_part[PART_OUT].ch_callback.cb_name != NULL || channel->ch_part[PART_OUT].ch_bufref.br_buf != NULL) && has_out_msg) ! || ((channel->ch_part[PART_ERR].ch_callback.cb_name != NULL || channel->ch_part[PART_ERR].ch_bufref.br_buf != NULL) && has_err_msg); } *************** *** 1178,1206 **** return buf; } static void ! set_callback( ! char_u **cbp, ! partial_T **pp, ! char_u *callback, ! partial_T *partial) { ! free_callback(*cbp, *pp); ! if (callback != NULL && *callback != NUL) { ! if (partial != NULL) ! *cbp = partial_name(partial); ! else ! { ! *cbp = vim_strsave(callback); ! func_ref(*cbp); ! } } else ! *cbp = NULL; ! *pp = partial; ! if (partial != NULL) ! ++partial->pt_refcount; } /* --- 1178,1213 ---- return buf; } + /* + * Copy callback from "src" to "dest", incrementing the refcounts. + */ static void ! copy_callback(callback_T *dest, callback_T *src) { ! dest->cb_partial = src->cb_partial; ! if (dest->cb_partial != NULL) { ! dest->cb_name = src->cb_name; ! dest->cb_free_name = FALSE; ! ++dest->cb_partial->pt_refcount; ! } ! else ! { ! dest->cb_name = vim_strsave(src->cb_name); ! dest->cb_free_name = TRUE; ! func_ref(src->cb_name); } + } + + static void + free_set_callback(callback_T *cbp, callback_T *callback) + { + free_callback(cbp); + + if (callback->cb_name != NULL && *callback->cb_name != NUL) + copy_callback(cbp, callback); else ! cbp->cb_name = NULL; } /* *************** *** 1233,1251 **** channel->ch_part[PART_IN].ch_block_write = 1; if (opt->jo_set & JO_CALLBACK) ! set_callback(&channel->ch_callback, &channel->ch_partial, ! opt->jo_callback, opt->jo_partial); if (opt->jo_set & JO_OUT_CALLBACK) ! set_callback(&channel->ch_part[PART_OUT].ch_callback, ! &channel->ch_part[PART_OUT].ch_partial, ! opt->jo_out_cb, opt->jo_out_partial); if (opt->jo_set & JO_ERR_CALLBACK) ! set_callback(&channel->ch_part[PART_ERR].ch_callback, ! &channel->ch_part[PART_ERR].ch_partial, ! opt->jo_err_cb, opt->jo_err_partial); if (opt->jo_set & JO_CLOSE_CALLBACK) ! set_callback(&channel->ch_close_cb, &channel->ch_close_partial, ! opt->jo_close_cb, opt->jo_close_partial); channel->ch_drop_never = opt->jo_drop_never; if ((opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER) --- 1240,1254 ---- channel->ch_part[PART_IN].ch_block_write = 1; if (opt->jo_set & JO_CALLBACK) ! free_set_callback(&channel->ch_callback, &opt->jo_callback); if (opt->jo_set & JO_OUT_CALLBACK) ! free_set_callback(&channel->ch_part[PART_OUT].ch_callback, ! &opt->jo_out_cb); if (opt->jo_set & JO_ERR_CALLBACK) ! free_set_callback(&channel->ch_part[PART_ERR].ch_callback, ! &opt->jo_err_cb); if (opt->jo_set & JO_CLOSE_CALLBACK) ! free_set_callback(&channel->ch_close_cb, &opt->jo_close_cb); channel->ch_drop_never = opt->jo_drop_never; if ((opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER) *************** *** 1349,1356 **** channel_set_req_callback( channel_T *channel, ch_part_T part, ! char_u *callback, ! partial_T *partial, int id) { cbq_T *head = &channel->ch_part[part].ch_cb_head; --- 1352,1358 ---- channel_set_req_callback( channel_T *channel, ch_part_T part, ! callback_T *callback, int id) { cbq_T *head = &channel->ch_part[part].ch_cb_head; *************** *** 1358,1374 **** if (item != NULL) { ! item->cq_partial = partial; ! if (partial != NULL) ! { ! ++partial->pt_refcount; ! item->cq_callback = callback; ! } ! else ! { ! item->cq_callback = vim_strsave(callback); ! func_ref(item->cq_callback); ! } item->cq_seq_nr = id; item->cq_prev = head->cq_prev; head->cq_prev = item; --- 1360,1366 ---- if (item != NULL) { ! copy_callback(&item->cq_callback, callback); item->cq_seq_nr = id; item->cq_prev = head->cq_prev; head->cq_prev = item; *************** *** 1638,1645 **** * This does not redraw but sets channel_need_redraw; */ static void ! invoke_callback(channel_T *channel, char_u *callback, partial_T *partial, ! typval_T *argv) { typval_T rettv; int dummy; --- 1630,1636 ---- * This does not redraw but sets channel_need_redraw; */ static void ! invoke_callback(channel_T *channel, callback_T *callback, typval_T *argv) { typval_T rettv; int dummy; *************** *** 1650,1657 **** argv[0].v_type = VAR_CHANNEL; argv[0].vval.v_channel = channel; ! call_func(callback, -1, &rettv, 2, argv, NULL, ! 0L, 0L, &dummy, TRUE, partial, NULL); clear_tv(&rettv); channel_need_redraw = TRUE; } --- 1641,1648 ---- argv[0].v_type = VAR_CHANNEL; argv[0].vval.v_channel = channel; ! call_callback(callback, -1, &rettv, 2, argv, NULL, ! 0L, 0L, &dummy, TRUE, NULL); clear_tv(&rettv); channel_need_redraw = TRUE; } *************** *** 2414,2425 **** typval_T *argv) { ch_log(channel, "Invoking one-time callback %s", ! (char *)item->cq_callback); /* Remove the item from the list first, if the callback * invokes ch_close() the list will be cleared. */ remove_cb_node(cbhead, item); ! invoke_callback(channel, item->cq_callback, item->cq_partial, argv); ! free_callback(item->cq_callback, item->cq_partial); vim_free(item); } --- 2405,2416 ---- typval_T *argv) { ch_log(channel, "Invoking one-time callback %s", ! (char *)item->cq_callback.cb_name); /* 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); ! free_callback(&item->cq_callback); vim_free(item); } *************** *** 2553,2560 **** ch_mode_T ch_mode = ch_part->ch_mode; cbq_T *cbhead = &ch_part->ch_cb_head; cbq_T *cbitem; ! char_u *callback = NULL; ! partial_T *partial = NULL; buf_T *buffer = NULL; char_u *p; --- 2544,2550 ---- ch_mode_T ch_mode = ch_part->ch_mode; cbq_T *cbhead = &ch_part->ch_cb_head; cbq_T *cbitem; ! callback_T *callback = NULL; buf_T *buffer = NULL; char_u *p; *************** *** 2567,2586 **** if (cbitem->cq_seq_nr == 0) break; if (cbitem != NULL) ! { ! callback = cbitem->cq_callback; ! partial = cbitem->cq_partial; ! } ! else if (ch_part->ch_callback != NULL) ! { ! callback = ch_part->ch_callback; ! partial = ch_part->ch_partial; ! } ! else ! { ! callback = channel->ch_callback; ! partial = channel->ch_partial; ! } buffer = ch_part->ch_bufref.br_buf; if (buffer != NULL && (!bufref_valid(&ch_part->ch_bufref) --- 2557,2567 ---- if (cbitem->cq_seq_nr == 0) break; if (cbitem != NULL) ! callback = &cbitem->cq_callback; ! else if (ch_part->ch_callback.cb_name != NULL) ! callback = &ch_part->ch_callback; ! else if (channel->ch_callback.cb_name != NULL) ! callback = &channel->ch_callback; buffer = ch_part->ch_bufref.br_buf; if (buffer != NULL && (!bufref_valid(&ch_part->ch_bufref) *************** *** 2642,2648 **** { /* If there is a close callback it may use ch_read() to get the * messages. */ ! if (channel->ch_close_cb == NULL && !channel->ch_drop_never) drop_messages(channel, part); return FALSE; } --- 2623,2629 ---- { /* If there is a close callback it may use ch_read() to get the * messages. */ ! if (channel->ch_close_cb.cb_name == NULL && !channel->ch_drop_never) drop_messages(channel, part); return FALSE; } *************** *** 2761,2768 **** { /* invoke the channel callback */ ch_log(channel, "Invoking channel callback %s", ! (char *)callback); ! invoke_callback(channel, callback, partial, argv); } } } --- 2742,2749 ---- { /* invoke the channel callback */ ch_log(channel, "Invoking channel callback %s", ! (char *)callback->cb_name); ! invoke_callback(channel, callback, argv); } } } *************** *** 2956,2973 **** ch_part_T part; /* Invoke callbacks and flush buffers before the close callback. */ ! if (channel->ch_close_cb != NULL) ch_log(channel, "Invoking callbacks and flushing buffers before closing"); for (part = PART_SOCK; part < PART_IN; ++part) { ! if (channel->ch_close_cb != NULL || channel->ch_part[part].ch_bufref.br_buf != NULL) { /* Increment the refcount to avoid the channel being freed * halfway. */ ++channel->ch_refcount; ! if (channel->ch_close_cb == NULL) ch_log(channel, "flushing %s buffers before closing", part_names[part]); while (may_invoke_callback(channel, part)) --- 2937,2954 ---- ch_part_T part; /* Invoke callbacks and flush buffers before the close callback. */ ! if (channel->ch_close_cb.cb_name != NULL) ch_log(channel, "Invoking callbacks and flushing buffers before closing"); for (part = PART_SOCK; part < PART_IN; ++part) { ! if (channel->ch_close_cb.cb_name != NULL || channel->ch_part[part].ch_bufref.br_buf != NULL) { /* Increment the refcount to avoid the channel being freed * halfway. */ ++channel->ch_refcount; ! if (channel->ch_close_cb.cb_name == NULL) ch_log(channel, "flushing %s buffers before closing", part_names[part]); while (may_invoke_callback(channel, part)) *************** *** 2976,2982 **** } } ! if (channel->ch_close_cb != NULL) { typval_T argv[1]; typval_T rettv; --- 2957,2963 ---- } } ! if (channel->ch_close_cb.cb_name != NULL) { typval_T argv[1]; typval_T rettv; *************** *** 2986,3004 **** * halfway. */ ++channel->ch_refcount; ch_log(channel, "Invoking close callback %s", ! (char *)channel->ch_close_cb); argv[0].v_type = VAR_CHANNEL; argv[0].vval.v_channel = channel; ! call_func(channel->ch_close_cb, -1, ! &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, ! channel->ch_close_partial, NULL); clear_tv(&rettv); channel_need_redraw = TRUE; /* the callback is only called once */ ! free_callback(channel->ch_close_cb, channel->ch_close_partial); ! channel->ch_close_cb = NULL; ! channel->ch_close_partial = NULL; if (channel_need_redraw) { --- 2967,2982 ---- * halfway. */ ++channel->ch_refcount; ch_log(channel, "Invoking close callback %s", ! (char *)channel->ch_close_cb.cb_name); argv[0].v_type = VAR_CHANNEL; argv[0].vval.v_channel = channel; ! call_callback(&channel->ch_close_cb, -1, ! &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, NULL); clear_tv(&rettv); channel_need_redraw = TRUE; /* the callback is only called once */ ! free_callback(&channel->ch_close_cb); if (channel_need_redraw) { *************** *** 3061,3067 **** cbq_T *node = cb_head->cq_next; remove_cb_node(cb_head, node); ! free_callback(node->cq_callback, node->cq_partial); vim_free(node); } --- 3039,3045 ---- cbq_T *node = cb_head->cq_next; remove_cb_node(cb_head, node); ! free_callback(&node->cq_callback); vim_free(node); } *************** *** 3071,3079 **** remove_json_node(json_head, json_head->jq_next); } ! free_callback(ch_part->ch_callback, ch_part->ch_partial); ! ch_part->ch_callback = NULL; ! ch_part->ch_partial = NULL; while (ch_part->ch_writeque.wq_next != NULL) remove_from_writeque(&ch_part->ch_writeque, --- 3049,3055 ---- remove_json_node(json_head, json_head->jq_next); } ! free_callback(&ch_part->ch_callback); while (ch_part->ch_writeque.wq_next != NULL) remove_from_writeque(&ch_part->ch_writeque, *************** *** 3092,3103 **** channel_clear_one(channel, PART_OUT); channel_clear_one(channel, PART_ERR); channel_clear_one(channel, PART_IN); ! free_callback(channel->ch_callback, channel->ch_partial); ! channel->ch_callback = NULL; ! channel->ch_partial = NULL; ! free_callback(channel->ch_close_cb, channel->ch_close_partial); ! channel->ch_close_cb = NULL; ! channel->ch_close_partial = NULL; } #if defined(EXITFREE) || defined(PROTO) --- 3068,3075 ---- channel_clear_one(channel, PART_OUT); channel_clear_one(channel, PART_ERR); channel_clear_one(channel, PART_IN); ! free_callback(&channel->ch_callback); ! free_callback(&channel->ch_close_cb); } #if defined(EXITFREE) || defined(PROTO) *************** *** 3991,4009 **** /* Set the callback. An empty callback means no callback and not reading * the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not * allowed. */ ! if (opt->jo_callback != NULL && *opt->jo_callback != NUL) { if (eval) { semsg(_("E917: Cannot use a callback with %s()"), fun); return NULL; } ! channel_set_req_callback(channel, *part_read, ! opt->jo_callback, opt->jo_partial, id); } if (channel_send(channel, part_send, text, len, fun) == OK ! && opt->jo_callback == NULL) return channel; return NULL; } --- 3963,3980 ---- /* Set the callback. An empty callback means no callback and not reading * the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not * allowed. */ ! if (opt->jo_callback.cb_name != NULL && *opt->jo_callback.cb_name != NUL) { if (eval) { semsg(_("E917: Cannot use a callback with %s()"), fun); return NULL; } ! channel_set_req_callback(channel, *part_read, &opt->jo_callback, id); } if (channel_send(channel, part_send, text, len, fun) == OK ! && opt->jo_callback.cb_name == NULL) return channel; return NULL; } *************** *** 4559,4584 **** void free_job_options(jobopt_T *opt) { ! if (opt->jo_partial != NULL) ! partial_unref(opt->jo_partial); ! else if (opt->jo_callback != NULL) ! func_unref(opt->jo_callback); ! if (opt->jo_out_partial != NULL) ! partial_unref(opt->jo_out_partial); ! else if (opt->jo_out_cb != NULL) ! func_unref(opt->jo_out_cb); ! if (opt->jo_err_partial != NULL) ! partial_unref(opt->jo_err_partial); ! else if (opt->jo_err_cb != NULL) ! func_unref(opt->jo_err_cb); ! if (opt->jo_close_partial != NULL) ! partial_unref(opt->jo_close_partial); ! else if (opt->jo_close_cb != NULL) ! func_unref(opt->jo_close_cb); ! if (opt->jo_exit_partial != NULL) ! partial_unref(opt->jo_exit_partial); ! else if (opt->jo_exit_cb != NULL) ! func_unref(opt->jo_exit_cb); if (opt->jo_env != NULL) dict_unref(opt->jo_env); } --- 4530,4555 ---- void free_job_options(jobopt_T *opt) { ! if (opt->jo_callback.cb_partial != NULL) ! partial_unref(opt->jo_callback.cb_partial); ! else if (opt->jo_callback.cb_name != NULL) ! func_unref(opt->jo_callback.cb_name); ! if (opt->jo_out_cb.cb_partial != NULL) ! partial_unref(opt->jo_out_cb.cb_partial); ! else if (opt->jo_out_cb.cb_name != NULL) ! func_unref(opt->jo_out_cb.cb_name); ! if (opt->jo_err_cb.cb_partial != NULL) ! partial_unref(opt->jo_err_cb.cb_partial); ! else if (opt->jo_err_cb.cb_name != NULL) ! func_unref(opt->jo_err_cb.cb_name); ! if (opt->jo_close_cb.cb_partial != NULL) ! partial_unref(opt->jo_close_cb.cb_partial); ! else if (opt->jo_close_cb.cb_name != NULL) ! func_unref(opt->jo_close_cb.cb_name); ! if (opt->jo_exit_cb.cb_partial != NULL) ! partial_unref(opt->jo_exit_cb.cb_partial); ! else if (opt->jo_exit_cb.cb_name != NULL) ! func_unref(opt->jo_exit_cb.cb_name); if (opt->jo_env != NULL) dict_unref(opt->jo_env); } *************** *** 4771,4778 **** if (!(supported & JO_CALLBACK)) break; opt->jo_set |= JO_CALLBACK; ! opt->jo_callback = get_callback(item, &opt->jo_partial); ! if (opt->jo_callback == NULL) { semsg(_(e_invargval), "callback"); return FAIL; --- 4742,4749 ---- if (!(supported & JO_CALLBACK)) break; opt->jo_set |= JO_CALLBACK; ! opt->jo_callback = get_callback(item); ! if (opt->jo_callback.cb_name == NULL) { semsg(_(e_invargval), "callback"); return FAIL; *************** *** 4783,4790 **** 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) { semsg(_(e_invargval), "out_cb"); return FAIL; --- 4754,4761 ---- if (!(supported & JO_OUT_CALLBACK)) break; opt->jo_set |= JO_OUT_CALLBACK; ! opt->jo_out_cb = get_callback(item); ! if (opt->jo_out_cb.cb_name == NULL) { semsg(_(e_invargval), "out_cb"); return FAIL; *************** *** 4795,4802 **** 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) { semsg(_(e_invargval), "err_cb"); return FAIL; --- 4766,4773 ---- if (!(supported & JO_ERR_CALLBACK)) break; opt->jo_set |= JO_ERR_CALLBACK; ! opt->jo_err_cb = get_callback(item); ! if (opt->jo_err_cb.cb_name == NULL) { semsg(_(e_invargval), "err_cb"); return FAIL; *************** *** 4807,4814 **** 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) { semsg(_(e_invargval), "close_cb"); return FAIL; --- 4778,4785 ---- if (!(supported & JO_CLOSE_CALLBACK)) break; opt->jo_set |= JO_CLOSE_CALLBACK; ! opt->jo_close_cb = get_callback(item); ! if (opt->jo_close_cb.cb_name == NULL) { semsg(_(e_invargval), "close_cb"); return FAIL; *************** *** 4833,4840 **** if (!(supported & JO_EXIT_CB)) break; opt->jo_set |= JO_EXIT_CB; ! opt->jo_exit_cb = get_callback(item, &opt->jo_exit_partial); ! if (opt->jo_exit_cb == NULL) { semsg(_(e_invargval), "exit_cb"); return FAIL; --- 4804,4811 ---- if (!(supported & JO_EXIT_CB)) break; opt->jo_set |= JO_EXIT_CB; ! opt->jo_exit_cb = get_callback(item); ! if (opt->jo_exit_cb.cb_name == NULL) { semsg(_(e_invargval), "exit_cb"); return FAIL; *************** *** 5201,5207 **** #ifdef MSWIN vim_free(job->jv_tty_type); #endif ! free_callback(job->jv_exit_cb, job->jv_exit_partial); if (job->jv_argv != NULL) { for (i = 0; job->jv_argv[i] != NULL; i++) --- 5172,5178 ---- #ifdef MSWIN vim_free(job->jv_tty_type); #endif ! free_callback(&job->jv_exit_cb); if (job->jv_argv != NULL) { for (i = 0; job->jv_argv[i] != NULL; i++) *************** *** 5289,5295 **** job_need_end_check(job_T *job) { return job->jv_status == JOB_STARTED ! && (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL); } /* --- 5260,5266 ---- job_need_end_check(job_T *job) { return job->jv_status == JOB_STARTED ! && (job->jv_stoponexit != NULL || job->jv_exit_cb.cb_name != NULL); } /* *************** *** 5465,5486 **** if (job->jv_channel != NULL) ch_close_part(job->jv_channel, PART_IN); ! if (job->jv_exit_cb != NULL) { typval_T argv[3]; typval_T rettv; int dummy; /* Invoke the exit callback. Make sure the refcount is > 0. */ ! ch_log(job->jv_channel, "Invoking exit callback %s", job->jv_exit_cb); ++job->jv_refcount; argv[0].v_type = VAR_JOB; argv[0].vval.v_job = job; argv[1].v_type = VAR_NUMBER; argv[1].vval.v_number = job->jv_exitval; ! call_func(job->jv_exit_cb, -1, ! &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, ! job->jv_exit_partial, NULL); clear_tv(&rettv); --job->jv_refcount; channel_need_redraw = TRUE; --- 5436,5457 ---- if (job->jv_channel != NULL) ch_close_part(job->jv_channel, PART_IN); ! if (job->jv_exit_cb.cb_name != NULL) { typval_T argv[3]; typval_T rettv; int dummy; /* Invoke the exit callback. Make sure the refcount is > 0. */ ! ch_log(job->jv_channel, "Invoking exit callback %s", ! job->jv_exit_cb.cb_name); ++job->jv_refcount; argv[0].v_type = VAR_JOB; argv[0].vval.v_job = job; argv[1].v_type = VAR_NUMBER; argv[1].vval.v_number = job->jv_exitval; ! call_callback(&job->jv_exit_cb, -1, ! &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL); clear_tv(&rettv); --job->jv_refcount; channel_need_redraw = TRUE; *************** *** 5622,5647 **** } if (opt->jo_set & JO_EXIT_CB) { ! free_callback(job->jv_exit_cb, 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_partial = opt->jo_exit_partial; ! if (job->jv_exit_partial != NULL) ! { ! job->jv_exit_cb = opt->jo_exit_cb; ! ++job->jv_exit_partial->pt_refcount; ! } ! else ! { ! job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); ! func_ref(job->jv_exit_cb); ! } ! } } } --- 5593,5606 ---- } if (opt->jo_set & JO_EXIT_CB) { ! free_callback(&job->jv_exit_cb); ! if (opt->jo_exit_cb.cb_name == NULL || *opt->jo_exit_cb.cb_name == NUL) { ! job->jv_exit_cb.cb_name = NULL; ! job->jv_exit_cb.cb_partial = NULL; } else ! copy_callback(&job->jv_exit_cb, &opt->jo_exit_cb); } } *************** *** 5959,5965 **** dict_add_string(dict, "tty_out", job->jv_tty_out); dict_add_number(dict, "exitval", job->jv_exitval); ! dict_add_string(dict, "exit_cb", job->jv_exit_cb); dict_add_string(dict, "stoponexit", job->jv_stoponexit); #ifdef UNIX dict_add_string(dict, "termsig", job->jv_termsig); --- 5918,5924 ---- dict_add_string(dict, "tty_out", job->jv_tty_out); dict_add_number(dict, "exitval", job->jv_exitval); ! dict_add_string(dict, "exit_cb", job->jv_exit_cb.cb_name); dict_add_string(dict, "stoponexit", job->jv_stoponexit); #ifdef UNIX dict_add_string(dict, "termsig", job->jv_termsig); *************** *** 6059,6065 **** curwin->w_cursor.lnum = lnum + 1; curwin->w_cursor.col = 0; ! if (curbuf->b_prompt_callback == NULL || *curbuf->b_prompt_callback == NUL) return; text = ml_get(lnum); prompt = prompt_text(); --- 6018,6025 ---- curwin->w_cursor.lnum = lnum + 1; curwin->w_cursor.col = 0; ! if (curbuf->b_prompt_callback.cb_name == NULL ! || *curbuf->b_prompt_callback.cb_name == NUL) return; text = ml_get(lnum); prompt = prompt_text(); *************** *** 6069,6077 **** argv[0].vval.v_string = vim_strsave(text); argv[1].v_type = VAR_UNKNOWN; ! call_func(curbuf->b_prompt_callback, -1, ! &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, ! curbuf->b_prompt_partial, NULL); clear_tv(&argv[0]); clear_tv(&rettv); } --- 6029,6036 ---- argv[0].vval.v_string = vim_strsave(text); argv[1].v_type = VAR_UNKNOWN; ! call_callback(&curbuf->b_prompt_callback, -1, ! &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, NULL); clear_tv(&argv[0]); clear_tv(&rettv); } *************** *** 6086,6100 **** int dummy; typval_T argv[1]; ! if (curbuf->b_prompt_interrupt == NULL ! || *curbuf->b_prompt_interrupt == NUL) return FALSE; argv[0].v_type = VAR_UNKNOWN; got_int = FALSE; // don't skip executing commands ! call_func(curbuf->b_prompt_interrupt, -1, ! &rettv, 0, argv, NULL, 0L, 0L, &dummy, TRUE, ! curbuf->b_prompt_int_partial, NULL); clear_tv(&rettv); return TRUE; } --- 6045,6058 ---- int dummy; typval_T argv[1]; ! if (curbuf->b_prompt_interrupt.cb_name == NULL ! || *curbuf->b_prompt_interrupt.cb_name == NUL) return FALSE; argv[0].v_type = VAR_UNKNOWN; got_int = FALSE; // don't skip executing commands ! call_callback(&curbuf->b_prompt_interrupt, -1, ! &rettv, 0, argv, NULL, 0L, 0L, &dummy, TRUE, NULL); clear_tv(&rettv); return TRUE; } *** ../vim-8.1.1436/src/proto/channel.pro 2019-01-12 22:47:01.264088074 +0100 --- src/proto/channel.pro 2019-05-31 23:09:12.017119094 +0200 *************** *** 12,18 **** void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options); void channel_set_options(channel_T *channel, jobopt_T *opt); ! void channel_set_req_callback(channel_T *channel, ch_part_T part, char_u *callback, partial_T *partial, int id); void channel_buffer_free(buf_T *buf); void channel_write_any_lines(void); void channel_write_new_lines(buf_T *buf); --- 12,18 ---- void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options); void channel_set_options(channel_T *channel, jobopt_T *opt); ! void channel_set_req_callback(channel_T *channel, ch_part_T part, callback_T *callback, int id); void channel_buffer_free(buf_T *buf); void channel_write_any_lines(void); void channel_write_new_lines(buf_T *buf); *** ../vim-8.1.1436/src/buffer.c 2019-05-30 22:32:10.804178558 +0200 --- src/buffer.c 2019-05-31 22:28:27.560903172 +0200 *************** *** 862,868 **** #endif #ifdef FEAT_JOB_CHANNEL vim_free(buf->b_prompt_text); ! free_callback(buf->b_prompt_callback, buf->b_prompt_partial); #endif buf_hashtab_remove(buf); --- 862,868 ---- #endif #ifdef FEAT_JOB_CHANNEL vim_free(buf->b_prompt_text); ! free_callback(&buf->b_prompt_callback); #endif buf_hashtab_remove(buf); *** ../vim-8.1.1436/src/userfunc.c 2019-05-28 23:08:12.080648632 +0200 --- src/userfunc.c 2019-05-31 22:55:38.981219323 +0200 *************** *** 1447,1452 **** --- 1447,1476 ---- } /* + * Invoke call_func() with a callback. + */ + int + call_callback( + callback_T *callback, + int len, // length of "name" or -1 to use strlen() + typval_T *rettv, // return value goes here + int argcount, // number of "argvars" + typval_T *argvars, // vars for arguments, must have "argcount" + // PLUS ONE elements! + int (* argv_func)(int, typval_T *, int), + // function to fill in argvars + 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" + { + return call_func(callback->cb_name, len, rettv, argcount, argvars, + argv_func, firstline, lastline, doesrange, evaluate, + callback->cb_partial, selfdict); + } + + /* * Call a function with its resolved parameters * * "argv_func", when not NULL, can be used to fill in arguments only when the *** ../vim-8.1.1436/src/proto/userfunc.pro 2018-12-18 21:56:25.084495836 +0100 --- src/proto/userfunc.pro 2019-05-31 22:55:34.269243311 +0200 *************** *** 8,13 **** --- 8,14 ---- void restore_funccal(void); void free_all_functions(void); int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); + int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount, typval_T *argvars, int (*argv_func)(int, typval_T *, int), linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, int (*argv_func)(int, typval_T *, int), linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in); char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fdp, partial_T **partial); void ex_function(exarg_T *eap); *** ../vim-8.1.1436/src/eval.c 2019-05-28 23:08:12.056648758 +0200 --- src/eval.c 2019-05-31 23:15:01.475368819 +0200 *************** *** 5920,5929 **** dtv.vval.v_channel = job->jv_channel; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } ! if (job->jv_exit_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = job->jv_exit_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } } --- 5920,5929 ---- dtv.vval.v_channel = job->jv_channel; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } ! if (job->jv_exit_cb.cb_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = job->jv_exit_cb.cb_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } } *************** *** 5946,5974 **** set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack); for (cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL; cq = cq->cq_next) ! if (cq->cq_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = cq->cq_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } ! if (ch->ch_part[part].ch_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = ch->ch_part[part].ch_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } } ! if (ch->ch_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = ch->ch_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } ! if (ch->ch_close_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = ch->ch_close_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } } --- 5946,5975 ---- set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack); for (cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL; cq = cq->cq_next) ! if (cq->cq_callback.cb_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = cq->cq_callback.cb_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } ! if (ch->ch_part[part].ch_callback.cb_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = ! ch->ch_part[part].ch_callback.cb_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } } ! if (ch->ch_callback.cb_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = ch->ch_callback.cb_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } ! if (ch->ch_close_cb.cb_partial != NULL) { dtv.v_type = VAR_PARTIAL; ! dtv.vval.v_partial = ch->ch_close_cb.cb_partial; set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } } *** ../vim-8.1.1436/src/ex_cmds2.c 2019-05-28 23:08:12.060648736 +0200 --- src/ex_cmds2.c 2019-05-31 23:06:01.714074164 +0200 *************** *** 282,288 **** static void free_timer(timer_T *timer) { ! free_callback(timer->tr_callback, timer->tr_partial); vim_free(timer); } --- 282,288 ---- static void free_timer(timer_T *timer) { ! free_callback(&timer->tr_callback); vim_free(timer); } *************** *** 325,333 **** argv[0].vval.v_number = (varnumber_T)timer->tr_id; argv[1].v_type = VAR_UNKNOWN; ! call_func(timer->tr_callback, -1, ! &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, ! timer->tr_partial, NULL); clear_tv(&rettv); } --- 325,332 ---- argv[0].vval.v_number = (varnumber_T)timer->tr_id; argv[1].v_type = VAR_UNKNOWN; ! call_callback(&timer->tr_callback, -1, ! &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, NULL); clear_tv(&rettv); } *************** *** 542,558 **** { if (dict_add(dict, di) == FAIL) vim_free(di); - else if (timer->tr_partial != NULL) - { - di->di_tv.v_type = VAR_PARTIAL; - di->di_tv.vval.v_partial = timer->tr_partial; - ++timer->tr_partial->pt_refcount; - } else ! { ! di->di_tv.v_type = VAR_FUNC; ! di->di_tv.vval.v_string = vim_strsave(timer->tr_callback); ! } } } --- 541,548 ---- { if (dict_add(dict, di) == FAIL) vim_free(di); else ! put_callback(&timer->tr_callback, &di->di_tv); } } *************** *** 578,592 **** for (timer = first_timer; timer != NULL; timer = timer->tr_next) { ! if (timer->tr_partial != NULL) { tv.v_type = VAR_PARTIAL; ! tv.vval.v_partial = timer->tr_partial; } else { tv.v_type = VAR_FUNC; ! tv.vval.v_string = timer->tr_callback; } abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); } --- 568,582 ---- for (timer = first_timer; timer != NULL; timer = timer->tr_next) { ! if (timer->tr_callback.cb_partial != NULL) { tv.v_type = VAR_PARTIAL; ! tv.vval.v_partial = timer->tr_callback.cb_partial; } else { tv.v_type = VAR_FUNC; ! tv.vval.v_string = timer->tr_callback.cb_name; } abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL); } *** ../vim-8.1.1436/src/popupwin.c 2019-05-30 22:32:10.804178558 +0200 --- src/popupwin.c 2019-05-31 23:07:27.541643206 +0200 *************** *** 149,158 **** if (get_lambda_tv(&ptr, &tv, TRUE) == OK) { wp->w_popup_timer = create_timer(nr, 0); ! wp->w_popup_timer->tr_callback = vim_strsave(partial_name(tv.vval.v_partial)); ! func_ref(wp->w_popup_timer->tr_callback); ! wp->w_popup_timer->tr_partial = tv.vval.v_partial; } } #endif --- 149,158 ---- if (get_lambda_tv(&ptr, &tv, TRUE) == OK) { wp->w_popup_timer = create_timer(nr, 0); ! wp->w_popup_timer->tr_callback.cb_name = vim_strsave(partial_name(tv.vval.v_partial)); ! func_ref(wp->w_popup_timer->tr_callback.cb_name); ! wp->w_popup_timer->tr_callback.cb_partial = tv.vval.v_partial; } } #endif *** ../vim-8.1.1436/src/version.c 2019-05-31 20:42:04.694287075 +0200 --- src/version.c 2019-06-01 13:19:22.800368900 +0200 *************** *** 769,770 **** --- 769,772 ---- { /* Add new patch number below this line */ + /**/ + 1437, /**/ -- hundred-and-one symptoms of being an internet addict: 70. ISDN lines are added to your house on a hourly basis /// 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 ///