channels: update documentation

This commit is contained in:
Björn Linse 2017-06-10 09:55:06 +02:00
parent 753d0091e8
commit baa981ea21
7 changed files with 301 additions and 82 deletions

168
runtime/doc/channel.txt Normal file
View File

@ -0,0 +1,168 @@
*channel.txt* Nvim
NVIM REFERENCE MANUAL by Thiago de Arruda
Nvim's facilities for async io *channel*
Type <M-]> to see the table of contents.
==============================================================================
1. Introduction *channel-intro*
Channels are nvim's way of communicating with external processes.
There are several ways to open a channel:
1. Through stdin/stdout when `nvim` is started with `--headless`, and a startup
script or --cmd command opens the stdio channel using |stdioopen()|.
2. Through stdin, stdout and stderr of a process spawned by |jobstart()|.
3. Through the PTY master end of a PTY opened with
`jobstart(..., {'pty': v:true})` or |termopen()|.
4. By connecting to a TCP/IP socket or named pipe with |sockconnect()|.
5. By another process connecting to a socket listened to by nvim. This only
supports RPC channels, see |rpc-connecting|.
Channels support multiple modes or protocols. In the most basic
mode of operation, raw bytes are read and written to the channel.
The |rpc| protocol, based on the msgpack-rpc standard, enables nvim and the
process at the other end to send remote calls and events to each other.
Additionally, the builtin |terminal-emulator|, is implemented on top of PTY
channels.
==============================================================================
2. Reading and writing raw bytes *channel-bytes*
By default, channels opened by vimscript functions will operate with raw
bytes. Additionally, for a job channel using rpc, bytes can still be
read over its stderr. Similarily, only bytes can be written to nvim's own stderr.
*channel-callback* *buffered*
*on_stdout* *on_stderr* *on_stdin* *on_data*
A callback function `on_{stream}` will be invoked with data read from the
channel. By default, the callback will be invoked immediately when data is
available, to facilitate interactive communication. The same callback will
then be invoked with empty data, to indicate that the stream reached EOF.
Alternatively the `{stream}_buffered` option can be set to invoke the callback
only when the underlying stream reaches EOF, and will then be passed in
complete output. This is helpful when only the complete output is useful, and
not partial data. Futhermore if `{stream}_buffered` is set but not a callback,
the data is saved in the options dict, with the stream name as key.
- The arguments passed to the callback function are:
0: The channel id
1: the raw data read from the channel, formatted as a |readfile()|-style
list. If EOF occured, a single empty string `['']` will be passed in.
Note that the items in this list do not directly correspond to actual
lines in the output. See |channel-lines|
2: Stream name as a string, like `"stdout"`. This is to allow multiple
on_{event} handlers to be implemented by the same function. The available
events depend on how the channel was opened and in what mode/protocol.
*channel-lines*
Note:
stream event handlers may receive partial (incomplete) lines. For a given
invocation of on_stdout etc, `a:data` is not guaranteed to end
with a newline.
- `abcdefg` may arrive as `['abc']`, `['defg']`.
- `abc\nefg` may arrive as `['abc', '']`, `['efg']` or `['abc']`,
`['','efg']`, or even `['ab']`, `['c','efg']`.
If you only are interested in complete output when the process exits,
use buffered mode. Otherwise, an easy way to deal with this:
initialize a list as `['']`, then append to it as follows: >
let s:chunks = ['']
func! s:on_event(job_id, data, event) dict
let s:chunks[-1] .= a:data[0]
call extend(s:chunks, a:data[1:])
endf
<
Additionally, if the callbacks are Dictionary functions, |self| can be used to
refer to the options dictionary containing the callbacks. |Partial|s can also be
used as callbacks.
Data can be sent to the channel using the |chansend()| function. Here is a
simple example, echoing some data through a cat-process:
>
function! s:OnEvent(id, data, event) dict
let str = join(a:data, "\n")
echomsg str
endfunction
let id = jobstart(['cat'], {'on_stdout': function('s:OnEvent') } )
call chansend(id, "hello!")
<
Here is a example of setting a buffer to the result of grep, but only after
all data has been processed:
>
function! s:OnEvent(id, data, event) dict
call nvim_buf_set_lines(2, 0, -1, v:true, a:data)
endfunction
let id = jobstart(['grep', '^[0-9]'], { 'on_stdout': function('s:OnEvent'),
\ 'stdout_buffered':v:true } )
call chansend(id, "stuff\n10 PRINT \"NVIM\"\nxx")
" no output is received, buffer is empty
call chansend(id, "xx\n20 GOTO 10\nzz\n")
call chanclose(id, 'stdin')
" now buffer has result
<
For additional examples with jobs, see |job-control|.
*channel-pty*
A special case is PTY channels opened by `jobstart(..., {'pty': v:true})` .
No preprocessing of ANSI escape sequences is done, these will be sent raw to
the callback. However, change of PTY size can be signaled to the slave using
|jobresize()|. See also |terminal-emulator|.
==============================================================================
3. Communicating using msgpack-rpc *channel-rpc*
When channels are opened with the `rpc` option set to true, the channel can be
used for remote method calls in both directions, see |msgpack-rpc|. Note that
rpc channels are implicitly trusted and the process at the other end can
invoke any |api| function!
==============================================================================
4. Using the stdio channel *channel-stdio*
When invoked normally, nvim will use stdin and stdout to interact with the
user over the terminal interface (TUI). However when invoked with
`--headless`, the TUI is not started and stdin and stdout can be used as a
channel. To open the stdio channel |stdioopen()| must be called during
|startup|, as there later will be no way of invoking a command. As a
convenience, the stdio channel will always have channel id 1.
Here is an example:
>
func! OnEvent(id, data, event)
if a:data == [""]
quit
end
call chansend(a:id, map(a:data, {i,v -> toupper(v)}))
endfunc
call stdioopen({'on_stdin': 'OnEvent'})
<
Put this in `uppercase.vim` and invoke nvim with
>
nvim --headless --cmd "source uppercase.vim"
<
*--embed*
An common use case is another program embedding nvim and communicating with it
over rpc. Therefore, the option `--embed` exists as a shorthand for
`nvim --headless --cmd "call stdioopen({'rpc': v:true})"`
Nvim's stderr is implicitly open as a write-only bytes channel. It will
always have channel id 2, however to be explicit |v:stderr| can be used.
==============================================================================
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -37,6 +37,8 @@ Functions ~
*file_readable()* Obsolete name for |filereadable()|.
*highlight_exists()* Obsolete name for |hlexists()|.
*highlightID()* Obsolete name for |hlID()|.
*jobclose()* Obsolete name for |chanclose()|
*jobsend()* Obsolete name for |chansend()|
*last_buffer_nr()* Obsolete name for bufnr("$").
Modifiers ~

View File

@ -1818,6 +1818,13 @@ v:shell_error Result of the last shell command. When non-zero, the last
*v:statusmsg* *statusmsg-variable*
v:statusmsg Last given status message. It's allowed to set this variable.
*v:stderr* *stderr-variable*
v:stderr Channel id for stderr. Unlike stdin and stdout (see
|stdioopen()|), stderr is always open for writing. This channel
ID is always 2, but this variable can be used to be explicit.
Example: >
:call chansend(v:stderr, "something bad happened\n")
<
*v:swapname* *swapname-variable*
v:swapname Only valid when executing |SwapExists| autocommands: Name of
the swap file found. Read-only.
@ -1989,6 +1996,8 @@ call({func}, {arglist} [, {dict}])
any call {func} with arguments {arglist}
ceil({expr}) Float round {expr} up
changenr() Number current change number
chanclose({id}[, {stream}]) Number Closes a channel or one of its streams
chansend({id}, {data}) Number Writes {data} to channel
char2nr({expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr}
cindent({lnum}) Number C indent for line {lnum}
clearmatches() none clear all matches
@ -2137,13 +2146,11 @@ isdirectory({directory}) Number |TRUE| if {directory} is a directory
islocked({expr}) Number |TRUE| if {expr} is locked
id({expr}) String identifier of the container
items({dict}) List key-value pairs in {dict}
jobclose({job}[, {stream}]) Number Closes a job stream(s)
jobpid({job}) Number Returns pid of a job.
jobresize({job}, {width}, {height})
Number Resize {job}'s pseudo terminal window
jobsend({job}, {data}) Number Writes {data} to {job}'s stdin
jobpid({id}) Number Returns pid of a job.
jobresize({id}, {width}, {height})
Number Resize pseudo terminal window of a job
jobstart({cmd}[, {opts}]) Number Spawns {cmd} as a job
jobstop({job}) Number Stops a job
jobstop({id}) Number Stops a job
jobwait({ids}[, {timeout}]) Number Wait for a set of jobs
join({list} [, {sep}]) String join {list} items into one String
json_decode({expr}) any Convert {expr} from JSON
@ -2226,7 +2233,6 @@ rpcnotify({channel}, {event}[, {args}...])
Sends an |RPC| notification to {channel}
rpcrequest({channel}, {method}[, {args}...])
Sends an |RPC| request to {channel}
rpcstop({channel}) Closes an |RPC| {channel}
screenattr({row}, {col}) Number attribute at screen position
screenchar({row}, {col}) Number character at screen position
screencol() Number current cursor column
@ -2268,6 +2274,8 @@ shiftwidth() Number effective value of 'shiftwidth'
simplify({filename}) String simplify filename as much as possible
sin({expr}) Float sine of {expr}
sinh({expr}) Float hyperbolic sine of {expr}
sockconnect({mode}, {address} [, {opts}])
Number Connects to socket
sort({list} [, {func} [, {dict}]])
List sort {list}, using {func} to compare
soundfold({word}) String sound-fold {word}
@ -2277,6 +2285,7 @@ spellsuggest({word} [, {max} [, {capital}]])
split({expr} [, {pat} [, {keepempty}]])
List make |List| from {pat} separated {expr}
sqrt({expr}) Float square root of {expr}
stdioopen({dict}) Number open stdio in a headless instance.
str2float({expr}) Float convert String to Float
str2nr({expr} [, {base}]) Number convert String to Number
strchars({expr} [, {skipcc}]) Number character length of the String {expr}
@ -2761,6 +2770,35 @@ changenr() *changenr()*
redo it is the number of the redone change. After undo it is
one less than the number of the undone change.
chanclose({id}[, {stream}]) {Nvim} *chanclose()*
Close a channel or a specific stream associated with it.
For a job, {stream} can be one of "stdin", "stdout",
"stderr" or "rpc" (closes stdin/stdout for a job started
with `"rpc":v:true`) If {stream} is omitted, all streams
are closed. If the channel is a pty, this will then close the
pty master, sending SIGHUP to the job process.
For a socket, there is only one stream, and {stream} should be
ommited.
chansend({id}, {data}) {Nvim} *chansend()*
Send data to channel {id}. For a job, it writes it to the
stdin of the process. For the stdio channel |channel-stdio|,
it writes to Nvim's stdout. Returns the number of bytes
written if the write succeeded, 0 otherwise.
See |channel-bytes| for more information.
{data} may be a string, string convertible, or a list. If
{data} is a list, the items will be joined by newlines; any
newlines in an item will be sent as NUL. To send a final
newline, include a final empty string. Example: >
:call chansend(id, ["abc", "123\n456", ""])
< will send "abc<NL>123<NUL>456<NL>".
chansend() writes raw data, not RPC messages. If the channel
was created with `"rpc":v:true` then the channel expects RPC
messages, use |rpcnotify()| and |rpcrequest()| instead.
char2nr({expr} [, {utf8}]) *char2nr()*
Return number value of the first char in {expr}. Examples: >
char2nr(" ") returns 32
@ -4931,12 +4969,6 @@ items({dict}) *items()*
entry and the value of this entry. The |List| is in arbitrary
order.
jobclose({job}[, {stream}]) *jobclose()*
Close {stream} of |job-id| {job}, where {stream} is one of:
"stdin", "stdout", "stderr", "rpc" (RPC channel of a job
started with `"rpc":v:true`). If {stream} is omitted, all
streams are closed. If the job is a pty job, this will close
the pty master, sending SIGHUP to the job process.
jobpid({job}) *jobpid()*
Return the PID (process id) of |job-id| {job}.
@ -4946,22 +4978,6 @@ jobresize({job}, {width}, {height}) *jobresize()*
columns and {height} rows.
Fails if the job was not started with `"pty":v:true`.
jobsend({job}, {data}) *jobsend()*
Writes to stdin of the process associated with |job-id| {job}.
Returns 1 if the write succeeded, 0 otherwise.
See |job-control|.
{data} may be a string, string convertible, or a list. If
{data} is a list, the items will be joined by newlines; any
newlines in an item will be sent as NUL. To send a final
newline, include a final empty string. Example: >
:call jobsend(j, ["abc", "123\n456", ""])
< will send "abc<NL>123<NUL>456<NL>".
jobsend() writes raw data, not RPC messages. If the job was
created with `"rpc":v:true` then the channel expects RPC
messages, use |rpcnotify()| and |rpcrequest()| instead.
jobstart({cmd}[, {opts}]) *jobstart()*
Spawns {cmd} as a job.
If {cmd} is a List it runs directly (no 'shell').
@ -4971,6 +4987,11 @@ jobstart({cmd}[, {opts}]) *jobstart()*
Returns |job-id| on success, 0 on invalid arguments (or job
table is full), -1 if {cmd}[0] or 'shell' is not executable.
For communication over the job's stdio, it is represented as a
|channel|, and a channel ID is returned on success. Use
|chansend()| (or |rpcnotify()| and |rpcrequest()| if "rpc" option
was used) to send data to stdin and |chanclose()| to close stdio
streams without stopping the job explicitly.
See |job-control| and |rpc|.
@ -4987,7 +5008,9 @@ jobstart({cmd}[, {opts}]) *jobstart()*
*jobstart-options*
{opts} is a dictionary with these keys:
|on_stdout|: stdout event handler (function name or |Funcref|)
stdout_buffered : read stdout in |buffered| mode.
|on_stderr|: stderr event handler (function name or |Funcref|)
stderr_buffered : read stderr in |buffered| mode.
|on_exit| : exit event handler (function name or |Funcref|)
cwd : Working directory of the job; defaults to
|current-directory|.
@ -5009,9 +5032,14 @@ jobstart({cmd}[, {opts}]) *jobstart()*
{opts} is passed as |self| dictionary to the callback; the
caller may set other keys to pass application-specific data.
Returns:
- The channel ID on success
- 0 on invalid arguments
- -1 if {cmd}[0] is not executable.
See |job-control|, |channels|, and |msgpack-rpc| for more information.
jobstop({job}) *jobstop()*
Stop |job-id| {job} by sending SIGTERM to the job process. If
jobstop({id}) *jobstop()*
Stop |job-id| {id} by sending SIGTERM to the job process. If
the process does not terminate after a timeout then SIGKILL
will be sent. When the job terminates its |on_exit| handler
(if any) will be invoked.
@ -6328,13 +6356,11 @@ rpcstart({prog}[, {argv}]) {Nvim} *rpcstart()*
:let id = jobstart(['prog', 'arg1', 'arg2'], {'rpc': v:true})
rpcstop({channel}) {Nvim} *rpcstop()*
Closes an |RPC| {channel}. If the channel is a job
started with |jobstart()| the job is killed.
It is better to use |jobstop()| in this case, or use
|jobclose|(id, "rpc") to only close the channel without
killing the job.
Closes the socket connection if the channel was opened by
connecting to |v:servername|.
Deprecated. This function was used to stop a job with |rpc|
channel, and additionally closed rpc sockets. Instead use
|jobstop()| to stop any job, and |chanclose|(id, "rpc") to close
rpc communication without stopping the job. Use |chanclose|(id)
to close any socket.
screenattr({row}, {col}) *screenattr()*
Like |screenchar()|, but return the attribute. This is a rather
@ -7034,15 +7060,20 @@ sockconnect({mode}, {address}, {opts}) *sockconnect()*
{address} should be the path of a named pipe. If {mode} is
"tcp" then {address} should be of the form "host:port" where
the host should be an ip adderess or host name, and port the
port number. Currently only rpc sockets are supported, so
{opts} must be passed with "rpc" set to |TRUE|.
port number.
Returns a |channel| ID. Close the socket with |chanclose()|.
Use |chansend()| to send data over a bytes socket, and
|rpcrequest()| and |rpcnotify()| to communicate with a RPC
socket.
{opts} is a dictionary with these keys:
rpc : If set, |msgpack-rpc| will be used to communicate
over the socket.
|on_data| : callback invoked when data was read from socket
data_buffered : read data from socket in |buffered| mode.
rpc : If set, |msgpack-rpc| will be used to communicate
over the socket.
Returns:
- The channel ID on success, which is used by
|rpcnotify()| and |rpcrequest()| and |rpcstop()|.
- The channel ID on success (greater than zero)
- 0 on invalid arguments or connection failure.
sort({list} [, {func} [, {dict}]]) *sort()* *E702*
@ -7194,6 +7225,27 @@ sqrt({expr}) *sqrt()*
"nan" may be different, it depends on system libraries.
stdioopen({opts}) *stdioopen()*
In a nvim launched with the |--headless| option, this opens
stdin and stdout as a |channel|. This function can only be
invoked once per instance. See |channel-stdio| for more
information and examples. Note that stderr is not handled by
this function, see |v:stderr|.
Returns a |channel| ID. Close the stdio descriptors with |chanclose()|.
Use |chansend()| to send data to stdout, and
|rpcrequest()| and |rpcnotify()| to communicate over RPC.
{opts} is a dictionary with these keys:
|on_stdin| : callback invoked when stdin is written to.
stdin_buffered : read stdin in |buffered| mode.
rpc : If set, |msgpack-rpc| will be used to communicate
over stdio
Returns:
- The channel ID on success (this is always 1)
- 0 on invalid arguments
str2float({expr}) *str2float()*
Convert String {expr} to a Float. This mostly works the same
as when using a floating point number in an expression, see

View File

@ -20,6 +20,8 @@ When a job starts it is assigned a number, unique for the life of the current
Nvim session. Functions like |jobstart()| return job ids. Functions like
|jobsend()|, |jobstop()|, |rpcnotify()|, and |rpcrequest()| take job ids.
The job's stdio streams are represented as a |channel|. It is possible to send
and recieve raw bytes, or use |msgpack-rpc|.
==============================================================================
Usage *job-control-usage*
@ -40,9 +42,9 @@ Example: >
call append(line('$'), str)
endfunction
let s:callbacks = {
\ 'on_stdout': function('s:JobHandler'),
\ 'on_stderr': function('s:JobHandler'),
\ 'on_exit': function('s:JobHandler')
\ 'on_stdout': function('s:OnEvent'),
\ 'on_stderr': function('s:OnEvent'),
\ 'on_exit': function('s:OnEvent')
\ }
let job1 = jobstart(['bash'], extend({'shell': 'shell 1'}, s:callbacks))
let job2 = jobstart(['bash', '-c', 'for i in {1..10}; do echo hello $i!; sleep 1; done'], extend({'shell': 'shell 2'}, s:callbacks))
@ -59,26 +61,14 @@ Description of what happens:
- `JobHandler()` callback is passed to |jobstart()| to handle various job
events. It displays stdout/stderr data received from the shells.
*on_stdout*
Arguments passed to on_stdout callback:
0: |job-id|
1: List of lines read from the stream. If the last item is not "" (empty
string), then it is an incomplete line that might be continued at the
next on_stdout invocation. See Note 2 below.
2: Event type: "stdout"
*on_stderr*
Arguments passed to on_stderr callback:
0: |job-id|
1: List of lines read from the stream. If the last item is not "" (empty
string), then it is an incomplete line that might be continued at the
next on_stderr invocation. See Note 2 below.
2: Event type: "stderr"
For |on_stdout| and |on_stderr| see |channel-callback|.
*on_exit*
Arguments passed to on_exit callback:
0: |job-id|
1: Exit-code of the process.
2: Event type: "exit"
Note: Buffered stdout/stderr data which has not been flushed by the sender
will not trigger the on_stdout/on_stderr callback (but if the process
ends, the on_exit callback will be invoked).
@ -137,13 +127,19 @@ The above example could be written in this "object-oriented" style: >
let instance = Shell.new('bomb',
\ 'for i in $(seq 9 -1 1); do echo $i 1>&$((i % 2 + 1)); sleep 1; done')
<
To send data to the job's stdin, use |jobsend()|: >
:call jobsend(job1, "ls\n")
:call jobsend(job1, "invalid-command\n")
:call jobsend(job1, "exit\n")
To send data to the job's stdin, use |chansend()|: >
:call chansend(job1, "ls\n")
:call chansend(job1, "invalid-command\n")
:call chansend(job1, "exit\n")
<
A job may be killed with |jobstop()|: >
:call jobstop(job1)
<
A job may be killed at any time with the |jobstop()| function:
>
:call jobstop(job1)
<
Individual streams can be closed without killing the job, see |chanclose()|.
==============================================================================
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -1,4 +1,3 @@
*msgpack_rpc.txt* Nvim
NVIM REFERENCE MANUAL by Thiago de Arruda
@ -61,24 +60,24 @@ To get a formatted dump of the API using python (requires the `pyyaml` and
==============================================================================
3. Connecting *rpc-connecting*
There are several ways to open a msgpack-rpc channel to an Nvim instance:
See |channel-intro|, for various ways to open a channel. Most of the channel
opening functions take an `rpc` key in the options dictionary, to enable rpc.
1. Through stdin/stdout when `nvim` is started with `--embed`. This is how
applications can embed Nvim.
Additionally, rpc channels can be opened by other processes connecting to
TCP/IP sockets or named pipes listened to by nvim.
2. Through stdin/stdout of some other process spawned by |jobstart()|.
Set the "rpc" key to |v:true| in the options dict to use the job's stdin
and stdout as a single msgpack channel that is processed directly by
Nvim. Then it is not possible to process raw data to or from the
process's stdin and stdout. stderr can still be used, though.
An rpc socket is automatically created with each instance. The socket
location is stored in |v:servername|. By default this is a named pipe
with an automatically generated address. See |XXX|.
3. Through the socket automatically created with each instance. The socket
location is stored in |v:servername|.
4. Through a TCP/IP socket. To make Nvim listen on a TCP/IP socket, set the
|$NVIM_LISTEN_ADDRESS| environment variable before starting Nvim: >
To make Nvim listen on a TCP/IP socket instead, set the
|$NVIM_LISTEN_ADDRESS| environment variable before starting Nvim: >
NVIM_LISTEN_ADDRESS=127.0.0.1:6666 nvim
<
<Also, more sockets and named pipes can be listened on using |serverstart()|.
Note that localhost TCP sockets are generally less secure than named pipes,
and can lead to vunerabilities like remote code execution.
Connecting to the socket is the easiest way a programmer can test the API,
which can be done through any msgpack-rpc client library or full-featured
|api-client|. Here's a Ruby script that prints 'hello world!' in the current

View File

@ -117,8 +117,8 @@ variables:
- *b:term_title* The settable title of the terminal, typically displayed in
the window title or tab title of a graphical terminal emulator. Programs
running in the terminal can set this title via an escape sequence.
- *b:terminal_job_id* The nvim job ID of the job running in the terminal. See
|job-control| for more information.
- |'channel'| The nvim channel ID for the underlying PTY.
|chansend()| can be used to send input to the terminal.
- *b:terminal_job_pid* The PID of the top-level process running in the
terminal.

View File

@ -351,6 +351,8 @@ argument.
*--headless*
--headless Do not start the built-in UI.
See |channel-stdio| for how to use stdio for other purposes
instead.
See also |silent-mode|, which does start a (limited) UI.
==============================================================================