]> granicus.if.org Git - vim/commitdiff
patch 8.0.0105 v8.0.0105
authorBram Moolenaar <Bram@vim.org>
Tue, 29 Nov 2016 20:54:44 +0000 (21:54 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 29 Nov 2016 20:54:44 +0000 (21:54 +0100)
Problem:    When using ch_read() with zero timeout, can't tell the difference
            between reading an empty line and nothing available.
Solution:   Add ch_canread().

runtime/doc/channel.txt
runtime/doc/eval.txt
src/channel.c
src/evalfunc.c
src/proto/channel.pro
src/testdir/shared.vim
src/testdir/test_channel.vim
src/version.c

index 73105986f64fb7ce9ab526c371bb33576014f18e..2c3f837300646b6a99382b58406af9c5889a85d4 100644 (file)
@@ -418,7 +418,11 @@ This uses the channel timeout.  To read without a timeout, just get any
 message that is available: >
        let output = ch_read(channel, {'timeout': 0})
 When no message was available then the result is v:none for a JSON or JS mode
-channels, an empty string for a RAW or NL channel.
+channels, an empty string for a RAW or NL channel.  You can use |ch_canread()|
+to check if there is something to read.
+
+Note that when there is no callback message are dropped.  To avoid that add a
+close callback to the channel.
 
 To read all output from a RAW channel that is available: >
        let output = ch_readraw(channel)
@@ -470,6 +474,11 @@ This depends on the system (on Unix this happens because closing the write end
 of a pipe causes the read end to get EOF).  To avoid this make the job sleep
 for a short while before it exits.
 
+Note that if the job exits before you read the output, the output may be lost.
+This depends on the system (on Unix this happens because closing the write end
+of a pipe causes the read end to get EOF).  To avoid this make the job sleep
+for a short while before it exits.
+
 The handler defined for "out_cb" will not receive stderr.  If you want to
 handle that separately, add an "err_cb" handler: >
     let job = job_start(command, {"out_cb": "MyHandler",
index 1a11a41411020b2650e51b2644e85bb952711aa4..75c2ea113204f173ebaf1af2cdb2b278fbe0a6f6 100644 (file)
@@ -2009,6 +2009,7 @@ byteidxcomp({expr}, {nr}) Number  byte index of {nr}'th char in {expr}
 call({func}, {arglist} [, {dict}])
                                any     call {func} with arguments {arglist}
 ceil({expr})                   Float   round {expr} up
+ch_canread({handle})           Number  check if there is something to read
 ch_close({handle})             none    close {handle}
 ch_close_in({handle})          none    close in part of {handle}
 ch_evalexpr({handle}, {expr} [, {options}])
@@ -2980,16 +2981,28 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
                don't fit, a vertical layout is used anyway.  For some systems
                the horizontal layout is always used.
 
+ch_canread({handle})                                           *ch_canread()*
+               Return non-zero when there is something to read from {handle}.
+               {handle} can be a Channel or a Job that has a Channel.
+
+               This is useful to read from a channel at a convenient time,
+               e.g. from a timer.
+
+               Note that messages are dropped when the channel does not have
+               a callback.  Add a close callback to avoid that.
+
+               {only available when compiled with the |+channel| feature}
+
 ch_close({handle})                                             *ch_close()*
                Close {handle}.  See |channel-close|.
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
                A close callback is not invoked.
 
                {only available when compiled with the |+channel| feature}
 
 ch_close_in({handle})                                          *ch_close_in()*
                Close the "in" part of {handle}.  See |channel-close-in|.
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
                A close callback is not invoked.
 
                {only available when compiled with the |+channel| feature}
@@ -2998,7 +3011,7 @@ ch_evalexpr({handle}, {expr} [, {options}])                       *ch_evalexpr()*
                Send {expr} over {handle}.  The {expr} is encoded
                according to the type of channel.  The function cannot be used
                with a raw channel.  See |channel-use|.
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
                                                                *E917*
                {options} must be a Dictionary.  It must not have a "callback"
                entry.  It can have a "timeout" entry to specify the timeout
@@ -3012,7 +3025,7 @@ ch_evalexpr({handle}, {expr} [, {options}])                       *ch_evalexpr()*
 
 ch_evalraw({handle}, {string} [, {options}])           *ch_evalraw()*
                Send {string} over {handle}.
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
 
                Works like |ch_evalexpr()|, but does not encode the request or
                decode the response.  The caller is responsible for the
@@ -3025,7 +3038,7 @@ ch_evalraw({handle}, {string} [, {options}])              *ch_evalraw()*
 
 ch_getbufnr({handle}, {what})                           *ch_getbufnr()*
                Get the buffer number that {handle} is using for {what}.
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
                {what} can be "err" for stderr, "out" for stdout or empty for
                socket output.
                Returns -1 when there is no buffer.
@@ -3099,7 +3112,7 @@ ch_open({address} [, {options}])                          *ch_open()*
 
 ch_read({handle} [, {options}])                                        *ch_read()*
                Read from {handle} and return the received message.
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
                See |channel-more|.
                {only available when compiled with the |+channel| feature}
 
@@ -3113,7 +3126,7 @@ ch_sendexpr({handle}, {expr} [, {options}])                       *ch_sendexpr()*
                according to the type of channel.  The function cannot be used
                with a raw channel.
                See |channel-use|.                              *E912*
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
 
                {only available when compiled with the |+channel| feature}
 
@@ -3134,7 +3147,7 @@ ch_setoptions({handle}, {options})                        *ch_setoptions()*
                        "timeout"       default read timeout in msec
                        "mode"          mode for the whole channel
                See |ch_open()| for more explanation.
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
 
                Note that changing the mode may cause queued messages to be
                lost.
@@ -3148,7 +3161,7 @@ ch_status({handle} [, {options}])                         *ch_status()*
                        "open"          channel can be used
                        "buffered"      channel can be read, not written to
                        "closed"        channel can not be used
-               {handle} can be Channel or a Job that has a Channel.
+               {handle} can be Channel or a Job that has a Channel.
                "buffered" is used when the channel was closed but there is
                still data that can be obtained with |ch_read()|.
 
index 6c5a4ff1d67a4fc0ba6805e30fc016ff1c2e3be1..e5f28003ee2e91a9bf209e740fd23ee34df6063a 100644 (file)
@@ -2603,7 +2603,7 @@ channel_is_open(channel_T *channel)
 /*
  * Return TRUE if "channel" has JSON or other typeahead.
  */
-    static int
+    int
 channel_has_readahead(channel_T *channel, ch_part_T part)
 {
     ch_mode_T  ch_mode = channel->ch_part[part].ch_mode;
index 846a914165c0e5eb600a7710c9b36ac1f613c270..88e4852fea28ec9d2b74ef297c83bafebbf052a7 100644 (file)
@@ -76,6 +76,7 @@ static void f_call(typval_T *argvars, typval_T *rettv);
 static void f_ceil(typval_T *argvars, typval_T *rettv);
 #endif
 #ifdef FEAT_JOB_CHANNEL
+static void f_ch_canread(typval_T *argvars, typval_T *rettv);
 static void f_ch_close(typval_T *argvars, typval_T *rettv);
 static void f_ch_close_in(typval_T *argvars, typval_T *rettv);
 static void f_ch_evalexpr(typval_T *argvars, typval_T *rettv);
@@ -499,6 +500,7 @@ static struct fst
     {"ceil",           1, 1, f_ceil},
 #endif
 #ifdef FEAT_JOB_CHANNEL
+    {"ch_canread",     1, 1, f_ch_canread},
     {"ch_close",       1, 1, f_ch_close},
     {"ch_close_in",    1, 1, f_ch_close_in},
     {"ch_evalexpr",    2, 3, f_ch_evalexpr},
@@ -1778,6 +1780,21 @@ f_ceil(typval_T *argvars, typval_T *rettv)
 #endif
 
 #ifdef FEAT_JOB_CHANNEL
+/*
+ * "ch_canread()" function
+ */
+    static void
+f_ch_canread(typval_T *argvars, typval_T *rettv)
+{
+    channel_T *channel = get_channel_arg(&argvars[0], TRUE, TRUE, 0);
+
+    rettv->vval.v_number = 0;
+    if (channel != NULL)
+       rettv->vval.v_number = channel_has_readahead(channel, PART_SOCK)
+                           || channel_has_readahead(channel, PART_OUT)
+                           || channel_has_readahead(channel, PART_ERR);
+}
+
 /*
  * "ch_close()" function
  */
index 8640fa7bb78a3c3795d8402ee07723e89ead24ff..f127268ed1c130e024d4f591635d6ce956ea7fe2 100644 (file)
@@ -25,6 +25,7 @@ void channel_consume(channel_T *channel, ch_part_T part, int len);
 int channel_collapse(channel_T *channel, ch_part_T part, int want_nl);
 int channel_can_write_to(channel_T *channel);
 int channel_is_open(channel_T *channel);
+int channel_has_readahead(channel_T *channel, ch_part_T part);
 char *channel_status(channel_T *channel, int req_part);
 void channel_info(channel_T *channel, dict_T *dict);
 void channel_close(channel_T *channel, int invoke_close_cb);
index 45a2ea496dadcabfcc4d44ab6e4cdfaacbba0789..8160af385132fca329858653d6f7ec79a9ebfdde 100644 (file)
@@ -88,7 +88,7 @@ func RunServer(cmd, testfunc, args)
 
     call call(function(a:testfunc), [port])
   catch
-    call assert_false(1, "Caught exception: " . v:exception)
+    call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint)
   finally
     call s:kill_server(a:cmd)
   endtry
index 42e0e04b8176f3623d70c7db2411c8713b6c4f0b..85f80e25cce622a1dddc78852e2e9ae0aa858e00 100644 (file)
@@ -58,6 +58,9 @@ func Ch_communicate(port)
   " string with ][ should work
   call assert_equal('this][that', ch_evalexpr(handle, 'echo this][that'))
 
+  " nothing to read now
+  call assert_equal(0, ch_canread(handle))
+
   " sending three messages quickly then reading should work
   for i in range(3)
     call ch_sendexpr(handle, 'echo hello ' . i)
@@ -368,7 +371,7 @@ func Ch_raw_one_time_callback(port)
   endif
   call ch_setoptions(handle, {'mode': 'raw'})
 
-  " The message are sent raw, we do our own JSON strings here.
+  " The messages are sent raw, we do our own JSON strings here.
   call ch_sendraw(handle, "[1, \"hello!\"]\n", {'callback': 'Ch_handleRaw1'})
   call WaitFor('g:Ch_reply1 != ""')
   call assert_equal("[1, \"got it\"]", g:Ch_reply1)
@@ -431,7 +434,10 @@ func Test_raw_pipe()
     return
   endif
   call ch_log('Test_raw_pipe()')
-  let job = job_start(s:python . " test_channel_pipe.py", {'mode': 'raw'})
+  " Add a dummy close callback to avoid that messages are dropped when calling
+  " ch_canread().
+  let job = job_start(s:python . " test_channel_pipe.py",
+       \ {'mode': 'raw', 'close_cb': {chan -> 0}})
   call assert_equal(v:t_job, type(job))
   call assert_equal("run", job_status(job))
 
@@ -458,6 +464,9 @@ func Test_raw_pipe()
     call assert_equal("something\n", substitute(msg, "\r", "", 'g'))
 
     call ch_sendraw(job, "double this\n")
+    let g:handle = job_getchannel(job)
+    call WaitFor('ch_canread(g:handle)')
+    unlet g:handle
     let msg = ch_readraw(job)
     call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g'))
 
index 0e3554ddcba1d6968431c0d78b91c26ad9846518..02d1be454b9f324d6ab7b6bea7935c9612784f42 100644 (file)
@@ -764,6 +764,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    105,
 /**/
     104,
 /**/