]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 7.4.1372 v7.4.1372
authorBram Moolenaar <Bram@vim.org>
Sat, 20 Feb 2016 18:56:13 +0000 (19:56 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 20 Feb 2016 18:56:13 +0000 (19:56 +0100)
Problem:    channel read implementation is incomplete.
Solution:   Add ch_read() and options for ch_readraw().

src/channel.c
src/eval.c
src/proto/channel.pro
src/structs.h
src/testdir/test_channel.vim
src/version.c

index 169db9cd36ad54a992b1fb055d5c17aad8e38cc7..fe94dd4d05495012202eb393239f4471600b7ce2 100644 (file)
@@ -1696,12 +1696,11 @@ channel_read(channel_T *channel, int part, char *func)
  * Returns NULL in case of error or timeout.
  */
     char_u *
-channel_read_block(channel_T *channel, int part)
+channel_read_block(channel_T *channel, int part, int timeout)
 {
     char_u     *buf;
     char_u     *msg;
     ch_mode_T  mode = channel->ch_part[part].ch_mode;
-    int                timeout = channel->ch_part[part].ch_timeout;
     sock_T     fd = channel->ch_part[part].ch_fd;
     char_u     *nl;
 
@@ -1753,16 +1752,23 @@ channel_read_block(channel_T *channel, int part)
 /*
  * Read one JSON message with ID "id" from "channel"/"part" and store the
  * result in "rettv".
+ * When "id" is -1 accept any message;
  * Blocks until the message is received or the timeout is reached.
  */
     int
-channel_read_json_block(channel_T *channel, int part, int id, typval_T **rettv)
+channel_read_json_block(
+       channel_T *channel,
+       int part,
+       int timeout,
+       int id,
+       typval_T **rettv)
 {
     int                more;
     sock_T     fd;
 
     ch_log(channel, "Reading JSON");
-    channel->ch_part[part].ch_block_id = id;
+    if (id != -1)
+       channel->ch_part[part].ch_block_id = id;
     for (;;)
     {
        more = channel_parse_json(channel, part);
@@ -1781,10 +1787,9 @@ channel_read_json_block(channel_T *channel, int part, int id, typval_T **rettv)
            if (channel_parse_messages())
                continue;
 
-           /* Wait for up to the channel timeout. */
+           /* Wait for up to the timeout. */
            fd = channel->ch_part[part].ch_fd;
-           if (fd == INVALID_FD || channel_wait(channel, fd,
-                                  channel->ch_part[part].ch_timeout) == FAIL)
+           if (fd == INVALID_FD || channel_wait(channel, fd, timeout) == FAIL)
                break;
            channel_read(channel, part, "channel_read_json_block");
        }
@@ -2161,4 +2166,13 @@ channel_get_mode(channel_T *channel, int part)
     return channel->ch_part[part].ch_mode;
 }
 
+/*
+ * Return the timeout of "channel"/"part"
+ */
+    int
+channel_get_timeout(channel_T *channel, int part)
+{
+    return channel->ch_part[part].ch_timeout;
+}
+
 #endif /* FEAT_CHANNEL */
index 2a0f92a102a7b12e2f35395e3d80908caae3ff80..0c723ec471872198465865d190362d9329f28387 100644 (file)
@@ -507,6 +507,7 @@ static void f_ch_close(typval_T *argvars, typval_T *rettv);
 static void f_ch_log(typval_T *argvars, typval_T *rettv);
 static void f_ch_logfile(typval_T *argvars, typval_T *rettv);
 static void f_ch_open(typval_T *argvars, typval_T *rettv);
+static void f_ch_read(typval_T *argvars, typval_T *rettv);
 static void f_ch_readraw(typval_T *argvars, typval_T *rettv);
 static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
 static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
@@ -8129,6 +8130,7 @@ static struct fst
     {"ch_log",         1, 2, f_ch_log},
     {"ch_logfile",     1, 2, f_ch_logfile},
     {"ch_open",                1, 2, f_ch_open},
+    {"ch_read",                1, 2, f_ch_read},
     {"ch_readraw",     1, 2, f_ch_readraw},
     {"ch_sendexpr",    2, 3, f_ch_sendexpr},
     {"ch_sendraw",     2, 3, f_ch_sendraw},
@@ -9881,7 +9883,7 @@ get_callback(typval_T *arg)
 get_job_options(typval_T *tv, jobopt_T *opt, int supported)
 {
     typval_T   *item;
-    char_u     *mode;
+    char_u     *val;
     dict_T     *dict;
     int                todo;
     hashitem_T *hi;
@@ -9909,18 +9911,18 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
                if (!(supported & JO_MODE))
                    break;
                opt->jo_set |= JO_MODE;
-               mode = get_tv_string(item);
-               if (STRCMP(mode, "nl") == 0)
+               val = get_tv_string(item);
+               if (STRCMP(val, "nl") == 0)
                    opt->jo_mode = MODE_NL;
-               else if (STRCMP(mode, "raw") == 0)
+               else if (STRCMP(val, "raw") == 0)
                    opt->jo_mode = MODE_RAW;
-               else if (STRCMP(mode, "js") == 0)
+               else if (STRCMP(val, "js") == 0)
                    opt->jo_mode = MODE_JS;
-               else if (STRCMP(mode, "json") == 0)
+               else if (STRCMP(val, "json") == 0)
                    opt->jo_mode = MODE_JSON;
                else
                {
-                   EMSG2(_(e_invarg2), mode);
+                   EMSG2(_(e_invarg2), val);
                    return FAIL;
                }
            }
@@ -9950,6 +9952,27 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
                opt->jo_set |= JO_TIMEOUT;
                opt->jo_timeout = get_tv_number(item);
            }
+           else if (STRCMP(hi->hi_key, "part") == 0)
+           {
+               if (!(supported & JO_PART))
+                   break;
+               opt->jo_set |= JO_PART;
+               val = get_tv_string(item);
+               if (STRCMP(val, "err") == 0)
+                   opt->jo_part = PART_ERR;
+               else
+               {
+                   EMSG2(_(e_invarg2), val);
+                   return FAIL;
+               }
+           }
+           else if (STRCMP(hi->hi_key, "id") == 0)
+           {
+               if (!(supported & JO_ID))
+                   break;
+               opt->jo_set |= JO_ID;
+               opt->jo_id = get_tv_number(item);
+           }
            else
                break;
            --todo;
@@ -10107,29 +10130,76 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
 }
 
 /*
- * "ch_readraw()" function
+ * Common for ch_read() and ch_readraw().
  */
     static void
-f_ch_readraw(typval_T *argvars, typval_T *rettv)
+common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
 {
     channel_T  *channel;
     int                part;
+    jobopt_T   opt;
+    int                mode;
+    int                timeout;
+    int                id = -1;
+    typval_T   *listtv = NULL;
 
     /* return an empty string by default */
     rettv->v_type = VAR_STRING;
     rettv->vval.v_string = NULL;
 
-    /* TODO: use timeout from the options */
-    /* TODO: read from stderr */
+    opt.jo_set = 0;
+    if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID)
+                                                                     == FAIL)
+       return;
 
     channel = get_channel_arg(&argvars[0]);
     if (channel != NULL)
     {
-       part = channel_part_read(channel);
-       rettv->vval.v_string = channel_read_block(channel, part);
+       if (opt.jo_set & JO_PART)
+           part = opt.jo_part;
+       else
+           part = channel_part_read(channel);
+       mode = channel_get_mode(channel, part);
+       timeout = channel_get_timeout(channel, part);
+       if (opt.jo_set & JO_TIMEOUT)
+           timeout = opt.jo_timeout;
+
+       if (raw || mode == MODE_RAW || mode == MODE_NL)
+           rettv->vval.v_string = channel_read_block(channel, part, timeout);
+       else
+       {
+           if (opt.jo_set & JO_ID)
+               id = opt.jo_id;
+           channel_read_json_block(channel, part, timeout, id, &listtv);
+           if (listtv != NULL)
+               *rettv = *listtv;
+           else
+           {
+               rettv->v_type = VAR_SPECIAL;
+               rettv->vval.v_number = VVAL_NONE;
+           }
+       }
     }
 }
 
+/*
+ * "ch_read()" function
+ */
+    static void
+f_ch_read(typval_T *argvars, typval_T *rettv)
+{
+    common_channel_read(argvars, rettv, FALSE);
+}
+
+/*
+ * "ch_readraw()" function
+ */
+    static void
+f_ch_readraw(typval_T *argvars, typval_T *rettv)
+{
+    common_channel_read(argvars, rettv, TRUE);
+}
+
 /*
  * common for "sendexpr()" and "sendraw()"
  * Returns the channel if the caller should read the response.
@@ -10177,6 +10247,7 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
     ch_mode_T  ch_mode;
     int                part_send;
     int                part_read;
+    int                timeout;
 
     /* return an empty string by default */
     rettv->v_type = VAR_STRING;
@@ -10204,7 +10275,10 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
     vim_free(text);
     if (channel != NULL)
     {
-       if (channel_read_json_block(channel, part_read, id, &listtv) == OK)
+       /* TODO: timeout from options */
+       timeout = channel_get_timeout(channel, part_read);
+       if (channel_read_json_block(channel, part_read, timeout, id, &listtv)
+                                                                       == OK)
        {
            list_T *list = listtv->vval.v_list;
 
@@ -10227,6 +10301,7 @@ f_ch_sendraw(typval_T *argvars, typval_T *rettv)
     char_u     *text;
     channel_T  *channel;
     int                part_read;
+    int                timeout;
 
     /* return an empty string by default */
     rettv->v_type = VAR_STRING;
@@ -10235,7 +10310,11 @@ f_ch_sendraw(typval_T *argvars, typval_T *rettv)
     text = get_tv_string_buf(&argvars[1], buf);
     channel = send_common(argvars, text, 0, "sendraw", &part_read);
     if (channel != NULL)
-       rettv->vval.v_string = channel_read_block(channel, part_read);
+    {
+       /* TODO: timeout from options */
+       timeout = channel_get_timeout(channel, part_read);
+       rettv->vval.v_string = channel_read_block(channel, part_read, timeout);
+    }
 }
 
 /*
index 610a135eba060ace415f794fa0fa88973fe2e635..fa06f4f294126f74e5726081ecf8affa08d66554 100644 (file)
@@ -23,9 +23,9 @@ void channel_clear(channel_T *channel);
 void channel_free_all(void);
 int channel_get_id(void);
 void channel_read(channel_T *channel, int part, char *func);
-char_u *channel_read_block(channel_T *channel, int part);
-int channel_read_json_block(channel_T *channel, int part, int id, typval_T **rettv);
-channel_T *channel_fd2channel(sock_T fd, int *part);
+char_u *channel_read_block(channel_T *channel, int part, int timeout);
+int channel_read_json_block(channel_T *channel, int part, int timeout, int id, typval_T **rettv);
+channel_T *channel_fd2channel(sock_T fd, int *partp);
 void channel_handle_events(void);
 int channel_send(channel_T *channel, int part, char_u *buf, char *fun);
 int channel_poll_setup(int nfd_in, void *fds_in);
@@ -37,4 +37,5 @@ int set_ref_in_channel(int copyID);
 int channel_part_send(channel_T *channel);
 int channel_part_read(channel_T *channel);
 ch_mode_T channel_get_mode(channel_T *channel, int part);
+int channel_get_timeout(channel_T *channel, int part);
 /* vim: set ft=c : */
index e93313e3b26fca2940278fee960d6a39b34e3c02..814b3f74737bb4db6842c7a9d6dcb3e4f651b7b1 100644 (file)
@@ -1377,6 +1377,8 @@ struct channel_S {
 #define JO_CALLBACK    2       /* channel callback */
 #define JO_WAITTIME    4       /* only for ch_open() */
 #define JO_TIMEOUT     8       /* all timeouts */
+#define JO_PART                16      /* "part" */
+#define JO_ID          32      /* "id" */
 #define JO_ALL         0xffffff
 
 /*
@@ -1390,6 +1392,8 @@ typedef struct
     char_u     *jo_callback;   /* not allocated! */
     int                jo_waittime;
     int                jo_timeout;
+    int                jo_part;
+    int                jo_id;
 } jobopt_T;
 
 
index 2ed3abffacef476976237bf379a34baa2a403e18..cfe3250561eddc0bd2a8b3c13ed3d7a68a3f0be5 100644 (file)
@@ -184,6 +184,21 @@ func s:communicate(port)
 
   call assert_equal('ok', ch_sendexpr(handle, 'empty-request'))
 
+  " Reading while there is nothing available.
+  call assert_equal(v:none, ch_read(handle, {'timeout': 0}))
+  let start = reltime()
+  call assert_equal(v:none, ch_read(handle, {'timeout': 333}))
+  let elapsed = reltime(start)
+  call assert_true(reltimefloat(elapsed) > 0.3)
+  call assert_true(reltimefloat(elapsed) < 0.6)
+
+  " Send without waiting for a response, then wait for a response.
+  call ch_sendexpr(handle, 'wait a bit',  {'callback': 0})
+  let resp = ch_read(handle)
+  call assert_equal(type([]), type(resp))
+  call assert_equal(type(11), type(resp[0]))
+  call assert_equal('waited', resp[1])
+
   " make the server quit, can't check if this works, should not hang.
   call ch_sendexpr(handle, '!quit!', {'callback': 0})
 endfunc
@@ -292,8 +307,7 @@ func Test_connect_waittime()
     " Oops, port does exists.
     call ch_close(handle)
   else
-    " Failed connection doesn't wait the full time on Unix.
-    " TODO: why is MS-Windows different?
+    " Failed connection should wait about 500 msec.
     let elapsed = reltime(start)
     call assert_true(reltimefloat(elapsed) < 1.0)
   endif
index b39ac60f790cde344a0810043fdbdfb11169d95e..f82e8741857b71e22fbf98bf610c4fa953c0a90b 100644 (file)
@@ -747,6 +747,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1372,
 /**/
     1371,
 /**/