# HG changeset patch # User Bram Moolenaar # Date 1559388607 -7200 # Node ID a836d122231aeef88a70b53c888f7a45608865dc # Parent e5dab34ded7352a4207875d6ff8311873232a652 patch 8.1.1437: code to handle callbacks is duplicated commit https://github.com/vim/vim/commit/3a97bb3f0f8bd118ae23f1c97e55d84ff42eef20 Author: Bram Moolenaar 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. diff --git a/src/buffer.c b/src/buffer.c --- 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); diff --git a/src/change.c b/src/change.c --- 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); } diff --git a/src/channel.c b/src/channel.c --- 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; } diff --git a/src/eval.c b/src/eval.c --- 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); } } diff --git a/src/evalfunc.c b/src/evalfunc.c --- 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; } } diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c --- 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); } diff --git a/src/popupwin.c b/src/popupwin.c --- 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 diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- 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); diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro --- 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 : */ diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- 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); diff --git a/src/structs.h b/src/structs.h --- 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 */ diff --git a/src/userfunc.c b/src/userfunc.c --- 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 diff --git a/src/version.c b/src/version.c --- 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,