diff src/channel.c @ 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 2679e636e862
children 3f2e0b62003d
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)
 	{