changeset 8210:b717dae2f26d v7.4.1398

commit https://github.com/vim/vim/commit/4e221c99e85ed40c98892068a01270b9e7492d98 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Feb 23 13:20:22 2016 +0100 patch 7.4.1398 Problem: The close-cb option is not implemented yet. Solution: Implemente close-cb. (Yasuhiro Matsumoto)
author Christian Brabandt <cb@256bit.org>
date Tue, 23 Feb 2016 13:30:07 +0100
parents 30b7880d7522
children 6116980b4cfa
files src/channel.c src/eval.c src/proto/channel.pro src/structs.h src/testdir/test_channel.py src/testdir/test_channel.vim src/version.c
diffstat 7 files changed, 116 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -485,7 +485,11 @@ static char *e_cannot_connect = N_("E902
  * Returns NULL for failure.
  */
     channel_T *
-channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void))
+channel_open(
+	char *hostname,
+	int port_in,
+	int waittime,
+	void (*nb_close_cb)(void))
 {
     int			sd = -1;
     struct sockaddr_in	server;
@@ -711,7 +715,7 @@ channel_open(char *hostname, int port_in
     }
 
     channel->CH_SOCK_FD = (sock_T)sd;
-    channel->ch_close_cb = close_cb;
+    channel->ch_nb_close_cb = nb_close_cb;
 
 #ifdef FEAT_GUI
     channel_gui_register(channel);
@@ -790,6 +794,15 @@ channel_set_options(channel_T *channel, 
 	else
 	    *cbp = NULL;
     }
+    if (opt->jo_set & JO_CLOSE_CALLBACK)
+    {
+	cbp = &channel->ch_close_cb;
+	vim_free(*cbp);
+	if (opt->jo_close_cb != NULL && *opt->jo_close_cb != NUL)
+	    *cbp = vim_strsave(opt->jo_close_cb);
+	else
+	    *cbp = NULL;
+    }
 }
 
 /*
@@ -1255,7 +1268,7 @@ may_invoke_callback(channel_T *channel, 
     ch_mode_T	ch_mode = channel->ch_part[part].ch_mode;
     char_u	*callback = NULL;
 
-    if (channel->ch_close_cb != NULL)
+    if (channel->ch_nb_close_cb != NULL)
 	/* this channel is handled elsewhere (netbeans) */
 	return FALSE;
 
@@ -1477,7 +1490,28 @@ channel_close(channel_T *channel)
     }
 #endif
 
-    channel->ch_close_cb = NULL;
+    if (channel->ch_close_cb != NULL)
+    {
+	  typval_T	argv[1];
+	  typval_T	rettv;
+	  int		dummy;
+
+	  /* invoke the close callback; increment the refcount to avoid it
+	   * being freed halfway */
+	  argv[0].v_type = VAR_CHANNEL;
+	  argv[0].vval.v_channel = channel;
+	  ++channel->ch_refcount;
+	  call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb),
+				 &rettv, 1, argv, 0L, 0L, &dummy, TRUE, NULL);
+	  clear_tv(&rettv);
+	  --channel->ch_refcount;
+
+	  /* the callback is only called once */
+	  vim_free(channel->ch_close_cb);
+	  channel->ch_close_cb = NULL;
+    }
+
+    channel->ch_nb_close_cb = NULL;
     channel_clear(channel);
 }
 
@@ -1539,6 +1573,8 @@ channel_clear(channel_T *channel)
 #endif
     vim_free(channel->ch_callback);
     channel->ch_callback = NULL;
+    vim_free(channel->ch_close_cb);
+    channel->ch_close_cb = NULL;
 }
 
 #if defined(EXITFREE) || defined(PROTO)
@@ -1732,8 +1768,8 @@ channel_read(channel_T *channel, int par
 	 * keep stdin and stderr open?  Probably not, assume the other side
 	 * has died. */
 	channel_close(channel);
-	if (channel->ch_close_cb != NULL)
-	    (*channel->ch_close_cb)();
+	if (channel->ch_nb_close_cb != NULL)
+	    (*channel->ch_nb_close_cb)();
 
 	if (len < 0)
 	{
--- a/src/eval.c
+++ b/src/eval.c
@@ -10067,6 +10067,18 @@ get_job_options(typval_T *tv, jobopt_T *
 		    return FAIL;
 		}
 	    }
+	    else if (STRCMP(hi->hi_key, "close-cb") == 0)
+	    {
+		if (!(supported & JO_CLOSE_CALLBACK))
+		    break;
+		opt->jo_set |= JO_CLOSE_CALLBACK;
+		opt->jo_close_cb = get_callback(item);
+		if (opt->jo_close_cb == NULL)
+		{
+		    EMSG2(_(e_invarg2), "close-cb");
+		    return FAIL;
+		}
+	    }
 	    else if (STRCMP(hi->hi_key, "waittime") == 0)
 	    {
 		if (!(supported & JO_WAITTIME))
@@ -18924,7 +18936,7 @@ item_compare2(const void *s1, const void
 
     rettv.v_type = VAR_UNKNOWN;		/* clear_tv() uses this */
     res = call_func(sortinfo->item_compare_func,
-		 		 (int)STRLEN(sortinfo->item_compare_func),
+				 (int)STRLEN(sortinfo->item_compare_func),
 				 &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
 				 sortinfo->item_compare_selfdict);
     clear_tv(&argv[0]);
--- a/src/proto/channel.pro
+++ b/src/proto/channel.pro
@@ -7,10 +7,10 @@ channel_T *add_channel(void);
 void channel_free(channel_T *channel);
 void channel_gui_register(channel_T *channel);
 void channel_gui_register_all(void);
-channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void));
+channel_T *channel_open(char *hostname, int port_in, int waittime, void (*nb_close_cb)(void));
 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);
-void channel_set_options(channel_T *channel, jobopt_T *options);
+void channel_set_options(channel_T *channel, jobopt_T *opt);
 void channel_set_req_callback(channel_T *channel, int part, char_u *callback, int id);
 char_u *channel_get(channel_T *channel, int part);
 int channel_collapse(channel_T *channel, int part);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1366,9 +1366,12 @@ struct channel_S {
 				 * first error until the connection works
 				 * again. */
 
-    void	(*ch_close_cb)(void); /* callback for when channel is closed */
+    void	(*ch_nb_close_cb)(void);
+				/* callback for Netbeans when channel is
+				 * closed */
 
     char_u	*ch_callback;	/* call when any msg is not handled */
+    char_u	*ch_close_cb;	/* call when channel is closed */
 
     job_T	*ch_job;	/* Job that uses this channel; this does not
 				 * count as a reference to avoid a circular
@@ -1377,25 +1380,27 @@ struct channel_S {
     int		ch_refcount;	/* reference count */
 };
 
-#define JO_MODE		0x0001	/* channel mode */
-#define JO_IN_MODE	0x0002	/* stdin mode */
-#define JO_OUT_MODE	0x0004	/* stdout mode */
-#define JO_ERR_MODE	0x0008	/* stderr mode */
-#define JO_CALLBACK	0x0010	/* channel callback */
-#define JO_OUT_CALLBACK	0x0020	/* stdout callback */
-#define JO_ERR_CALLBACK	0x0040	/* stderr callback */
-#define JO_WAITTIME	0x0080	/* only for ch_open() */
-#define JO_TIMEOUT	0x0100	/* all timeouts */
-#define JO_OUT_TIMEOUT	0x0200	/* stdout timeouts */
-#define JO_ERR_TIMEOUT	0x0400	/* stderr timeouts */
-#define JO_PART		0x0800	/* "part" */
-#define JO_ID		0x1000	/* "id" */
-#define JO_STOPONEXIT	0x2000	/* "stoponexit" */
-#define JO_EXIT_CB	0x4000	/* "exit-cb" */
-#define JO_ALL		0xffffff
+#define JO_MODE		    0x0001	/* channel mode */
+#define JO_IN_MODE	    0x0002	/* stdin mode */
+#define JO_OUT_MODE	    0x0004	/* stdout mode */
+#define JO_ERR_MODE	    0x0008	/* stderr mode */
+#define JO_CALLBACK	    0x0010	/* channel callback */
+#define JO_OUT_CALLBACK	    0x0020	/* stdout callback */
+#define JO_ERR_CALLBACK	    0x0040	/* stderr callback */
+#define JO_CLOSE_CALLBACK   0x0080	/* close callback */
+#define JO_WAITTIME	    0x0100	/* only for ch_open() */
+#define JO_TIMEOUT	    0x0200	/* all timeouts */
+#define JO_OUT_TIMEOUT	    0x0400	/* stdout timeouts */
+#define JO_ERR_TIMEOUT	    0x0800	/* stderr timeouts */
+#define JO_PART		    0x1000	/* "part" */
+#define JO_ID		    0x2000	/* "id" */
+#define JO_STOPONEXIT	    0x4000	/* "stoponexit" */
+#define JO_EXIT_CB	    0x8000	/* "exit-cb" */
+#define JO_ALL		    0xffffff
 
 #define JO_MODE_ALL	(JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
-#define JO_CB_ALL	(JO_CALLBACK + JO_OUT_CALLBACK + JO_ERR_CALLBACK)
+#define JO_CB_ALL \
+    (JO_CALLBACK + JO_OUT_CALLBACK + JO_ERR_CALLBACK + JO_CLOSE_CALLBACK)
 #define JO_TIMEOUT_ALL	(JO_TIMEOUT + JO_OUT_TIMEOUT + JO_ERR_TIMEOUT)
 
 /*
@@ -1412,6 +1417,7 @@ typedef struct
     char_u	*jo_callback;	/* not allocated! */
     char_u	*jo_out_cb;	/* not allocated! */
     char_u	*jo_err_cb;	/* not allocated! */
+    char_u	*jo_close_cb;	/* not allocated! */
     int		jo_waittime;
     int		jo_timeout;
     int		jo_out_timeout;
--- a/src/testdir/test_channel.py
+++ b/src/testdir/test_channel.py
@@ -140,6 +140,10 @@ class ThreadedTCPRequestHandler(socketse
                         print("sending: {}".format(cmd))
                         self.request.sendall(cmd.encode('utf-8'))
                         response = ""
+                    elif decoded[1] == 'close me':
+                        print("closing")
+                        self.request.close()
+                        response = ""
                     elif decoded[1] == 'wait a bit':
                         time.sleep(0.2)
                         response = "waited"
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -471,9 +471,9 @@ endfunc
 
 """""""""
 
-let s:job_ret = 'not yet'
+let s:job_exit_ret = 'not yet'
 function MyExitCb(job, status)
-  let s:job_ret = 'done'
+  let s:job_exit_ret = 'done'
 endfunc
 
 function s:test_exit_callback(port)
@@ -490,6 +490,32 @@ func Test_exit_callback()
 
     " calling job_status() triggers the callback
     call job_status(s:exit_job)
-    call assert_equal('done', s:job_ret)
+    call assert_equal('done', s:job_exit_ret)
   endif
 endfunc
+
+"""""""""
+
+let s:ch_close_ret = 'alive'
+function MyCloseCb(ch)
+  let s:ch_close_ret = 'closed'
+endfunc
+
+function s:test_close_callback(port)
+  let handle = ch_open('localhost:' . a:port, s:chopt)
+  if ch_status(handle) == "fail"
+    call assert_false(1, "Can't open channel")
+    return
+  endif
+  call ch_setoptions(handle, {'close-cb': 'MyCloseCb'})
+
+  call assert_equal('', ch_sendexpr(handle, 'close me'))
+  sleep 20m
+  call assert_equal('closed', s:ch_close_ret)
+endfunc
+
+func Test_close_callback()
+  call ch_log('Test_close_callback()')
+  call s:run_server('s:test_close_callback')
+endfunc
+
--- a/src/version.c
+++ b/src/version.c
@@ -749,6 +749,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1398,
+/**/
     1397,
 /**/
     1396,