changeset 16872:a836d122231a v8.1.1437

patch 8.1.1437: code to handle callbacks is duplicated commit https://github.com/vim/vim/commit/3a97bb3f0f8bd118ae23f1c97e55d84ff42eef20 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jun 1 13:28:35 2019 +0200 patch 8.1.1437: code to handle callbacks is duplicated Problem: Code to handle callbacks is duplicated. Solution: Add callback_T and functions to deal with it.
author Bram Moolenaar <Bram@vim.org>
date Sat, 01 Jun 2019 13:30:07 +0200
parents e5dab34ded73
children 1958b437e892
files src/buffer.c src/change.c src/channel.c src/eval.c src/evalfunc.c src/ex_cmds2.c src/popupwin.c src/proto/channel.pro src/proto/evalfunc.pro src/proto/userfunc.pro src/structs.h src/userfunc.c src/version.c
diffstat 13 files changed, 331 insertions(+), 314 deletions(-) [+]
line wrap: on
line diff
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -862,7 +862,7 @@ free_buffer(buf_T *buf)
 #endif
 #ifdef FEAT_JOB_CHANNEL
     vim_free(buf->b_prompt_text);
-    free_callback(buf->b_prompt_callback, buf->b_prompt_partial);
+    free_callback(&buf->b_prompt_callback);
 #endif
 
     buf_hashtab_remove(buf);
--- a/src/change.c
+++ b/src/change.c
@@ -270,36 +270,34 @@ may_record_change(
     void
 f_listener_add(typval_T *argvars, typval_T *rettv)
 {
-    char_u	*callback;
-    partial_T	*partial;
+    callback_T	callback;
     listener_T	*lnr;
     buf_T	*buf = curbuf;
 
-    callback = get_callback(&argvars[0], &partial);
-    if (callback == NULL)
+    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, partial);
+	free_callback(&callback);
 	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;
+    set_callback(&lnr->lr_callback, &callback);
 
     lnr->lr_id = ++next_listener_id;
     rettv->vval.v_number = lnr->lr_id;
@@ -344,7 +342,7 @@ f_listener_remove(typval_T *argvars, typ
 		    prev->lr_next = lnr->lr_next;
 		else
 		    buf->b_listener = lnr->lr_next;
-		free_callback(lnr->lr_callback, lnr->lr_partial);
+		free_callback(&lnr->lr_callback);
 		vim_free(lnr);
 	    }
 	    prev = lnr;
@@ -418,8 +416,8 @@ invoke_listeners(buf_T *buf)
 
     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);
+	call_callback(&lnr->lr_callback, -1, &rettv,
+				    5, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
 	clear_tv(&rettv);
     }
 
--- a/src/channel.c
+++ b/src/channel.c
@@ -348,7 +348,7 @@ channel_still_useful(channel_T *channel)
 	return FALSE;
 
     /* If there is a close callback it may still need to be invoked. */
-    if (channel->ch_close_cb != NULL)
+    if (channel->ch_close_cb.cb_name != NULL)
 	return TRUE;
 
     /* If reading from or a buffer it's still useful. */
@@ -366,12 +366,12 @@ channel_still_useful(channel_T *channel)
     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
+    return (channel->ch_callback.cb_name != NULL && (has_sock_msg
 		|| has_out_msg || has_err_msg))
-	    || ((channel->ch_part[PART_OUT].ch_callback != NULL
+	    || ((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 != NULL
+	    || ((channel->ch_part[PART_ERR].ch_callback.cb_name != NULL
 		       || channel->ch_part[PART_ERR].ch_bufref.br_buf != NULL)
 		    && has_err_msg);
 }
@@ -1178,29 +1178,36 @@ find_buffer(char_u *name, int err, int m
     return buf;
 }
 
+/*
+ * Copy callback from "src" to "dest", incrementing the refcounts.
+ */
     static void
-set_callback(
-	char_u **cbp,
-	partial_T **pp,
-	char_u *callback,
-	partial_T *partial)
+copy_callback(callback_T *dest, callback_T *src)
 {
-    free_callback(*cbp, *pp);
-    if (callback != NULL && *callback != NUL)
+    dest->cb_partial = src->cb_partial;
+    if (dest->cb_partial != NULL)
     {
-	if (partial != NULL)
-	    *cbp = partial_name(partial);
-	else
-	{
-	    *cbp = vim_strsave(callback);
-	    func_ref(*cbp);
-	}
+	dest->cb_name = src->cb_name;
+	dest->cb_free_name = FALSE;
+	++dest->cb_partial->pt_refcount;
     }
     else
-	*cbp = NULL;
-    *pp = partial;
-    if (partial != NULL)
-	++partial->pt_refcount;
+    {
+	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,19 +1240,15 @@ channel_set_options(channel_T *channel, 
 	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);
+	free_set_callback(&channel->ch_callback, &opt->jo_callback);
     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);
+	free_set_callback(&channel->ch_part[PART_OUT].ch_callback,
+							      &opt->jo_out_cb);
     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);
+	free_set_callback(&channel->ch_part[PART_ERR].ch_callback,
+							      &opt->jo_err_cb);
     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);
+	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,8 +1352,7 @@ channel_set_options(channel_T *channel, 
 channel_set_req_callback(
 	channel_T   *channel,
 	ch_part_T   part,
-	char_u	    *callback,
-	partial_T   *partial,
+	callback_T  *callback,
 	int	    id)
 {
     cbq_T *head = &channel->ch_part[part].ch_cb_head;
@@ -1358,17 +1360,7 @@ channel_set_req_callback(
 
     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);
-	}
+	copy_callback(&item->cq_callback, callback);
 	item->cq_seq_nr = id;
 	item->cq_prev = head->cq_prev;
 	head->cq_prev = item;
@@ -1638,8 +1630,7 @@ channel_write_new_lines(buf_T *buf)
  * 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)
+invoke_callback(channel_T *channel, callback_T *callback, typval_T *argv)
 {
     typval_T	rettv;
     int		dummy;
@@ -1650,8 +1641,8 @@ invoke_callback(channel_T *channel, char
     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);
+    call_callback(callback, -1, &rettv, 2, argv, NULL,
+						   0L, 0L, &dummy, TRUE, NULL);
     clear_tv(&rettv);
     channel_need_redraw = TRUE;
 }
@@ -2414,12 +2405,12 @@ invoke_one_time_callback(
 	typval_T    *argv)
 {
     ch_log(channel, "Invoking one-time callback %s",
-						   (char *)item->cq_callback);
+					    (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, item->cq_partial, argv);
-    free_callback(item->cq_callback, item->cq_partial);
+    invoke_callback(channel, &item->cq_callback, argv);
+    free_callback(&item->cq_callback);
     vim_free(item);
 }
 
@@ -2553,8 +2544,7 @@ may_invoke_callback(channel_T *channel, 
     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;
+    callback_T	*callback = NULL;
     buf_T	*buffer = NULL;
     char_u	*p;
 
@@ -2567,20 +2557,11 @@ may_invoke_callback(channel_T *channel, 
 	if (cbitem->cq_seq_nr == 0)
 	    break;
     if (cbitem != NULL)
-    {
-	callback = cbitem->cq_callback;
-	partial = cbitem->cq_partial;
-    }
-    else if (ch_part->ch_callback != NULL)
-    {
-	callback = ch_part->ch_callback;
-	partial = ch_part->ch_partial;
-    }
-    else
-    {
-	callback = channel->ch_callback;
-	partial = channel->ch_partial;
-    }
+	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,7 +2623,7 @@ may_invoke_callback(channel_T *channel, 
 	{
 	    /* 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)
+	    if (channel->ch_close_cb.cb_name == NULL && !channel->ch_drop_never)
 		drop_messages(channel, part);
 	    return FALSE;
 	}
@@ -2761,8 +2742,8 @@ may_invoke_callback(channel_T *channel, 
 	    {
 		/* invoke the channel callback */
 		ch_log(channel, "Invoking channel callback %s",
-							    (char *)callback);
-		invoke_callback(channel, callback, partial, argv);
+						    (char *)callback->cb_name);
+		invoke_callback(channel, callback, argv);
 	    }
 	}
     }
@@ -2956,18 +2937,18 @@ channel_close(channel_T *channel, int in
 	ch_part_T	part;
 
 	/* Invoke callbacks and flush buffers before the close callback. */
-	if (channel->ch_close_cb != NULL)
+	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 != NULL
+	    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 == NULL)
+		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,7 +2957,7 @@ channel_close(channel_T *channel, int in
 	    }
 	}
 
-	if (channel->ch_close_cb != NULL)
+	if (channel->ch_close_cb.cb_name != NULL)
 	{
 	      typval_T	argv[1];
 	      typval_T	rettv;
@@ -2986,19 +2967,16 @@ channel_close(channel_T *channel, int in
 	       * halfway. */
 	      ++channel->ch_refcount;
 	      ch_log(channel, "Invoking close callback %s",
-						(char *)channel->ch_close_cb);
+					 (char *)channel->ch_close_cb.cb_name);
 	      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);
+	      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, channel->ch_close_partial);
-	      channel->ch_close_cb = NULL;
-	      channel->ch_close_partial = NULL;
+	      free_callback(&channel->ch_close_cb);
 
 	      if (channel_need_redraw)
 	      {
@@ -3061,7 +3039,7 @@ channel_clear_one(channel_T *channel, ch
 	cbq_T *node = cb_head->cq_next;
 
 	remove_cb_node(cb_head, node);
-	free_callback(node->cq_callback, node->cq_partial);
+	free_callback(&node->cq_callback);
 	vim_free(node);
     }
 
@@ -3071,9 +3049,7 @@ channel_clear_one(channel_T *channel, ch
 	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;
+    free_callback(&ch_part->ch_callback);
 
     while (ch_part->ch_writeque.wq_next != NULL)
 	remove_from_writeque(&ch_part->ch_writeque,
@@ -3092,12 +3068,8 @@ channel_clear(channel_T *channel)
     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;
+    free_callback(&channel->ch_callback);
+    free_callback(&channel->ch_close_cb);
 }
 
 #if defined(EXITFREE) || defined(PROTO)
@@ -3991,19 +3963,18 @@ send_common(
     /* 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 (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, opt->jo_partial, id);
+	channel_set_req_callback(channel, *part_read, &opt->jo_callback, id);
     }
 
     if (channel_send(channel, part_send, text, len, fun) == OK
-						  && opt->jo_callback == NULL)
+					   && opt->jo_callback.cb_name == NULL)
 	return channel;
     return NULL;
 }
@@ -4559,26 +4530,26 @@ clear_job_options(jobopt_T *opt)
     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_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,8 +4742,8 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported & JO_CALLBACK))
 		    break;
 		opt->jo_set |= JO_CALLBACK;
-		opt->jo_callback = get_callback(item, &opt->jo_partial);
-		if (opt->jo_callback == NULL)
+		opt->jo_callback = get_callback(item);
+		if (opt->jo_callback.cb_name == NULL)
 		{
 		    semsg(_(e_invargval), "callback");
 		    return FAIL;
@@ -4783,8 +4754,8 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported & JO_OUT_CALLBACK))
 		    break;
 		opt->jo_set |= JO_OUT_CALLBACK;
-		opt->jo_out_cb = get_callback(item, &opt->jo_out_partial);
-		if (opt->jo_out_cb == NULL)
+		opt->jo_out_cb = get_callback(item);
+		if (opt->jo_out_cb.cb_name == NULL)
 		{
 		    semsg(_(e_invargval), "out_cb");
 		    return FAIL;
@@ -4795,8 +4766,8 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported & JO_ERR_CALLBACK))
 		    break;
 		opt->jo_set |= JO_ERR_CALLBACK;
-		opt->jo_err_cb = get_callback(item, &opt->jo_err_partial);
-		if (opt->jo_err_cb == NULL)
+		opt->jo_err_cb = get_callback(item);
+		if (opt->jo_err_cb.cb_name == NULL)
 		{
 		    semsg(_(e_invargval), "err_cb");
 		    return FAIL;
@@ -4807,8 +4778,8 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported & JO_CLOSE_CALLBACK))
 		    break;
 		opt->jo_set |= JO_CLOSE_CALLBACK;
-		opt->jo_close_cb = get_callback(item, &opt->jo_close_partial);
-		if (opt->jo_close_cb == NULL)
+		opt->jo_close_cb = get_callback(item);
+		if (opt->jo_close_cb.cb_name == NULL)
 		{
 		    semsg(_(e_invargval), "close_cb");
 		    return FAIL;
@@ -4833,8 +4804,8 @@ get_job_options(typval_T *tv, jobopt_T *
 		if (!(supported & JO_EXIT_CB))
 		    break;
 		opt->jo_set |= JO_EXIT_CB;
-		opt->jo_exit_cb = get_callback(item, &opt->jo_exit_partial);
-		if (opt->jo_exit_cb == NULL)
+		opt->jo_exit_cb = get_callback(item);
+		if (opt->jo_exit_cb.cb_name == NULL)
 		{
 		    semsg(_(e_invargval), "exit_cb");
 		    return FAIL;
@@ -5201,7 +5172,7 @@ job_free_contents(job_T *job)
 #ifdef MSWIN
     vim_free(job->jv_tty_type);
 #endif
-    free_callback(job->jv_exit_cb, job->jv_exit_partial);
+    free_callback(&job->jv_exit_cb);
     if (job->jv_argv != NULL)
     {
 	for (i = 0; job->jv_argv[i] != NULL; i++)
@@ -5289,7 +5260,7 @@ job_free_all(void)
 job_need_end_check(job_T *job)
 {
     return job->jv_status == JOB_STARTED
-		   && (job->jv_stoponexit != NULL || job->jv_exit_cb != NULL);
+	    && (job->jv_stoponexit != NULL || job->jv_exit_cb.cb_name != NULL);
 }
 
 /*
@@ -5465,22 +5436,22 @@ job_cleanup(job_T *job)
     if (job->jv_channel != NULL)
 	ch_close_part(job->jv_channel, PART_IN);
 
-    if (job->jv_exit_cb != NULL)
+    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);
+	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_func(job->jv_exit_cb, -1,
-	    &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
-	    job->jv_exit_partial, NULL);
+	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,26 +5593,14 @@ job_set_options(job_T *job, jobopt_T *op
     }
     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)
+	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 = NULL;
-	    job->jv_exit_partial = NULL;
+	    job->jv_exit_cb.cb_name = NULL;
+	    job->jv_exit_cb.cb_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);
-	    }
-	}
+	    copy_callback(&job->jv_exit_cb, &opt->jo_exit_cb);
     }
 }
 
@@ -5959,7 +5918,7 @@ job_info(job_T *job, dict_T *dict)
     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, "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,7 +6018,8 @@ invoke_prompt_callback(void)
     curwin->w_cursor.lnum = lnum + 1;
     curwin->w_cursor.col = 0;
 
-    if (curbuf->b_prompt_callback == NULL || *curbuf->b_prompt_callback == NUL)
+    if (curbuf->b_prompt_callback.cb_name == NULL
+	    || *curbuf->b_prompt_callback.cb_name == NUL)
 	return;
     text = ml_get(lnum);
     prompt = prompt_text();
@@ -6069,9 +6029,8 @@ invoke_prompt_callback(void)
     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);
+    call_callback(&curbuf->b_prompt_callback, -1,
+	      &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
     clear_tv(&argv[0]);
     clear_tv(&rettv);
 }
@@ -6086,15 +6045,14 @@ invoke_prompt_interrupt(void)
     int		dummy;
     typval_T	argv[1];
 
-    if (curbuf->b_prompt_interrupt == NULL
-					|| *curbuf->b_prompt_interrupt == NUL)
+    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_func(curbuf->b_prompt_interrupt, -1,
-	      &rettv, 0, argv, NULL, 0L, 0L, &dummy, TRUE,
-	      curbuf->b_prompt_int_partial, NULL);
+    call_callback(&curbuf->b_prompt_interrupt, -1,
+	      &rettv, 0, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
     clear_tv(&rettv);
     return TRUE;
 }
--- a/src/eval.c
+++ b/src/eval.c
@@ -5920,10 +5920,10 @@ set_ref_in_item(
 		dtv.vval.v_channel = job->jv_channel;
 		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
 	    }
-	    if (job->jv_exit_partial != NULL)
+	    if (job->jv_exit_cb.cb_partial != NULL)
 	    {
 		dtv.v_type = VAR_PARTIAL;
-		dtv.vval.v_partial = job->jv_exit_partial;
+		dtv.vval.v_partial = job->jv_exit_cb.cb_partial;
 		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
 	    }
 	}
@@ -5946,29 +5946,30 @@ set_ref_in_item(
 		    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)
+		    if (cq->cq_callback.cb_partial != NULL)
 		    {
 			dtv.v_type = VAR_PARTIAL;
-			dtv.vval.v_partial = cq->cq_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_partial != NULL)
+		if (ch->ch_part[part].ch_callback.cb_partial != NULL)
 		{
 		    dtv.v_type = VAR_PARTIAL;
-		    dtv.vval.v_partial = ch->ch_part[part].ch_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_partial != NULL)
+	    if (ch->ch_callback.cb_partial != NULL)
 	    {
 		dtv.v_type = VAR_PARTIAL;
-		dtv.vval.v_partial = ch->ch_partial;
+		dtv.vval.v_partial = ch->ch_callback.cb_partial;
 		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
 	    }
-	    if (ch->ch_close_partial != NULL)
+	    if (ch->ch_close_cb.cb_partial != NULL)
 	    {
 		dtv.v_type = VAR_PARTIAL;
-		dtv.vval.v_partial = ch->ch_close_partial;
+		dtv.vval.v_partial = ch->ch_close_cb.cb_partial;
 		set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
 	    }
 	}
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -9200,8 +9200,7 @@ f_printf(typval_T *argvars, typval_T *re
 f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED)
 {
     buf_T	*buf;
-    char_u	*callback;
-    partial_T	*partial;
+    callback_T	callback;
 
     if (check_secure())
 	return;
@@ -9209,17 +9208,12 @@ f_prompt_setcallback(typval_T *argvars, 
     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;
+    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,8 +9223,7 @@ f_prompt_setcallback(typval_T *argvars, 
 f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv UNUSED)
 {
     buf_T	*buf;
-    char_u	*callback;
-    partial_T	*partial;
+    callback_T	callback;
 
     if (check_secure())
 	return;
@@ -9238,17 +9231,12 @@ f_prompt_setinterrupt(typval_T *argvars,
     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;
+    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,42 +14619,104 @@ f_test_settime(typval_T *argvars, typval
 /*
  * 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)
-{
+ * "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)
     {
-	*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().
+	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
-free_callback(char_u *callback, partial_T *partial)
-{
-    if (partial != NULL)
-	partial_unref(partial);
-    else if (callback != NULL)
-    {
-	func_unref(callback);
-	vim_free(callback);
-    }
+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
@@ -14723,9 +14773,8 @@ f_timer_start(typval_T *argvars, typval_
     long	msec = (long)tv_get_number(&argvars[0]);
     timer_T	*timer;
     int		repeat = 0;
-    char_u	*callback;
+    callback_T	callback;
     dict_T	*dict;
-    partial_T	*partial;
 
     rettv->vval.v_number = -1;
     if (check_secure())
@@ -14742,21 +14791,16 @@ f_timer_start(typval_T *argvars, typval_
 	    repeat = dict_get_number(dict, (char_u *)"repeat");
     }
 
-    callback = get_callback(&argvars[1], &partial);
-    if (callback == NULL)
+    callback = get_callback(&argvars[1]);
+    if (callback.cb_name == 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;
+	free_callback(&callback);
+    else
+    {
+	set_callback(&timer->tr_callback, &callback);
 	rettv->vval.v_number = (varnumber_T)timer->tr_id;
     }
 }
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -282,7 +282,7 @@ remove_timer(timer_T *timer)
     static void
 free_timer(timer_T *timer)
 {
-    free_callback(timer->tr_callback, timer->tr_partial);
+    free_callback(&timer->tr_callback);
     vim_free(timer);
 }
 
@@ -325,9 +325,8 @@ timer_callback(timer_T *timer)
     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);
+    call_callback(&timer->tr_callback, -1,
+			&rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
     clear_tv(&rettv);
 }
 
@@ -542,17 +541,8 @@ add_timer_info(typval_T *rettv, timer_T 
     {
 	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);
-	}
+	    put_callback(&timer->tr_callback, &di->di_tv);
     }
 }
 
@@ -578,15 +568,15 @@ set_ref_in_timer(int copyID)
 
     for (timer = first_timer; timer != NULL; timer = timer->tr_next)
     {
-	if (timer->tr_partial != NULL)
+	if (timer->tr_callback.cb_partial != NULL)
 	{
 	    tv.v_type = VAR_PARTIAL;
-	    tv.vval.v_partial = timer->tr_partial;
+	    tv.vval.v_partial = timer->tr_callback.cb_partial;
 	}
 	else
 	{
 	    tv.v_type = VAR_FUNC;
-	    tv.vval.v_string = timer->tr_callback;
+	    tv.vval.v_string = timer->tr_callback.cb_name;
 	}
 	abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
     }
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -149,10 +149,10 @@ apply_options(win_T *wp, buf_T *buf UNUS
 	if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
 	{
 	    wp->w_popup_timer = create_timer(nr, 0);
-	    wp->w_popup_timer->tr_callback =
+	    wp->w_popup_timer->tr_callback.cb_name =
 				  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;
+	    func_ref(wp->w_popup_timer->tr_callback.cb_name);
+	    wp->w_popup_timer->tr_callback.cb_partial = tv.vval.v_partial;
 	}
     }
 #endif
--- a/src/proto/channel.pro
+++ b/src/proto/channel.pro
@@ -12,7 +12,7 @@ channel_T *channel_open_func(typval_T *a
 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_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);
--- a/src/proto/evalfunc.pro
+++ b/src/proto/evalfunc.pro
@@ -11,6 +11,8 @@ void mzscheme_call_vim(char_u *name, typ
 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);
+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 : */
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -8,6 +8,7 @@ void save_funccal(funccal_entry_T *entry
 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);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1237,6 +1237,17 @@ typedef struct dictvar_S dict_T;
 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,8 +1577,7 @@ struct jobvar_S
     char_u	*jv_tty_type;	// allocated
 #endif
     int		jv_exitval;
-    char_u	*jv_exit_cb;	/* allocated */
-    partial_T	*jv_exit_partial;
+    callback_T	jv_exit_cb;
 
     buf_T	*jv_in_buf;	/* buffer from "in-name" */
 
@@ -1606,8 +1616,7 @@ struct jsonq_S
 
 struct cbq_S
 {
-    char_u	*cq_callback;
-    partial_T	*cq_partial;
+    callback_T	cq_callback;
     int		cq_seq_nr;
     cbq_T	*cq_next;
     cbq_T	*cq_prev;
@@ -1689,8 +1698,7 @@ typedef struct {
     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;
+    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,10 +1739,8 @@ struct channel_S {
 #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;
+    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,16 +1839,11 @@ typedef struct
     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! */
+    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,8 +1887,7 @@ struct listener_S
 {
     listener_T	*lr_next;
     int		lr_id;
-    char_u	*lr_callback;
-    partial_T	*lr_partial;
+    callback_T	lr_callback;
 };
 #endif
 
@@ -1950,13 +1950,12 @@ struct timer_S
 #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;
+    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,13 +2508,11 @@ struct file_buffer
     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.
+    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 */
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1447,6 +1447,30 @@ func_call(
 }
 
 /*
+ * 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
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1437,
+/**/
     1436,
 /**/
     1435,