# HG changeset patch # User Christian Brabandt # Date 1455994805 -3600 # Node ID 74b44d06d3c7f5b15ecea941c5f1cfad928c92ab # Parent d5ab593ed881cefafa3ed53e4ec813cc9020ddd5 commit https://github.com/vim/vim/commit/6f3a544228c1faf92211cbaf8bbedb1dff883f90 Author: Bram Moolenaar Date: Sat Feb 20 19:56:13 2016 +0100 patch 7.4.1372 Problem: channel read implementation is incomplete. Solution: Add ch_read() and options for ch_readraw(). diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -1696,12 +1696,11 @@ channel_read(channel_T *channel, int par * 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, i /* * 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 *chann 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 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 */ diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -507,6 +507,7 @@ static void f_ch_close(typval_T *argvars 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 * 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->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,27 +10130,74 @@ f_ch_open(typval_T *argvars, typval_T *r } /* - * "ch_readraw()" function - */ - static void -f_ch_readraw(typval_T *argvars, typval_T *rettv) + * Common for ch_read() and ch_readraw(). + */ + static void +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); } /* @@ -10177,6 +10247,7 @@ f_ch_sendexpr(typval_T *argvars, typval_ 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_ 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 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 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); + } } /* diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -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 : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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; diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1372, +/**/ 1371, /**/ 1370,