summaryrefslogtreecommitdiffstats
path: root/runtime/doc/channel.txt
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-02-15 22:07:32 +0100
committerBram Moolenaar <Bram@vim.org>2016-02-15 22:07:32 +0100
commit38a55639d603823efcf2d2fdf542dbffdeb60b75 (patch)
treecaf0a634ab061f45cb038e1ef179eab58ac6b902 /runtime/doc/channel.txt
parentd807036d10615b960c814ef3890ecad335b57f56 (diff)
Update runtime files.
Diffstat (limited to 'runtime/doc/channel.txt')
-rw-r--r--runtime/doc/channel.txt346
1 files changed, 284 insertions, 62 deletions
diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt
index 2368750515..7938087b1b 100644
--- 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. You can move the cursor a word forward:
["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"})
-
-"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.
-
-"timeout" is the time to wait for a request when blocking, using
-ch_sendexpr(). Again in milliseconds. The default is 2000 (2 seconds).
+ 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.
+
+ 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)
+
+"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 is the whole message as a string.
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 a channel it will be closed.
*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() on a JSON or JS channel. The caller
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)
+
+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)
-NOT IMPLEMENTED YET
+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.
-To start another process: >
- call startjob({command})
+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)
-This does not wait for {command} to exit.
+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"})
+
+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.
+
+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")
- 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
+For more options see |job_stop()|.
vim:tw=78:ts=8:ft=help:norl: