Skip to content

Commit

Permalink
patch 7.4.1435
Browse files Browse the repository at this point in the history
Problem:    It is confusing that ch_sendexpr() and ch_sendraw() wait for a
            response.
Solution:   Add ch_evalexpr() and ch_evalraw().
  • Loading branch information
brammool committed Feb 27, 2016
1 parent b6ff811 commit 8b1862a
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 69 deletions.
29 changes: 15 additions & 14 deletions runtime/doc/channel.txt
@@ -1,4 +1,4 @@
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 23
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 27


VIM REFERENCE MANUAL by Bram Moolenaar
Expand Down Expand Up @@ -74,7 +74,7 @@ In T1 you should see:
=== socket opened === ~

You can now send a message to the server: >
echo ch_sendexpr(channel, 'hello!')
echo ch_evalexpr(channel, 'hello!')
The message is received in T1 and a response is sent back to Vim.
You can see the raw messages in T1. What Vim sends is:
Expand All @@ -101,7 +101,7 @@ Instead of giving a callback with every send call, it can also be specified
when opening the channel: >
call ch_close(channel)
let channel = ch_open('localhost:8765', {'callback': "MyHandler"})
call ch_sendexpr(channel, 'hello!', {'callback': 0})
call ch_sendexpr(channel, 'hello!')
==============================================================================
3. Opening a channel *channel-open*
Expand Down Expand Up @@ -171,7 +171,7 @@ Use |ch_status()| to see if the channel could be opened.
msec at least.

"timeout" The time to wait for a request when blocking, E.g. when using
ch_sendexpr(). In milliseconds. The default is 2000 (2
ch_evalexpr(). In milliseconds. The default is 2000 (2
seconds).
*out-timeout* *err-timeout*
"out-timeout" Timeout for stdout. Only when using pipes.
Expand Down Expand Up @@ -214,15 +214,15 @@ If there is an error reading or writing a channel it will be closed.
4. Using a JSON or JS channel *channel-use*

If mode is JSON then a message can be sent synchronously like this: >
let response = ch_sendexpr(channel, {expr})
let response = ch_evalexpr(channel, {expr})
This awaits a response from the other side.

When mode is JS this works the same, except that the messages use
JavaScript encoding. See |js_encode()| for the difference.

To send a message, without handling a response or letting the channel callback
handle the response: >
call ch_sendexpr(channel, {expr}, {'callback': 0})
call ch_sendexpr(channel, {expr})
To send a message and letting the response handled by a specific function,
asynchronously: >
Expand Down Expand Up @@ -263,8 +263,9 @@ On read error or ch_close(), when using a socket, the string "DETACH" is sent,
if still possible. The channel will then be inactive. For a JSON and JS mode
channel quotes are used around DETACH, otherwise there are no quotes.

It is also possible to use ch_sendraw() on a JSON or JS channel. The caller
is then completely responsible for correct encoding and decoding.
It is also possible to use ch_sendraw() and ch_evalraw() on a JSON or JS
channel. The caller is then completely responsible for correct encoding and
decoding.

==============================================================================
5. Channel commands *channel-commands*
Expand Down Expand Up @@ -363,7 +364,7 @@ Leave out the fourth argument if no response is to be sent:
6. Using a RAW or NL channel *channel-raw*

If mode is RAW or NL then a message can be send like this: >
let response = ch_sendraw(channel, {string})
let response = ch_evalraw(channel, {string})
The {string} is sent as-is. The response will be what can be read from the
channel right away. Since Vim doesn't know how to recognize the end of the
Expand All @@ -377,18 +378,18 @@ first NL. This can also be just the NL for an empty response.
If no NL was read before the channel timeout an empty string is returned.

To send a message, without expecting a response: >
call ch_sendraw(channel, {string}, 0)
call ch_sendraw(channel, {string})
The process can send back a response, the channel handler will be called with
it.

To send a message and letting the response handled by a specific function,
asynchronously: >
call ch_sendraw(channel, {string}, {callback})
call ch_sendraw(channel, {string}, {'callback': 'MyHandler'})
This {string} can also be JSON, use |json_encode()| to create it and
|json_decode()| to handle a received JSON message.

It is not possible to use |ch_sendexpr()| on a raw channel.
It is not possible to use |ch_evalexpr()| or |ch_sendexpr()| on a raw channel.

==============================================================================
7. More channel functions *channel-more*
Expand Down Expand Up @@ -447,8 +448,8 @@ If you want to handle both stderr and stdout with one handler use the
"callback" option: >
let job = job_start(command, {"callback": "MyHandler"})
You can send a message to the command with ch_sendraw(). If the channel is in
JSON or JS mode you can use ch_sendexpr().
You can send a message to the command with ch_evalraw(). If the channel is in
JSON or JS mode you can use ch_evalexpr().

There are several options you can use, see |job-options|.
For example, to start a job and write its output in buffer "dummy": >
Expand Down
45 changes: 34 additions & 11 deletions runtime/doc/eval.txt
Expand Up @@ -1818,6 +1818,10 @@ call( {func}, {arglist} [, {dict}])
any call {func} with arguments {arglist}
ceil( {expr}) Float round {expr} up
ch_close( {channel}) none close {channel}
ch_evalexpr( {channel}, {expr} [, {options}])
any evaluate {expr} on JSON {channel}
ch_evalraw( {channel}, {string} [, {options}])
any evaluate {string} on raw {channel}
ch_getjob( {channel}) Job get the Job of {channel}
ch_log( {msg} [, {channel}]) none write {msg} in the channel log file
ch_logfile( {fname} [, {mode}]) none start logging channel activity
Expand Down Expand Up @@ -2692,6 +2696,31 @@ ch_close({channel}) *ch_close()*
Close {channel}. See |channel-close|.
{only available when compiled with the |+channel| feature}

ch_evalexpr({channel}, {expr} [, {options}]) *ch_evalexpr()*
Send {expr} over {channel}. The {expr} is encoded
according to the type of channel. The function cannot be used
with a raw channel. See |channel-use|. *E912*
*E917*
{options} must be a Dictionary. It must not have a "callback"
entry.

ch_evalexpr() waits for a response and returns the decoded
expression. When there is an error or timeout it returns an
empty string.

{only available when compiled with the |+channel| feature}

ch_evalraw({channel}, {string} [, {options}]) *ch_evalraw()*
Send {string} over {channel}.
Works like |ch_evalexpr()|, but does not encode the request or
decode the response. The caller is responsible for the
correct contents. Also does not add a newline for a channel
in NL mode, the caller must do that. The NL in the response
is removed.
See |channel-use|.

{only available when compiled with the |+channel| feature}

ch_getjob({channel}) *ch_getjob()*
Get the Job associated with {channel}.
If there is no job calling |job_status()| on the returned Job
Expand Down Expand Up @@ -2769,16 +2798,11 @@ ch_sendexpr({channel}, {expr} [, {options}]) *ch_sendexpr()*
according to the type of channel. The function cannot be used
with a raw channel. See |channel-use|. *E912*

{options} must be a Dictionary.
When "callback" is a Funcref or the name of a function,
ch_sendexpr() returns immediately. The callback is invoked
when the response is received. See |channel-callback|.

Without "callback" ch_sendexpr() waits for a response and
returns the decoded expression. When there is an error or
timeout it returns an empty string.

When "callback" is zero no response is expected.
{options} must be a Dictionary. The "callback" item is a
Funcref or the name of a function it is invoked when the
response is received. See |channel-callback|.
Without "callback" the channel handler is invoked, otherwise
any received message is dropped.

{only available when compiled with the |+channel| feature}

Expand Down Expand Up @@ -7391,7 +7415,6 @@ scrollbind Compiled with 'scrollbind' support.
showcmd Compiled with 'showcmd' support.
signs Compiled with |:sign| support.
smartindent Compiled with 'smartindent' support.
sniff Compiled with SNiFF interface support.
spell Compiled with spell checking support |spell|.
startuptime Compiled with |--startuptime| support.
statusline Compiled with support for 'statusline', 'rulerformat'
Expand Down
78 changes: 67 additions & 11 deletions src/eval.c
Expand Up @@ -507,6 +507,8 @@ static void f_ceil(typval_T *argvars, typval_T *rettv);
#endif
#ifdef FEAT_CHANNEL
static void f_ch_close(typval_T *argvars, typval_T *rettv);
static void f_ch_evalexpr(typval_T *argvars, typval_T *rettv);
static void f_ch_evalraw(typval_T *argvars, typval_T *rettv);
# ifdef FEAT_JOB
static void f_ch_getjob(typval_T *argvars, typval_T *rettv);
# endif
Expand Down Expand Up @@ -8201,6 +8203,8 @@ static struct fst
#endif
#ifdef FEAT_CHANNEL
{"ch_close", 1, 1, f_ch_close},
{"ch_evalexpr", 2, 3, f_ch_evalexpr},
{"ch_evalraw", 2, 3, f_ch_evalraw},
# ifdef FEAT_JOB
{"ch_getjob", 1, 1, f_ch_getjob},
# endif
Expand Down Expand Up @@ -10485,7 +10489,13 @@ f_ch_readraw(typval_T *argvars, typval_T *rettv)
* Otherwise returns NULL.
*/
static channel_T *
send_common(typval_T *argvars, char_u *text, int id, char *fun, int *part_read)
send_common(
typval_T *argvars,
char_u *text,
int id,
int eval,
char *fun,
int *part_read)
{
channel_T *channel;
jobopt_T opt;
Expand All @@ -10502,9 +10512,17 @@ send_common(typval_T *argvars, char_u *text, int id, char *fun, int *part_read)
return NULL;

/* Set the callback. An empty callback means no callback and not reading
* the response. */
* the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not
* allowed. */
if (opt.jo_callback != NULL && *opt.jo_callback != NUL)
{
if (eval)
{
EMSG2(_("E917: Cannot use a callback with %s()"), fun);
return NULL;
}
channel_set_req_callback(channel, part_send, opt.jo_callback, id);
}

if (channel_send(channel, part_send, text, fun) == OK
&& opt.jo_callback == NULL)
Expand All @@ -10513,10 +10531,10 @@ send_common(typval_T *argvars, char_u *text, int id, char *fun, int *part_read)
}

/*
* "ch_sendexpr()" function
* common for "ch_evalexpr()" and "ch_sendexpr()"
*/
static void
f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
{
char_u *text;
typval_T *listtv;
Expand All @@ -10539,7 +10557,7 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
ch_mode = channel_get_mode(channel, part_send);
if (ch_mode == MODE_RAW || ch_mode == MODE_NL)
{
EMSG(_("E912: cannot use ch_sendexpr() with a raw or nl channel"));
EMSG(_("E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel"));
return;
}

Expand All @@ -10549,9 +10567,10 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
if (text == NULL)
return;

channel = send_common(argvars, text, id, "sendexpr", &part_read);
channel = send_common(argvars, text, id, eval,
eval ? "ch_evalexpr" : "ch_sendexpr", &part_read);
vim_free(text);
if (channel != NULL)
if (channel != NULL && eval)
{
/* TODO: timeout from options */
timeout = channel_get_timeout(channel, part_read);
Expand All @@ -10570,10 +10589,28 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
}

/*
* "ch_sendraw()" function
* "ch_evalexpr()" function
*/
static void
f_ch_sendraw(typval_T *argvars, typval_T *rettv)
f_ch_evalexpr(typval_T *argvars, typval_T *rettv)
{
ch_expr_common(argvars, rettv, TRUE);
}

/*
* "ch_sendexpr()" function
*/
static void
f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
{
ch_expr_common(argvars, rettv, FALSE);
}

/*
* common for "ch_evalraw()" and "ch_sendraw()"
*/
static void
ch_raw_common(typval_T *argvars, typval_T *rettv, int eval)
{
char_u buf[NUMBUFLEN];
char_u *text;
Expand All @@ -10586,15 +10623,34 @@ f_ch_sendraw(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = NULL;

text = get_tv_string_buf(&argvars[1], buf);
channel = send_common(argvars, text, 0, "sendraw", &part_read);
if (channel != NULL)
channel = send_common(argvars, text, 0, eval,
eval ? "ch_evalraw" : "ch_sendraw", &part_read);
if (channel != NULL && eval)
{
/* TODO: timeout from options */
timeout = channel_get_timeout(channel, part_read);
rettv->vval.v_string = channel_read_block(channel, part_read, timeout);
}
}

/*
* "ch_evalraw()" function
*/
static void
f_ch_evalraw(typval_T *argvars, typval_T *rettv)
{
ch_raw_common(argvars, rettv, TRUE);
}

/*
* "ch_sendraw()" function
*/
static void
f_ch_sendraw(typval_T *argvars, typval_T *rettv)
{
ch_raw_common(argvars, rettv, FALSE);
}

/*
* "ch_setoptions()" function
*/
Expand Down

0 comments on commit 8b1862a

Please sign in to comment.