Mercurial > vim
diff runtime/doc/channel.txt @ 8061:abd64cf67bcf
commit https://github.com/vim/vim/commit/38a55639d603823efcf2d2fdf542dbffdeb60b75
Author: Bram Moolenaar <Bram@vim.org>
Date: Mon Feb 15 22:07:32 2016 +0100
Update runtime files.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Mon, 15 Feb 2016 22:45:05 +0100 |
parents | 78106b0f2c56 |
children | 18a3f0f05244 |
line wrap: on
line diff
--- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -1,4 +1,4 @@ -*channel.txt* For Vim version 7.4. Last change: 2016 Feb 07 +*channel.txt* For Vim version 7.4. Last change: 2016 Feb 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -9,36 +9,72 @@ DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT Vim uses channels to communicate with other processes. -A channel uses a socket. *socket-interface* +A channel uses a socket or pipes *socket-interface* +Jobs can be used to start processes and communicate with them. Vim current supports up to 10 simultaneous channels. The Netbeans interface also uses a channel. |netbeans| -1. Demo |channel-demo| -2. Opening a channel |channel-open| -3. Using a JSON or JS channel |channel-use| -4. Vim commands |channel-commands| -5. Using a raw channel |channel-use| -6. Job control |job-control| +1. Overview |job-channel-overview| +2. Channel demo |channel-demo| +3. Opening a channel |channel-open| +4. Using a JSON or JS channel |channel-use| +5. Channel commands |channel-commands| +6. Using a RAW or NL channel |channel-raw| +7. More channel functions |channel-more| +8. Starting a job with a channel |job-start| +9. Starting a job without a channel |job-start-nochannel| +10. Job options |job-options| +11. Controlling a job |job-control| {Vi does not have any of these features} -{only available when compiled with the |+channel| feature} +{only when compiled with the |+channel| feature for channel stuff} +{only when compiled with the |+job| feature for job stuff} ============================================================================== -1. Demo *channel-demo* +1. Overview *job-channel-overview* + +There are four main types of jobs: +1. A deamon, serving several Vim instances. + Vim connects to it with a socket. +2. One job working with one Vim instance, asynchronously. + Uses a socket or pipes. +3. A job performing some work for a short time, asynchronously. + Uses a socket or pipes. +4. Running a filter, synchronously. + Uses pipes. + +For when using sockets See |job-start|, |job-may-start| and |channel-open|. +For 2 and 3, one or more jobs using pipes, see |job-start|. +For 4 use the ":{range}!cmd" command, see |filter|. + +Over the socket and pipes these protocols are available: +RAW nothing known, Vim cannot tell where a message ends +NL every message ends in a NL (newline) character +JSON JSON encoding |json_encode()| +JS JavaScript style JSON-like encoding |js_encode()| + +Common combination are: +- Using a job connected through pipes in NL mode. E.g., to run a style + checker and receive errors and warnings. +- Using a deamon, connecting over a socket in JSON mode. E.g. to lookup + crosss-refrences in a database. + +============================================================================== +2. Channel demo *channel-demo* This requires Python. The demo program can be found in $VIMRUNTIME/tools/demoserver.py Run it in one terminal. We will call this T1. Run Vim in another terminal. Connect to the demo server with: > - let handle = ch_open('localhost:8765') + let channel = ch_open('localhost:8765') In T1 you should see: === socket opened === ~ You can now send a message to the server: > - echo ch_sendexpr(handle, 'hello!') + echo ch_sendexpr(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: @@ -54,47 +90,63 @@ And you should see the message in Vim. Y ["normal","w"] ~ To handle asynchronous communication a callback needs to be used: > - func MyHandler(handle, msg) + func MyHandler(channel, msg) echo "from the handler: " . a:msg endfunc - call ch_sendexpr(handle, 'hello!', "MyHandler") + call ch_sendexpr(channel, 'hello!', "MyHandler") +Vim will not wait for a response. Now the server can send the response later +and MyHandler will be invoked. Instead of giving a callback with every send call, it can also be specified when opening the channel: > - call ch_close(handle) - let handle = ch_open('localhost:8765', {'callback': "MyHandler"}) - call ch_sendexpr(handle, 'hello!', 0) + call ch_close(channel) + let channel = ch_open('localhost:8765', {'callback': "MyHandler"}) + call ch_sendexpr(channel, 'hello!', 0) ============================================================================== -2. Opening a channel *channel-open* +3. Opening a channel *channel-open* To open a channel: > - let handle = ch_open({address} [, {argdict}]) + let channel = ch_open({address} [, {options}]) + +Use |ch_status()| to see if the channel could be opened. {address} has the form "hostname:port". E.g., "localhost:8765". -{argdict} is a dictionary with optional entries: +{options} is a dictionary with optional entries: "mode" can be: *channel-mode* "json" - Use JSON, see below; most convenient way. Default. "js" - Use JavaScript encoding, more efficient than JSON. + "nl" - Use messages that end in a NL character "raw" - Use raw messages *channel-callback* -"callback" is a function that is called when a message is received that is not -handled otherwise. It gets two arguments: the channel handle and the received -message. Example: > - func Handle(handle, msg) +"callback" A function that is called when a message is received that is + not handled otherwise. It gets two arguments: the channel + handle and the received message. Example: > + func Handle(channel, msg) echo 'Received: ' . a:msg endfunc - let handle = ch_open("localhost:8765", {"callback": "Handle"}) + let channel = ch_open("localhost:8765", {"callback": "Handle"}) +< + TODO: +"err-cb" A function like "callback" but used for stderr. Only for when + the channel uses pipes. -"waittime" is the time to wait for the connection to be made in milliseconds. -The default is zero, don't wait, which is useful if the server is supposed to -be running already. A negative number waits forever. + TODO: +"close-cb" A function that is called when the channel gets closed, other + than by calling ch_close(). It should be defined like this: > + func MyCloseHandler(channel) -"timeout" is the time to wait for a request when blocking, using -ch_sendexpr(). Again in milliseconds. The default is 2000 (2 seconds). +"waittime" The time to wait for the connection to be made in + milliseconds. The default is zero, don't wait, which is + useful if the server is supposed to be running already. A + negative number waits forever. + +"timeout" The time to wait for a request when blocking, using + ch_sendexpr(). Again in milliseconds. The default is 2000 (2 + seconds). When "mode" is "json" or "js" the "msg" argument is the body of the received message, converted to Vim types. @@ -103,18 +155,26 @@ When "mode" is "raw" the "msg" argument When "mode" is "json" or "js" the "callback" is optional. When omitted it is only possible to receive a message after sending one. -The handler can be added or changed later: > - call ch_setcallback(handle, {callback}) +TODO: +To change the channel options after opening it use ch_setoptions(). The +arguments are similar to what is passed to ch_open(), but "waittime" cannot be +given, since that only applies to opening the channel. + +The handler can be added or changed: > + call ch_setoptions(channel, {'callback': callback}) When "callback" is empty (zero or an empty string) the handler is removed. -NOT IMPLEMENTED YET -The timeout can be changed later: > - call ch_settimeout(handle, {msec}) -NOT IMPLEMENTED YET +The timeout can be changed: > + call ch_setoptions(channel, {'timeout': msec}) +< *E906* Once done with the channel, disconnect it like this: > - call ch_close(handle) + call ch_close(channel) +When a socket is used this will close the socket for both directions. When +pipes are used (stdin/stdout/stderr) they are all closed. This might not be +what you want! Stopping the job with job_stop() might be better. +TODO: Currently up to 10 channels can be in use at the same time. *E897* When the channel can't be opened you will get an error message. There is a @@ -126,21 +186,26 @@ If there is an error reading or writing *E896* *E630* *E631* ============================================================================== -3. 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: > - let response = ch_sendexpr(handle, {expr}) + let response = ch_sendexpr(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 |jsencode()| for the difference. +JavaScript encoding. See |js_encode()| for the difference. To send a message, without handling a response: > - call ch_sendexpr(handle, {expr}, 0) + call ch_sendexpr(channel, {expr}, 0) To send a message and letting the response handled by a specific function, asynchronously: > - call ch_sendexpr(handle, {expr}, {callback}) + call ch_sendexpr(channel, {expr}, {callback}) + +Vim will match the response with the request using the message ID. Once the +response is received the callback will be invoked. Further responses with the +same ID will be ignored. If your server sends back multiple responses you +need to send them with ID zero, they will be passed to the channel callback. The {expr} is converted to JSON and wrapped in an array. An example of the message that the receiver will get when {expr} is the string "hello": @@ -175,9 +240,7 @@ It is also possible to use ch_sendraw() is then completely responsible for correct encoding and decoding. ============================================================================== -4. Vim commands *channel-commands* - -PARTLY IMPLEMENTED: only "ex" and "normal" work +5. Channel commands *channel-commands* 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. @@ -251,43 +314,202 @@ Example: ["expr","setline('$', ['one', 'two', 'three'])"] ~ ============================================================================== -5. Using a raw channel *channel-raw* +6. Using a RAW or NL channel *channel-raw* If {mode} is "raw" then a message can be send like this: > - let response = ch_sendraw(handle, {string}) + let response = ch_sendraw(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 -message you need to take care of it yourself. +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. + +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 +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. +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(handle, {string}, 0) + call ch_sendraw(channel, {string}, 0) 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(handle, {string}, {callback}) + call ch_sendraw(channel, {string}, {callback}) -This {string} can also be JSON, use |jsonencode()| to create it and -|jsondecode()| to handle a received JSON message. +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. ============================================================================== -6. Job control *job-control* +7. More channel functions *channel-more* + +To obtain the status of a channel: ch_status(channel). The possible results +are: + "fail" Failed to open the channel. + "open" The channel can be used. + "closed" The channel was closed. + +TODO: +To objain the job associated with a channel: ch_getjob(channel) -NOT IMPLEMENTED YET +TODO: +To read one message from a channel: > + let output = ch_read(channel) +This uses the channel timeout. To read without a timeout, just get any +message that is available: > + let output = ch_read(channel, 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. + +To read all output from a RAW or NL channel that is available: > + let output = ch_readall(channel) +To read the error output: > + let output = ch_readall(channel, "err") +TODO: use channel timeout, no timeout or specify timeout? + +============================================================================== +8. Starting a job with a channel *job-start* *job* + +To start a job and open a channel for stdin/stdout/stderr: > + let job = job_start(command, {options}) + +You can get the channel with: > + let channel = job_getchannel(job) -To start another process: > - call startjob({command}) +The channel will use NL mode. If you want another mode it's best to specify +this in {options}. When changing the mode later some text may have already +been received and not parsed correctly. + +If the command produces a line of output that you want to deal with, specify +a handler for stdout: > + let job = job_start(command, {"out-cb": "MyHandler"}) +The function will be called with the channel and a message. You would define +it like this: > + func MyHandler(channel, msg) + +Without the handler you need to read the output with ch_read(). + +The handler defined for "out-cb" will also receive stderr. If you want to +handle that separately, add an "err-cb" handler: > + let job = job_start(command, {"out-cb": "MyHandler", + \ "err-cb": "ErrHandler"}) -This does not wait for {command} to exit. +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(). + +There are several options you can use, see |job-options|. + +TODO: +To run a job and read its output once it is done: > + + let job = job_start({command}, {'exit-cb': 'MyHandler'}) + func MyHandler(job, status) + let channel = job_getchannel() + let output = ch_readall(channel) + " parse output + endfunc + +============================================================================== +9. Starting a job without a channel *job-start-nochannel* + +To start another process without creating a channel: > + let job = job_start(command, {"in-io": "null", "out-io": "null"}) + +This starts {command} in the background, Vim does not wait for it to finish. TODO: +When Vim sees that neither stdin, stdout or stderr are connected, no channel +will be created. Often you will want to include redirection in the command to +avoid it getting stuck. - let handle = startjob({command}, 's') # uses stdin/stdout - let handle = startjob({command}, '', {address}) # uses socket - let handle = startjob({command}, 'd', {address}) # start if connect fails +There are several options you can use, see |job-options|. + +TODO: *job-may-start* +To start a job only when connecting to an address does not work use +job_maystart('command', {address}, {options}), For Example: > + let job = job_maystart(command, address, {"waittime": 1000}) + let channel = job_gethandle(job) + +This comes down to: > + let channel = ch_open(address, {"waittime": 0}) + if ch_status(channel) == "fail" + let job = job_start(command) + let channel = ch_open(address, {"waittime": 1000}) + call job_sethandle(channel) + endif +Note that the specified waittime applies to when the job has been started. +This gives the job some time to make the port available. + +============================================================================== +10. Job options *job-options* + +The {options} argument in job_start() is a dictionary. All entries are +optional. The same options can be used with job_setoptions(job, {options}). + +TODO: *job-out-cb* +"out-cb": handler Callback for when there is something to read on + stdout. +TODO: *job-err-cb* +"err-cb": handler Callback for when there is something to read on + stderr. Defaults to the same callback as "out-cb". +TODO: *job-close-cb* +"close-cb": handler Callback for when the channel is closed. Same as + "close-cb" on ch_open(). +TODO: *job-exit-cb* +"exit-cb": handler Callback for when the job ends. The arguments are the + job and the exit status. +TODO: *job-killonexit* +"killonexit": 1 Stop the job when Vim exits. +"killonexit": 0 Do not stop the job when Vim exits. + The default is 1. +TODO: *job-term* +"term": "open" Start a terminal and connect the job + stdin/stdout/stderr to it. + +TODO: *job-in-io* +"in-io": "null" disconnect stdin +"in-io": "pipe" stdin is connected to the channel (default) +"in-io": "file" stdin reads from a file +"in-file": "/path/file" the file to read from + +TODO: *job-out-io* +"out-io": "null" disconnect stdout +"out-io": "pipe" stdout is connected to the channel (default) +"out-io": "file" stdout writes to a file +"out-file": "/path/file" the file to write to +"out-io": "buffer" stdout appends to a buffer +"out-buffer": "name" buffer to append to + +TODO: *job-err-io* +"err-io": "out" same as stdout (default) +"err-io": "null" disconnect stderr +"err-io": "pipe" stderr is connected to the channel +"err-io": "file" stderr writes to a file +"err-file": "/path/file" the file to write to +"err-io": "buffer" stderr appends to a buffer +"err-buffer": "name" buffer to append to + +TODO: more options + + +============================================================================== +11. Controlling a job *job-control* + +To get the status of a job: > + echo job_status(job) + +To make a job stop running: > + job_stop(job) + +This is the normal way to end a job. On Unix it sends a SIGTERM to the job. +It is possible to use other ways to stop the job, or even send arbitrary +signals. E.g. to force a job to stop, "kill it": > + job_stop(job, "kill") + +For more options see |job_stop()|. vim:tw=78:ts=8:ft=help:norl: