(
neovim-nightly tests are temporarily disabled)
First and foremost I am grateful and full of admiration towards noib3 for creating nvim-oxi, the more that I work on my nvimo fork the more that I understand it to be a bloated abomination disfiguring the minimalistic elegance and beauty of nvim-oxi.
With that said, the core motivation for nvimo is being enthusiastic about Rust and wanting to use a Rust based editor, but being too attached to NVIM/NeoVIM to switch to Zed or Helix. Consequentially, contrary to nvim-oxi's pragmatic goal of creating a small collection of high-performance plugins, nvimo's delusional and far over the rainbow goal is to RIIR the nvim ecosystem and ultimetally maybe even nvim itself, with that in mind, using the Rust ecosystem as much as possible rather than trying to rewrite it too seems like a fair concession.
- From nvim-oxi:
- Direct Rust bindings to nvim's C API (
vim.api.*) that avoid the overhead and limitations of using RPC channels. - Native Rust types for the API's arguments and return values.
- Binding to
vim.schedule. - Basic
libuvintegration. - A testing framework for running tests inside of a nvim instance.
- Direct Rust bindings to nvim's C API (
- From mlua:
- Robust error and panic propagation across the Rust-Lua boundary.
- Generally an industry standard Rust-Lua API.
- From mlua-extras:
- Tooling for generating
lua-lsdefinition files from Rust types and documentation.
- Tooling for generating
neovim-0-11/neovim-0-12/neovim-nightly: Sets the target nvim release for which to compilenvimo.- Exactly one of those features should be used, not adding any of them will generate a compile time error, using more than one will resolve to the one corresponding to the newest nvim version.
libuv: Enable basiclibuvintegration.test: Enable the testing framework.- TODO:
test-terminator(enableslibuvandtest). mlua-extras: Enablemlua-extrasre-export.
The following features are re-exports of mlua features that enable
corresponding functionality in nvimo and/or mlua-extras where relevant.
NOTE: Since nvimo is locked to the luajit and module features,
the lua*, vendored, module and send (which is locked to vendored)
features are not re-exported. Also the serde feature is not re-exported
since nvimo heavily relies on serde and thus it is always enabled.
async: enable async/await support (any executor can be used, eg. tokio or async-std).error-send: makemlua:Error: Send + Sync.macros: enable procedural macros (such aschunk!).- NOTE: This does not currently effect any of the
nvimomacros.
- NOTE: This does not currently effect any of the
anyhow: enableanyhow::Errorconversion into Lua (enableserror-send).userdata-wrappers: opt intoimpl UserDataforRc<T>/Arc<T>/Rc<RefCell<T>>/Arc<Mutex<T>>whereT: UserData
The first step is to create a new library crate with cargo new --lib {your_plugin} and edit the generated Cargo.toml to include:
[lib]
crate-type = ["cdylib"]
[dependencies]
nvimo = "0.0.1"NOTE: mlua and mlua-extras should NOT be directly added to avoid
potential version and feature conflicts, please use the nvimo::mlua
and nvimo::mlua_extras re-exports instead. All relevant mlua and
mlua-extras features are re-exported as nvimo features
(see mlua Re-Exports above).
Next, in lib.rs we'll annotate the entry point of the plugin with the
#[mlua::lua_module] macro and add the nvimo::init shim:
// lib.rs
use nvim::mlua;
#[mlua::lua_module]
fn foo(lua: &mlua::Lua) -> mlua::Result<i32> {
nvimo::init(lua)?;
Ok(42)
}macOS users will also need to set a few linker arguments to tell the Rust
linker that the FFI functions nvimo links to will only be available at
runtime. A possible way to do this is to create a .cargo/config file with the
following content:
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]After building the crate with cargo build {--release}, cargo will place the
compiled artifacts in target/debug or target/release depending on whether
you built a debug or release version of the crate. If the package name
specified in Cargo.toml is "foo", the library will be named:
libfoo.soon Linux;libfoo.dylibon macOS;foo.dllon Windows.
Next, we need to tell nvim where to load the plugin from. Create a new
directory named lua and place the compiled library inside it, renaming it to
foo.soon Linux;foo.soon macOS;foo.dllon Windows (i.e. no renaming).
Now open nvim and add the parent directory of lua to the
runtimepath, for
example with :set rtp+=~/foobar, assuming lua is in ~/foobar/lua.
And we're done. You can now call the require function to load the plugin just
like any other Lua plugin, which will return the output of the foo() function
defined in lib.rs:
print(require("foo")) -- prints `42`Anything that uses nvim_oxi::lua directly (e.g. implementations of
nvim_oxi::lua:{Pushable, Poppable} for custom types).
If you need this to give nvimo a try please open an issue with
a request for a compatibility layer for nvim_oxi::lua (preferably with
links to key places in your plugin/config where you need such compatibility).
Until such a request appears adding nvim_oxi::lua compatibility
is low priority.
Hopefully everything else.
Usage of nvimo::mlua::lua will work but give deprecation warnings.
The cargo dependency needs to be changed from nvim-oxi = {...} to
nvimo = { ... }.
Or nvim-oxi = { package = "nvimo", ... }
To avoid the need to change nvim_oxi to nvimo everywhere.
Please see the compatibility example, the overall list of changes is:
- The
mluare-export must be in scope (i.e.use nvim_oxi::mlua;). - The entry point macro needs to be changed from
#[nvim_oxi::plugin]to#[mlua::lua_module]. - The entry point function's signature and body need to be adjusted to
#[mlua::lua_module]:- A
lua: &mlua::Luaparameter needs to be added. - The return type needs to be wrapped in
mlua::Result. nvim_oxi::init(lua)?;needs to be called at the function body's start.
- A
Please see the examples directory as well as the mlua examples and the mlua-extras examples.
Turning on the test feature enables #[nvimo::test], which replaces the
regular #[test] macro and allows you to test a piece of code from within a
nvim instance using Rust's testing framework.
For example:
use nvimo::api;
#[nvimo::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"));
}When cargo test is executed, the generated code will spawn a new nvim
process with the nvim binary in your $PATH, test your code, and exit.
There's a gotcha: you can't have two tests with the same name in the same crate, even if they belong to different modules. For example, this won't work:
mod a {
#[nvimo::test]
fn foo() {}
}
mod b {
#[nvimo::test]
fn foo() {}
}Note that all integration tests must live inside a separate cdylib crate with
the following build script:
// build.rs
fn main() -> Result<(), nvimo::tests::BuildError> {
nvimo::tests::build()
}