🔗 Rust bindings to all things Neovim https://crates.io/crates/nvim-oxi
Find a file
2024-11-19 18:47:41 +08:00
.cargo 🔧 restructuring examples dirs 2022-09-24 19:34:46 +02:00
.github/workflows workflows(ci): use dtolnay's rust-toolchain to install stable Rust 2024-06-17 13:59:21 +08:00
crates types: format 2024-11-01 09:21:43 +08:00
examples deps: update mlua to 0.10 2024-10-26 13:50:23 +11:00
src Merge pull request #190 from replcat/mlua-0.10 2024-11-01 09:34:35 +08:00
tests api: test that foreground highlights are set correctly 2024-10-07 18:13:04 +08:00
.gitignore 🔧 update .gitignore 2022-09-27 01:56:40 +02:00
build.rs oxi: add feature neovim-0-10, remove neovim-0-8 2024-05-28 09:18:13 +08:00
Cargo.toml deps: update mlua to 0.10 2024-10-26 13:50:23 +11:00
CHANGELOG.md chore: update CHANGELOG 2024-11-01 09:23:15 +08:00
flake.lock Update flake inputs 2024-11-19 18:47:41 +08:00
flake.nix Update flake inputs 2024-11-19 18:47:41 +08:00
LICENSE 🎉 init 2022-05-12 13:26:34 +02:00
README.md update README 2023-04-21 00:27:17 -05:00
rustfmt.toml implement (De)Serialize for LuaFn, LuaFnMut and LuaFnOnce 2022-06-03 13:19:16 +02:00

🔗 nvim-oxi

Latest version CI Docs

nvim-oxi provides safe and idiomatic Rust bindings to the rich API exposed by the Neovim text editor.

The project is mostly intended for plugin authors, although nothing's stopping end users from writing their Neovim configs in Rust.

How

The traditional way to write Neovim plugins in languages other than the "builtin" ones, i.e. Vimscript or Lua, is via RPC channels. This approach comes with a few limitations mostly due to having to (de)serialize everything to MessagePack-encoded messages, prohibiting things like attaching callbacks to keymaps or scheduling functions.

nvim-oxi takes a different approach. It leverages Rust's foreign function interface (FFI) support to hook straight into the Neovim C code, allowing feature parity with "in process" plugins while also avoiding the need for an extra IO layer.

This thread on the Neovim discourse goes into a bit more detail for anyone who's interested.

Why

Why bother when Neovim already has Lua as a first-class citizen? Mainly two reasons:

  • access to the Rust ecosystem: Lua is a great, minimal scripting language but can also be limiting when writing more complex plugins. In contrast Rust is a fully-fledged, statically typed language with a huge ecosystem of crates for (de)serialization, networking, IO, green threads, etc;

  • nvim-oxi provides a fully typed API: everything from optional function fields to callback arguments is checked at compile-time. This allows plugin authors to spend less time reading through the help docs and more time iterating via cargo checks.

Examples

The examples directory contains several examples of how to use nvim-oxi. It also contains instructions on how to setup your Rust crate, where to place the compiled artifacts and how to load the final plugin from Neovim.

If you're still not sure about something feel free to open a new issue and I might add a new example documenting your use case (if it can be done).

Testing

The test feature flag enables the #[nvim_oxi::test] proc macro. This macro replaces the regular #[test] annotations and can be used to test a piece of code from within a Neovim instance using Rust's excellent testing framework.

For example:

use nvim_oxi::{self as oxi, api};

#[oxi::test]
fn set_get_del_var() {
    api::set_var("foo", 42).unwrap();
    assert_eq!(Ok(42), api::get_var("foo"));
    assert_eq!(Ok(()), api::del_var("foo"));
}

Then cargo test will spawn a new Neovim process with an empty config, run that code and exit. There are a couple of gotchas:

  • after changing a piece of code, cargo build has to be run before you can test that with cargo test;

  • you cannot have two test functions with the same name, even if they belong to different modules. For example this won't work:

mod a {
    #[nvim_oxi::test]
    fn foo() {}
}

mod b {
    #[nvim_oxi::test]
    fn foo() {}
}