# HG changeset patch # User Christian Brabandt # Date 1454703304 -3600 # Node ID 1f0743f4f88f6947af9d4fe12691c93d9354e6bc # Parent 77f8fc0045930a2765ff937391ba53eec7761811 commit https://github.com/vim/vim/commit/a07fec9c85d062acd9dd433a2e681770f459ba47 Author: Bram Moolenaar Date: Fri Feb 5 21:04:08 2016 +0100 patch 7.4.1262 Problem: The channel callback is not invoked. Solution: Make a list of pending callbacks. diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -84,6 +84,15 @@ struct jsonqueue }; typedef struct jsonqueue jsonq_T; +struct cbqueue +{ + char_u *callback; + int seq_nr; + struct cbqueue *next; + struct cbqueue *prev; +}; +typedef struct cbqueue cbq_T; + typedef struct { sock_T ch_fd; /* the socket, -1 for a closed channel */ int ch_idx; /* used by channel_poll_setup() */ @@ -106,7 +115,7 @@ typedef struct { void (*ch_close_cb)(void); /* callback for when channel is closed */ char_u *ch_callback; /* function to call when a msg is not handled */ - char_u *ch_req_callback; /* function to call for current request */ + cbq_T ch_cb_head; /* dummy node for pre-request callbacks */ int ch_json_mode; /* TRUE for a json channel */ jsonq_T ch_json_head; /* dummy node, header for circular queue */ @@ -168,6 +177,8 @@ add_channel(void) /* initialize circular queues */ ch->ch_head.next = &ch->ch_head; ch->ch_head.prev = &ch->ch_head; + ch->ch_cb_head.next = &ch->ch_cb_head; + ch->ch_cb_head.prev = &ch->ch_cb_head; ch->ch_json_head.next = &ch->ch_json_head; ch->ch_json_head.prev = &ch->ch_json_head; @@ -426,15 +437,23 @@ channel_set_callback(int idx, char_u *ca } /* - * Set the callback for channel "idx" for the next response. + * Set the callback for channel "idx" for the response with "id". */ void -channel_set_req_callback(int idx, char_u *callback) +channel_set_req_callback(int idx, char_u *callback, int id) { - /* TODO: make a list of callbacks */ - vim_free(channels[idx].ch_req_callback); - channels[idx].ch_req_callback = callback == NULL - ? NULL : vim_strsave(callback); + cbq_T *cbhead = &channels[idx].ch_cb_head; + cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T)); + + if (item != NULL) + { + item->callback = vim_strsave(callback); + item->seq_nr = id; + item->prev = cbhead->prev; + cbhead->prev = item; + item->next = cbhead; + item->prev->next = item; + } } /* @@ -599,6 +618,19 @@ channel_parse_json(int ch_idx) /* * Remove "node" from the queue that it is in and free it. + * Also frees the contained callback name. + */ + static void +remove_cb_node(cbq_T *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; + vim_free(node->callback); + vim_free(node); +} + +/* + * Remove "node" from the queue that it is in and free it. * Caller should have freed or used node->value. */ static void @@ -628,8 +660,7 @@ channel_get_json(int ch_idx, int id, typ typval_T *tv = &l->lv_first->li_tv; if ((id > 0 && tv->v_type == VAR_NUMBER && tv->vval.v_number == id) - || (id <= 0 - && (tv->v_type != VAR_NUMBER || tv->vval.v_number < 0))) + || id <= 0) { *rettv = item->value; remove_json_node(item); @@ -742,9 +773,10 @@ may_invoke_callback(int idx) typval_T *typetv; typval_T argv[3]; int seq_nr = -1; - int json_mode = channels[idx].ch_json_mode; + channel_T *channel = &channels[idx]; + int json_mode = channel->ch_json_mode; - if (channels[idx].ch_close_cb != NULL) + if (channel->ch_close_cb != NULL) /* this channel is handled elsewhere (netbeans) */ return FALSE; @@ -804,17 +836,27 @@ may_invoke_callback(int idx) argv[1].vval.v_string = msg; } - if (channels[idx].ch_req_callback != NULL && seq_nr != 0) + if (seq_nr > 0) { - /* TODO: check the sequence number */ - /* invoke the one-time callback */ - invoke_callback(idx, channels[idx].ch_req_callback, argv); - channels[idx].ch_req_callback = NULL; + cbq_T *cbhead = &channel->ch_cb_head; + cbq_T *cbitem = cbhead->next; + + /* invoke the one-time callback with the matching nr */ + while (cbitem != cbhead) + { + if (cbitem->seq_nr == seq_nr) + { + invoke_callback(idx, cbitem->callback, argv); + remove_cb_node(cbitem); + break; + } + cbitem = cbitem->next; + } } - else if (channels[idx].ch_callback != NULL) + else if (channel->ch_callback != NULL) { /* invoke the channel callback */ - invoke_callback(idx, channels[idx].ch_callback, argv); + invoke_callback(idx, channel->ch_callback, argv); } /* else: drop the message TODO: give error */ @@ -844,6 +886,7 @@ channel_close(int idx) { channel_T *channel = &channels[idx]; jsonq_T *jhead; + cbq_T *cbhead; if (channel->ch_fd >= 0) { @@ -859,6 +902,10 @@ channel_close(int idx) while (channel_peek(idx) != NULL) vim_free(channel_get(idx)); + cbhead = &channel->ch_cb_head; + while (cbhead->next != cbhead) + remove_cb_node(cbhead->next); + jhead = &channel->ch_json_head; while (jhead->next != jhead) { diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -9800,7 +9800,7 @@ f_ch_open(typval_T *argvars, typval_T *r * Otherwise returns -1. */ static int -send_common(typval_T *argvars, char_u *text, char *fun) +send_common(typval_T *argvars, char_u *text, int id, char *fun) { int ch_idx; char_u *callback = NULL; @@ -9815,10 +9815,10 @@ send_common(typval_T *argvars, char_u *t if (callback == NULL) return -1; } - /* Set the callback or clear it. An empty callback means no callback and - * not reading the response. */ - channel_set_req_callback(ch_idx, - callback != NULL && *callback == NUL ? NULL : callback); + /* Set the callback. An empty callback means no callback and not reading + * the response. */ + if (callback != NULL && *callback != NUL) + channel_set_req_callback(ch_idx, callback, id); if (channel_send(ch_idx, text, fun) == OK && callback == NULL) return ch_idx; @@ -9845,7 +9845,7 @@ f_ch_sendexpr(typval_T *argvars, typval_ if (text == NULL) return; - ch_idx = send_common(argvars, text, "sendexpr"); + ch_idx = send_common(argvars, text, id, "sendexpr"); vim_free(text); if (ch_idx >= 0) { @@ -9883,7 +9883,7 @@ f_ch_sendraw(typval_T *argvars, typval_T rettv->vval.v_string = NULL; text = get_tv_string_buf(&argvars[1], buf); - ch_idx = send_common(argvars, text, "sendraw"); + ch_idx = send_common(argvars, text, 0, "sendraw"); if (ch_idx >= 0) rettv->vval.v_string = channel_read_block(ch_idx); } diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -3,7 +3,7 @@ void channel_gui_register_all(void); int channel_open(char *hostname, int port_in, void (*close_cb)(void)); void channel_set_json_mode(int idx, int json_mode); void channel_set_callback(int idx, char_u *callback); -void channel_set_req_callback(int idx, char_u *callback); +void channel_set_req_callback(int idx, char_u *callback, int id); char_u *channel_get(int idx); int channel_collapse(int idx); int channel_is_open(int idx); diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -69,6 +69,13 @@ func s:kill_server() endif endfunc +let s:responseHandle = -1 +let s:responseMsg = '' +func s:RequestHandler(handle, msg) + let s:responseHandle = a:handle + let s:responseMsg = a:msg +endfunc + func Test_communicate() let handle = s:start_server() if handle < 0 @@ -86,6 +93,12 @@ func Test_communicate() call assert_equal('added1', getline(line('$') - 1)) call assert_equal('added2', getline('$')) + " Send a request with a specific handler. + call ch_sendexpr(handle, 'hello!', 's:RequestHandler') + sleep 10m + call assert_equal(handle, s:responseHandle) + call assert_equal('got it', s:responseMsg) + " Send an eval request that works. call assert_equal('ok', ch_sendexpr(handle, 'eval-works')) sleep 10m diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -743,6 +743,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1262, +/**/ 1261, /**/ 1260,