xynovim plugins

The xyno plugins: nine Neovim plugins extracted from xynovim

These are the original parts of my Neovim config that I extracted into standalone plugins, so anyone can use them without the rest of xynovim. There are nine: four standalone plugins (Local History, an agent sandbox, a remote clipboard provider and an auto-save guard) and a Rust toolkit of five that share one base. All nine are public, tagged v0.1.0 with a GitHub release (xyno-sandbox is at v0.1.1), and install with lazy.nvim.

The plugins are MIT licensed. xynovim itself, the configuration they come from, stays Apache-2.0. Linux is the platform I test on; for Windows and macOS each section below copies what its README says, and where nothing was run I say so. They are my own independent projects.

The nine at a glance

PluginWhat it doesAlso needsPlatform
xyno-history.nvimLocal History: snapshots on private git refsgitLinux tested
xyno-sandbox.nvimRuns an agent in bubblewrap, refuses without itbubblewrap (Linux)Linux only
xyno-clipboard.nvimOSC 52 clipboard provider for tmux, SSH and herdrLinux; wl-copy and wl-paste optionalLinux only
xyno-guard.nvimKeeps auto-save off dependency sources and changed filesan auto-save plugin with a condition functionLinux tested
xyno-rust.nvimCargo context, targets, routes, nav, restart, statuslinecargo, snacks.nvim, rust-analyzerLinux tested
xyno-new.nvimNew Rust bin, lib, workspace, egui projectxyno-rust.nvim, cargoLinux tested
xyno-imports.nvimAuto-import, ranked choice, organize importsxyno-rust.nvim, cargo, rust-analyzerLinux tested
xyno-crates.nvimcrates.io search, crate add, features, crate importxyno-rust.nvim, xyno-imports.nvim, snacks.nvim, cargoLinux tested
xyno-macros.nvimMacro attribute completion, signature help, MacroExpandxyno-rust.nvim, xyno-imports.nvim, cargo, rust-analyzerLinux tested

All nine need Neovim 0.12+. The platform column is the tested one: Linux. What each README says about Windows and macOS is in the section of the plugin.

The dependency order of the Rust toolkit is xyno-rust.nvim first, then xyno-new.nvim and xyno-imports.nvim (each needs only xyno-rust.nvim), then xyno-crates.nvim and xyno-macros.nvim (each needs xyno-rust.nvim and xyno-imports.nvim). The four standalone plugins can be installed in any combination, alone or next to the toolkit.

Install the whole Rust toolkit

lazy.nvim installs what each spec lists under dependencies, so the order inside the file does not matter; the order below is the dependency order, which is also the order to read the plugins in.

  1. xyno-rust.nvim, with snacks.nvim for the pickers.
  2. xyno-new.nvim and xyno-imports.nvim, which need only xyno-rust.nvim.
  3. xyno-crates.nvim and xyno-macros.nvim, which need xyno-rust.nvim and xyno-imports.nvim.

One complete spec, assembled from the install snippets of the five READMEs (lua/plugins/xyno.lua in your config, for example). The blink.cmp entry at the end is optional and only gives xyno-macros.nvim its completion sources; rust-analyzer has to be started through vim.lsp (I use rustaceanvim) for the live parts.

return {
  -- 1. the base: every other Rust plugin needs it
  {
    "xynorash/xyno-rust.nvim",
    dependencies = { "folke/snacks.nvim" }, -- pickers and terminals
    ft = "rust",
    cmd = { "RustContext", "RustTargets", "RustTools", "RustRoutes", "RustNew", "RustRestart", "ActJob" },
    opts = {},
    keys = {
      { "<leader>rF", "<cmd>RustContext<cr>", ft = "rust", desc = "Cargo features / target" },
      { "<leader>rX", "<cmd>RustTargets<cr>", ft = "rust", desc = "Cargo targets: run / test / bench" },
      { "<leader>rZ", "<cmd>RustTools<cr>", ft = "rust", desc = "Cargo tools" },
      { "<leader>rN", "<cmd>RustRoutes<cr>", ft = "rust", desc = "HTTP routes" },
      { "<leader>ru", function() require("xyno_rust.rust_nav").goto_super() end, ft = "rust", desc = "Go to super method" },
      { "<leader>rl", function() require("xyno_rust.rust_nav").local_usages() end, ft = "rust", desc = "Usages in file (R/W)" },
      { "<leader>tD", function() require("xyno_rust.rust_tasks").doctests() end, ft = "rust", desc = "Doctests" },
    },
  },
  -- 2. new projects: needs xyno-rust
  {
    "xynorash/xyno-new.nvim",
    dependencies = { "xynorash/xyno-rust.nvim" },
    cmd = { "RustNewBin", "RustNewLib", "RustNewWorkspace", "RustNewMember", "EguiNew" },
    opts = {},
    keys = {
      { "<leader>rnb", "<cmd>RustNewBin<cr>", desc = "New Rust binary" },
      { "<leader>rni", "<cmd>RustNewLib<cr>", desc = "New Rust library" },
      { "<leader>rnw", "<cmd>RustNewWorkspace<cr>", desc = "New Rust workspace" },
      { "<leader>rnm", "<cmd>RustNewMember<cr>", desc = "New member in this workspace" },
      { "<leader>rne", "<cmd>EguiNew<cr>", desc = "New egui project" },
    },
  },
  -- 3. imports: needs xyno-rust
  {
    "xynorash/xyno-imports.nvim",
    dependencies = { "xynorash/xyno-rust.nvim" },
    ft = "rust",
    cmd = { "ImportName", "ImportForget", "ImportOrganize" },
    opts = {},
    keys = {
      { "<leader>rI", "<cmd>ImportName<cr>", ft = "rust", desc = "Import name (ranked)" },
      { "<leader>rO", "<cmd>ImportOrganize<cr>", ft = "rust", desc = "Organize imports" },
    },
  },
  -- 4. crates: needs xyno-rust and xyno-imports
  {
    "xynorash/xyno-crates.nvim",
    dependencies = {
      "xynorash/xyno-rust.nvim",
      "xynorash/xyno-imports.nvim",
      "folke/snacks.nvim", -- pickers
    },
    ft = "rust",
    cmd = { "CrateAdd", "CrateFeatures" },
    opts = {},
    keys = {
      { "<leader>rA", "<cmd>CrateAdd<cr>", ft = { "rust", "toml" }, desc = "Add crate (crates.io search)" },
      { "<leader>rG", function() require("xyno_crates.crate_features").fix_at_cursor() end, ft = "rust", desc = "Fix missing crate feature" },
    },
  },
  -- 5. macros: needs xyno-rust and xyno-imports
  {
    "xynorash/xyno-macros.nvim",
    dependencies = { "xynorash/xyno-rust.nvim", "xynorash/xyno-imports.nvim" },
    ft = "rust",
    cmd = { "MacroExpand", "MacroFormatFix", "MacroSignature", "ProcMacroStatus", "MacroAttrs" },
    opts = {},
    keys = {
      { "<leader>rx", "<cmd>MacroExpand<cr>", ft = "rust", desc = "Expand macro under cursor" },
      { "<leader>rq", "<cmd>ProcMacroStatus<cr>", ft = "rust", desc = "Proc-macro status" },
    },
  },
  -- optional: the completion sources of xyno-macros (blink.cmp 1.x)
  {
    "saghen/blink.cmp",
    version = "1.*", -- blink.cmp 2.x needs saghen/blink.lib too; the sources are written against 1.x
    opts = function(_, opts)
      return require("xyno_macros").blink_opts(opts)
    end,
  },
}

The standalone plugins

These four have no dependencies on other xyno plugins.

xyno-history.nvim

Local History for Neovim: every throttled save is snapshotted into private git refs, so deletions, resets and bad edits can be recovered.

Every throttled save is snapshotted into private git refs, so deletions, resets and bad edits can be recovered. A snapshot is a commit on refs/lochist/<branch> whose tree is the whole worktree: untracked files included, .gitignore respected, target/ left out. It is built with a private index file per Neovim process, so your index, your stash and your branches are untouched.

Saves are throttled to one snapshot per minute and the last save of a window is never lost. Without the plugin the snapshots are plain git (git log refs/lochist/main), and they stay in the repository until pruned.

The idea is inspired by the Local History of JetBrains IDEs. I still use JetBrains tools; this plugin is my own independent project and is not affiliated with JetBrains.

Needs. Neovim 0.12+, git.

Commands.

  • :LocalHistory: pick a snapshot of the current file and diff against it
  • :LocalHistoryRestore: pick a snapshot and replace the buffer text with it (buffer left modified, u undoes it)
  • :LocalHistoryPrune[!] [N | Nd]: keep the newest N snapshots per branch, or the last N days (default 30d), then git gc

Options. require("xyno_history").setup(opts) takes commands, keymaps (false by default; true maps <leader>gH), max_bytes (per-file cap, 10 MB when nil) and max_total_bytes (cap of one snapshot, 200 MB when nil).

Install (lazy.nvim)

{
  "xynorash/xyno-history.nvim",
  event = "VeryLazy", -- setup() registers the autocmds and commands, so load it early
  opts = {},
  keys = {
    { "<leader>gH", "<cmd>LocalHistory<cr>", desc = "Local history (file)" },
    { "<leader>gR", "<cmd>LocalHistoryRestore<cr>", desc = "Local history restore" },
  },
}

Platform status. Linux tested; Windows and macOS not verified

  • Linux: tested by the specs in the plugin repository (nvim -l tests/run.lua, 65 tests, plus a lazy.nvim smoke test in CI). The specs use bash stubs and kill -0, so they run on Linux and macOS only.
  • Windows: no evidence at all. In the original xynovim CI this code's spec was skipped on Windows, so nothing about it is demonstrated there.
  • macOS: not tested there. In xynovim's CI the original spec ran in the one fully green macOS run; other macOS runs were flaky or failed elsewhere, so I do not claim it works.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-history.nvim). Links. Repository and README of xyno-history.nvim, release v0.1.0 of xyno-history.nvim.

xyno-sandbox.nvim

Runs a command-line coding agent inside a bubblewrap sandbox and refuses to start it without one. Linux only.

Command-line coding agents that run without permission prompts have all of your privileges. This wraps them: the filesystem is read-only except the project and a scratch directory, credentials are hidden, secret environment variables are stripped, and anything that would later run outside the sandbox (git hooks, editor task files, direnv, cargo config, the agent's own settings) is locked read-only.

Where no sandbox can be created, the agent does not start. That fail-closed default is the point of the plugin. The policy knows the directories of one agent CLI, but wrap() takes any command.

Needs. Neovim 0.12+, Linux with bubblewrap (bwrap) and unprivileged user namespaces.

Commands.

  • :XynoSandboxStatus: shows how an agent would run here: sandboxed, unsandboxed (and why), or refused (and why)
  • :XynoSandboxRun {cmd} [args...]: runs the command in a terminal split, wrapped; if no sandbox is available it prints a one-line error and starts nothing

Options. require("xyno_sandbox").setup(opts) takes sandbox (false is a deliberate opt-out), allow_unsandboxed (true runs unsandboxed only when no backend exists), keymaps and commands. Only the booleans false and true count. The Lua API has status(), wrap(cmd, project, tmp_dir) and run().

Install (lazy.nvim)

{
  "xynorash/xyno-sandbox.nvim",
  cmd = { "XynoSandboxStatus", "XynoSandboxRun" },
  opts = {},
  -- no dependencies on other plugins
  keys = {
    { "<leader>as", "<Cmd>XynoSandboxStatus<CR>", desc = "Sandbox status" },
  },
}

Platform status. Linux only

  • Linux: the specs in the plugin repository run on Linux; that is the only platform where the sandbox works and the only one I claim.
  • Windows and macOS: there is no bubblewrap, so wrap() refuses and nothing starts. In the original xynovim CI the sandbox spec is skipped on both by design.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-sandbox.nvim). Links. Repository and README of xyno-sandbox.nvim, release v0.1.1 of xyno-sandbox.nvim.

xyno-clipboard.nvim

A clipboard provider for sessions whose yanks must reach another machine: tmux, SSH and herdr panes get OSC 52 copy, with no change anywhere else.

I work in tmux over SSH a lot. The default clipboard detection picks nothing useful there, and a plain OSC 52 provider breaks pasting from other applications on my Wayland desktop. This one copies through OSC 52 (inside tmux that becomes a tmux buffer shared by every attached client) and, when a local Wayland clipboard exists, also keeps it in sync and pastes from it.

The provider is installed only when $TMUX, $SSH_TTY, $SSH_CONNECTION or $HERDR_PANE_ID is set, or when an ancestor process has a name containing herdr. Elsewhere nothing is installed. It has no keys and works through the normal "+ and "* registers.

Needs. Neovim 0.12+, Linux; optional wl-copy and wl-paste with WAYLAND_DISPLAY.

Commands.

  • :XynoClipboardStatus: shows whether the provider is active and why, or why it is not

Options. require("xyno_clipboard").setup(opts) takes commands and osc52. vim.g.xyno_clipboard_osc52 = false stops the OSC 52 emit at runtime; the local Wayland copy still happens.

Install (lazy.nvim)

{
  "xynorash/xyno-clipboard.nvim",
  lazy = false, -- the provider should exist before the first yank
  opts = {},
}

Platform status. Linux only

  • Linux: covered by the specs in the plugin repository (nvim -l tests/run.lua).
  • Windows and macOS: Linux only by design. setup() returns early without installing anything. In xynovim's own CI this module was skipped on both, so nothing about it is demonstrated there.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-clipboard.nvim). Links. Repository and README of xyno-clipboard.nvim, release v0.1.0 of xyno-clipboard.nvim.

xyno-guard.nvim

Keeps auto-save away from files it must never write: dependency and standard-library sources, files changed on disk, and similar. Works with any auto-save plugin.

I use auto-save all day while writing Rust, and three things kept going wrong: a save landing in ~/.cargo/registry or the standard library sources I was only reading, a save overwriting a file that git checkout or a formatter had just changed on disk, and Neovim stopping at a blocking W12 prompt that swallowed my keystrokes. This is the guard I put in front of the writes.

can_save(buf) is the auto-save condition. Dependency and standard-library buffers are opened nomodifiable and readonly, and a file whose mtime changed on disk is not overwritten (:w! keeps your buffer, :e! takes the disk version). It sets no keymaps and registers no commands.

Needs. Neovim 0.12+; an auto-save plugin that accepts a condition function (I use okuuva/auto-save.nvim).

API (module xyno_guard).

  • setup(opts?): installs the autocmds and records the buffers already loaded
  • can_save(buf): may this buffer be written now? Use it as the auto-save condition
  • is_dependency(name): is this file below ~/.cargo/registry, ~/.cargo/git, ~/.rustup, $CARGO_HOME/{registry,git} or $RUSTUP_HOME?
  • save_all(): writes every modified file buffer that can_save allows

Options. There are no options yet; setup() accepts an argument for later and can be called again safely. Without lazy.nvim, call require("xyno_guard").setup() and pass require("xyno_guard").can_save as the condition of your auto-save plugin.

Install (lazy.nvim)

{
  "okuuva/auto-save.nvim",
  event = { "InsertLeave", "TextChanged", "TextChangedI" },
  dependencies = { "xynorash/xyno-guard.nvim" },
  opts = function()
    require("xyno_guard").setup()
    return { condition = require("xyno_guard").can_save }
  end,
}

Platform status. Linux tested; Windows and macOS not verified

  • Linux: tested by the specs in the plugin repository (nvim --headless --clean -l tests/run.lua, 39 tests).
  • Windows and macOS: the original spec for this code in xynovim was not among the files its platforms workflow skipped there, and that workflow passed. The specs of the plugin repository have not been run on either, so I treat both as unverified.
  • The ~/.cargo and ~/.rustup roots are Rust-specific; other languages' dependency directories are not covered.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-guard.nvim). Links. Repository and README of xyno-guard.nvim, release v0.1.0 of xyno-guard.nvim.

The Rust toolkit

xyno-rust.nvim is the base; the other four build on it. Their install snippets below name their dependencies, so each one can also be installed alone with what it needs.

xyno-rust.nvim

Rust project plumbing for Neovim: a per-project Cargo context, a targets picker, an HTTP route picker, navigation helpers, language-server restart handling and a statusline readiness indicator.

rust-analyzer and bacon-ls (clippy) are two servers that each run cargo. If they disagree on features or target, mutually exclusive features give bogus errors from one of them. This keeps one choice per workspace and applies it to both, and adds the small glue around it: run a target, find the route handlers of a web service, restart a server that died, see in the statusline whether the servers are ready.

It is also the shared base of the other xyno Rust plugins, which require its modules (xyno_rust.rust_context, xyno_rust.crate_meta, xyno_rust.code_actions, ...). Without snacks.nvim the plugin still loads, but the picker commands print one error line.

The Cargo context and the tools picker are inspired by the Cargo tool window of the JetBrains IDEs. I still use JetBrains tools; this plugin is my own independent project and is not affiliated with JetBrains.

Needs. Neovim 0.12+, cargo, snacks.nvim (pickers), rust-analyzer through vim.lsp, the tree-sitter rust parser.

Commands.

  • :RustContext: pick Cargo features and a target; applied to the running rust-analyzer and bacon-ls and stored for the workspace (show and reset as arguments)
  • :RustTargets: pick a target (bin, example, test, bench, lib) and run, test, build, bench or profile it in a bottom terminal
  • :RustTools: pick a workspace tool: timings, bloat, machete, deny, audit, duplicate versions, coverage summary
  • :RustRoutes: pick an axum, actix or rocket route and jump to it
  • :RustNew [name]: cargo new (bin or lib) in the current directory and open it
  • :ActJob: run the GitHub Actions job under the cursor with act
  • :RustRestart: restart rust-analyzer and bacon-ls, also when one has died

Options. require("xyno_rust").setup(opts) takes commands, keymaps (false by default; true maps <leader>rF, rX, rZ, rN, ru, rl and <leader>tD buffer-locally in Rust files) and modules to switch the context, tasks, routes, status and restart parts off. The statusline component with lualine is require("xyno_rust.lsp_status").component.

Install (lazy.nvim)

{
  "xynorash/xyno-rust.nvim",
  dependencies = { "folke/snacks.nvim" }, -- pickers and terminals
  ft = "rust",
  cmd = { "RustContext", "RustTargets", "RustTools", "RustRoutes", "RustNew", "RustRestart", "ActJob" },
  opts = {},
  keys = {
    { "<leader>rF", "<cmd>RustContext<cr>", ft = "rust", desc = "Cargo features / target" },
    { "<leader>rX", "<cmd>RustTargets<cr>", ft = "rust", desc = "Cargo targets: run / test / bench" },
    { "<leader>rZ", "<cmd>RustTools<cr>", ft = "rust", desc = "Cargo tools" },
    { "<leader>rN", "<cmd>RustRoutes<cr>", ft = "rust", desc = "HTTP routes" },
    { "<leader>ru", function() require("xyno_rust.rust_nav").goto_super() end, ft = "rust", desc = "Go to super method" },
    { "<leader>rl", function() require("xyno_rust.rust_nav").local_usages() end, ft = "rust", desc = "Usages in file (R/W)" },
    { "<leader>tD", function() require("xyno_rust.rust_tasks").doctests() end, ft = "rust", desc = "Doctests" },
  },
}

Platform status. Linux tested; Windows and macOS not verified

  • Linux: tested by the specs in the plugin repository (nvim -l tests/run.lua), with a real rust-analyzer and cargo where the specs need them. The live bacon-ls half of the original supervision specs is not part of that repository, so bacon-ls itself is not exercised there.
  • Windows: the Windows guards of the original code are kept, but I have no evidence for them from the plugin repository. In xynovim's CI the Windows job passed overall (run 39673715062), but the supervision and statusline spec is skipped whole there.
  • macOS: nothing tested there. In xynovim's CI the supervision and statusline spec is skipped on macOS, and the macOS runs were not stable, so I do not claim it works.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-rust.nvim). Links. Repository and README of xyno-rust.nvim, release v0.1.0 of xyno-rust.nvim.

xyno-new.nvim

New Rust projects from inside Neovim: a binary, a library, a workspace or an egui app, each behind a few guarded prompts.

cargo new makes a binary or a library, but not a workspace, and starting a project means leaving the editor to find a directory, check the name rules and cd. This asks for the name and the parent directory, applies cargo's name rules before anything is created, builds the workspace layout cargo has no command for, leaves nothing behind when a step fails, and opens the new project's source file.

A workspace inherits its version and edition to the first member. :EguiNew starts from the current eframe_template instead of a stale copy and executes nothing from it. A dashboard_keys() function adds the entries to a snacks.nvim dashboard, but only if you call it.

Needs. xyno-rust.nvim, Neovim 0.12+, cargo; optional git; curl and unzip for :EguiNew.

Commands.

  • :RustNewBin [name]: cargo new --bin --vcs git, then switch to the project and open src/main.rs
  • :RustNewLib [name]: the same with --lib and src/lib.rs
  • :RustNewWorkspace [name]: writes the workspace Cargo.toml, .gitignore and the first member, and runs git init -b main (no commit)
  • :RustNewMember [name]: adds a binary or library crate to the workspace of the current buffer
  • :EguiNew [name]: downloads the current eframe_template as a ZIP and personalises it

Options. require("xyno_new").setup(opts) takes commands, keymaps (false by default; true maps <leader>rnb, rni, rnw, rnm, rne), projects_dir, egui_template and egui_build_after_new.

Install (lazy.nvim)

{
  "xynorash/xyno-new.nvim",
  dependencies = { "xynorash/xyno-rust.nvim" },
  cmd = { "RustNewBin", "RustNewLib", "RustNewWorkspace", "RustNewMember", "EguiNew" },
  opts = {},
  keys = {
    { "<leader>rnb", "<cmd>RustNewBin<cr>", desc = "New Rust binary" },
    { "<leader>rni", "<cmd>RustNewLib<cr>", desc = "New Rust library" },
    { "<leader>rnw", "<cmd>RustNewWorkspace<cr>", desc = "New Rust workspace" },
    { "<leader>rnm", "<cmd>RustNewMember<cr>", desc = "New member in this workspace" },
    { "<leader>rne", "<cmd>EguiNew<cr>", desc = "New egui project" },
  },
}

Platform status. Linux tested; Windows and macOS not verified

  • Linux: tested by the specs in the plugin repository (nvim -l tests/run.lua) with a real cargo; the egui flow runs against a local fake template, plus one test against the real template that skips without network.
  • Windows: the Windows branches of the original code are kept (tar instead of unzip), but I have no evidence for them. In xynovim's CI the specs for this code were skipped whole on Windows.
  • macOS: the specs ran there in xynovim's CI, whose macOS runs were not stable overall, so I do not claim it works.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-new.nvim). Links. Repository and README of xyno-new.nvim, release v0.1.0 of xyno-new.nvim.

xyno-imports.nvim

Imports without the menu for Rust: auto-import of unresolved names, ranked disambiguation with a per-project memory, and organize imports, on top of rust-analyzer code actions.

rust-analyzer offers Import X as a code action, but you have to stop, open the menu and pick. When there is exactly one candidate that is a wasted round trip, and when there are several the list is in server order, not in the order your project would choose. This does the single case for you (or asks for one key), ranks the several-candidates case by what the project already does, and remembers the choice.

The mode option decides what happens to a name that resolves to nothing: "ask" (the default, never silent), "single" or "off"; vim.g.xyno_auto_import overrides it at runtime. Several candidates are never guessed at: use :ImportName.

The auto-import flow and organize imports are inspired by the import handling of the JetBrains IDEs. I still use JetBrains tools; this plugin is my own independent project and is not affiliated with JetBrains.

Needs. xyno-rust.nvim, Neovim 0.12+, cargo, rust-analyzer through vim.lsp, the tree-sitter rust parser.

Commands.

  • :ImportName [name]: list the candidates for the name (default: under the cursor), best first with the reasons, and import the chosen one; the choice is remembered per project
  • :ImportForget [name]: forget the remembered choice for the name, or all choices of this project
  • :ImportOrganize: rust-analyzer's merge, normalize and remove-unused import assists, applied in one undo step

Options. require("xyno_imports").setup(opts) takes commands, keymaps (false by default; true maps <leader>rI and <leader>rO in Rust files), mode and config for the engine tuning.

Install (lazy.nvim)

{
  "xynorash/xyno-imports.nvim",
  dependencies = { "xynorash/xyno-rust.nvim" },
  ft = "rust",
  cmd = { "ImportName", "ImportForget", "ImportOrganize" },
  opts = {},
  keys = {
    { "<leader>rI", "<cmd>ImportName<cr>", ft = "rust", desc = "Import name (ranked)" },
    { "<leader>rO", "<cmd>ImportOrganize<cr>", ft = "rust", desc = "Organize imports" },
  },
}

Platform status. Linux tested; Windows and macOS not verified

  • Linux: tested by the specs in the plugin repository (nvim -l tests/run.lua): the scoring, memory, scan and flow against a fake rust-analyzer client, plus cases against a real rust-analyzer on a small fixture crate where it is available.
  • Windows: the Windows guards of the original code are kept, but I have no evidence for them from the plugin repository. In xynovim's CI the live rust-analyzer flows of neighbouring specs failed or hung there as a known issue, so I do not claim the live part works on Windows.
  • macOS: nothing tested there. One auto-import timing spec timed out on macOS in xynovim's CI and is skipped there as a known issue, so I do not claim it works.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-imports.nvim). Links. Repository and README of xyno-imports.nvim, release v0.1.0 of xyno-imports.nvim.

xyno-crates.nvim

Cargo dependencies from inside Neovim: search crates.io, add crates with their features, enable a missing feature from a code action, and import from crates that are not dependencies yet.

Adding a dependency means leaving the editor for crates.io, copying a name, finding out which features exist, and running cargo add with the right flags. And when #[derive(Serialize)] fails because serde was added without its derive feature, the error does not say which feature to turn on. This does those steps where the cursor is.

Two in-process language clients put actions into the normal code-action menu: Add crate rand for an unknown crate, and `Enable feature derive of serde for a missing feature. The unknown-crate rules are conservative: a false positive is worse than a miss. vim.g.xyno_crate_check = false` switches the hint off.

The crate-adding flow is inspired by the dependency handling of the JetBrains IDEs. I still use JetBrains tools; this plugin is my own independent project and is not affiliated with JetBrains.

Needs. xyno-rust.nvim, xyno-imports.nvim, snacks.nvim (for :CrateAdd), Neovim 0.12+, cargo, the tree-sitter rust parser, network access to crates.io.

Commands.

  • :CrateAdd [query]: search crates.io, pick the crate, its features and the target, then run cargo add
  • :CrateFeatures [crate]: explore the cargo features of a dependency and toggle one; Cargo.toml is edited

Options. require("xyno_crates").setup(opts) takes commands, keymaps (false by default; true maps <leader>rA and <leader>rG) and modules (add, features, import).

Install (lazy.nvim)

{
  "xynorash/xyno-crates.nvim",
  dependencies = {
    "xynorash/xyno-rust.nvim",
    "xynorash/xyno-imports.nvim",
    "folke/snacks.nvim", -- pickers
  },
  ft = "rust",
  cmd = { "CrateAdd", "CrateFeatures" },
  opts = {},
  keys = {
    { "<leader>rA", "<cmd>CrateAdd<cr>", ft = { "rust", "toml" }, desc = "Add crate (crates.io search)" },
    { "<leader>rG", function() require("xyno_crates.crate_features").fix_at_cursor() end, ft = "rust", desc = "Fix missing crate feature" },
  },
}

Platform status. Linux tested; Windows and macOS not verified

  • Linux: tested by the specs in the plugin repository (nvim -l tests/run.lua), among them flows against the real crates.io, cargo and rust-analyzer on small fixture crates (they skip with a printed reason when the network, cargo or rust-analyzer are not available).
  • Windows: the Windows guards of the original code are kept, but I have no evidence for them from the plugin repository. In xynovim's CI the live rust-analyzer flows of the hint, code action and import specs failed or hung there as a known issue, so I do not claim those work on Windows.
  • macOS: nothing tested there. The macOS runs in xynovim's CI were not stable, so I do not claim it works.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-crates.nvim). Links. Repository and README of xyno-crates.nvim, release v0.1.0 of xyno-crates.nvim.

xyno-macros.nvim

Rust macro help in Neovim: completion inside helper attributes and derives (serde, clap, tokio, sqlx and more), macro_rules! signature help, :MacroExpand and proc-macro status.

rust-analyzer returns nothing inside #[serde(...)] or #[arg(...)], so the keys and the enumerated values (rename_all = "camelCase") were typed from memory. This plugin completes those attributes from small JSON schemas per crate (serde, clap, tokio, sqlx, thiserror, derive_more, strum, bon, schemars, utoipa), shows the arms of a macro_rules! while you write a call, expands the macro under the cursor, and lists the proc-macro crates and the macros rust-analyzer could not expand.

A project overrides or adds schemas with .xyno-macros/attrs/*.json in the workspace root. The completion sources need blink.cmp, pinned to 1.* because I only tried them against 1.x; everything else works without it.

The macro expansion view and the macro-aware completion are inspired by what the JetBrains Rust tooling does with macros. I still use JetBrains tools; this plugin is my own independent project and is not affiliated with JetBrains.

Needs. xyno-rust.nvim, xyno-imports.nvim, Neovim 0.12+, cargo, rust-analyzer through vim.lsp, the tree-sitter rust parser; optional blink.cmp.

Commands.

  • :MacroExpand: expand the macro under the cursor in a read-only split; falls back to cargo expand
  • :MacroFormatFix: append the missing arguments (todo!()) of the format macro call at the cursor
  • :MacroSignature: show the matcher arms of the macro_rules! call at the cursor
  • :ProcMacroStatus: list the proc-macro crates of the workspace and the macros that were not expanded
  • :MacroAttrs: show what the attribute completion sees at the cursor, for finding out why a completion is missing

Options. require("xyno_macros").setup(opts) takes commands, keymaps (false by default; true maps <leader>rx, <leader>rq and insert-mode <C-k>), snippets_allowed and attrs. require("xyno_macros").blink_opts(opts) registers the blink.cmp sources.

Install (lazy.nvim)

{
  "xynorash/xyno-macros.nvim",
  dependencies = { "xynorash/xyno-rust.nvim", "xynorash/xyno-imports.nvim" },
  ft = "rust",
  cmd = { "MacroExpand", "MacroFormatFix", "MacroSignature", "ProcMacroStatus", "MacroAttrs" },
  opts = {},
  keys = {
    { "<leader>rx", "<cmd>MacroExpand<cr>", ft = "rust", desc = "Expand macro under cursor" },
    { "<leader>rq", "<cmd>ProcMacroStatus<cr>", ft = "rust", desc = "Proc-macro status" },
  },
}

Platform status. Linux tested; Windows and macOS not verified

  • Linux: tested by the specs in the plugin repository (nvim -l tests/run.lua): the lexer, schema, matcher and report logic table-wise, plus cases against a real rust-analyzer on a small fixture crate where it is available. The completion-menu specs need blink.cmp and skip without it.
  • Windows: the guards of the original code are kept, but I have no evidence for them from the plugin repository. In xynovim's CI the live part of the macro tool specs failed or hung there as a known issue; I do not claim the live part works on Windows.
  • macOS: nothing tested there. In xynovim's CI the live part of the macro tool specs hung on macOS in one run and the macOS runs were not stable, so I do not claim it works.

License. MIT, Copyright (c) 2026 Xynorash (LICENSE of xyno-macros.nvim). Links. Repository and README of xyno-macros.nvim, release v0.1.0 of xyno-macros.nvim.

Questions

Do I need xynovim to use them?

No. Each plugin was pulled out of xynovim as a standalone plugin, and its README lists what it needs: Neovim 0.12+, a few command-line tools and, for the Rust toolkit, xyno-rust.nvim. None of them requires xynovim, LazyVim or any other config. The READMEs give lazy.nvim specs; xyno-guard.nvim also says how to use it without lazy.nvim (call require("xyno_guard").setup() and pass can_save to your auto-save plugin). xynovim itself still has the same code in its own tree, under another namespace.

Which Neovim version do they need?

All nine list Neovim 0.12+ as a requirement. The CI of each repository runs on Ubuntu with Neovim 0.12.5.

What does the sandbox plugin not protect against?

xyno-sandbox.nvim is a denylist on a read-only view of the whole filesystem, with the network open because the model needs it. It is not a security boundary against a determined attacker. The README lists the residual risks: the agent's credential file under its config directory in $HOME must stay readable and writable, so an agent can read that token; everything unlisted under $HOME is readable; loopback services and abstract sockets are reachable; cargo's registry/src and git are writable, so your own cargo may later build planted code; a nested .git deeper than two levels is not locked; and files the project lacks (.envrc, .vscode/tasks.json, rust-toolchain.toml, bacon.toml) can be created inside the project. It also works only on Linux with bubblewrap, and it refuses to start the agent where no sandbox can be created.

Why separate repositories?

I extracted them from my config so anyone can use one part without taking the rest. Each repository has its own README, specs, CI, tag and release, and you add only the plugins you want to your own lazy.nvim spec. The four standalone plugins have no dependencies on other plugins. The five Rust plugins share xyno-rust.nvim as a base, which is why it is its own repository and why the others list it under dependencies.

Where do I report issues?

On the Issues tab of the repository of the plugin concerned: xyno-history.nvim issues, xyno-sandbox.nvim issues, xyno-clipboard.nvim issues, xyno-guard.nvim issues, xyno-rust.nvim issues, xyno-new.nvim issues, xyno-imports.nvim issues, xyno-crates.nvim issues, xyno-macros.nvim issues. If you are not sure which one it is, xyno-rust.nvim is the base of the Rust toolkit. These are personal projects that I maintain on my own time.

How do I pin a version?

Every plugin is tagged v0.1.0 (xyno-sandbox v0.1.1) and has a GitHub release of that name. In a lazy.nvim spec add tag = "v0.1.0" to the plugin (lazy.nvim also records the exact commit in its lockfile, lazy-lock.json). That is lazy.nvim behaviour, not a feature of the plugins, so pin the dependencies of a toolkit plugin the same way.

License

The nine plugins are MIT licensed, Copyright (c) 2026 Xynorash; each repository has a LICENSE file. xynovim, the configuration they were extracted from, is licensed under Apache-2.0.

In the repository