diff options
-rw-r--r-- | runtime/doc/eval.txt | 34 | ||||
-rw-r--r-- | runtime/doc/msgpack_rpc.txt | 245 |
2 files changed, 278 insertions, 1 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index cdad6a9d7e..51e09596fc 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1934,6 +1934,12 @@ repeat( {expr}, {count}) String repeat {expr} {count} times resolve( {filename}) String get filename a shortcut points to reverse( {list}) List reverse {list} in-place round( {expr}) Float round off {expr} +rpcnotify({channel}, {event}[, {args}...]) + Sends a |msgpack-rpc| notification to {channel} +rpcrequest({channel}, {method}[, {args}...]) + Sends a |msgpack-rpc| request to {channel} +rpcstart({prog}[, {argv}]) Spawns {prog} and opens a |msgpack-rpc| channel +rpcstop({channel}) Closes a |msgpack-rpc| {channel} screenattr( {row}, {col}) Number attribute at screen position screenchar( {row}, {col}) Number character at screen position screencol() Number current cursor column @@ -5043,6 +5049,32 @@ round({expr}) *round()* < -5.0 {only available when compiled with the |+float| feature} +rpcnotify({channel}, {event}[, {args}...]) {Nvim} *rpcnotify()* + Sends {event} to {channel} via |msgpack-rpc| and returns + immediately. If {channel} is 0, the event is broadcast to all + channels. Example: > + :au VimLeave call rpcnotify(0, "leaving") + +rpcrequest({channel}, {method}[, {args}...]) {Nvim} *rpcrequest()* + Sends a request to {channel} to invoke {method} via + |msgpack-rpc| and blocks until a response is received. + Example: > + :let result = rpcrequest(rpc_chan, "func", 1, 2, 3) + +rpcstart({prog}[, {argv}]) {Nvim} *rpcstart()* + Spawns {prog} as a job(optionally passing the {argv} list), + and open a |msgpack-rpc| channel with the spawned process + stdin/stdout. Returns the channel id, which is used by + |rpcrequest()|, |rpcnotify()| and |rpcstop()| + It expects the rpc channel id as argument. Example: > + :let rpc_chan = rpcstart('prog', ['arg1', 'arg2']) + +rpcstop({channel}) {Nvim} *rpcstop()* + Closes a |msgpack-rpc| channel, possibly created via + |rpcspawn()| (Though it will also close channels created by + connections to |NVIM_LISTEN_ADDRESS|). It accepts the rpc + channel id as only argument. + screenattr(row, col) *screenattr()* Like screenchar(), but return the attribute. This is a rather arbitrary number that can only be used to compare to the @@ -8874,4 +8906,4 @@ This is not allowed when the textlock is active: - etc. - vim:tw=78:ts=8:ft=help:norl: + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt new file mode 100644 index 0000000000..5e926b7318 --- /dev/null +++ b/runtime/doc/msgpack_rpc.txt @@ -0,0 +1,245 @@ +*msgpack_rpc.txt* For Nvim. {Nvim} + + + NVIM REFERENCE MANUAL by Thiago de Arruda + + +The Msgpack-RPC Interface to Nvim *msgpack-rpc* + +1. Introduction |msgpack-rpc-intro| +2. API |msgpack-rpc-api| +3. Connecting |msgpack-rpc-connecting| +4. Clients |msgpack-rpc-clients| +5. Types |msgpack-rpc-types| +6. Wrapping methods |msgpack-rpc-wrap-methods| +7. Vimscript functions |msgpack-rpc-vim-functions| + +============================================================================== +1. Introduction *msgpack-rpc-intro* + +The primary means of controlling a running nvim instance is through +MessagePack-RPC, a messaging protocol that uses the MessagePack serialization +format: https://github.com/msgpack/msgpack/blob/7498cf3/spec.md. +From now on, we'll be referring to the protocol as msgpack-rpc. + +At this point, only plugins use msgpack-rpc, but eventually even user +interaction will be achieved through the protocol, since user interfaces will +be separate programs that control a headless nvim instance. + +This is what can be achieved by connecting to the msgpack-rpc interface: + +- Call any nvim API function +- Listen for nvim events +- Receive remote calls from nvim + +Nvim's msgpack-rpc interface can be seen as a more powerful version of Vim's +`clientserver` feature. + +============================================================================== +2. API *msgpack-rpc-api* + +Nvim C API is automatically exposed to the msgpack-rpc interface by the +build system, which parses headers at src/nvim/api from the project root. A +dispatch function is generated, and it will match msgpack-rpc method names +with non-static API functions, converting/validating arguments and return +values back to msgpack. + +Client libraries will normally provide wrappers that hide msgpack-rpc details +from programmers, which can be automatically generated by reading bundled api +metadata from a compiled nvim instance. + +There are two ways to obtain API metadata: + +- By connecting to a running nvim instance and calling `vim_get_api_metadata` + via msgpack-rpc. This is the preferred way for clients written in + dynamically-typed languages, which can define functions at runtime. +- Through the `--api-info` command-line option, which makes nvim to dump a + msgpack blob containing the metadata to stdout and exit. This is preferred + when writing clients for statically-typed languages, which require a + separate compilation step. + +Here's a simple way to get human-readable description of the API(requires +python and the pyyaml/msgpack-python pip packages): +> + nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' > api.yaml + +============================================================================== +3. Connecting *msgpack-rpc-connecting* + + +There are four ways to open msgpack-rpc streams to nvim: + +1. Through nvim's stdin/stdout when started with the `--embed` option. This + how other programs can embed nvim. + +2. Through stdin/stdout of a program spawned by the |rpcstart()| function. + +3. Through the socket automatically created with every instance. To find out + the socket location(which is random by default) from a running nvim + instance, one can inspect the *$NVIM_LISTEN_ADDRESS* environment variable + like this: +> + :echo $NVIM_LISTEN_ADDRESS +< +4. Through a tcp/ip socket. To make nvim listen on a tcp/ip socket, you need + to set the NVIM_LISTEN_ADDRESS environment variable before starting, like + this: +> + NVIM_LISTEN_ADDRESS=127.0.0.1:6666 nvim +< +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 a fully-featured +Nvim client(which we'll see below). Here's a ruby script that will print the +string 'hello world!' on the current nvim instance: +> + #!/usr/bin/env ruby + # Requires msgpack-rpc: gem install msgpack-rpc + # + # To run this script, execute it from a running nvim instance(notice the + # trailing '&' which is required since nvim won't process events while + # running a blocking command): + # + # :!./hello.rb & + # + # Or from another shell by setting NVIM_LISTEN_ADDRESS: + # $ NVIM_LISTEN_ADDRESS=[address] ./hello.rb + + require 'msgpack/rpc' + require 'msgpack/rpc/transport/unix' + + vim = MessagePack::RPC::Client.new(MessagePack::RPC::UNIXTransport.new, ENV['NVIM_LISTEN_ADDRESS']) + result = vim.call(:vim_command, 'echo "hello world!"') +< +A better way is to use the python REPL with the `neovim` package, where API +functions can be called interactively: +> + >>> import neovim; vim = neovim.connect('[address]') + >>> vim.command('echo "hello world!"') +< +============================================================================== +4. Implementing new clients *msgpack-rpc-clients* + +Nvim is still alpha and there's no in-depth documentation explaining how to +properly implement a client library. The python client(neovim pip package) +will be always up-to-date with the latest API changes, so it's source code is +best documentation currently available. There are some guidelines however: + +- Separate the transport layer from the rest of the library(See + |msgpack-rpc-connecting| for details of how a client can connect to nvim). +- Use a msgpack library that implements the spec version 5, Nvim uses the + BIN/EXT types. +- Read api metadata in order to create client-side wrappers for all + msgpack-rpc methods. +- Use a single-threaded event loop library/pattern. +- Use a fiber/coroutine library for the language you are implementing a client + for. These greatly simplify concurrency and allow the library to expose a + blocking API on top of a non-blocking event loop without the complexity + that comes with preemptive multi-tasking. +- Don't assume anything about the order that responses to msgpack-rpc requests + will arrive. +- Clients should expect to receive msgpack-rpc requests, which need to be + handled immediately since Nvim is blocked while waiting for the client + response. +- Clients should expect to receive msgpack-rpc notifications, but these don't + need to be handled immediately because they won't block Nvim(Though you + probably want to handle them immediately anyway). + + +Most of the complexity could be handled by a msgpack-rpc library that supports +server->client requests and notifications, but it's not clear if this is part +of the msgpack-rpc spec. At least the ruby msgpack-rpc library does not seem +to support it: +https://github.com/msgpack-rpc/msgpack-rpc-ruby/blob/master/lib/msgpack/rpc/transport/tcp.rb#L150-L158 + +============================================================================== +5. Types *msgpack-rpc-types* + +Nvim's C API uses custom types for all functions(some are just typedefs +around C99 standard types). The types can be split into two groups: + +- Basic types that map natively to msgpack(and probably have a default + representation in msgpack-supported programming languages) +- Special Nvim types that map to msgpack ext with custom type codes. + +Basic type mapping: + +Nil -> msgpack nil +Boolean -> msgpack boolean +Integer (signed 64-bit integer) -> msgpack integer +Float (IEEE 754 double precision) -> msgpack float +String -> msgpack binary +Array -> msgpack array +Dictionary -> msgpack map + +Special Nvim types that use msgpack ext: + +Buffer -> enum value kObjectTypeBuffer +Window -> enum value kObjectTypeWindow +Tabpage -> enum value kObjectTypeTabpage + +The most reliable way of determining the type codes for the special nvim types +is at runtime by inspecting the `types` key of metadata dictionary returned by +`vim_get_api_metadata` method. Here's an example json representation of the +`types` object: +> + "types": { + "Buffer": { + "id": 0 + }, + "Window": { + "id": 1 + }, + "Tabpage": { + "id": 2 + } + } +< +Even for statically compiled clients, it's a good practice to avoid hardcoding +the type codes, because a client may build for a Nvim version and connect to +another that may have different type codes. + +============================================================================== +6. Wrapping methods *msgpack-rpc-wrap-methods* + +As mentioned before, clients should provide an API that hides msgpack-rpc +details from programmers, and the API metadata object contains information +that makes this task easier: + +- The "functions" key contains a list of metadata objects for individual + functions. +- Each function metadata object has type information about the return value + and parameters. These can be used for generating strongly-typed APIs in + static languages. +- Container types may be decorated with type/size constraints, eg: + ArrayOf(Buffer) or ArrayOf(Integer, 2). This can be useful to generate even + more strongly-typed APIs. +- Methods that operate instances of Nvim's types are prefixed with the type + name in lower case. Eg: `buffer_get_line` represents the `get_line` method + of a Buffer instance. +- Global methods are prefixed with `vim`. Eg: `vim_list_buffers` + +So, for a object-oriented language, a client library would have the classes +that represent Nvim's types, and the methods of each class could be defined +by inspecting the method name prefix. There could also be a singleton Vim +class with methods mapped to functions prefixed with `vim_` + +============================================================================== +7. Vimscript functions *msgpack-rpc-vim-functions* + +Four functions related to msgpack-rpc are available to vimscript: + +- |rpcstart()|: Similarly to |jobstart()|, this will spawn a co-process with + it's standard handles connected to Nvim, the difference is that it's not + possible to process raw data to/from the process stdin/stdout/stderr(Since + the job's stdin/stdout combo are used as a msgpack channgel that is + processed directly by Nvim C code). +- |rpcstop()|: Same as |jobstop()|, but operates on handles returned by + |rpcstart().| +- |rpcrequest()|: Sends a msgpack-rpc request to the process. +- |rpcnotify()|: Sends a msgpack-rpc notification to the process. + +The last two functions may also be used with channels created from +connections to |NVIM_LISTEN_ADDRESS|. + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: |