1
0
forked from aniani/vim

patch 7.4.1341

Problem:    It's difficult to add more arguments to ch_sendraw() and
            ch_sendexpr().
Solution:   Make the third option a dictionary.
This commit is contained in:
Bram Moolenaar
2016-02-16 21:03:07 +01:00
parent 7d63f62460
commit 910b8aac5d
10 changed files with 91 additions and 54 deletions

View File

@@ -1,4 +1,4 @@
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 15 *channel.txt* For Vim version 7.4. Last change: 2016 Feb 16
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -117,7 +117,7 @@ Use |ch_status()| to see if the channel could be opened.
"mode" can be: *channel-mode* "mode" can be: *channel-mode*
"json" - Use JSON, see below; most convenient way. Default. "json" - Use JSON, see below; most convenient way. Default.
"js" - Use JavaScript encoding, more efficient than JSON. "js" - Use JS (JavaScript) encoding, more efficient than JSON.
"nl" - Use messages that end in a NL character "nl" - Use messages that end in a NL character
"raw" - Use raw messages "raw" - Use raw messages
@@ -188,11 +188,11 @@ If there is an error reading or writing a channel it will be closed.
============================================================================== ==============================================================================
4. Using a JSON or JS channel *channel-use* 4. Using a JSON or JS channel *channel-use*
If {mode} is "json" then a message can be sent synchronously like this: > If mode is JSON then a message can be sent synchronously like this: >
let response = ch_sendexpr(channel, {expr}) let response = ch_sendexpr(channel, {expr})
This awaits a response from the other side. This awaits a response from the other side.
When {mode} is "js" this works the same, except that the messages use When mode is JS this works the same, except that the messages use
JavaScript encoding. See |js_encode()| for the difference. JavaScript encoding. See |js_encode()| for the difference.
To send a message, without handling a response: > To send a message, without handling a response: >
@@ -242,7 +242,7 @@ is then completely responsible for correct encoding and decoding.
============================================================================== ==============================================================================
5. Channel commands *channel-commands* 5. Channel commands *channel-commands*
With a "json" channel the process can send commands to Vim that will be With a JSON channel the process can send commands to Vim that will be
handled by Vim internally, it does not require a handler for the channel. handled by Vim internally, it does not require a handler for the channel.
Possible commands are: *E903* *E904* *E905* Possible commands are: *E903* *E904* *E905*
@@ -316,14 +316,15 @@ Example:
============================================================================== ==============================================================================
6. Using a RAW or NL channel *channel-raw* 6. Using a RAW or NL channel *channel-raw*
If {mode} is "raw" then a message can be send like this: > If mode is RAW or NL then a message can be send like this: >
let response = ch_sendraw(channel, {string}) let response = ch_sendraw(channel, {string})
The {string} is sent as-is. The response will be what can be read from the 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 channel right away. Since Vim doesn't know how to recognize the end of the
message you need to take care of it yourself. The timeout applies for reading message you need to take care of it yourself. The timeout applies for reading
the first byte, after that it will not wait for anything more. the first byte, after that it will not wait for anything more.
If {mode} is "nl" you can send a message in a similar way. You are expected If mode is "nl" you can send a message in a similar way. You are expected
to put in the NL after each message. Thus you can also send several messages to put in the NL after each message. Thus you can also send several messages
ending in a NL at once. The response will be the text up to and including the ending in a NL at once. The response will be the text up to and including the
first NL. This can also be just the NL for an empty response. first NL. This can also be just the NL for an empty response.
@@ -450,6 +451,7 @@ The {options} argument in job_start() is a dictionary. All entries are
optional. The same options can be used with job_setoptions(job, {options}). optional. The same options can be used with job_setoptions(job, {options}).
TODO: *job-out-cb* TODO: *job-out-cb*
"callback": handler
"out-cb": handler Callback for when there is something to read on "out-cb": handler Callback for when there is something to read on
stdout. stdout.
TODO: *job-err-cb* TODO: *job-err-cb*
@@ -484,7 +486,7 @@ TODO: *job-out-io*
"out-buffer": "name" buffer to append to "out-buffer": "name" buffer to append to
TODO: *job-err-io* TODO: *job-err-io*
"err-io": "out" same as stdout (default) "err-io": "out" same type as stdout (default)
"err-io": "null" disconnect stderr "err-io": "null" disconnect stderr
"err-io": "pipe" stderr is connected to the channel "err-io": "pipe" stderr is connected to the channel
"err-io": "file" stderr writes to a file "err-io": "file" stderr writes to a file

View File

@@ -1,4 +1,4 @@
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 13 *eval.txt* For Vim version 7.4. Last change: 2016 Feb 16
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1821,9 +1821,9 @@ ch_close( {handle}) none close a channel
ch_logfile( {fname} [, {mode}]) none start logging channel activity ch_logfile( {fname} [, {mode}]) none start logging channel activity
ch_open( {address} [, {argdict})] Number open a channel to {address} ch_open( {address} [, {argdict})] Number open a channel to {address}
ch_readraw( {handle}) String read from channel {handle} ch_readraw( {handle}) String read from channel {handle}
ch_sendexpr( {handle}, {expr} [, {callback}]) ch_sendexpr( {handle}, {expr} [, {options}])
any send {expr} over JSON channel {handle} any send {expr} over JSON channel {handle}
ch_sendraw( {handle}, {string} [, {callback}]) ch_sendraw( {handle}, {string} [, {options}])
any send {string} over raw channel {handle} any send {string} over raw channel {handle}
ch_status( {handle}) String status of channel {handle} ch_status( {handle}) String status of channel {handle}
changenr() Number current change number changenr() Number current change number
@@ -2725,28 +2725,32 @@ ch_readraw({handle}) *ch_readraw()*
within that time an empty string is returned. within that time an empty string is returned.
TODO: depends on channel mode. TODO: depends on channel mode.
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()* ch_sendexpr({handle}, {expr} [, {options}]) *ch_sendexpr()*
Send {expr} over channel {handle}. The {expr} is encoded Send {expr} over channel {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|. *E912* with a raw channel. See |channel-use|. *E912*
When {callback} is given returns immediately. Without {options} must be a Dictionary.
{callback} waits for a response and returns the decoded When "callback" is a Funcref or the name of a function,
expression. When there is an error or timeout returns an ch_sendexpr() returns immediately. The callback is invoked
empty string. when the response is received. See |channel-callback|.
When {callback} is zero no response is expected. Without "callback" ch_sendexpr() waits for a response and
Otherwise {callback} must be a Funcref or the name of a returns the decoded expression. When there is an error or
function. It is called when the response is received. See timeout it returns an empty string.
|channel-callback|.
When "callback" is zero no response is expected.
{only available when compiled with the |+channel| feature} {only available when compiled with the |+channel| feature}
ch_sendraw({handle}, {string} [, {callback}]) *ch_sendraw()* ch_sendraw({handle}, {string} [, {options}]) *ch_sendraw()*
Send {string} over channel {handle}. Send {string} over channel {handle}.
Works like |ch_sendexpr()|, but does not encode the request or Works like |ch_sendexpr()|, 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
correct contents. See |channel-use|. 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} {only available when compiled with the |+channel| feature}
@@ -7274,7 +7278,7 @@ listcmds Compiled with commands for the buffer list |:files|
and the argument list |arglist|. and the argument list |arglist|.
localmap Compiled with local mappings and abbr. |:map-local| localmap Compiled with local mappings and abbr. |:map-local|
lua Compiled with Lua interface |Lua|. lua Compiled with Lua interface |Lua|.
mac Macintosh version of Vim. mac Any Macintosh version of Vim.
macunix Compiled for OS X, with darwin macunix Compiled for OS X, with darwin
osx Compiled for OS X, with or without darwin osx Compiled for OS X, with or without darwin
menu Compiled with support for |:menu|. menu Compiled with support for |:menu|.

View File

@@ -696,6 +696,18 @@ channel_set_callback(channel_T *channel, char_u *callback)
channel->ch_callback = vim_strsave(callback); channel->ch_callback = vim_strsave(callback);
} }
/*
* Set various properties from an "options" argument.
*/
void
channel_set_options(channel_T *channel, jobopt_T *options)
{
channel_set_mode(channel, options->jo_mode);
if (options->jo_callback != NULL && *options->jo_callback != NUL)
channel_set_callback(channel, options->jo_callback);
}
/* /*
* Set the callback for channel "channel" for the response with "id". * Set the callback for channel "channel" for the response with "id".
*/ */

View File

@@ -9930,15 +9930,18 @@ f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED)
} }
/* /*
* Get the "mode" entry from "dict", if it exists, and parse the mode name. * Get the option entries from "dict", and parse them.
* If the mode is invalide return FAIL. * If an option value is invalid return FAIL.
*/ */
static int static int
get_mode_arg(dict_T *dict, jobopt_T *opt) get_job_options(dict_T *dict, jobopt_T *opt)
{ {
dictitem_T *item; dictitem_T *item;
char_u *mode; char_u *mode;
if (dict == NULL)
return OK;
if ((item = dict_find(dict, (char_u *)"mode", -1)) != NULL) if ((item = dict_find(dict, (char_u *)"mode", -1)) != NULL)
{ {
mode = get_tv_string(&item->di_tv); mode = get_tv_string(&item->di_tv);
@@ -9956,6 +9959,17 @@ get_mode_arg(dict_T *dict, jobopt_T *opt)
return FAIL; return FAIL;
} }
} }
if ((item = dict_find(dict, (char_u *)"callback", -1)) != NULL)
{
opt->jo_callback = get_callback(&item->di_tv);
if (opt->jo_callback == NULL)
{
EMSG2(_(e_invarg2), "callback");
return FAIL;
}
}
return OK; return OK;
} }
@@ -9966,7 +9980,6 @@ get_mode_arg(dict_T *dict, jobopt_T *opt)
f_ch_open(typval_T *argvars, typval_T *rettv) f_ch_open(typval_T *argvars, typval_T *rettv)
{ {
char_u *address; char_u *address;
char_u *callback = NULL;
char_u *p; char_u *p;
char *rest; char *rest;
int port; int port;
@@ -10004,20 +10017,19 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
} }
options.jo_mode = MODE_JSON; options.jo_mode = MODE_JSON;
options.jo_callback = NULL;
if (argvars[1].v_type == VAR_DICT) if (argvars[1].v_type == VAR_DICT)
{ {
dict_T *dict = argvars[1].vval.v_dict; dict_T *dict = argvars[1].vval.v_dict;
dictitem_T *item; dictitem_T *item;
/* parse argdict */ /* parse argdict */
if (get_mode_arg(dict, &options) == FAIL) if (get_job_options(dict, &options) == FAIL)
return; return;
if ((item = dict_find(dict, (char_u *)"waittime", -1)) != NULL) if ((item = dict_find(dict, (char_u *)"waittime", -1)) != NULL)
waittime = get_tv_number(&item->di_tv); waittime = get_tv_number(&item->di_tv);
if ((item = dict_find(dict, (char_u *)"timeout", -1)) != NULL) if ((item = dict_find(dict, (char_u *)"timeout", -1)) != NULL)
timeout = get_tv_number(&item->di_tv); timeout = get_tv_number(&item->di_tv);
if ((item = dict_find(dict, (char_u *)"callback", -1)) != NULL)
callback = get_callback(&item->di_tv);
} }
if (waittime < 0 || timeout < 0) if (waittime < 0 || timeout < 0)
{ {
@@ -10029,10 +10041,8 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
if (channel != NULL) if (channel != NULL)
{ {
rettv->vval.v_channel = channel; rettv->vval.v_channel = channel;
channel_set_mode(channel, options.jo_mode); channel_set_options(channel, &options);
channel_set_timeout(channel, timeout); channel_set_timeout(channel, timeout);
if (callback != NULL && *callback != NUL)
channel_set_callback(channel, callback);
} }
} }
@@ -10082,6 +10092,7 @@ send_common(typval_T *argvars, char_u *text, int id, char *fun)
{ {
channel_T *channel; channel_T *channel;
char_u *callback = NULL; char_u *callback = NULL;
jobopt_T options;
channel = get_channel_arg(&argvars[0]); channel = get_channel_arg(&argvars[0]);
if (channel == NULL) if (channel == NULL)
@@ -10089,10 +10100,16 @@ send_common(typval_T *argvars, char_u *text, int id, char *fun)
if (argvars[2].v_type != VAR_UNKNOWN) if (argvars[2].v_type != VAR_UNKNOWN)
{ {
callback = get_callback(&argvars[2]); if (argvars[2].v_type != VAR_DICT)
if (callback == NULL) {
EMSG(_(e_invarg));
return NULL; return NULL;
} }
options.jo_callback = NULL;
if (get_job_options(argvars[2].vval.v_dict, &options) == FAIL)
return NULL;
callback = options.jo_callback;
}
/* Set the callback. An empty callback means no callback and not reading /* Set the callback. An empty callback means no callback and not reading
* the response. */ * the response. */
if (callback != NULL && *callback != NUL) if (callback != NULL && *callback != NUL)
@@ -14511,17 +14528,15 @@ f_job_start(typval_T *argvars UNUSED, typval_T *rettv)
/* Default mode is NL. */ /* Default mode is NL. */
options.jo_mode = MODE_NL; options.jo_mode = MODE_NL;
options.jo_callback = NULL;
if (argvars[1].v_type != VAR_UNKNOWN) if (argvars[1].v_type != VAR_UNKNOWN)
{ {
dict_T *dict;
if (argvars[1].v_type != VAR_DICT) if (argvars[1].v_type != VAR_DICT)
{ {
EMSG(_(e_invarg)); EMSG(_(e_invarg));
return; return;
} }
dict = argvars[1].vval.v_dict; if (get_job_options(argvars[1].vval.v_dict, &options) == FAIL)
if (get_mode_arg(dict, &options) == FAIL)
return; return;
} }

View File

@@ -5127,7 +5127,7 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
# ifdef FEAT_CHANNEL # ifdef FEAT_CHANNEL
channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]); channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]);
channel_set_job(channel, job); channel_set_job(channel, job);
channel_set_mode(channel, options->jo_mode); channel_set_options(channel, options);
# ifdef FEAT_GUI # ifdef FEAT_GUI
channel_gui_register(channel); channel_gui_register(channel);
# endif # endif

View File

@@ -5125,7 +5125,7 @@ mch_start_job(char *cmd, job_T *job, jobopt_T *options)
job->jv_channel = channel; job->jv_channel = channel;
channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]); channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]);
channel_set_job(channel, job); channel_set_job(channel, job);
channel_set_mode(channel, options->jo_mode); channel_set_options(channel, options);
# ifdef FEAT_GUI # ifdef FEAT_GUI
channel_gui_register(channel); channel_gui_register(channel);

View File

@@ -7,9 +7,10 @@ void channel_gui_register_all(void);
channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)); channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void));
void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err);
void channel_set_job(channel_T *channel, job_T *job); void channel_set_job(channel_T *channel, job_T *job);
void channel_set_mode(channel_T *channel, ch_mode_T ch_mode); void channel_set_mode(channel_T *channel, ch_mode_T mode);
void channel_set_timeout(channel_T *channel, int timeout); void channel_set_timeout(channel_T *channel, int timeout);
void channel_set_callback(channel_T *channel, char_u *callback); void channel_set_callback(channel_T *channel, char_u *callback);
void channel_set_options(channel_T *channel, jobopt_T *options);
void channel_set_req_callback(channel_T *channel, char_u *callback, int id); void channel_set_req_callback(channel_T *channel, char_u *callback, int id);
char_u *channel_get(channel_T *channel); char_u *channel_get(channel_T *channel);
int channel_collapse(channel_T *channel); int channel_collapse(channel_T *channel);

View File

@@ -1373,11 +1373,12 @@ struct channel_S {
}; };
/* /*
* Options for job commands. * Options for job and channel commands.
*/ */
typedef struct typedef struct
{ {
ch_mode_T jo_mode; ch_mode_T jo_mode; /* "mode" */
char_u *jo_callback; /* "callback", not allocated! */
} jobopt_T; } jobopt_T;

View File

@@ -117,7 +117,7 @@ func s:communicate(port)
call assert_equal('added more', getline('$')) call assert_equal('added more', getline('$'))
" Send a request with a specific handler. " Send a request with a specific handler.
call ch_sendexpr(handle, 'hello!', 's:RequestHandler') call ch_sendexpr(handle, 'hello!', {'callback': 's:RequestHandler'})
sleep 10m sleep 10m
if !exists('s:responseHandle') if !exists('s:responseHandle')
call assert_false(1, 's:responseHandle was not set') call assert_false(1, 's:responseHandle was not set')
@@ -128,7 +128,7 @@ func s:communicate(port)
unlet s:responseHandle unlet s:responseHandle
let s:responseMsg = '' let s:responseMsg = ''
call ch_sendexpr(handle, 'hello!', function('s:RequestHandler')) call ch_sendexpr(handle, 'hello!', {'callback': function('s:RequestHandler')})
sleep 10m sleep 10m
if !exists('s:responseHandle') if !exists('s:responseHandle')
call assert_false(1, 's:responseHandle was not set') call assert_false(1, 's:responseHandle was not set')
@@ -171,7 +171,7 @@ func s:communicate(port)
call assert_equal('ok', ch_sendexpr(handle, 'empty-request')) call assert_equal('ok', ch_sendexpr(handle, 'empty-request'))
" make the server quit, can't check if this works, should not hang. " make the server quit, can't check if this works, should not hang.
call ch_sendexpr(handle, '!quit!', 0) call ch_sendexpr(handle, '!quit!', {'callback': 0})
endfunc endfunc
func Test_communicate() func Test_communicate()
@@ -242,7 +242,7 @@ func s:channel_handler(port)
call assert_equal('we called you', s:reply) call assert_equal('we called you', s:reply)
" Test that it works while not waiting on a numbered message. " Test that it works while not waiting on a numbered message.
call ch_sendexpr(handle, 'call me again', 0) call ch_sendexpr(handle, 'call me again', {'callback': 0})
sleep 10m sleep 10m
call assert_equal('we did call you', s:reply) call assert_equal('we did call you', s:reply)
endfunc endfunc
@@ -292,11 +292,11 @@ func Test_raw_pipe()
call assert_equal("run", job_status(job)) call assert_equal("run", job_status(job))
try try
let handle = job_getchannel(job) let handle = job_getchannel(job)
call ch_sendraw(handle, "echo something\n", 0) call ch_sendraw(handle, "echo something\n", {'callback': 0})
let msg = ch_readraw(handle) let msg = ch_readraw(handle)
call assert_equal("something\n", substitute(msg, "\r", "", 'g')) call assert_equal("something\n", substitute(msg, "\r", "", 'g'))
call ch_sendraw(handle, "double this\n", 0) call ch_sendraw(handle, "double this\n", {'callback': 0})
let msg = ch_readraw(handle) let msg = ch_readraw(handle)
call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g')) call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g'))
@@ -315,10 +315,10 @@ func Test_nl_pipe()
call assert_equal("run", job_status(job)) call assert_equal("run", job_status(job))
try try
let handle = job_getchannel(job) let handle = job_getchannel(job)
call ch_sendraw(handle, "echo something\n", 0) call ch_sendraw(handle, "echo something\n", {'callback': 0})
call assert_equal("something", ch_readraw(handle)) call assert_equal("something", ch_readraw(handle))
call ch_sendraw(handle, "double this\n", 0) call ch_sendraw(handle, "double this\n", {'callback': 0})
call assert_equal("this", ch_readraw(handle)) call assert_equal("this", ch_readraw(handle))
call assert_equal("AND this", ch_readraw(handle)) call assert_equal("AND this", ch_readraw(handle))
@@ -340,7 +340,7 @@ endfunc
" Test that "unlet handle" in a handler doesn't crash Vim. " Test that "unlet handle" in a handler doesn't crash Vim.
func s:unlet_handle(port) func s:unlet_handle(port)
let s:channelfd = ch_open('localhost:' . a:port, s:chopt) let s:channelfd = ch_open('localhost:' . a:port, s:chopt)
call ch_sendexpr(s:channelfd, "test", function('s:UnletHandler')) call ch_sendexpr(s:channelfd, "test", {'callback': function('s:UnletHandler')})
sleep 10m sleep 10m
call assert_equal('what?', s:unletResponse) call assert_equal('what?', s:unletResponse)
endfunc endfunc
@@ -360,7 +360,7 @@ endfunc
" Test that "unlet handle" in a handler doesn't crash Vim. " Test that "unlet handle" in a handler doesn't crash Vim.
func s:close_handle(port) func s:close_handle(port)
let s:channelfd = ch_open('localhost:' . a:port, s:chopt) let s:channelfd = ch_open('localhost:' . a:port, s:chopt)
call ch_sendexpr(s:channelfd, "test", function('s:CloseHandler')) call ch_sendexpr(s:channelfd, "test", {'callback': function('s:CloseHandler')})
sleep 10m sleep 10m
call assert_equal('what?', s:unletResponse) call assert_equal('what?', s:unletResponse)
endfunc endfunc

View File

@@ -747,6 +747,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 */
/**/
1341,
/**/ /**/
1340, 1340,
/**/ /**/