A lightweight Neovim RPC client for Zig that discovers the runtime API via nvim_get_api_info, manages transports, and ships a high-level MessagePack helper so application code never needs to touch the raw zig-msgpack API.
- Runtime API discovery – obtain structured metadata (
ApiInfo,ApiFunction,ApiParameter) describing exactly what the connected Neovim instance exposes. - Multiple transports – talk to Neovim over Unix sockets, TCP, stdio pipes, named pipes on Windows, or auto-spawned
nvim --embedprocesses. - Allocator-friendly client – the caller keeps ownership of allocations and can decide how long payloads live.
- MessagePack façade –
znvim.msgpackwrapszig-msgpackwith ergonomic constructors (msgpack.array,msgpack.object,msgpack.encode, …) plus type-safe readers (msgpack.expectString,msgpack.asArray, …). - Examples ready to build –
zig build examplesproduces runnable binaries that demonstrate common workflows.
- Zig 0.15.x
- A running Neovim instance compiled with
--embedsupport (standard builds already have it)
Add the package to your project using zig fetch:
zig fetch --save https://github.com/jinzhongjia/znvim/archive/main.tar.gzWire the module in build.zig:
const znvim_dep = b.dependency("znvim", .{
.target = target,
.optimize = optimize,
});
const znvim = znvim_dep.module("znvim");
exe.root_module.addImport("znvim", znvim);const std = @import("std");
const znvim = @import("znvim");
const msgpack = znvim.msgpack;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer switch (gpa.deinit()) {
.ok => {},
.leak => std.debug.print("warning: leaked allocations\n", .{}),
};
const allocator = gpa.allocator();
const address = std.process.getEnvVarOwned(allocator, "NVIM_LISTEN_ADDRESS") catch {
std.debug.print("Set NVIM_LISTEN_ADDRESS to your Neovim socket before running.\n", .{});
return error.MissingAddress;
};
defer allocator.free(address);
var client = try znvim.Client.init(allocator, .{ .socket_path = address });
defer client.deinit();
try client.connect();
// Ask Neovim to evaluate a small expression.
const expr = try msgpack.string(allocator, "join(['zig', 'nvim'], '-')");
defer msgpack.free(expr, allocator);
const params = [_]msgpack.Value{expr};
const response = try client.request("vim_eval", ¶ms);
defer msgpack.free(response, allocator);
if (msgpack.asString(response)) |result| {
std.debug.print("Neovim answered: {s}\n", .{result});
}
}Launch Neovim and point NVIM_LISTEN_ADDRESS at the exposed socket:
nvim --headless --listen /tmp/nvim.sock &
export NVIM_LISTEN_ADDRESS=/tmp/nvim.sock
zig build examples
./zig-out/bin/print_api| Example | Description |
|---|---|
print_api.zig |
Connects and prints the first few discovered API functions. |
run_command.zig |
Issues an nvim_command notification that writes to :messages. |
eval_expression.zig |
Evaluates a Vimscript expression and inspects the returned payload. |
buffer_lines.zig |
Reads the current buffer, replaces its content, then verifies the change. |
api_lookup.zig |
Looks up metadata for a single API function by name. |
Build all examples in one go with zig build examples; binaries are placed under zig-out/bin/.
Comprehensive documentation is available in the doc/ directory:
- Quick Start - Get started in 5 minutes
- Connection Methods - Learn all connection options (Unix Socket, Named Pipe, TCP, ChildProcess, Stdio)
- API Usage - Complete guide to calling Neovim APIs with examples
- Event Subscription - Handle buffer events, autocommands, and UI events
- Advanced Usage - Thread safety, memory management, performance optimization
- Code Examples - Real-world examples (file editor, REPL, formatter, etc.)
- Common Patterns - Best practices and design patterns
👉 Start here: doc/README.md
The znvim.msgpack namespace aims to be the only interface most users need:
const payload = try msgpack.object(allocator, .{
.name = "my-command",
.enabled = true,
.retries = 3,
});
defer msgpack.free(payload, allocator);
const arr = try msgpack.array(allocator, &.{ payload });
const parsed = msgpack.expectArray(arr) catch return error.NotArray;Use msgpack.encode(allocator, value) for generic encoding of common Zig types, and the expect* / as* helpers when decoding responses.
Run tests:
zig build testGenerate API documentation:
zig build docsThe generated documentation will be in the zig-out/docs/ directory. Open zig-out/docs/index.html in your browser.
Run performance benchmarks:
zig build run-benchmarkTo work on the documentation, keep the English README.md and the Chinese translation README.zh.md in sync.
MIT