]> granicus.if.org Git - vim/commitdiff
patch 7.4.1336 v7.4.1336
authorBram Moolenaar <Bram@vim.org>
Tue, 16 Feb 2016 18:25:12 +0000 (19:25 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 16 Feb 2016 18:25:12 +0000 (19:25 +0100)
Problem:    Channel NL mode is not supported yet.
Solution:   Add NL mode support to channels.

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

index e0ae267a723ac6c56e82394ec34ce85355f1cc12..3cfe1bea314822f62a3e99ec2e0ea24d252e39c2 100644 (file)
@@ -669,12 +669,12 @@ channel_set_job(channel_T *channel, job_T *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_u *cmd, typval_T *arg2, typval_T *arg3)
 
 /*
  * 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;
+
+           /* 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);
 
-       /* For a raw channel we don't know where the message ends, just get
-        * everything. */
-       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 *buf, int len)
        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 which, char *func)
 }
 
 /*
- * 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;
 }
 
 /*
index 4fd117f9ef8e7529e7dc2f5b3673f55c2c7cc2ea..c6f5cc26fbcc4f7e1f724fb2fac1c726e247ff7b 100644 (file)
@@ -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);
index c5e61be5c58d4b23768a7a457d0ae7ff0f48b96d..e1c88627f84a693baaeb0f624c058cf88e614897 100644 (file)
@@ -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);
index c97f7fecf956581f9c197ac32eb4bbd34cebc3a6..25d25797e16fc4d7b22444d5e3bfabd5cb75adbf 100644 (file)
@@ -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);
index 2fa6e10246210d7f9bb543b1eab0c6032934c867..19c59ec96141d59c69b3041bd11ba530e74d359c 100644 (file)
@@ -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);
index 72387672350fe13bac8c5a0d605bfb3107b22fc8..eb72f90f241f0ce33fab9dacc9c1cb6c05f9c131 100644 (file)
@@ -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
index 62b5c89b422938d15c21efec2125b0b5ead33a7c..e9c2a98e70a3f8c2a53be9f8793d03a2941d2088 100644 (file)
@@ -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
index 495fa80124620cf868ba86c65971615350b98b43..5994d27ffd09fcb24793b2d486c723974a3e0736 100644 (file)
@@ -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()
 
index 15d40bb6509ae7025bc21fa693e8d7872ea50db2..b07f6b816c7019640c76cf9cb924fa0d98fb4f46 100644 (file)
@@ -747,6 +747,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1336,
 /**/
     1335,
 /**/