Patch 8.0.0105
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().
Files: src/evalfunc.c, src/channel.c, src/proto/channel.pro,
src/testdir/test_channel.vim, src/testdir/shared.vim,
runtime/doc/eval.txt, runtime/doc/channel.txt
*** ../vim-8.0.0104/src/evalfunc.c 2016-11-24 15:09:03.409856638 +0100
--- src/evalfunc.c 2016-11-29 20:23:04.633430178 +0100
***************
*** 76,81 ****
--- 76,82 ----
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,504 ****
--- 500,506 ----
{"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},
***************
*** 1779,1784 ****
--- 1781,1801 ----
#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
*/
static void
*** ../vim-8.0.0104/src/channel.c 2016-11-26 15:13:29.402218088 +0100
--- src/channel.c 2016-11-29 20:13:58.177247346 +0100
***************
*** 2603,2609 ****
/*
* Return TRUE if "channel" has JSON or other typeahead.
*/
! static int
channel_has_readahead(channel_T *channel, ch_part_T part)
{
ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
--- 2603,2609 ----
/*
* Return TRUE if "channel" has JSON or other typeahead.
*/
! int
channel_has_readahead(channel_T *channel, ch_part_T part)
{
ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
*** ../vim-8.0.0104/src/proto/channel.pro 2016-11-26 15:13:29.402218088
+0100
--- src/proto/channel.pro 2016-11-29 20:14:03.549209705 +0100
***************
*** 25,30 ****
--- 25,31 ----
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);
*** ../vim-8.0.0104/src/testdir/test_channel.vim 2016-11-17
17:25:28.212093109 +0100
--- src/testdir/test_channel.vim 2016-11-29 21:52:55.853041485 +0100
***************
*** 58,63 ****
--- 58,66 ----
" 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,374 ****
endif
call ch_setoptions(handle, {'mode': 'raw'})
! " The message 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)
--- 371,377 ----
endif
call ch_setoptions(handle, {'mode': 'raw'})
! " 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,437 ****
return
endif
call ch_log('Test_raw_pipe()')
! let job = job_start(s:python . " test_channel_pipe.py", {'mode': 'raw'})
call assert_equal(v:t_job, type(job))
call assert_equal("run", job_status(job))
--- 434,443 ----
return
endif
call ch_log('Test_raw_pipe()')
! " 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,463 ****
--- 464,472 ----
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'))
*** ../vim-8.0.0104/src/testdir/shared.vim 2016-10-27 20:00:03.665357405
+0200
--- src/testdir/shared.vim 2016-11-29 20:25:10.408554811 +0100
***************
*** 88,94 ****
call call(function(a:testfunc), [port])
catch
! call assert_false(1, "Caught exception: " . v:exception)
finally
call s:kill_server(a:cmd)
endtry
--- 88,94 ----
call call(function(a:testfunc), [port])
catch
! call assert_false(1, 'Caught exception: "' . v:exception . '" in ' .
v:throwpoint)
finally
call s:kill_server(a:cmd)
endtry
*** ../vim-8.0.0104/runtime/doc/eval.txt 2016-11-24 15:09:03.413856612
+0100
--- runtime/doc/eval.txt 2016-11-29 21:45:28.872201614 +0100
***************
*** 2008,2013 ****
--- 2009,2015 ----
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}])
***************
*** 2979,2994 ****
don't fit, a vertical layout is used anyway. For some systems
the horizontal layout is always used.
ch_close({handle}) *ch_close()*
Close {handle}. See |channel-close|.
! {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.
A close callback is not invoked.
{only available when compiled with the |+channel| feature}
--- 2981,3008 ----
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 a 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 a Channel or a Job that has a Channel.
A close callback is not invoked.
{only available when compiled with the |+channel| feature}
***************
*** 2997,3003 ****
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.
*E917*
{options} must be a Dictionary. It must not have a "callback"
entry. It can have a "timeout" entry to specify the timeout
--- 3011,3017 ----
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 a 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
***************
*** 3011,3017 ****
ch_evalraw({handle}, {string} [, {options}]) *ch_evalraw()*
Send {string} over {handle}.
! {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,3031 ----
ch_evalraw({handle}, {string} [, {options}]) *ch_evalraw()*
Send {string} over {handle}.
! {handle} can be a 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
***************
*** 3024,3030 ****
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.
{what} can be "err" for stderr, "out" for stdout or empty for
socket output.
Returns -1 when there is no buffer.
--- 3038,3044 ----
ch_getbufnr({handle}, {what}) *ch_getbufnr()*
Get the buffer number that {handle} is using for {what}.
! {handle} can be a 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.
***************
*** 3070,3077 ****
|ch_logfile()|.
When {handle} is passed the channel number is used for the
message.
! {handle} can be Channel or a Job that has a Channel. The
! Channel must open.
ch_logfile({fname} [, {mode}])
*ch_logfile()*
Start logging channel activity to {fname}.
--- 3084,3091 ----
|ch_logfile()|.
When {handle} is passed the channel number is used for the
message.
! {handle} can be a Channel or a Job that has a Channel. The
! Channel must be open for the channel number to be used.
ch_logfile({fname} [, {mode}])
*ch_logfile()*
Start logging channel activity to {fname}.
***************
*** 3098,3104 ****
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.
See |channel-more|.
{only available when compiled with the |+channel| feature}
--- 3112,3118 ----
ch_read({handle} [, {options}])
*ch_read()*
Read from {handle} and return the received message.
! {handle} can be a Channel or a Job that has a Channel.
See |channel-more|.
{only available when compiled with the |+channel| feature}
***************
*** 3112,3118 ****
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.
{only available when compiled with the |+channel| feature}
--- 3126,3132 ----
according to the type of channel. The function cannot be used
with a raw channel.
See |channel-use|. *E912*
! {handle} can be a Channel or a Job that has a Channel.
{only available when compiled with the |+channel| feature}
***************
*** 3133,3139 ****
"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.
Note that changing the mode may cause queued messages to be
lost.
--- 3147,3153 ----
"timeout" default read timeout in msec
"mode" mode for the whole channel
See |ch_open()| for more explanation.
! {handle} can be a Channel or a Job that has a Channel.
Note that changing the mode may cause queued messages to be
lost.
***************
*** 3147,3153 ****
"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.
"buffered" is used when the channel was closed but there is
still data that can be obtained with |ch_read()|.
--- 3161,3167 ----
"open" channel can be used
"buffered" channel can be read, not written to
"closed" channel can not be used
! {handle} can be a 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()|.
***************
*** 3708,3716 ****
is zero remove the item from the |List| or |Dictionary|.
{expr2} must be a |string| or |Funcref|.
! if {expr2} is a |string|, inside {expr2} |v:val| has the value
of the current item. For a |Dictionary| |v:key| has the key
! of the current item.
Examples: >
call filter(mylist, 'v:val !~ "OLD"')
< Removes the items where "OLD" appears. >
--- 3722,3731 ----
is zero remove the item from the |List| or |Dictionary|.
{expr2} must be a |string| or |Funcref|.
! If {expr2} is a |string|, inside {expr2} |v:val| has the value
of the current item. For a |Dictionary| |v:key| has the key
! of the current item and for a |List| |v:key| has the index of
! the current item.
Examples: >
call filter(mylist, 'v:val !~ "OLD"')
< Removes the items where "OLD" appears. >
*** ../vim-8.0.0104/runtime/doc/channel.txt 2016-09-12 12:45:25.000000000
+0200
--- runtime/doc/channel.txt 2016-11-29 21:43:53.144857530 +0100
***************
*** 418,424 ****
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.
To read all output from a RAW channel that is available: >
let output = ch_readraw(channel)
--- 418,428 ----
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. 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)
***************
*** 465,470 ****
--- 469,479 ----
Without the handler you need to read the output with |ch_read()| or
|ch_readraw()|. You can do this in the close callback, see |read-in-close-cb|.
+ 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",
*** ../vim-8.0.0104/src/version.c 2016-11-26 17:45:50.040909819 +0100
--- src/version.c 2016-11-29 20:14:29.545027586 +0100
***************
*** 766,767 ****
--- 766,769 ----
{ /* Add new patch number below this line */
+ /**/
+ 105,
/**/
--
hundred-and-one symptoms of being an internet addict:
55. You ask your doctor to implant a gig in your brain.
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.