changeset 7933:1f0743f4f88f v7.4.1262

commit https://github.com/vim/vim/commit/a07fec9c85d062acd9dd433a2e681770f459ba47 Author: Bram Moolenaar <Bram@vim.org> 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.
author Christian Brabandt <cb@256bit.org>
date Fri, 05 Feb 2016 21:15:04 +0100
parents 77f8fc004593
children 1aab88611034
files src/channel.c src/eval.c src/proto/channel.pro src/testdir/test_channel.vim src/version.c
diffstat 5 files changed, 88 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- 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)
 	{
--- 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);
 }
--- 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);
--- 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
--- 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,