From 82b21f7161e146bbab1ae4c094e490b7413d1b98 Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Thu, 8 Jun 2023 16:38:03 +0200 Subject: [PATCH] docs: add README --- README.md | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..162025b --- /dev/null +++ b/README.md @@ -0,0 +1,137 @@ +# znvim + +Zig bindings for Neovim's internal C functions. The ABI follows Neovim's master branch! + +## Usage + +1. Add the dependency to your project's `build.zig.zon`: + +```zig +.{ + .name = "my-config", + .version = "0.0.0", + + .dependencies = .{ + .znvim = .{ + .url = "https://mzte.de/git/LordMZTE/znvim/archive/.tar.gz", + .hash = "", + }, + }, +} +``` + +2. Structure your `build.zig` something like this: + +```zig +const std = @import("std"); + +pub fn build(b: *std.build.Builder) !void { + const target = b.standardTargetOptions(.{}); + + if (target.os_tag orelse @import("builtin").os.tag == .windows) + // windows is an error in many ways + return error.Windows; + + const mode = b.standardOptimizeOption(.{}); + + // Build your config as a dynamically linked library. + const lib = b.addSharedLibrary(.{ + .name = "my-config", + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = mode, + }); + + // Declare the znvim dependency here. + const znvim_dep = b.dependency("znvim", .{ .target = target, .optimize = mode }); + + // Add both the nvim_c and znvim modules. You can import these independantly. + lib.addModule("nvim", znvim_dep.module("nvim_c")); + lib.addModule("znvim", znvim_dep.module("znvim")); + + // Link libc. + lib.linkLibC(); + + // LuaJIT is required in order to create a native Lua module neovim can load as well as + // for your Lua API. + lib.linkSystemLibrary("luajit"); + + // IMPORTANT: without this, lua errors inside your module will cause nvim to SIGABRT! + lib.unwind_tables = true; + + // Set the output path to be something sensical. + b.getInstallStep().dependOn(&b.addInstallFile(lib.getOutputSource(), "share/nvim/mzte-nv.so").step); + + b.installArtifact(lib); +} +``` + +3. In your `main.zig`, you want a function called `luaopen_my_config` (where `my_config` is your module name) + This function is loaded by neovim's LuaJIT runtime via `dlsym` and called as an entry point on each `require` of your module. + +```zig +const znvim = @import("znvim"); + +// cImport LuaJIT +const c = @cImport({ + @cInclude("lua.h"); + @cInclude("lualib.h"); + @cInclude("lauxlib.h"); +}); + +pub const std_options = struct { + // You may want to override logFn here in order to redirect std.log log messages to, for example + // nvim notifications. Not doing so will clutter the GUI on log messages. +}; + +var is_initialized = false; + +export fn luaopen_my_config(l: ?*c.lua_State) c_int { + // Make sure we only run initiailzation code once. + // I also recommend putting this into a seperate function you call from Lua once, but that's + // outside the scope of this quick guide. + if (!is_initialized) { + is_initialized = true; + + // For example, you could set an option: + // See docs of types inside the znvim module. + znvim.OptionValue.of(true).setLog("number", .both) catch {}; + } + + c.lua_newtable(l); // create a new lua table to return from `require` + // ... add stuff to that table here + return 1; // 1 indicates one value returned from the lua function +} +``` + +4. Build your config to `~/.local` +```bash +zig build \ + -Doptimize=ReleaseFast \ # Use ReleaseSafe for easier to debug segfaults :D + -p ~/.local # The output path you set in build.zig will be appended to this. +``` + +5. In Lua, add the path to your shared object to `package.cpath`: +```lua +package.cpath = package.cpath .. ";" .. vim.loop.os_homedir() .. "/.loca/share/nvim/my-config.so" + +-- You can now require("my_config")! +``` + +## WTF??? + +The whole point of this is so I (and you too!) can write your neovim config in Zig, without going +through the Lua API (or the rather bulky `nvim_*` API functions). + +The way this works is by compiling your config's Zig part to a native Lua module, which is then +loaded using just a few lines of Lua code. Here, you can also create a Lua API to call into. + +## Structure + +znvim is split into 2 modules: `nvim_c` and `znvim`. The former is a `translate-c`'d file of all +of neovim's headers while the latter contains a (work in progress) Zig API for them. + +## Development + +Apart from writing the Zig API, we need to keep `nvim_c.zig` updated. This file contains the raw C +bindings. It's generated by `generate_bindings.sh`.