diff options
Diffstat (limited to 'runtime/doc/msgpack_rpc.txt')
-rw-r--r-- | runtime/doc/msgpack_rpc.txt | 499 |
1 files changed, 328 insertions, 171 deletions
diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index bafb9dfc2c..c074eb43ff 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -1,97 +1,93 @@ -*msgpack_rpc.txt* For Nvim. {Nvim} +*msgpack_rpc.txt* {Nvim} NVIM REFERENCE MANUAL by Thiago de Arruda -The Msgpack-RPC Interface to Nvim *msgpack-rpc* +RPC API for Nvim *RPC* *rpc* *msgpack-rpc* -1. Introduction |msgpack-rpc-intro| -2. API mapping |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 |rpc-intro| +2. API mapping |rpc-api| +3. Connecting |rpc-connecting| +4. Clients |rpc-api-client| +5. Types |rpc-types| +6. Remote UIs |rpc-remote-ui| ============================================================================== -1. Introduction *msgpack-rpc-intro* +1. Introduction *rpc-intro* -The primary way to control 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 refer to the protocol as msgpack-rpc. +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 -At this point, only plugins use msgpack-rpc, but eventually even user -interaction will happen through it, since user interfaces will be separate -programs that control a headless Nvim instance. +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: -By connecting to the msgpack-rpc interface, programs can: + - Call any API function + - Listen for events + - Receive remote calls from Nvim -- Call any Nvim API function -- Listen for Nvim events -- Receive remote calls from Nvim - -Nvim's msgpack-rpc interface is like a more powerful version of Vim's -`clientserver` feature. +The RPC API is like a more powerful version of Vim's `clientserver` feature. ============================================================================== -2. API mapping *msgpack-rpc-api* + 2. API mapping *rpc-api* -The Nvim C API, see |nvim-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, which matches msgpack-rpc method -names with non-static API functions, converting/validating arguments and return -values back to msgpack. +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 will normally provide wrappers that hide msgpack-rpc details -from programmers. The wrappers can be automatically generated by reading -bundled API metadata from a compiled Nvim instance. +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 two ways to obtain API metadata: +There are three ways to obtain API metadata: -1. By connecting to a running Nvim instance and calling `vim_get_api_info` - via msgpack-rpc. This is best for clients written in dynamically-typed - languages, which can define functions at runtime. + 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. By starting Nvim with the `--api-info` command-line option, which makes Nvim - dump a blob of msgpack metadata to standard output and exit. This is best - for clients written in statically-typed languages, which require a separate - compilation step. + 2. Start Nvim with the |--api-info| option. Useful for clients written in + statically-compiled languages. -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. 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 *msgpack-rpc-connecting* +3. Connecting *rpc-connecting* -There are four ways to open msgpack-rpc streams to Nvim: +There are several ways to open a msgpack-rpc channel to an Nvim instance: -1. Through Nvim's stdin/stdout when it's started with the `--embed` option. - This is how other programs can embed Nvim. + 1. Through stdin/stdout when `nvim` is started with `--embed`. This is how + applications can embed Nvim. -2. Through the stdin/stdout of a program spawned by the |rpcstart()| function. + 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. - *$NVIM_LISTEN_ADDRESS* -3. Through the socket automatically created with each instance. To get the - socket location for a running Nvim instance (which is random by default), - see the |$NVIM_LISTEN_ADDRESS| environment variable: -> - :echo $NVIM_LISTEN_ADDRESS -< - See also |v:servername|. + 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 in a shell before starting Nvim: -> + 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 fully-featured Nvim client -(which we'll see in the next section). Here's a Ruby script that prints 'hello -world!' in the current Nvim instance: +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 @@ -109,7 +105,7 @@ world!' in the current Nvim instance: require 'msgpack/rpc/transport/unix' nvim = MessagePack::RPC::Client.new(MessagePack::RPC::UNIXTransport.new, ENV['NVIM_LISTEN_ADDRESS']) - result = nvim.call(:vim_command, 'echo "hello world!"') + 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: @@ -118,72 +114,110 @@ functions can be called interactively: >>> nvim = attach('socket', path='[address]') >>> nvim.command('echo "hello world!"') < -One can also spawn and connect to an embedded Nvim instance via |rpcstart()| +You can also embed an Nvim instance via |jobstart()|, and communicate using +|rpcrequest()| and |rpcnotify()|: > - let vim = rpcstart('nvim', ['--embed']) - echo rpcrequest(vim, 'vim_eval', '"Hello " . "world!"') - call rpcstop(vim) + let nvim = jobstart(['nvim', '--embed'], {'rpc': v:true}) + echo rpcrequest(nvim, 'nvim_eval', '"Hello " . "world!"') + call jobstop(nvim) < ============================================================================== -4. Implementing new clients *msgpack-rpc-clients* - -Nvim is still in alpha, so there's no in-depth documentation explaining how to -properly implement a client library yet. The Python client (the pip package -"neovim") will always be up-to-date with the latest API changes, so its source -code is the 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 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). - -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: - +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 *msgpack-rpc-types* +5. Types *rpc-types* -Nvim's C API uses custom types for all functions, se |nvim-api-types|. -For the purpose of mapping to msgpack, he types can be split into two groups: +The Nvim C API uses custom types for all functions. |api-types| +For the purpose of mapping to msgpack, 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 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: +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 + 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 Nvim types that use msgpack EXT: +Special types (msgpack EXT) ~ -Buffer -> enum value kObjectTypeBuffer -Window -> enum value kObjectTypeWindow -Tabpage -> enum value kObjectTypeTabpage + Buffer -> enum value kObjectTypeBuffer + Window -> enum value kObjectTypeWindow + Tabpage -> enum value kObjectTypeTabpage An API method expecting one of these types may be passed an integer instead, although they are not interchangeable. For example, a Buffer may be passed as @@ -191,70 +225,193 @@ an integer, but not a Window or Tabpage. The most reliable way of determining the type codes for the special Nvim types is to inspect the `types` key of metadata dictionary returned by the -`vim_get_api_info` method at runtime. Here's an example JSON representation of +`nvim_get_api_info` method at runtime. Here's a sample JSON representation of the `types` object: > "types": { "Buffer": { - "id": 0 + "id": 0, + "prefix": "nvim_buf_" }, "Window": { - "id": 1 + "id": 1, + "prefix": "nvim_win_" }, "Tabpage": { - "id": 2 + "id": 2, + "prefix": "nvim_tabpage_" } } < -Even for statically compiled clients, it's a 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. 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, e.g. - 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, e.g. `buffer_get_line` represents the `get_line` method - of a Buffer instance. -- Global methods are prefixed with `vim`, e.g. `vim_get_buffers`. - -So, for an 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_` +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. ============================================================================== -7. Vimscript functions *msgpack-rpc-vim-functions* - -Four msgpack-rpc functions are available in Vimscript: - -1. |rpcstart()|: Similarly to |jobstart()|, this will spawn a co-process with - its standard handles connected to Nvim. The difference is that it's not - possible to process raw data to or from the process's stdin, stdout, or - stderr. This is because the job's stdin and stdout are used as a single - msgpack channel that is processed directly by Nvim. - -2. |rpcstop()|: Same as |jobstop()|, but operates on handles returned by - |rpcstart()|. - -3. |rpcrequest()|: Sends a msgpack-rpc request to the process. - -4. |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|. +6. Remote UIs *rpc-remote-ui* + +Nvim allows Graphical user interfaces to be implemented by separate processes +communicating with Nvim over the RPC API. Currently the ui model conists of a +terminal-like grid with one single, monospace font size, with a few elements +that could be drawn separately from the grid (for the momemnt only the popup +menu) + +After connecting to a nvim instance (typically a spawned, embedded instance) +use the |nvim_ui_attach|(width, height, options) API method to tell nvim that your +program wants to draw the nvim screen on a grid with "width" times +"height" cells. "options" should be a dictionary with the following (all +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). + `popupmenu_external`: Instead of drawing the completion popupmenu on + the grid, Nvim will send higher-level events to + the ui and let it draw the popupmenu. + Defaults to false. + +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 are processed (not any intermediate +state after processing only a part of the array). + +Screen updates are arrays. The first element a string describing the kind +of update. + +["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_change", mode] + The mode changed. Currently sent when "insert", "replace", "cmdline" and + "normal" modes are entered. A client could for instance change the cursor + shape. + +["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. ============================================================================== vim:tw=78:ts=8:noet:ft=help:norl: |