NVIM REFERENCE MANUAL by Thiago de Arruda RPC API for Nvim *RPC* *rpc* *msgpack-rpc* Type |gO| to see the table of contents. ============================================================================== 1. Introduction *rpc-intro* The primary way to control Nvim programmatically is the RPC API, which speaks MessagePack-RPC ("msgpack-rpc"), a messaging protocol that uses the MessagePack serialization format: https://github.com/msgpack/msgpack/blob/0b8f5ac/spec.md All kinds of Nvim "clients" use the RPC API: user interfaces (GUIs), remote plugins, scripts like "nvr" (https://github.com/mhinz/neovim-remote), and even `nvim` itself can control other `nvim` instances. By connecting to the RPC API programs can: - Call any API function - Listen for events - Receive remote calls from Nvim The RPC API is like a more powerful version of Vim's `clientserver` feature. ============================================================================== 2. API mapping *rpc-api* The Nvim C |API| is automatically exposed to the RPC API by the build system, which parses headers at src/nvim/api/*. A dispatch function is generated which matches RPC API method names with public API functions, converting/validating arguments and return values back to msgpack. Client libraries (|api-client|s) normally provide wrappers that hide msgpack-rpc details from application developers. The wrappers can be automatically generated by reading bundled API metadata from a compiled Nvim instance. There are three ways to obtain API metadata: 1. Connect to a running Nvim instance and call `nvim_get_api_info` via msgpack-rpc. This is best for clients written in dynamic languages which can define functions at runtime. 2. Start Nvim with the |--api-info| option. Useful for clients written in statically-compiled languages. 3. Use the |api_info()| vimscript function. To get a human-readable list of API functions: > :new|put =map(api_info().functions, 'v:val.name') < To get a formatted dump of the API using python (requires the `pyyaml` and `msgpack-python` packages): > nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' < ============================================================================== 3. Connecting *rpc-connecting* 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. Additionally, rpc channels can be opened by other processes connecting to TCP/IP sockets or named pipes listened to by nvim. 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|. To make Nvim listen on a TCP/IP socket instead, specify |--listen|: > nvim --listen 127.0.0.1:6666 #!/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' nvim = MessagePack::RPC::Client.new(MessagePack::RPC::UNIXTransport.new, ENV['NVIM_LISTEN_ADDRESS']) result = nvim.call(:nvim_command, 'echo "hello world!"') < A better way is to use the Python REPL with the `neovim` package, where API functions can be called interactively: > >>> from neovim import attach >>> nvim = attach('socket', path='[address]') >>> nvim.command('echo "hello world!"') < You can also embed an Nvim instance via |jobstart()|, and communicate using |rpcrequest()| and |rpcnotify()|: > let nvim = jobstart(['nvim', '--embed'], {'rpc': v:true}) echo rpcrequest(nvim, 'nvim_eval', '"Hello " . "world!"') call jobstop(nvim) < ============================================================================== 4. Implementing API clients *rpc-api-client* *api-client* "API clients" wrap the Nvim API to provide idiomatic "SDKs" for their respective platforms (see |dev-jargon|). You can build a new API client for your favorite platform or programming language. Existing API clients are listed here: https://github.com/neovim/neovim/wiki/Related-projects#api-clients The Python client is the reference implementation for API clients. It is always up-to-date with the Nvim API, so its source code and test suite are authoritative references. https://github.com/neovim/python-client API client implementation guidelines ~ - Separate the transport layer from the rest of the library. See |rpc-connecting| for details on how clients can connect to Nvim. - Use a MessagePack library that implements at least version 5 of the MessagePack spec, which supports the `bin` and `ext` types used by Nvim. - 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 being used for implementing a client. 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 multitasking. - Don't assume anything about the order that responses to msgpack-rpc requests will arrive. - Clients should expect msgpack-rpc requests, which need to be handled immediately because 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 (although they should probably be handled immediately anyway). Note: Most of the complexity could be handled by a msgpack-rpc library that supports server to 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 API metadata object ~ API clients exist to hide msgpack-rpc details. The API metadata object contains information that makes this task easier (see also |rpc-types|): - The "version" key contains the Nvim version, API level, and API backwards-compatibility level. - The "functions" key contains a list of metadata objects for individual functions. - Each function metadata object has |rpc-types| 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, e.g. ArrayOf(Buffer) or ArrayOf(Integer, 2). This can be useful to generate even more strongly-typed APIs. - Functions that are considered to be methods that operate on instances of Nvim special types (msgpack EXT) will have the `"method"` attribute set to `true`. The receiver type is the type of the first argument. The method names are prefixed with `nvim_` plus a shortened type name, e.g. `nvim_buf_get_lines` represents the `get_lines` method of a Buffer instance. - Global functions have `"method"` set to `false` and are prefixed with just `nvim_`, e.g. `nvim_get_buffers`. So for an object-oriented language, an API client contains the classes representing Nvim special types, and the methods of each class could be defined by stripping the prefix for the type as defined in the `types` metadata (this will always be the first two "_"-separated parts of the function name). There could also be a singleton Vim class with methods where the `nvim_` prefix is stripped off. ============================================================================== 5. Types *rpc-types* The Nvim C API uses custom types for all functions. |api-types| At the RPC layer, types form two groups: - Basic types that map natively to msgpack (and have a default representation in most msgpack-supported programming languages) - Special Nvim types that map to msgpack EXT with custom type codes. Basic types ~ Nil -> msgpack nil Boolean -> msgpack boolean Integer (signed 64-bit integer) -> msgpack integer Float (IEEE 754 double precision) -> msgpack float String -> msgpack string Array -> msgpack array Dictionary -> msgpack map Note: in function calls, empty Array is accepted for Dictionary parameter. Special types (msgpack EXT) ~ Buffer -> enum value kObjectTypeBuffer Window -> enum value kObjectTypeWindow Tabpage -> enum value kObjectTypeTabpage API functions expecting one of the special EXT types may be passed an integer instead, but not another EXT type. E.g. Buffer may be passed as an integer but not as a Window or Tabpage. The EXT object data is the object id encoded as a msgpack integer: For buffers this is the |bufnr()| and for windows the |window-ID|. For tabpages the id is an internal handle, not the tabpage number. To determine the type codes of the special EXT types, inspect the `types` key of the |api-metadata| at runtime. Example JSON representation: > "types": { "Buffer": { "id": 0, "prefix": "nvim_buf_" }, "Window": { "id": 1, "prefix": "nvim_win_" }, "Tabpage": { "id": 2, "prefix": "nvim_tabpage_" } } Even for statically compiled clients it is good practice to avoid hardcoding the type codes, because a client may be built against one Nvim version but connect to another with different type codes. vim:tw=78:ts=8:ft=help:norl: