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: > message that is available: >
let output = ch_read(channel, {'timeout': 0}) let output = ch_read(channel, {'timeout': 0})
When no message was available then the result is v:none for a JSON or JS mode 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: > To read all output from a RAW channel that is available: >
let output = ch_readraw(channel) 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 of a pipe causes the read end to get EOF). To avoid this make the job sleep
for a short while before it exits. 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 The handler defined for "out_cb" will not receive stderr. If you want to
handle that separately, add an "err_cb" handler: > handle that separately, add an "err_cb" handler: >
let job = job_start(command, {"out_cb": "MyHandler", 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}]) call({func}, {arglist} [, {dict}])
any call {func} with arguments {arglist} any call {func} with arguments {arglist}
ceil({expr}) Float round {expr} up 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({handle}) none close {handle}
ch_close_in({handle}) none close in part of {handle} ch_close_in({handle}) none close in part of {handle}
ch_evalexpr({handle}, {expr} [, {options}]) 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 don't fit, a vertical layout is used anyway. For some systems
the horizontal layout is always used. 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()* ch_close({handle}) *ch_close()*
Close {handle}. See |channel-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. A close callback is not invoked.
{only available when compiled with the |+channel| feature} {only available when compiled with the |+channel| feature}
ch_close_in({handle}) *ch_close_in()* ch_close_in({handle}) *ch_close_in()*
Close the "in" part of {handle}. See |channel-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. A close callback is not invoked.
{only available when compiled with the |+channel| feature} {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 Send {expr} over {handle}. The {expr} is encoded
according to the type of channel. The function cannot be used according to the type of channel. The function cannot be used
with a raw channel. See |channel-use|. 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* *E917*
{options} must be a Dictionary. It must not have a "callback" {options} must be a Dictionary. It must not have a "callback"
entry. It can have a "timeout" entry to specify the timeout 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()* ch_evalraw({handle}, {string} [, {options}]) *ch_evalraw()*
Send {string} over {handle}. 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 Works like |ch_evalexpr()|, but does not encode the request or
decode the response. The caller is responsible for the 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()* ch_getbufnr({handle}, {what}) *ch_getbufnr()*
Get the buffer number that {handle} is using for {what}. 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 {what} can be "err" for stderr, "out" for stdout or empty for
socket output. socket output.
Returns -1 when there is no buffer. Returns -1 when there is no buffer.
@@ -3099,7 +3112,7 @@ ch_open({address} [, {options}]) *ch_open()*
ch_read({handle} [, {options}]) *ch_read()* ch_read({handle} [, {options}]) *ch_read()*
Read from {handle} and return the received message. 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|. See |channel-more|.
{only available when compiled with the |+channel| feature} {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 according to the type of channel. The function cannot be used
with a raw channel. with a raw channel.
See |channel-use|. *E912* 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} {only available when compiled with the |+channel| feature}
@@ -3134,7 +3147,7 @@ ch_setoptions({handle}, {options}) *ch_setoptions()*
"timeout" default read timeout in msec "timeout" default read timeout in msec
"mode" mode for the whole channel "mode" mode for the whole channel
See |ch_open()| for more explanation. 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 Note that changing the mode may cause queued messages to be
lost. lost.
@@ -3148,7 +3161,7 @@ ch_status({handle} [, {options}]) *ch_status()*
"open" channel can be used "open" channel can be used
"buffered" channel can be read, not written to "buffered" channel can be read, not written to
"closed" channel can not be used "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 "buffered" is used when the channel was closed but there is
still data that can be obtained with |ch_read()|. 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. * Return TRUE if "channel" has JSON or other typeahead.
*/ */
static int int
channel_has_readahead(channel_T *channel, ch_part_T part) channel_has_readahead(channel_T *channel, ch_part_T part)
{ {
ch_mode_T ch_mode = channel->ch_part[part].ch_mode; 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); static void f_ceil(typval_T *argvars, typval_T *rettv);
#endif #endif
#ifdef FEAT_JOB_CHANNEL #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(typval_T *argvars, typval_T *rettv);
static void f_ch_close_in(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); static void f_ch_evalexpr(typval_T *argvars, typval_T *rettv);
@@ -499,6 +500,7 @@ static struct fst
{"ceil", 1, 1, f_ceil}, {"ceil", 1, 1, f_ceil},
#endif #endif
#ifdef FEAT_JOB_CHANNEL #ifdef FEAT_JOB_CHANNEL
{"ch_canread", 1, 1, f_ch_canread},
{"ch_close", 1, 1, f_ch_close}, {"ch_close", 1, 1, f_ch_close},
{"ch_close_in", 1, 1, f_ch_close_in}, {"ch_close_in", 1, 1, f_ch_close_in},
{"ch_evalexpr", 2, 3, f_ch_evalexpr}, {"ch_evalexpr", 2, 3, f_ch_evalexpr},
@@ -1778,6 +1780,21 @@ f_ceil(typval_T *argvars, typval_T *rettv)
#endif #endif
#ifdef FEAT_JOB_CHANNEL #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 * "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_collapse(channel_T *channel, ch_part_T part, int want_nl);
int channel_can_write_to(channel_T *channel); int channel_can_write_to(channel_T *channel);
int channel_is_open(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); char *channel_status(channel_T *channel, int req_part);
void channel_info(channel_T *channel, dict_T *dict); void channel_info(channel_T *channel, dict_T *dict);
void channel_close(channel_T *channel, int invoke_close_cb); 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]) call call(function(a:testfunc), [port])
catch catch
call assert_false(1, "Caught exception: " . v:exception) call assert_false(1, 'Caught exception: "' . v:exception . '" in ' . v:throwpoint)
finally finally
call s:kill_server(a:cmd) call s:kill_server(a:cmd)
endtry endtry

View File

@@ -58,6 +58,9 @@ func Ch_communicate(port)
" string with ][ should work " string with ][ should work
call assert_equal('this][that', ch_evalexpr(handle, 'echo this][that')) 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 " sending three messages quickly then reading should work
for i in range(3) for i in range(3)
call ch_sendexpr(handle, 'echo hello ' . i) call ch_sendexpr(handle, 'echo hello ' . i)
@@ -368,7 +371,7 @@ func Ch_raw_one_time_callback(port)
endif endif
call ch_setoptions(handle, {'mode': 'raw'}) 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 ch_sendraw(handle, "[1, \"hello!\"]\n", {'callback': 'Ch_handleRaw1'})
call WaitFor('g:Ch_reply1 != ""') call WaitFor('g:Ch_reply1 != ""')
call assert_equal("[1, \"got it\"]", g:Ch_reply1) call assert_equal("[1, \"got it\"]", g:Ch_reply1)
@@ -431,7 +434,10 @@ func Test_raw_pipe()
return return
endif endif
call ch_log('Test_raw_pipe()') 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(v:t_job, type(job))
call assert_equal("run", job_status(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 assert_equal("something\n", substitute(msg, "\r", "", 'g'))
call ch_sendraw(job, "double this\n") 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) let msg = ch_readraw(job)
call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g')) call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g'))

View File

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