changeset 8084:3ea56a74077f v7.4.1336

commit https://github.com/vim/vim/commit/9a6e33a19b18f20c25b73392cd2faa3ec4890c8c Author: Bram Moolenaar <Bram@vim.org> Date: Tue Feb 16 19:25:12 2016 +0100 patch 7.4.1336 Problem: Channel NL mode is not supported yet. Solution: Add NL mode support to channels.
author Christian Brabandt <cb@256bit.org>
date Tue, 16 Feb 2016 19:30:05 +0100
parents 56fcca2c82a2
children b335c6cc87b4
files src/channel.c src/os_win32.c src/proto/channel.pro src/proto/os_unix.pro src/proto/os_win32.pro src/structs.h src/testdir/test_channel.vim src/testdir/test_channel_pipe.py src/version.c
diffstat 9 files changed, 150 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -669,12 +669,12 @@ channel_set_job(channel_T *channel, job_
 }
 
 /*
- * Set the json mode of channel "channel" to "ch_mode".
+ * Set the mode of channel "channel" to "mode".
  */
     void
-channel_set_json_mode(channel_T *channel, ch_mode_T ch_mode)
+channel_set_mode(channel_T *channel, ch_mode_T mode)
 {
-    channel->ch_mode = ch_mode;
+    channel->ch_mode = mode;
 }
 
 /*
@@ -1057,7 +1057,8 @@ channel_exe_cmd(channel_T *channel, char
 
 /*
  * Invoke a callback for channel "channel" if needed.
- * Return OK when a message was handled, there might be another one.
+ * TODO: add "which" argument, read stderr.
+ * Return TRUE when a message was handled, there might be another one.
  */
     static int
 may_invoke_callback(channel_T *channel)
@@ -1074,7 +1075,7 @@ may_invoke_callback(channel_T *channel)
 	/* this channel is handled elsewhere (netbeans) */
 	return FALSE;
 
-    if (ch_mode != MODE_RAW)
+    if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
     {
 	/* Get any json message in the queue. */
 	if (channel_get_json(channel, -1, &listtv) == FAIL)
@@ -1113,18 +1114,51 @@ may_invoke_callback(channel_T *channel)
     }
     else if (channel_peek(channel) == NULL)
     {
-	/* nothing to read on raw channel */
+	/* nothing to read on RAW or NL channel */
 	return FALSE;
     }
     else
     {
-	/* If there is no callback, don't do anything. */
+	/* If there is no callback drop the message. */
 	if (channel->ch_callback == NULL)
+	{
+	    while ((msg = channel_get(channel)) != NULL)
+		vim_free(msg);
 	    return FALSE;
+	}
+
+	if (ch_mode == MODE_NL)
+	{
+	    char_u  *nl;
+	    char_u  *buf;
 
-	/* For a raw channel we don't know where the message ends, just get
-	 * everything. */
-	msg = channel_get_all(channel);
+	    /* See if we have a message ending in NL in the first buffer.  If
+	     * not try to concatenate the first and the second buffer. */
+	    while (TRUE)
+	    {
+		buf = channel_peek(channel);
+		nl = vim_strchr(buf, NL);
+		if (nl != NULL)
+		    break;
+		if (channel_collapse(channel) == FAIL)
+		    return FALSE; /* incomplete message */
+	    }
+	    if (nl[1] == NUL)
+		/* get the whole buffer */
+		msg = channel_get(channel);
+	    else
+	    {
+		/* Copy the message into allocated memory and remove it from
+		 * the buffer. */
+		msg = vim_strnsave(buf, (int)(nl - buf));
+		mch_memmove(buf, nl + 1, STRLEN(nl + 1) + 1);
+	    }
+	}
+	else
+	    /* For a raw channel we don't know where the message ends, just
+	     * get everything we have. */
+	    msg = channel_get_all(channel);
+
 	argv[1].v_type = VAR_STRING;
 	argv[1].vval.v_string = msg;
     }
@@ -1276,12 +1310,20 @@ channel_save(channel_T *channel, char_u 
 	return FAIL;	    /* out of memory */
     }
 
-    /* TODO: don't strip CR when channel is in raw mode */
-    p = node->rq_buffer;
-    for (i = 0; i < len; ++i)
-	if (buf[i] != CAR || i + 1 >= len || buf[i + 1] != NL)
-	    *p++ = buf[i];
-    *p = NUL;
+    if (channel->ch_mode == MODE_NL)
+    {
+	/* Drop any CR before a NL. */
+	p = node->rq_buffer;
+	for (i = 0; i < len; ++i)
+	    if (buf[i] != CAR || i + 1 >= len || buf[i + 1] != NL)
+		*p++ = buf[i];
+	*p = NUL;
+    }
+    else
+    {
+	mch_memmove(node->rq_buffer, buf, len);
+	node->rq_buffer[len] = NUL;
+    }
 
     /* append node to the tail of the queue */
     node->rq_next = NULL;
@@ -1570,21 +1612,33 @@ channel_read(channel_T *channel, int whi
 }
 
 /*
- * Read from raw channel "channel".  Blocks until there is something to read or
- * the timeout expires.
+ * Read from RAW or NL channel "channel".  Blocks until there is something to
+ * read or the timeout expires.
+ * TODO: add "which" argument and read from stderr.
  * Returns what was read in allocated memory.
  * Returns NULL in case of error or timeout.
  */
     char_u *
 channel_read_block(channel_T *channel)
 {
-    ch_log(channel, "Reading raw\n");
-    if (channel_peek(channel) == NULL)
+    char_u	*buf;
+    char_u	*msg;
+    ch_mode_T	mode = channel->ch_mode;
+    sock_T	fd = get_read_fd(channel);
+    char_u	*nl;
+
+    ch_logsn(channel, "Blocking %s read, timeout: %d msec\n",
+			mode == MODE_RAW ? "RAW" : "NL", channel->ch_timeout);
+
+    while (TRUE)
     {
-	sock_T fd = get_read_fd(channel);
+	buf = channel_peek(channel);
+	if (buf != NULL && (mode == MODE_RAW
+			 || (mode == MODE_NL && vim_strchr(buf, NL) != NULL)))
+	    break;
+	if (buf != NULL && channel_collapse(channel) == OK)
+	    continue;
 
-	/* TODO: read both out and err if they are different */
-	ch_log(channel, "No readahead\n");
 	/* Wait for up to the channel timeout. */
 	if (fd == CHAN_FD_INVALID
 		|| channel_wait(channel, fd, channel->ch_timeout) == FAIL)
@@ -1592,9 +1646,30 @@ channel_read_block(channel_T *channel)
 	channel_read(channel, -1, "channel_read_block");
     }
 
-    /* TODO: only get the first message */
-    ch_log(channel, "Returning readahead\n");
-    return channel_get_all(channel);
+    if (mode == MODE_RAW)
+    {
+	msg = channel_get_all(channel);
+    }
+    else
+    {
+	nl = vim_strchr(buf, NL);
+	if (nl[1] == NUL)
+	{
+	    /* get the whole buffer */
+	    msg = channel_get(channel);
+	    *nl = NUL;
+	}
+	else
+	{
+	    /* Copy the message into allocated memory and remove it from the
+	     * buffer. */
+	    msg = vim_strnsave(buf, (int)(nl - buf));
+	    mch_memmove(buf, nl + 1, STRLEN(nl + 1) + 1);
+	}
+    }
+    if (log_fd != NULL)
+	ch_logn(channel, "Returning %d bytes\n", (int)STRLEN(msg));
+    return msg;
 }
 
 /*
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -5034,7 +5034,7 @@ mch_call_shell(
 
 #if defined(FEAT_JOB) || defined(PROTO)
     void
-mch_start_job(char *cmd, job_T *job)
+mch_start_job(char *cmd, job_T *job, jobopt_T *options)
 {
     STARTUPINFO		si;
     PROCESS_INFORMATION	pi;
@@ -5121,6 +5121,7 @@ mch_start_job(char *cmd, job_T *job)
     job->jv_channel = channel;
     channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]);
     channel_set_job(channel, job);
+    channel_set_mode(channel, options->jo_mode);
 
 #   ifdef FEAT_GUI
      channel_gui_register(channel);
--- a/src/proto/channel.pro
+++ b/src/proto/channel.pro
@@ -7,7 +7,7 @@ void channel_gui_register_all(void);
 channel_T *channel_open(char *hostname, int port_in, int waittime, void (*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_json_mode(channel_T *channel, ch_mode_T ch_mode);
+void channel_set_mode(channel_T *channel, ch_mode_T ch_mode);
 void channel_set_timeout(channel_T *channel, int timeout);
 void channel_set_callback(channel_T *channel, char_u *callback);
 void channel_set_req_callback(channel_T *channel, char_u *callback, int id);
--- a/src/proto/os_unix.pro
+++ b/src/proto/os_unix.pro
@@ -57,7 +57,7 @@ void mch_set_shellsize(void);
 void mch_new_shellsize(void);
 int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc);
 int mch_call_shell(char_u *cmd, int options);
-void mch_start_job(char **argv, job_T *job);
+void mch_start_job(char **argv, job_T *job, jobopt_T *options);
 char *mch_job_status(job_T *job);
 int mch_stop_job(job_T *job, char_u *how);
 void mch_clear_job(job_T *job);
--- a/src/proto/os_win32.pro
+++ b/src/proto/os_win32.pro
@@ -40,7 +40,7 @@ void mch_set_shellsize(void);
 void mch_new_shellsize(void);
 void mch_set_winsize_now(void);
 int mch_call_shell(char_u *cmd, int options);
-void mch_start_job(char *cmd, job_T *job);
+void mch_start_job(char *cmd, job_T *job, jobopt_T *options);
 char *mch_job_status(job_T *job);
 int mch_stop_job(job_T *job, char_u *how);
 void mch_clear_job(job_T *job);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1372,6 +1372,14 @@ struct channel_S {
     int		ch_refcount;	/* reference count */
 };
 
+/*
+ * Options for job commands.
+ */
+typedef struct
+{
+    ch_mode_T	jo_mode;
+} jobopt_T;
+
 
 /* structure used for explicit stack while garbage collecting hash tables */
 typedef struct ht_stack_S
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -284,7 +284,30 @@ func Test_connect_waittime()
   endif
 endfunc
 
-func Test_pipe()
+func Test_raw_pipe()
+  if !has('job')
+    return
+  endif
+  let job = job_start(s:python . " test_channel_pipe.py", {'mode': 'raw'})
+  call assert_equal("run", job_status(job))
+  try
+    let handle = job_getchannel(job)
+    call ch_sendraw(handle, "echo something\n", 0)
+    let msg = ch_readraw(handle)
+    call assert_equal("something\n", substitute(msg, "\r", "", 'g'))
+
+    call ch_sendraw(handle, "double this\n", 0)
+    let msg = ch_readraw(handle)
+    call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g'))
+
+    let reply = ch_sendraw(handle, "quit\n")
+    call assert_equal("Goodbye!\n", substitute(reply, "\r", "", 'g'))
+  finally
+    call job_stop(job)
+  endtry
+endfunc
+
+func Test_nl_pipe()
   if !has('job')
     return
   endif
@@ -293,9 +316,14 @@ func Test_pipe()
   try
     let handle = job_getchannel(job)
     call ch_sendraw(handle, "echo something\n", 0)
-    call assert_equal("something\n", ch_readraw(handle))
+    call assert_equal("something", ch_readraw(handle))
+
+    call ch_sendraw(handle, "double this\n", 0)
+    call assert_equal("this", ch_readraw(handle))
+    call assert_equal("AND this", ch_readraw(handle))
+
     let reply = ch_sendraw(handle, "quit\n")
-    call assert_equal("Goodbye!\n", reply)
+    call assert_equal("Goodbye!", reply)
   finally
     call job_stop(job)
   endtry
--- a/src/testdir/test_channel_pipe.py
+++ b/src/testdir/test_channel_pipe.py
@@ -21,4 +21,7 @@ if __name__ == "__main__":
         if typed.startswith("echo"):
             print(typed[5:-1])
             sys.stdout.flush()
+        if typed.startswith("double"):
+            print(typed[7:-1] + "\nAND " + typed[7:-1])
+            sys.stdout.flush()
 
--- a/src/version.c
+++ b/src/version.c
@@ -748,6 +748,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1336,
+/**/
     1335,
 /**/
     1334,