nvf

Helpful Tips

This section provides helpful tips that may be considered "unorthodox" or "too advanced" for some users. We will cover basic debugging steps, offline documentation, configuring nvf with pure Lua and using custom plugin sources in nvf in this section. For general configuration tips, please see previous chapters.

Debugging nvf

There may be instances where the your Nix configuration evaluates to invalid Lua, or times when you will be asked to provide your built Lua configuration for easier debugging by nvf maintainers. nvf provides two helpful utilities out of the box.

nvf-print-config and nvf-print-config-path will be bundled with nvf as lightweight utilities to help you view or share your built configuration when necessary.

To view your configuration with syntax highlighting, you may use the https://github.com/sharkdp/bat">bat pager.

nvf-print-config | bat --language=lua

Alternatively, cat or less may also be used.

Accessing neovimConfig

It is also possible to access the configuration for the wrapped package. The built Neovim package will contain a neovimConfig attribute in its passthru.

Offline Documentation

The manpages provided by nvf contains an offline version of the option search normally available at https://notashelf.github.io/nvf/options.html">https://notashelf.github.io/nvf/options.html. You may use the man 5 nvf command to view option documentation from the comfort of your terminal.

Note that this is only available for NixOS and Home-Manager module installations.

Pure Lua Configuration

We recognize that you might not always want to configure your setup purely in Nix, sometimes doing things in Lua is simply the "superior" option. In such a case you might want to configure your Neovim instance using Lua, and nothing but Lua. It is also possible to mix Lua and Nix configurations.

Pure Lua or hybrid Lua/Nix configurations can be achieved in two different ways. Purely, by modifying Neovim's runtime directory or impurely by placing Lua configuration in a directory found in $ HOME. For your convenience, this section will document both methods as they can be used.

Pure Runtime Directory

As of 0.6, nvf allows you to modify Neovim's runtime path to suit your needs. One of the ways the new runtime option is to add a configuration located relative to your flake.nix, which must be version controlled in pure flakes manner.

{
  # Let us assume we are in the repository root, i.e., the same directory as the
  # flake.nix. For the sake of the argument, we will assume that the Neovim lua
  # configuration is in a nvim/ directory relative to flake.nix.
  vim = {
    additionalRuntimeDirectories = [
      # This will be added to Neovim's runtime paths. Conceptually, this behaves
      # very similarly to ~/.config/nvim but you may not place a top-level
      # init.lua to be able to require it directly.
      ./nvim
    ];
  };
}

This will add the nvim directory, or rather, the store path that will be realised after your flake gets copied to the Nix store, to Neovim's runtime directory. You may now create a lua/myconfig directory within this nvim directory, and call it with .

{pkgs, ...}: {
  vim = {
    additionalRuntimeDirectories = [
      # You can list more than one file here.
      ./nvim-custom-1

      # To make sure list items are ordered, use lib.mkBefore or lib.mkAfter
      # Simply placing list items in a given order will **not** ensure that
      # this list  will be deterministic.
      ./nvim-custom-2
    ];

    startPlugins = [pkgs.vimPlugins.gitsigns];

    # Neovim supports in-line syntax highlighting for multi-line strings.
    # Simply place the filetype in a /* comment */ before the line.
    luaConfigRC.myconfig = /* lua */ ''
      -- Call the Lua module from ./nvim/lua/myconfig
      require("myconfig")

      -- Any additional Lua configuration that you might want *after* your own
      -- configuration. For example, a plugin setup call.
      require('gitsigns').setup({})
    '';
  };
}

Impure Absolute Directory

As of https://github.com/neovim/neovim/pull/22128">Neovim 0.9, $NVIM_APPNAME is a variable expected by Neovim to decide on the configuration directory. nvf sets this variable as "nvf", meaning ~/.config/nvf will be regarded as the configuration directory by Neovim, similar to how ~/.config/nvim behaves in regular installations. This allows some degree of Lua configuration, backed by our low-level wrapper https://github.com/Gerg-L/mnw">mnw. Creating a lua/ directory located in $ NVIM_APPNAME ("nvf" by default) and placing your configuration in, e.g., ~/.config/nvf/lua/myconfig will allow you to require it as a part of the Lua module system through nvf's module system.

Let's assume your ~/.config/nvf/lua/myconfig/init.lua consists of the following:

-- init.lua
vim.keymap.set("n", " ", "<Nop>", { silent = true, remap = false })
vim.g.mapleader = " "

The following Nix configuration via will allow loading this

{
  # The attribute name "myconfig-dir" here is arbitrary. It is required to be
  # a *named* attribute by the DAG system, but the name is entirely up to you.
  vim.luaConfigRC.myconfig-dir = ''
    require("myconfig")

    -- Any additional Lua
  '';
}

After you load your custom configuration, you may use an init.lua located in your custom configuration directory to configure Neovim exactly as you would without a wrapper like nvf. If you want to place your require call in a specific position (i.e., before or after options you set in nvf) the https://notashelf.github.io/nvf/index.xhtml#ch-using-dags">DAG system will let you place your configuration in a location of your choosing.

Adding Plugins From Different Sources

nvf attempts to avoid depending on Nixpkgs for Neovim plugins. For the most part, this is accomplished by defining each plugin's source and building them from source.

To define plugin sources, we use https://github.com/andir/npins">npins and pin each plugin source using builtin fetchers. You are not bound by this restriction. In your own configuration, any kind of fetcher or plugin source is fine.

Nixpkgs & Friends

vim.startPlugins and vim.optPlugins options take either a string, in which case a plugin from nvf's internal plugins registry will be used, or a package. If your plugin does not require any setup, or ordering for it s configuration, then it is possible to add it to vim.startPlugins to load it on startup.

{pkgs, ...}: {
  # Aerial does require some setup. In the case you pass a plugin that *does*
  # require manual setup, then you must also call the setup function.
  vim.startPlugins = [pkgs.vimPlugins.aerial-nvim];
}

This will fetch aerial.nvim from nixpkgs, and add it to Neovim's runtime path to be loaded manually. Although for plugins that require manual setup, you are encouraged to use https://notashelf.github.io/nvf/options.html#opt-vim.extraPlugins">vim.extraPlugins.

{
  vim.extraPlugins = {
    aerial = {
      package = pkgs.vimPlugins.aerial-nvim;
      setup = "require('aerial').setup {}";
    };
  };
}

More details on the extraPlugins API is documented in the https://notashelf.github.io/nvf/index.xhtml#ch-custom-plugins">custom plugins section.

Building Your Own Plugins

In the case a plugin is not available in Nixpkgs, or the Nixpkgs package is outdated (or, more likely, broken) it is possible to build the plugins from source using a tool, such as https://github.com/andir/npins">npins. You may also use your flake inputs as sources.

Example using plugin inputs:

{
  # In your flake.nix
  inputs = {
    aerial-nvim = {
      url = "github:stevearc/aerial.nvim"
      flake = false;
    };
  };

  # Make sure that 'inputs' is properly propagated into Nvf, for example, through
  # specialArgs.
  outputs = { ... };
}

In the case, you may use the input directly for the plugin's source attribute in buildVimPlugin.

# Make sure that 'inputs' is properly propagated! It will be missing otherwise
# and the resulting errors might be too obscure.
{inputs, ...}: let
  aerial-from-source = pkgs.vimUtils.buildVimPlugin {
      name = "aerial-nvim";
      src = inputs.aerial-nvim;
    };
in {
  vim.extraPlugins = {
    aerial = {
      package = aerial-from-source;
      setup = "require('aerial').setup {}";
    };
  };
}

Alternatively, if you do not want to keep track of the source using flake inputs or npins, you may call fetchFromGitHub (or other fetchers) directly. An example would look like this.

regexplainer = buildVimPlugin {
  name = "nvim-regexplainer";
  src = fetchFromGitHub {
    owner = "bennypowers";
    repo = "nvim-regexplainer";
    rev = "4250c8f3c1307876384e70eeedde5149249e154f";
    hash = "sha256-15DLbKtOgUPq4DcF71jFYu31faDn52k3P1x47GL3+b0=";
  };

  # The 'buildVimPlugin' imposes some "require checks" on all plugins build from
  # source. Failing tests, if they are not relevant, can be disabled using the
  # 'nvimSkipModule' argument to the 'buildVimPlugin' function.
  nvimSkipModule = [
    "regexplainer"
    "regexplainer.buffers.init"
    "regexplainer.buffers.popup"
    "regexplainer.buffers.register"
    "regexplainer.buffers.shared"
    "regexplainer.buffers.split"
    "regexplainer.component.descriptions"
    "regexplainer.component.init"
    "regexplainer.renderers.narrative.init"
    "regexplainer.renderers.narrative.narrative"
    "regexplainer.renderers.init"
    "regexplainer.utils.defer"
    "regexplainer.utils.init"
    "regexplainer.utils.treesitter"
  ];
}