mirror of
https://github.com/neovim/neovim.git
synced 2024-12-29 14:41:06 -07:00
393 lines
17 KiB
Plaintext
393 lines
17 KiB
Plaintext
|
|
|
|
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
|
|
<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
|
|
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 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, 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. Buffer Updates *buffer-updates* *rpc-buffer-updates*
|
|
|
|
A dedicated API has been created to allow co-processes to be notified when a
|
|
buffer is changed in any way. It is difficult and error-prone to try and do
|
|
this with autocommands such as |TextChanged|.
|
|
|
|
*buffer-updates-events*
|
|
BufferUpdates Events~
|
|
|
|
The co-process will start receiving the following notification events:
|
|
|
|
nvim_buf_updates_start[{buf}, {changedtick}, {linedata}, {more}] *nvim_buf_updates_start*
|
|
|
|
Neovim will send at least one of these notifications to confirm that
|
|
buffer updates are registered for this plugin, and possibly send the buffer's
|
|
contents. If the buffer is very large, neovim might send the contents through
|
|
in multiple events to avoid loading the entire buffer's contents into
|
|
memory at once.
|
|
|
|
{buf} is an API handle for the buffer.
|
|
|
|
{changedtick} is the value of |b:changedtick| for the buffer. If you
|
|
send an API command back to neovim you can check the value of
|
|
|b:changedtick| as part of your request to ensure that no other
|
|
changes have been made.
|
|
|
|
{linedata} is a list of strings containing the buffer's contents. If this
|
|
list contains 100 strings, then they represent lines 1-100 of the buffer.
|
|
Newline characters are not included in the strings, so empty lines will be
|
|
given as empty strings. If you receive another |nvim_buf_updates_start|
|
|
notification with another {linedata} list, then these lines represent the
|
|
next N lines of the buffer. I.e., a second notification with another list of
|
|
100 strings will represent lines 101-200 of the buffer. If you send the
|
|
|nvim_buf_updates_start| request with its argument set to `"False"`, this
|
|
will be empty.
|
|
|
|
{linedata} will always have at least 1 item, but the maximum length is
|
|
determined by neovim and not guaranteed to be any particular size. Also the
|
|
number of {linedata} items may vary between notifications, so your plugin
|
|
must be prepared to receive the line data in whatever size lists neovim
|
|
decides to split it into.
|
|
|
|
{more} is a boolean which tells you whether or not to expect more
|
|
|nvim_buf_updates_start| notifications. When {more} is false, you can be certain
|
|
that you now have the entire buffer's contents.
|
|
|
|
nvim_buf_update[{buf}, {changedtick}, {firstline}, {lastline}, {linedata}] *nvim_buf_update*
|
|
|
|
Indicates that the lines between {firstline} and {lastline} (end-exclusive,
|
|
zero-indexed) have been replaced with the new line data contained in the
|
|
{linedata} list. All buffer changes (even adding single characters) will be
|
|
transmitted as whole-line changes.
|
|
|
|
{buf} is an API handle for the buffer.
|
|
|
|
{changedtick} is the value of |b:changedtick| for the buffer. If you send an
|
|
API command back to neovim you can check the value of |b:changedtick| as
|
|
part of your request to ensure that no other changes have been made.
|
|
|
|
{firstline} is the integer line number of the first line that was replaced.
|
|
Note that {firstline} is zero-indexed, so if line `1` was replaced then
|
|
{firstline} will be `0` instead of `1`. {firstline} is guaranteed to always
|
|
be less than or equal to the number of lines that were in the buffer before
|
|
the lines were replaced.
|
|
|
|
{lastline} is the integer line number of the first line that was not replaced
|
|
(i.e. the range {firstline}, {lastline} is end-exclusive). Note that
|
|
{lastline} is zero-indexed, so if line numbers 2 to 5 were replaced, this
|
|
will be `5` instead of `6`. {lastline} is guaranteed to always be less than
|
|
or equal to the number of lines that were in the buffer before the lines were
|
|
replaced.
|
|
|
|
{linedata} is a list of strings containing the contents of the new buffer
|
|
lines. Newline characters are not included in the strings, so empty lines
|
|
will be given as empty strings.
|
|
|
|
Note: sometimes {changedtick} will be |v:null|, which means that the buffer
|
|
text *looks* like it has changed, but actually hasn't. In this case the lines
|
|
in {linedata} contain the modified text that is shown to the user, but
|
|
doesn't reflect the actual buffer contents. Currently this behaviour is
|
|
only used for the 'inccommand' option.
|
|
|
|
nvim_buf_update_tick[{buf}, {changedtick}] *nvim_buf_update_tick*
|
|
|
|
Indicates that |b:changedtick| was incremented for the buffer {buf}, but no
|
|
text was changed. This is currently only used by undo/redo.
|
|
|
|
{buf} is an API handle for the buffer.
|
|
|
|
{changedtick} is the new value of |b:changedtick| for that buffer.
|
|
|
|
nvim_buf_updates_end[{buf}] *nvim_buf_updates_end*
|
|
|
|
{buf} is an API handle for the buffer.
|
|
|
|
Indicates that buffer updates for the nominated buffer have been disabled,
|
|
either by calling |nvim_buf_detach| or because the buffer was unloaded
|
|
(see |buffer-updates-limitations| for more information).
|
|
|
|
*buffer-updates-limitations*
|
|
Limitations~
|
|
|
|
Note that any of the following actions will also turn off buffer updates because
|
|
the buffer contents are unloaded from memory:
|
|
|
|
- Closing all a buffer's windows (unless 'hidden' is enabled).
|
|
- Using |:edit| to reload the buffer
|
|
- reloading the buffer after it is changed from outside neovim.
|
|
|
|
*buffer-updates-examples*
|
|
Examples~
|
|
|
|
If buffer updates are activated a new empty buffer (and sending the buffer's
|
|
content on the initial notification has been requested), the following
|
|
|nvim_buf_updates_start| event will be sent: >
|
|
|
|
nvim_buf_updates_start[{buf}, [""], v:false]
|
|
|
|
If the user adds 2 new lines to the start of a buffer, the following event
|
|
would be generated: >
|
|
|
|
nvim_buf_update[{buf}, 0, 0, ["line1", "line2"]]
|
|
|
|
If the puts the cursor on a line containing the text `"Hello world"` and adds
|
|
a `!` character to the end using insert mode, the following event would be
|
|
generated: >
|
|
|
|
nvim_buf_update[{buf}, {linenr}, {linenr} + 1, ["Hello world!"]]
|
|
|
|
If the user moves their cursor to line 3 of a buffer and deletes 20 lines
|
|
using `20dd`, the following event will be generated: >
|
|
|
|
nvim_buf_update[{buf}, 2, 20, []]
|
|
|
|
If the user selects lines 3-5 of a buffer using |linewise-visual| mode and
|
|
then presses `p` to paste in a new block of 6 lines, then the following event
|
|
would be sent to the co-process: >
|
|
|
|
nvim_buf_update[{buf}, 2, 5, ['pasted line 1', 'pasted
|
|
line 2', 'pasted line 3', 'pasted line 4', 'pasted line 5', 'pasted line
|
|
6']]
|
|
|
|
If the user uses :edit to reload a buffer then the following event would be
|
|
generated: >
|
|
|
|
nvim_buf_updates_end[{buf}]
|
|
|
|
vim:tw=78:ts=8:ft=help:norl:
|