build.gif

  flowchart TD
    A[Start] --> B{Is plugin in nixpkgs?}

    B -->|Yes| C[Add to optionalPlugins]
    B -->|No| D[Add to flake inputs]

    C --> E[Add any plugin dependencies and tools]
    D --> E

    E --> G[Register to lze in init.lua]

    G --> K[Run nix build .]
    K --> L[Test with ./result/bin/nvim]
TL;DR

Check it out: nixCats-nvim-example

Run git clone https://github.com/kohane27/nixCats-nvim-example.git ~/.config/nixCats-nvim and start tinkering!

Caveat

My template assumes you are familiar with the Neovim plugin ecosystem, like adding plugins with lazy.nvim in a normal Linux/MacOS environment. This guide only shows you how to do so in Nix/NixOS.

Introduction

I used nixCats-nvim’s example template to get started.

Info
I daily drive this for my development work. This is how I use it. Maybe it’s not idiomatic or even incorrect, but I have been eating my own dog food.

How to Use

git clone https://github.com/kohane27/nixCats-nvim-example.git ~/.config/nixCats-nvim
Warning
This config has to be cloned to ~/.config/nixCats-nvim/. The directory name has to be called nixCats-nvim.

I. Using Nix to download and manage plugins and tools

1. Adding plugins to flake.nix

flake.nix is the entry point for the plugins and tools you want to be managed by Nix. When you want to add any plugins or tools (LSP servers, fzf, stylua etc), this is the file to modify.

Check if the plugin is available in nixpkgs (The naming convention is suffixing -nvim, i.e., if you want to download telescope.nvim search telescope-nvim).

If yes, add it to the optionalPlugins attribute set

If the plugin is not available in nixpkgs, add the following to flake inputs:

"plugins-hlargs" = {
  url = "github:m-demare/hlargs.nvim";
  flake = false;
};

Note that the name must start with plugins-.

2. Install the plugin’s dependencies

For example, for telescope.nvim, the installation guide for lazy.nvim is below:

{
    "nvim-telescope/telescope.nvim",
    dependencies = { "nvim-lua/plenary.nvim" },
}

So you need to put plenary-nvim to startupPlugins.

Info
Plugin lib like vim-repeat, plenary-nvim, dressing-nvim, nui-nvim, nvim-web-devicons etc are required and shared by many plugins so I don’t bother setting them up in optionalPlugins attribute set.

3. Install the plugin’s external dependencies

For example, telescope.nvim requires ripgrep, so we put ripgrep to lspsAndRuntimeDeps.

What if the plugin is not available in Nix and requires a build step?

By build step I mean the following:

{
    "nvim-telescope/telescope-fzf-native.nvim",
    build = "cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release",
}

Just cry and hope someone step up and contribute it to Nix. jk. You can study how it’s done (ref: avante-nvim) and package it yourself (and hopefully contribute to Nix so that other people can benefit from it).

II. Using lua to configure plugins

1. Import your plugin to lua/custom/plugins/init.lua:

require("lze").load({
  { import = "custom.plugins.telescope" },
})
Info
We don’t need to touch the top-level init.lua. It’ll always be just require("custom").

2. Create the plugin init.lua:

With custom.plugins.telescope, create the corresponding lua/custom/plugins/telescope/ and init.lua:

return {
  { "telescope-fzf-native.nvim", dep_of = "telescope.nvim" },
  {
    "telescope.nvim",
    after = function(plugin)
      local telescope = require("telescope")
      telescope.setup({
        extensions = {
          fzf = { fuzzy = true },
        },
      })
      telescope.load_extension("fzf")
    end,
  },
}

2a. Declaring correct names

The name (line 4) is not necessarily the name you expect:

  • For plugins added via inputs in flake.nix (i.e., plugins not available in nixpkgs), it’s just the plugin name without -nvim, e.g., if inputs is plugins-hlargs, the name required is hlargs.

  • For plugins from nixpkgs, it’s usually the plugin name wth .nvim, e.g., if pkgs.vimPlugins.toggleterm-nvim, the name is toggleterm.nvim. For nvim-tree.lua, it’s NOT nvim-tree or nvim-tree.nvim, it’s nvim-tree.lua.

IMPORTANT
Run :NixCats pawsible to confirm the required names.

2b. Declaring correct dependencies

Continuing our telescope example above. The line { "telescope-fzf-native.nvim", dep_of = "telescope.nvim" } is setting the dependency required by the extension fzf.

Example
You can literally read { "telescope-fzf-native.nvim", dep_of = "telescope.nvim" } as “telescope-fzf-native.nvim is dependencies of telescope.nvim”.

For example, when you see the following lazy.nvim spec:

{
  "yetone/avante.nvim",
  dependencies = {
    {
      "MeanderingProgrammer/render-markdown.nvim",
      ft = { "markdown", "Avante" },
    },
  },
})

The equivalent in lua/custom/plugins/ai/avante/init.lua:

return {
  { "avante.nvim" },
  {
    "render-markdown.nvim",
    dep_of = { "avante.nvim" },
  },
}

As you can see, using lze requires you to understand where the plugin comes from and what it needs. It’s more involved than just copying lazy.nvim’s to a new file and have it configure for you.

III. Run nix build

Info
All commands need to be run in ~/.config/nixCats-nvim/.

Run nix build "." to build the binary at ./result/bin/nvim Execute and pray there’s no error and your plugin is working.

Note
To update plugins, run nix flake update.

home-manager Integration

Since I’m using home-manager, I just add it to my path home.sessionPath = [ "$HOME/.config/nixCats-nvim/result/bin" ];

After rebooting:

➜ which nvim
/home/username/.config/nixCats-nvim/result/bin/nvim

Tips

1. Use regularCats to quickly set up and config new plugins

Nobody has the patience to make a small change, run nix build . and wait 20 seconds. regularCats is the perfect use case for it.

The following builds the binary ./bin/regularCats:

nix build ".#regularCats" --out-link regularCats

Once you have made sure the plugin is downloaded and loaded by Nix (:NixCats pawsible), you just make changes to the plugin init.lua and run ./bin/regularCats to see the changes immediately.

I have the following in my home-manager’s aliases.nix:

programs.zsh.shellAliases = {
    v = "$(readlink -f ~/.config/nixCats-nvim/result/bin/nvim)";
    vv = "$(readlink -f ~/.config/nixCats-nvim/regularCats/bin/regularCats)";
};
Note
Whenever you make changes to flake.nix (add or remove plugins or adding new tools), you still need to run nix build ".".

2. Use GitHub search and reference other people’s nixCats-nvim

I used GitHub search when I was setting up mine. I believe my repo is the most complete bloated nixCats-nvim on all of GitHub. But it also serves as an example of how to do various things. Just remove the stuff you don’t need.

3. Debugging

If your plugin is not working:

  1. flake.nix: Move the plugin from optionalPlugins to startupPlugins attribute set
  2. set lazy = false in your plugin init.lua

For example, when I was setting up typescript-tools-nvim, I realized it did not work if I put it under optionalPlugins. Even adding lazy = false did not work. After a lot of headache, BirdeeHub and I resolved it.