mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 11:15:14 -07:00
446 lines
18 KiB
Plaintext
446 lines
18 KiB
Plaintext
*msgpack_rpc.txt* Nvim
|
||
|
||
|
||
NVIM REFERENCE MANUAL by Thiago de Arruda
|
||
|
||
|
||
RPC API for Nvim *RPC* *rpc* *msgpack-rpc*
|
||
|
||
Type <M-]> 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*
|
||
|
||
There are several ways to open a msgpack-rpc channel to an Nvim instance:
|
||
|
||
1. Through stdin/stdout when `nvim` is started with `--embed`. This is how
|
||
applications can embed 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.
|
||
|
||
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: >
|
||
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 full-featured
|
||
|api-client|. Here's a Ruby script that prints 'hello world!' in 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'
|
||
|
||
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 reciever 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, 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 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
|
||
|
||
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.
|
||
|
||
==============================================================================
|
||
6. Remote UIs *rpc-remote-ui*
|
||
|
||
GUIs can be implemented as external processes communicating with Nvim over the
|
||
RPC API. The UI model consists of a terminal-like grid with a single,
|
||
monospace font size. Some elements (UI "widgets") can be drawn separately from
|
||
the grid ("externalized").
|
||
|
||
After connecting to Nvim (usually a spawned, embedded instance) use the
|
||
|nvim_ui_attach| API method to tell Nvim that your program wants to draw the
|
||
Nvim screen on a grid of width × height cells. `options` must be
|
||
a dictionary with these (optional) keys:
|
||
`rgb` Controls what color format to use.
|
||
Set to true (default) to use 24-bit rgb
|
||
colors.
|
||
Set to false to use terminal color codes (at
|
||
most 256 different colors).
|
||
`ext_popupmenu` Externalize the popupmenu. |ui-ext-popupmenu|
|
||
`ext_tabline` Externalize the tabline. |ui-ext-tabline|
|
||
Externalized widgets will not be drawn by
|
||
Nvim; only high-level data will be published
|
||
in new UI event kinds.
|
||
|
||
Nvim will then send msgpack-rpc notifications, with the method name "redraw"
|
||
and a single argument, an array of screen updates (described below). These
|
||
should be processed in order. Preferably the user should only be able to see
|
||
the screen state after all updates in the same "redraw" event are processed
|
||
(not any intermediate state after processing only a part of the array).
|
||
|
||
Future versions of Nvim may add new update kinds and may append new parameters
|
||
to existing update kinds. Clients must be prepared to ignore such extensions
|
||
to be forward-compatible. |api-contract|
|
||
|
||
Screen updates are tuples whose first element is the string name of the update
|
||
kind.
|
||
|
||
["resize", width, height]
|
||
The grid is resized to `width` and `height` cells.
|
||
|
||
["clear"]
|
||
Clear the screen.
|
||
|
||
["eol_clear"]
|
||
Clear from the cursor position to the end of the current line.
|
||
|
||
["cursor_goto", row, col]
|
||
Move the cursor to position (row, col). Currently, the same cursor is
|
||
used to define the position for text insertion and the visible cursor.
|
||
However, only the last cursor position, after processing the entire
|
||
array in the "redraw" event, is intended to be a visible cursor
|
||
position.
|
||
|
||
["update_fg", color]
|
||
["update_bg", color]
|
||
["update_sp", color]
|
||
Set the default foreground, background and special colors
|
||
respectively.
|
||
|
||
["highlight_set", attrs]
|
||
Set the attributes that the next text put on the screen will have.
|
||
`attrs` is a dict with the keys below. Any absent key is reset
|
||
to its default value. Color defaults are set by the `update_fg` etc
|
||
updates. All boolean keys default to false.
|
||
|
||
`foreground`: foreground color.
|
||
`background`: backround color.
|
||
`special`: color to use for underline and undercurl, when present.
|
||
`reverse`: reverse video. Foreground and background colors are
|
||
switched.
|
||
`italic`: italic text.
|
||
`bold`: bold text.
|
||
`underline`: underlined text. The line has `special` color.
|
||
`undercurl`: undercurled text. The curl has `special` color.
|
||
|
||
["put", text]
|
||
The (utf-8 encoded) string `text` is put at the cursor position
|
||
(and the cursor is advanced), with the highlights as set by the
|
||
last `highlight_set` update.
|
||
|
||
["set_scroll_region", top, bot, left, right]
|
||
Define the scroll region used by `scroll` below.
|
||
|
||
["scroll", count]
|
||
Scroll the text in the scroll region. The diagrams below illustrate
|
||
what will happen, depending on the scroll direction. "=" is used to
|
||
represent the SR(scroll region) boundaries and "-" the moved rectangles.
|
||
Note that dst and src share a common region.
|
||
|
||
If count is bigger than 0, move a rectangle in the SR up, this can
|
||
happen while scrolling down.
|
||
>
|
||
+-------------------------+
|
||
| (clipped above SR) | ^
|
||
|=========================| dst_top |
|
||
| dst (still in SR) | |
|
||
+-------------------------+ src_top |
|
||
| src (moved up) and dst | |
|
||
|-------------------------| dst_bot |
|
||
| src (cleared) | |
|
||
+=========================+ src_bot
|
||
<
|
||
If count is less than zero, move a rectangle in the SR down, this can
|
||
happen while scrolling up.
|
||
>
|
||
+=========================+ src_top
|
||
| src (cleared) | |
|
||
|------------------------ | dst_top |
|
||
| src (moved down) and dst| |
|
||
+-------------------------+ src_bot |
|
||
| dst (still in SR) | |
|
||
|=========================| dst_bot |
|
||
| (clipped below SR) | v
|
||
+-------------------------+
|
||
<
|
||
["set_title", title]
|
||
["set_icon", icon]
|
||
Set the window title, and icon (minimized) window title, respectively.
|
||
In windowing systems not distinguishing between the two, "set_icon"
|
||
can be ignored.
|
||
|
||
["mouse_on"]
|
||
["mouse_off"]
|
||
Tells the client whether mouse support, as determined by |'mouse'|
|
||
option, is considered to be active in the current mode. This is mostly
|
||
useful for a terminal frontend, or other situations where nvim mouse
|
||
would conflict with other usages of the mouse. It is safe for a client
|
||
to ignore this and always send mouse events.
|
||
|
||
["busy_on"]
|
||
["busy_off"]
|
||
Nvim started or stopped being busy, and possibly not responsible to user
|
||
input. This could be indicated to the user by hiding the cursor.
|
||
|
||
["suspend"]
|
||
|:suspend| command or |Ctrl-Z| mapping is used. A terminal client (or other
|
||
client where it makes sense) could suspend itself. Other clients can
|
||
safely ignore it.
|
||
|
||
["bell"]
|
||
["visual_bell"]
|
||
Notify the user with an audible or visual bell, respectively.
|
||
|
||
["update_menu"]
|
||
The menu mappings changed.
|
||
|
||
["mode_info_set", cursor_style_enabled, mode_info]
|
||
`cursor_style_enabled` is a boolean indicating if the UI should set the cursor
|
||
style. `mode_info` is a list of mode property maps. The current mode is given
|
||
by the `mode_idx` field of the `mode_change` event.
|
||
|
||
Each mode property map may contain these keys:
|
||
KEY DESCRIPTION ~
|
||
`cursor_shape`: "block", "horizontal", "vertical"
|
||
`cell_percentage`: Cell % occupied by the cursor.
|
||
`blinkwait`, `blinkon`, `blinkoff`: See |cursor-blinking|.
|
||
`hl_id`: Cursor highlight group.
|
||
`hl_lm`: Cursor highlight group if 'langmap' is active.
|
||
`short_name`: Mode code name, see 'guicursor'.
|
||
`name`: Mode descriptive name.
|
||
`mouse_shape`: (To be implemented.)
|
||
|
||
Some keys are missing in some modes.
|
||
|
||
["mode_change", mode, mode_idx]
|
||
The mode changed. The first parameter `mode` is a string representing the
|
||
current mode. `mode_idx` is an index into the array received in the
|
||
`mode_info_set` event. UIs should change the cursor style according to the
|
||
properties specified in the corresponding item. The set of modes reported will
|
||
change in new versions of Nvim, for instance more submodes and temporary
|
||
states might be represented as separate modes.
|
||
|
||
*ui-ext-popupmenu*
|
||
["popupmenu_show", items, selected, row, col]
|
||
When `popupmenu_external` is set to true, nvim will not draw the
|
||
popupmenu on the grid, instead when the popupmenu is to be displayed
|
||
this update is sent. `items` is an array of the items to show, the
|
||
items are themselves arrays of the form [word, kind, menu, info]
|
||
as defined at |complete-items|, except that `word` is replaced by
|
||
`abbr` if present. `selected` is the initially selected item, either a
|
||
zero-based index into the array of items, or -1 if no item is
|
||
selected. `row` and `col` is the anchor position, where the first
|
||
character of the completed word will be.
|
||
|
||
["popupmenu_select", selected]
|
||
An item in the currently displayed popupmenu is selected. `selected`
|
||
is either a zero-based index into the array of items from the last
|
||
`popupmenu_show` event, or -1 if no item is selected.
|
||
|
||
["popupmenu_hide"]
|
||
The popupmenu is hidden.
|
||
|
||
*ui-ext-tabline*
|
||
["tabline_update", curtab, tabs]
|
||
Tabline was updated. UIs should present this data in a custom tabline
|
||
widget.
|
||
curtab: Current Tabpage
|
||
tabs: List of Dicts [{ "tab": Tabpage, "name": String }, ...]
|
||
|
||
==============================================================================
|
||
vim:tw=78:ts=8:noet:ft=help:norl:
|