If Nix and Neovim have one thing in common, it's that many new users don't know where to get started. The first thing you'll likely notice is that the normal paradigm for managing add-ons to NeoVim involves using some add-on manager plug-in/framework to extend the editor's default capabilities. This poses a philosophical dilemma for an operating system such as NixOS, as package management is a system-level concern and changes to the software baseline are supposed to be repeatable and immutable, neither of which is possible with ad-hoc package management. The solution presented here is to create a custom NeoVim package that is inclusive of the various add-ons that the user wants with their editor and to deliver the editor with add-ons as a single, managed package.
kickstart-nix.nvim
makes the migration of Neovim configurations to Nix straightforward.
Note
This repository is meant to make configuration of Neovim on Nix easier. Remove the things you don't use and add what you miss.
- Manage plugins + external dependencies using Nix (managing plugins shouldn't be the responsibility of a plugin).
- Configuration entirely in Lua1 (Vimscript is also possible). This makes it easy to migrate from non-nix dotfiles.
- Use Neovim's built-in loading mechanisms. See:
- Use Neovim's built-in LSP client, with Nix managing language servers.
- Use either nixpkgs or flake inputs as plugin sources.
- Usable on any device with Neovim and Nix installed.
- Create multiple derivations with different sets of plugins, and simple regex filters to exclude config files.
- Uses Nix to generate a
.luarc.json
in the devShell'sshellHook
. This sets up lua-language-server to recognize all plugins and the Neovim API.
- Click on Use this template to start a repo based on this template. Do not fork it.
- Add/remove plugins to/from the Neovim overlay.
- Add/remove plugin configs to/from the
nvim/plugin
directory. - Modify as you wish (you will probably want to add a color theme, ...). See: Design.
- You can create more than one package using the
mkNeovim
function by- Passing different plugin lists.
- Adding
ignoreConfigRegexes
(e.g.= [ "^ftplugin/.*.lua" ]
).
Tip
The nix and lua files contain comments explaining what everything does in detail.
- Add your flake to you NixOS flake inputs.
- Add the overlay provided by this flake.
nixpkgs.overlays = [
# replace <kickstart-nix-nvim> with the name you chose
<kickstart-nix-nvim>.overlays.default
];
You can then add the overlay's output(s) to the systemPackages
:
environment.systemPackages = with pkgs; [
nvim-pkg # The default package added by the overlay
];
With Nix installed (flakes enabled), from the repo root:
nix profile install .#nvim
Directory structure:
── flake.nix
── nvim # Neovim configs (lua), equivalent to ~/.config/nvim
── nix # Nix configs
- Set options in
init.lua
. - Source autocommands, user commands, keymaps,
and configure plugins in individual files within the
plugin
directory. - Filetype-specific scripts (e.g. start LSP clients) in the
ftplugin
directory. - Library modules in the
lua/user
directory.
Directory structure:
── nvim
├── ftplugin # Sourced when opening a file type
│ └── <filetype>.lua
├── init.lua # Always sourced
├── lua # Shared library modules
│ └── user
│ └── <lib>.lua
├── plugin # Automatically sourced at startup
│ ├── autocommands.lua
│ ├── commands.lua
│ ├── keymaps.lua
│ ├── plugins.lua # Plugins that require a `setup` call
│ └── <plugin-config>.lua # Plugin configurations
└── after # Empty in this template
├── plugin # Sourced at the very end of startup (rarely needed)
└── ftplugin # Sourced when opening a filetype, after sourcing ftplugin scripts
Important
- Configuration variables (e.g.
vim.g.<plugin_config>
) should go innvim/init.lua
or a module that isrequire
d ininit.lua
. - Configurations for plugins that require explicit initialization
(e.g. via a call to a
setup()
function) should go innvim/plugin/<plugin>.lua
ornvim/plugin/plugins.lua
. - See Initialization order for details.
You can declare Neovim derivations in nix/neovim-overlay.nix
.
There are two ways to add plugins:
- The traditional way, using
nixpkgs
as the source. - By adding plugins as flake inputs (if you like living on the bleeding-edge).
Plugins added as flake inputs must be built in
nix/plugin-overlay.nix
.
Directory structure:
── flake.nix
── nix
├── mkNeovim.nix # Function for creating the Neovim derivation
└── neovim-overlay.nix # Overlay that adds Neovim derivation
This derivation creates an init.lua
as follows:
- Add
nvim/lua
to theruntimepath
. - Add the content of
nvim/init.lua
. - Add
nvim/*
to theruntimepath
. - Add
nvim/after
to theruntimepath
.
This means that modules in nvim/lua
can be require
d in init.lua
and nvim/*/*.lua
.
Modules in nvim/plugin/
are sourced automatically, as if they were plugins.
Because they are added to the runtime path at the end of the resulting init.lua
,
Neovim sources them after loading plugins.
This configuration comes with a few plugins pre-configured.
You can add or remove plugins by
- Adding/Removing them in the Nix list.
- Adding/Removing the config in
nvim/plugin/<plugin>.lua
.
If you have used this template and would like to fetch updates that were added later...
Add this template as a remote:
git remote add upstream [email protected]:nix-community/kickstart-nix.nvim.git
Fetch and merge changes:
git fetch upstream
git merge upstream/main --allow-unrelated-histories
When your neovim setup is a nix derivation, editing your config demands a different workflow than you are used to without nix. Here is how I usually do it:
- Perform modifications and stage any new files2.
- Run
nix run /path/to/neovim/#nvim
ornix run /path/to/neovim/#nvim -- <nvim-args>
This requires a rebuild of the nvim
derivation, but has the advantage
that if anything breaks, it's only broken during your test run.
If you want an impure, but faster feedback loop,
you can use $XDG_CONFIG_HOME/$NVIM_APPNAME
3, where $NVIM_APPNAME
defaults to nvim
if the appName
attribute is not set
in the mkNeovim
function.
This has one caveat: The wrapper which nix generates for the derivation
calls nvim
with -u /nix/store/path/to/generated-init.lua
.
So it won't source a local init.lua
file.
To work around this, you can put scripts in the plugin
or after/plugin
directory.
Footnotes
-
The absence of a Nix module DSL for Neovim configuration is deliberate. If you were to copy the
nvim
directory to$XDG_CONFIG_HOME
, and install the plugins, it would work out of the box. ↩ -
When adding new files, nix flakes won't pick them up unless they have been committed or staged. ↩
-
Assuming Linux. Refer to
:h initialization
for Darwin. ↩