{"id":25844140,"url":"https://github.com/notashelf/rns","last_synced_at":"2025-08-01T07:07:12.067Z","repository":{"id":276702587,"uuid":"929910729","full_name":"NotAShelf/rns","owner":"NotAShelf","description":"Exprimental library for enabling Neovim configuration in C","archived":false,"fork":false,"pushed_at":"2025-02-09T23:50:10.000Z","size":12,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-19T13:48:22.063Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NotAShelf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-09T17:25:24.000Z","updated_at":"2025-02-15T08:35:52.000Z","dependencies_parsed_at":"2025-02-10T00:29:30.961Z","dependency_job_id":null,"html_url":"https://github.com/NotAShelf/rns","commit_stats":null,"previous_names":["notashelf/rns"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAShelf%2Frns","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAShelf%2Frns/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAShelf%2Frns/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotAShelf%2Frns/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NotAShelf","download_url":"https://codeload.github.com/NotAShelf/rns/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241333927,"owners_count":19945897,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-01T07:27:52.908Z","updated_at":"2025-08-01T07:07:12.046Z","avatar_url":"https://github.com/NotAShelf.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RNS\n\nA library for writing Neovim configurations in systems programming languages\n([C](#c), [Zig](#zig), or with Rust). RNS provides a consistent C-compatible FFI\nlayer that lets you craft your Neovim setup using compiled languages instead of\nLua. _Highly_ experimental, almost fully untested. If you look at this and go\n\"_wow what a great idea, I will start using this project right this instance!_\"\nI have unfortunate news for your friends and family and it involves you being\nsent to a mental hospital.\n\nGreatly inspired by the awesome [CatNvim](https://github.com/rewhile/CatNvim),\nredesigned as a more generic and extensible\n\n## Features\n\n- Write Neovim configurations in C, Zig, or Rust\n- Plugin management API (register, install, update)\n- Structured plugin configuration system\n- LSP server configuration helpers\n- Keymapping creation with a clean API\n- Autocommand and autogroup management\n- Option setting with appropriate type enforcement\n\n## Usage\n\n\u003e [!WARNING]\n\u003e The API might change at any given time and without _any_ notice. This is not\n\u003e intended for public use (or any use, really) and I will not make any attempts\n\u003e to keep the library or its API stable.\n\nBuild RNS as a shared library:\n\n```bash\n# This will create target/release/librns.so\ncargo build --release\n```\n\nMove it to your working directory, and you may begin interfacing with the\nresulting shared library in C.\n\n```bash\n# Copy to working directory and create include directory\n# You could also symlink it if you plan to hack on RNS.\ncp target/release/librns.so ./librns.so\nmkdir -p include\ncp rns.h include/\n```\n\nWrite your configuration in C, and then compile it with `librns` in your library\npath. Note that you will also need `librns.so` in Neovim's `LD_LIBRARY_PATH`\nlater on.\n\n```bash\ngcc -o config.so -shared -fPIC init.c -Ltarget/release -lrns -Wl,-rpath,./ -I./include\n```\n\nNow you can load your new, compiled configuration:\n\n```lua\n-- init.lua\npackage.cpath = package.cpath .. \";./?.so\"\nlocal config = require(\"config\")\n```\n\n```bash\n# Ensure that librns.so is in Neovim's library path before\n# you run this. Otherwise your configuration will not be\n# loaded. Modifying cpath is a way of doing this but you\n# might also want to set it manually.\nnvim --clean -u path/to/your/init.lua\n```\n\nNow you may evaluate your life choices and consider how you even got here!\nEnjoy.\n\n### C\n\nThis is an example in C, just to give you an idea. I haven't done much to make\ninstallation or consumption of this library ergonomic, but you are welcome to\nsubmit pull requests or open issues to discuss your ideas :)\n\nJust to give you an idea of the truly awesome API. Please consider writing your\nown configurations if you end up using this :)\n\n```c\n#include \"include/rns.h\"\n#include \u003clauxlib.h\u003e\n#include \u003clua.h\u003e\n#include \u003clualib.h\u003e\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003cstring.h\u003e\n\nextern int luaopen_init(lua_State *L);\n\nstatic int set_config(lua_State *L) {\n  nvim_set_global(\"mapleader\", \" \");\n  nvim_set_global(\"maplocalleader\", \",\");\n  nvim_set_option_bool(\"number\", 1);\n  nvim_set_option_bool(\"relativenumber\", 1);\n  nvim_set_option_bool(\"expandtab\", 1);\n  nvim_set_option_int(\"tabstop\", 2);\n  nvim_set_option_int(\"shiftwidth\", 2);\n  nvim_set_option_bool(\"cursorline\", 1);\n  nvim_set_option_string(\"signcolumn\", \"yes\");\n\n  nvim_exec_command(\"colorscheme habamax\");\n  return 0;\n}\n\nstatic int setup_keymaps(lua_State *L) {\n  nvim_create_keymap(\"n\", \"\u003cleader\u003ew\", \":w\u003cCR\u003e\", NULL);\n  nvim_create_keymap(\"n\", \"\u003cleader\u003eq\", \":q\u003cCR\u003e\", NULL);\n  nvim_create_keymap(\"n\", \"\u003cC-h\u003e\", \"\u003cC-w\u003eh\", NULL);\n  nvim_create_keymap(\"n\", \"\u003cC-j\u003e\", \"\u003cC-w\u003ej\", NULL);\n  nvim_create_keymap(\"n\", \"\u003cC-k\u003e\", \"\u003cC-w\u003ek\", NULL);\n  nvim_create_keymap(\"n\", \"\u003cC-l\u003e\", \"\u003cC-w\u003el\", NULL);\n  return 0;\n}\n\nstatic int setup_autocmds(lua_State *L) {\n  nvim_create_augroup_lua(\"MySettings\", 1);\n  nvim_create_autocmd_lua(\"FileType\", \"markdown,text\",\n                          \"setlocal wrap linebreak\", \"MySettings\");\n  nvim_create_autocmd_lua(\"FileType\", \"rust\", \"setlocal tabstop=4 shiftwidth=4\",\n                          \"MySettings\");\n  return 0;\n}\n\nstatic int register_plugins(lua_State *L) {\n  register_plugin(\"nvim-lspconfig\", \"https://github.com/neovim/nvim-lspconfig\");\n  register_plugin(\"telescope\",\n                  \"https://github.com/nvim-telescope/telescope.nvim\");\n  register_plugin(\"plenary\", \"https://github.com/nvim-lua/plenary.nvim\");\n  return 0;\n}\n\nstatic int setup_plugin_install(lua_State *L) {\n  install_plugins();\n  return 0;\n}\n\nstatic int configure_plugins(lua_State *L) {\n  // Configure LSP in a structured way\n  plugin_config_begin(\"nvim-lspconfig\");\n  plugin_config_add_server(\"rust_analyzer\");\n  plugin_config_set_server_option(\"rust_analyzer\", \"checkOnSave.command\",\n                                  \"clippy\");\n  plugin_config_end();\n\n  // Configure telescope in a structured way\n  plugin_config_begin(\"telescope\");\n  plugin_config_set_mapping(\"telescope\", \"i\", \"\u003cC-j\u003e\", \"move_selection_next\");\n  plugin_config_add_keymap(\"n\", \"\u003cleader\u003eff\", \"telescope\", \"find_files\");\n  plugin_config_end();\n\n  // Load all plugin configurations\n  load_plugin_configs();\n\n  return 0;\n}\n\n__attribute__((visibility(\"default\"))) int luaopen_config(lua_State *L) {\n  luaopen_init(L);\n\n  luaL_Reg funcs[] = {{\"set_options\", set_config},\n                      {\"setup_keymaps\", setup_keymaps},\n                      {\"setup_autocmds\", setup_autocmds},\n                      {\"register_plugins\", register_plugins},\n                      {\"install_plugins\", setup_plugin_install},\n                      {\"configure_plugins\", configure_plugins},\n                      {NULL, NULL}};\n\n  luaL_newlib(L, funcs);\n  return 1;\n}\n```\n\n**compile your configuration** and then load it in your `init.lua`:\n\n```lua\n-- Basic setup\npackage.cpath = package.cpath .. \";./?.so\"\n\n-- Load the config module\nlocal config = require(\"config\")\n\n-- Print debug info\nprint(\"Configuration process completed\")\n```\n\nIf everything went well, Neovim will load without errors.\n\n### For Nix Users\n\nA basic flake.nix is provided by this repository. It is the only supported\nversion of consuming RNS as a 3rd party library.\n\n```nix\n{\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs?ref=nixos-unstable\";\n    rns = {\n      url = \"github:NotAShelf/rns\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n  };\n}\n```\n\nYou may build `inputs.rns.packages.\u003csystem\u003e.default` to access the dynamic\nlibrary and the header found in `include/`\n\n### Zig\n\nZig \"support\" is still experimental, because I'm quite new to Zig. If you know\nanything better, feel free to submit a pull request :)\n\n```zig\n// build.zig\nconst std = @import(\"std\");\n\npub fn build(b: *std.Build) void {\n    const target = b.standardTargetOptions(.{});\n    const optimize = b.standardOptimizeOption(.{});\n\n    const lib = b.addSharedLibrary(.{\n        .name = \"config\",\n        .root_source_file = b.path(\"config.zig\"),\n        .target = target,\n        .optimize = optimize,\n    });\n\n    // Add include path for rns.h\n    lib.addIncludePath(b.path(\".\"));\n\n    // Link against local librns.so shared library\n    const lib_path = b.path(\"./librns.so\");\n    lib.addObjectFile(lib_path);\n\n    // Add C library dependency\n    lib.linkLibC();\n\n    // Install the artifact\n    b.installArtifact(lib);\n}\n```\n\n```zig\n// config.zig\nconst std = @import(\"std\");\nconst c = @cImport({\n    @cDefine(\"_GNU_SOURCE\", \"1\");\n    @cInclude(\"include/rns.h\");\n});\n\nexport fn configure() c_int {\n    // Set basic options\n    _ = c.nvim_set_global(\"mapleader\", \" \");\n    _ = c.nvim_set_option_bool(\"number\", 1);\n    _ = c.nvim_set_option_bool(\"relativenumber\", 1);\n    _ = c.nvim_set_option_bool(\"expandtab\", 1);\n\n    // Set colorscheme\n    _ = c.nvim_exec_command(\"colorscheme habamax\");\n\n    // Register plugins\n    _ = c.register_plugin(\"telescope\", \"https://github.com/nvim-telescope/telescope.nvim\");\n    _ = c.register_plugin(\"plenary\", \"https://github.com/nvim-lua/plenary.nvim\");\n\n    // Install plugins\n    _ = c.install_plugins();\n\n    // Configure telescope\n    _ = c.plugin_config_begin(\"telescope\");\n    _ = c.plugin_config_set_mapping(\"telescope\", \"i\", \"\u003cC-j\u003e\", \"move_selection_next\");\n    _ = c.plugin_config_add_keymap(\"n\", \"\u003cleader\u003eff\", \"telescope\", \"find_files\");\n    _ = c.plugin_config_end();\n\n    return 0;\n}\n\n// Lua module entry point\nexport fn luaopen_libconfig(L: ?*anyopaque) c_int {\n    // Actually use the parameter to avoid compiler warning\n    if (L != null) {}\n\n    _ = configure();\n    return 1;\n}\n```\n\n```lua\n-- init.lua\n\n-- Load the RNS library\nlocal ffi = require(\"ffi\")\n\n-- Load the configuration library\nlocal config = package.loadlib(\"./zig-out/lib/libconfig.so\", \"luaopen_libconfig\")\nif not config then\n  error(\"Failed to load libconfig.so\")\nend\nconfig() -- Initialize the config\n\n-- Or alternatively, load it directly without handling the configuration via Lua\n-- local config = require('libconfig')\n\n-- You can also call individual RNS functions if needed\n-- Example:\n-- ffi.cdef[[\n--   int nvim_set_option_bool(const char *name, int value);\n--   int register_plugin(const char *name, const char *url);\n-- ]]\n-- local rns = ffi.load('./librns.so')\n-- rns.nvim_set_option_bool(\"number\", 1)\n-- rns.register_plugin(\"telescope\", \"https://github.com/nvim-telescope/telescope.nvim\")\n```\n\n### Assembly\n\nAre you thinking to yourself \"_what the fuck?_\" right now? I am.\n\n```asm\n; config.asm\nsection .data\n    mapleader db \" \", 0\n    lua_cmd db \"vim.g.mapleader = ' '\", 0\n    number_cmd db \"vim.opt.number = true\", 0\n    expandtab_cmd db \"vim.opt.expandtab = true\", 0\n    colorscheme_cmd db \"vim.cmd('colorscheme habamax')\", 0\n\nsection .note.GNU-stack\n\nsection .text\n    global configure:function\n    global luaopen_config:function\n    extern exec_lua\n\nconfigure:\n    push rbp\n    mov rbp, rsp\n\n    ; Use exec_lua to run Lua commands instead of direct C functions\n    lea rdi, [rel lua_cmd]\n    call exec_lua wrt ..plt\n\n    lea rdi, [rel number_cmd]\n    call exec_lua wrt ..plt\n\n    lea rdi, [rel expandtab_cmd]\n    call exec_lua wrt ..plt\n\n    lea rdi, [rel colorscheme_cmd]\n    call exec_lua wrt ..plt\n\n    xor eax, eax\n    pop rbp\n    ret\n\nluaopen_config:\n    push rbp\n    mov rbp, rsp\n    call configure\n    mov eax, 1\n    pop rbp\n    ret\n```\n\nCompile your configuration\n\n```bash\nnasm -f elf64 -o config.o config.asm  # For x86_64 Linux\n```\n\nand ccompile with embedded library path\n\n```bash\ngcc -shared -o config.so config.o -L. -lrns -Wl,-rpath,'$ORIGIN'\n```\n\n```lua\n-- init.lua\n-- Configure library paths\npackage.cpath = package.cpath .. \";./?.so\"\n\n-- Use FFI to preload librns.so\nlocal ffi = require(\"ffi\")\nffi.cdef[[void *dlopen(const char *filename, int flag);]]\nlocal lib = ffi.C.dlopen(\"./librns.so\", 2) -- RTLD_NOW = 2\n\n-- Load our assembly config\nlocal status, config = pcall(require, \"config\")\nif not status then\n  error(\"Failed to load config: \" .. tostring(config))\nend\n```\n\n## Why?\n\nTeehee.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotashelf%2Frns","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnotashelf%2Frns","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotashelf%2Frns/lists"}