0
0
mirror of https://github.com/vim/vim.git synced 2025-09-27 04:14:06 -04:00

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().
This commit is contained in:
Bram Moolenaar
2016-11-29 21:54:44 +01:00
parent f422bcc7f9
commit 4b785f69c0
8 changed files with 65 additions and 14 deletions

View 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",

View 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 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 Channel or a Job that has a Channel.
{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}
@@ -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 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
@@ -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 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
@@ -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 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.
@@ -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 a 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 a 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 a 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 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()|.

View 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;

View 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
*/

View 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);

View 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

View 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'))

View File

@@ -764,6 +764,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
105,
/**/
104,
/**/