Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

wlib.wrapperModules.neovim

Please see the template for an introductory example usage!

To initialize it, run nix flake init -t github:BirdeeHub/nix-wrapper-modules#neovim

If you are using zsh, you may need to escape the # character with a backslash.

The first thing to notice is config.settings.config_directory

Set it to an in-store, out-of-store, or lib.generators.mkLuaInline value!

It will be loaded just like a normal neovim configuration directory.

Plugins are provided via the config.specs option.

It takes a set of plugins, or a set of lists of plugins.

Each item that accepts a plugin may also be a spec with the plugin as its .data field, which may optionally be customized by config.specMods and then further processed by config.specCollect (not too hard) and config.specMaps (advanced).

The spec forms offer the ability to provide plugins, configuration (in lua, vimscript, or fennel), along with automatically translated lua values from nix in a finely controlled order.

A plugin may be an in-store or out-of-store path, but may not be an inline lua value.

Optionally supports the ability to avoid path collisions when installing multiple configured neovim packages!

You may do this via a combination of config.binName and config.settings.dont_link options.

This module provides an info plugin you can access in lua to get metadata about your nix install, as well as ad-hoc values you pass.

This module fully supports remote plugin hosts.

By the same mechanism, it also allows arbitrary other items to be bundled into the context of your neovim derivation, such as neovide, via an option which accepts wrapper modules for maximum flexibility.

That is also why there are so many options listed.

Skip past the hosts options to get to the good stuff. After you are done reading those, there is also a tips and tricks section at the bottom.

All Options:

wrapperModules/n/neovim:

hosts

This option takes a set of wrapper modules.

Neovim has “remote plugin hosts” which allow plugins in other languages.

You can wrap such a host and pass them here.

The resulting wrapped package will also be added to the PATH alongside nvim

In fact, some defaults have been provided!

You can hosts.python3.nvim-host.enable = true; and you can do the same with node and ruby

You can also wrap things that are not remote plugin hosts. For example neovide! This allows you to keep the configuration for these in sync

You can also hosts.neovide.nvim-host.enable = true;

Each wrapper module in the set is given a nvim-host option, and evaluated, with the result being accesible via config.hosts.<name>.<wrapper, nvim-host, binName, etc...>

the nvim-host option provides all the options of the wlib.modules.makeWrapper module again.

However these options are evaluated in the scope of the neovim wrapper, not the host wrapper.

In addition to the wlib.modules.makeWrapper options, it also adds the following options, which control how the host is added to neovim:

nvim-host.enable, nvim-host.package, nvim-host.var_path, nvim-host.enabled_variable, nvim-host.disabled_variable nvim-host.dontWrap

This allows you to do things like

hosts.neovide.nvim-host.flags."--neovim-bin" = "${placeholder "out"}/bin/${config.binName}";

If you do hosts.neovide.nvim-host.enable = true; it will do that for you.

Type: attribute set of (submodule)

Default: { }

Declared by:

hosts.<name>.package

The base package to wrap. This means config.builderFunction will be responsible for inheriting all other files from this package (like man page, /share, …)

The config.package value given by this option already has all values from config.overrides applied to it.

Type: str|path|drv

Declared by:

hosts.<name>.apply

Function to extend the current configuration with additional modules. Can accept a single module, or a list of modules. Re-evaluates the configuration with the original settings plus the new module(s).

Returns .config from the lib.evalModules result

Type: function that evaluates to a(n) raw value (read only)

Default: <function>

Declared by:

hosts.<name>.binName

The name of the binary output by wrapperFunction to $out/bin

If not specified, the default name from the package will be used.

Type: non-empty line

Default: "hello"

Declared by:

hosts.<name>.builderFunction

Outside of importing wlib.modules.symlinkScript module, which is included in wlib.modules.default, This is usually an option you will never have to redefine.

This option takes a function receiving the following arguments:

module arguments + wrapper + pkgs.callPackage

{
  wlib,
  config,
  wrapper,
  ... # <- anything you can get from pkgs.callPackage
}@initialArgs:
"<buildCommand>"

It is in charge of linking wrapper and config.outputs to the final package.

wrapper is the unchecked result of calling wrapperFunction, or null if one was not provided.

  • The function is to return a string which will be added to the buildCommand of the wrapper.

The builtin implementation, and also the wlib.modules.symlinkScript module, accept either a string to prepend to the returned buildCommand string, or a derivation to link with lndir

  • Alternatively, it may return a function which returns a set like:
{ wlib, config, wrapper, ... }@initialArgs:
drvArgs:
drvArgs // {}

If it does this, that function will be given the final computed derivation attributes, and it will be expected to return the final attribute set to be passed to pkgs.stdenv.mkDerivation.

Regardless of if you return a string or function, passthru.wrap, passthru.apply, passthru.eval, passthru.extendModules, passthru.override, passthru.overrideAttrs will be added to the thing you return, and config.sourceStdenv will be handled for you.

However:

  • You can also return a functor with a (required) mkDerivation field.
  { config, stdenv, wrapper, wlib, ... }@initialArgs:
  {
    inherit (stdenv) mkDerivation;
    __functor = {
      mkDerivation,
      __functor,
      defaultPhases, # [ "<all stdenv phases>" ... ]
      setupPhases, # phases: "if [ -z \"${phases[*]:-}\" ]; then phases="etc..."; fi"
      runPhases, # "for curPhase in ${phases[*]}; do runPhase \"$curPhase\"; done"
      ...
    }@self:
    defaultArgs:
    defaultArgs // (if config.sourceStdenv then { } else { buildCommand = ""; }
  }
  • If you do this:

    • You are in control over the entire derivation.
    • This means you need to take care of config.passthru and config.sourceStdenv yourself.
    • The mkDerivation function will be called with the final result of your functor.

As you can see, you are provided with some things to help you via the self argument to your functor.

The generated passthru items mentioned above are given to you as part of what is shown as defaultArgs above

And you are also given some helpers to help you run the phases if needed!

Tip: A functor is a set with a { __functor = self: args: ...; } field. You can call it like a function and it gets passed itself as its first argument!

Type: function that evaluates to a(n) (string or function that evaluates to a(n) attribute set of raw value)

Default: <function, args: {config, lib, lndir, wlib, wrapper}>

Declared by:

hosts.<name>.drv

Extra attributes to add to the resulting derivation.

Cannot affect passthru, or outputs. For that, use config.passthru, or config.outputs instead.

Also cannot override buildCommand. That is controlled by the config.builderFunction and config.sourceStdenv options.

Type: attrsRecursive

Default: { }

Declared by:

hosts.<name>.eval

Function to extend the current configuration with additional modules. Can accept a single module, or a list of modules. Re-evaluates the configuration with the original settings plus the new module(s).

Returns the raw lib.evalModules result

Type: function that evaluates to a(n) raw value (read only)

Default: <function>

Declared by:

hosts.<name>.exePath

The relative path to the executable to wrap. i.e. bin/exename

If not specified, the path gained from calling lib.getExe on config.package and subtracting the path to the package will be used.

Type: non-empty line

Default: "bin/hello"

Declared by:

hosts.<name>.extendModules

Alias for .extendModules so that you can call it from outside of wlib.types.subWrapperModule types

In addition, it is also a set which stores the function args for the module evaluation. This may prove useful when dealing with subWrapperModules or packages, which otherwise would not have access to some of them.

Type: function that evaluates to a(n) raw value (read only)

Default: <function, args: {modules?, prefix?, specialArgs?}>

Declared by:

hosts.<name>.meta.description

Description of the module.

Accepts either a string, or a set of { pre ? "", post ? "" }

Resulting config value will be a list of { pre, post, file }

Type: string or { pre ? “”, post ? “” } (converted to `[ { pre, post, file } ]`)

Default: ""

Declared by:

hosts.<name>.meta.maintainers

Maintainers of this module.

Type: list of (open submodule of attrsRecursive)

Default: [ ]

Declared by:

hosts.<name>.meta.maintainers.*.email

email

Type: null or string

Default: null

hosts.<name>.meta.maintainers.*.github

GitHub username

Type: string

hosts.<name>.meta.maintainers.*.githubId

GitHub id

Type: signed integer

hosts.<name>.meta.maintainers.*.matrix

Matrix ID

Type: null or string

Default: null

hosts.<name>.meta.maintainers.*.name

name

Type: string

Default: "‹name›"

hosts.<name>.meta.platforms

Supported platforms

Type: list of strings from enum of lib.platforms.all

Default: "lib.platforms.all"

Example:

[
  "x86_64-linux"
  "aarch64-linux"
]

Declared by:

hosts.<name>.nvim-host

Gives access to the full wlib.modules.makeWrapper options, but ran in the context of the nvim host, not this sub wrapper module.

Also adds the following options, which control how the host is added to neovim:

nvim-host.enable, nvim-host.package, nvim-host.var_path, nvim-host.enabled_variable, nvim-host.disabled_variable nvim-host.dontWrap

Type: submodule

Declared by:

hosts.<name>.nvim-host.enable

Enable this nvim host program.

If enabled it will be added to the path alongside the nvim wrapper.

It will also propagate options provided in this set to the nvim wrapper.

Type: boolean

Default: true

Declared by:

hosts.<name>.nvim-host.enabled_variable

vim.g.<value> will be set to the path to this wrapped host when the nvim host is enabled

Type: non-empty line

Default: "‹name›_host_prog"

Declared by:

hosts.<name>.nvim-host.package

The full path to be added to the PATH alongside the main nvim wrapper.

By default, the binary from this host wrapper module will be used.

This is the path which gets wrapped in the context of the nvim wrapper by the nvim-host options

This allows you to wrap this path in the context of the overall nvim derivation, and thus have access to that path via placeholder "out"

Type: non-empty line

Default: "/nix/store/6a7qckbl8zmrl5k3i530x87rhw92h035-hello-2.12.2/bin/hello"

Declared by:

hosts.<name>.nvim-host.addFlag

Wrapper for

–add-flag ARG

Prepend the single argument ARG to the invocation of the executable, before any command-line arguments.

This option takes a list. To group them more strongly, option may take a list of lists as well.

Any entry can instead be of type { data, name ? null, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG.

If no name is provided, it cannot be targeted.

Type: list of spec with main field: `data` of (str|path|drv or list of str|path|drv)

Default: [ ]

Example:

[
  "-v"
  "-f"
  [
    "--config"
    "\${./storePath.cfg}"
  ]
  [
    "-s"
    "idk"
  ]
]

Declared by:

hosts.<name>.nvim-host.addFlag.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.appendFlag

–append-flag ARG

Append the single argument ARG to the invocation of the executable, after any command-line arguments.

This option takes a list. To group them more strongly, option may take a list of lists as well.

Any entry can instead be of type { data, name ? null, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG.

If no name is provided, it cannot be targeted.

Type: list of spec with main field: `data` of (str|path|drv or list of str|path|drv)

Default: [ ]

Example:

[
  "-v"
  "-f"
  [
    "--config"
    "\${./storePath.cfg}"
  ]
  [
    "-s"
    "idk"
  ]
]

Declared by:

hosts.<name>.nvim-host.appendFlag.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.argv0

–argv0 NAME

Set the name of the executed process to NAME. If unset or null, defaults to EXECUTABLE.

overrides the setting from argv0type if set.

Type: null or string

Default: null

Declared by:

hosts.<name>.nvim-host.argv0type

argv0 overrides this option if not null or unset

Both shell and the nix implementations ignore this option, as the shell always resolves $0

However, the binary implementation will use this option

Values:

  • "inherit":

The executable inherits argv0 from the wrapper. Use instead of --argv0 '$0'.

  • "resolve":

If argv0 does not include a “/” character, resolve it against PATH.

  • Function form: str -> str

This one works only in the nix implementation. The others will treat it as inherit

Rather than calling exec, you get the command plus all its flags supplied, and you can choose how to run it.

e.g. command_string: "eval \"$(${command_string})\";

It will also be added to the end of the overall DAL, with the name NIX_RUN_MAIN_PACKAGE

Thus, you can make things run after it, but by default it is still last.

Type: one of “resolve”, “inherit” or function that evaluates to a(n) string

Default: "inherit"

Declared by:

hosts.<name>.nvim-host.chdir

–chdir DIR

Change working directory before running the executable. Use instead of --run "cd DIR".

Type: list of spec with main field: `data` of str|path|drv

Default: [ ]

Declared by:

hosts.<name>.nvim-host.chdir.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.disabled_variable

vim.g.<value> will be set to 0 when the nvim host is disabled

Type: non-empty line

Default: "loaded_‹name›_provider"

Declared by:

hosts.<name>.nvim-host.dontWrap

If true, do not process any hosts.*.nvim-host options for this host other than:

nvim-host.enable, nvim-host.package, nvim-host.var_path, nvim-host.enabled_variable, nvim-host.disabled_variable

Type: boolean

Default: false

Declared by:

hosts.<name>.nvim-host.env

Environment variables to set in the wrapper.

This option takes a set.

Any entry can instead be of type { data, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG, which will cause the resulting wrapper argument to be sorted accordingly

Type: attribute set of spec with main field: `data` of str|path|drv

Default: { }

Example:

{
  XDG_DATA_HOME = "/somewhere/on/your/machine";
}

Declared by:

hosts.<name>.nvim-host.env.<name>.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.envDefault

Environment variables to set in the wrapper.

Like env, but only adds the variable if not already set in the environment.

This option takes a set.

Any entry can instead be of type { data, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG, which will cause the resulting wrapper argument to be sorted accordingly

Type: attribute set of spec with main field: `data` of str|path|drv

Default: { }

Example:

{
  XDG_DATA_HOME = "/only/if/not/set";
}

Declared by:

hosts.<name>.nvim-host.envDefault.<name>.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.escapingFunction

The function to use to escape shell values

Caution: When using shell or binary implementations, these will be expanded at BUILD time.

You should probably leave this as is when using either of those implementations.

However, when using the nix implementation, they will expand at runtime! Which means wlib.escapeShellArgWithEnv may prove to be a useful substitute!

Type: function that evaluates to a(n) string

Default: "lib.escapeShellArg"

Declared by:

hosts.<name>.nvim-host.extraPackages

Additional packages to add to the wrapper’s runtime PATH. This is useful if the wrapped program needs additional libraries or tools to function correctly.

Adds all its entries to the DAG under the name NIX_PATH_ADDITIONS

Type: list of package

Default: [ ]

Declared by:

hosts.<name>.nvim-host.flagSeparator

Separator between flag names and values when generating args from flags. " " for --flag value or "=" for --flag=value

Type: string

Default: " "

Declared by:

hosts.<name>.nvim-host.flags

Flags to pass to the wrapper. The key is the flag name, the value is the flag value. If the value is true, the flag will be passed without a value. If the value is false or null, the flag will not be passed. If the value is a list, the flag will be passed multiple times with each value.

This option takes a set.

Any entry can instead be of type { data, before ? [], after ? [], esc-fn ? null, sep ? null }

The sep field may be used to override the value of config.flagSeparator

This will cause it to be added to the DAG, which will cause the resulting wrapper argument to be sorted accordingly

Type: attribute set of spec with main field: `data` of (null or boolean or str|path|drv or list of str|path|drv)

Default: { }

Example:

{
  "--config" = "\${./nixPath}";
}

Declared by:

hosts.<name>.nvim-host.flags.<name>.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.flags.<name>.sep

A per-item override of the default separator used for flags and their values

Type: null or string

Default: null

Declared by:

hosts.<name>.nvim-host.prefixContent

[
  [ "ENV" "SEP" "FILE" ]
]

Prefix ENV with contents of FILE and SEP at build time.

Also accepts sets like the other options

[
  [ "ENV" "SEP" "FILE" ]
  { data = [ "ENV" "SEP" "FILE" ]; esc-fn = lib.escapeShellArg; /* name, before, after */ }
]

Type: list of spec with main field: `data` of (List of length 3)

Default: [ ]

Declared by:

hosts.<name>.nvim-host.prefixContent.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.prefixVar

–prefix ENV SEP VAL

Prefix ENV with VAL, separated by SEP.

Type: list of spec with main field: `data` of (List of length 3)

Default: [ ]

Example:

[
  [
    "LD_LIBRARY_PATH"
    ":"
    "\${lib.makeLibraryPath (with pkgs; [ ... ])}"
  ]
  [
    "PATH"
    ":"
    "\${lib.makeBinPath (with pkgs; [ ... ])}"
  ]
]

Declared by:

hosts.<name>.nvim-host.prefixVar.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.runShell

–run COMMAND

Run COMMAND before executing the main program.

This option takes a list.

Any entry can instead be of type { data, name ? null, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG.

If no name is provided, it cannot be targeted.

Type: list of spec with main field: `data` of str|path|drv

Default: [ ]

Declared by:

hosts.<name>.nvim-host.runShell.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.runtimeLibraries

Additional libraries to add to the wrapper’s runtime LD_LIBRARY_PATH. This is useful if the wrapped program needs additional libraries or tools to function correctly.

Adds all its entries to the DAG under the name NIX_LIB_ADDITIONS

Type: list of package

Default: [ ]

Declared by:

hosts.<name>.nvim-host.suffixContent

[
  [ "ENV" "SEP" "FILE" ]
]

Suffix ENV with SEP and then the contents of FILE at build time.

Also accepts sets like the other options

[
  [ "ENV" "SEP" "FILE" ]
  { data = [ "ENV" "SEP" "FILE" ]; esc-fn = lib.escapeShellArg; /* name, before, after */ }
]

Type: list of spec with main field: `data` of (List of length 3)

Default: [ ]

Declared by:

hosts.<name>.nvim-host.suffixContent.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.suffixVar

–suffix ENV SEP VAL

Suffix ENV with VAL, separated by SEP.

Type: list of spec with main field: `data` of (List of length 3)

Default: [ ]

Example:

[
  [
    "LD_LIBRARY_PATH"
    ":"
    "\${lib.makeLibraryPath (with pkgs; [ ... ])}"
  ]
  [
    "PATH"
    ":"
    "\${lib.makeBinPath (with pkgs; [ ... ])}"
  ]
]

Declared by:

hosts.<name>.nvim-host.suffixVar.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.unsetVar

–unset VAR

Remove VAR from the environment.

Type: list of spec with main field: `data` of string

Default: [ ]

Declared by:

hosts.<name>.nvim-host.unsetVar.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

hosts.<name>.nvim-host.var_path

The path to be added to vim.g.<enabled_variable>

By default, the result of wrapping nvim-host.package with the other nvim-host.* options in the context of the outer neovim wrapper will be used.

Type: non-empty line

Default: "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9/bin/nvim-‹name›"

Declared by:

hosts.<name>.nvim-host.wrapperImplementation

the nix implementation is the default

It makes the escapingFunction most relevant.

This is because the shell and binary implementations use pkgs.makeWrapper or pkgs.makeBinaryWrapper, and arguments to these functions are passed at BUILD time.

So, generally, when not using the nix implementation, you should always prefer to have escapingFunction set to lib.escapeShellArg.

However, if you ARE using the nix implementation, using wlib.escapeShellArgWithEnv will allow you to use $ expansions, which will expand at runtime.

binary implementation is useful for programs which are likely to be used in “shebangs”, as macos will not allow scripts to be used for these.

However, it is more limited. It does not have access to runShell, prefixContent, and suffixContent options.

Chosing binary will thus cause values in those options to be ignored.

Type: one of “nix”, “shell”, “binary”

Default: "nix"

Declared by:

hosts.<name>.outputs

Override the list of nix outputs that get symlinked into the final package.

Default is config.package.outputs or [ "out" ] if invalid.

Type: non-empty list of string

Default:

[
  "out"
]

Declared by:

hosts.<name>.overrides

the list of .override and .overrideAttrs to apply to config.package

Accessing config.package will return the package with all overrides applied.

Accepts a list of { data, type ? null, name ? null, before ? [], after ? [] }

If type == null then data must be a function. It will receive and return the package.

If type is a string like override or overrideAttrs, it represents the attribute of config.package to pass the data field to.

If a raw value is given, it will be used as the data field, and type will be null.

config.package = pkgs.mpv;
config.overrides = [
  { # If they don't have a name they cannot be targeted!
    type = "override";
    after = [ "MPV_SCRIPTS" ];
    data = (prev: {
      scripts = (prev.scripts or []) ++ [ pkgs.mpvScripts.visualizer ];
    });
  }
  {
    name = "MPV_SCRIPTS";
    type = "override";
    data = (prev: {
      scripts = (prev.scripts or []) ++ [ pkgs.mpvScripts.modernz ];
    });
  }
  # the default `type` is `null`
  (pkg: pkg.override (prev: {
    scripts = (prev.scripts or []) ++ [ pkgs.mpvScripts.autocrop ];
  }))
  {
    type = null;
    before = [ "MPV_SCRIPTS" ];
    data = (pkg: pkg.override (prev: {
      scripts = (prev.scripts or []) ++ config.scripts;
    }));
  }
  { # It was already after "MPV_SCRIPTS" so this will stay where it is
    type = "overrideAttrs";
    after = [ "MPV_SCRIPTS" ];
    data = prev: {
      name = prev.name + "-wrapped";
    };
  }
];

The above will add config.scripts, then modernz then visualizer and finally autocrop

Then it will add -wrapped to the end of config.package’s name attribute.

The sort will not always put the value directly after the targeted value, it fulfils the requested before or after dependencies and no more.

You can modify the specs!

The type supports type merging, so you may redeclare it in order to add more options or change default values.

{ config, lib, wlib, pkgs, ... }:{
  options.overrides = lib.mkOption {
    type = wlib.types.seriesOf (wlib.types.spec ({ config, ... }: {
      options = {};
      config = {};
    }));
  };
}

Type: series of spec with main field: `data` of raw value

Default: [ ]

Declared by:

hosts.<name>.overrides.*.after

Items that this spec should be ordered after.

Type: list of string

Default: [ ]

Declared by:

hosts.<name>.overrides.*.before

Items that this spec should be ordered before.

Type: list of string

Default: [ ]

Declared by:

hosts.<name>.overrides.*.data

If type is null, then this is the function to call on the package.

If type is a string, then this is the data to pass to the function corresponding with that attribute.

Type: raw value

Declared by:

hosts.<name>.overrides.*.name

The name for targeting from the before or after fields of other specs.

If null it cannot be targeted by other specs.

Type: null or string

Default: null

Declared by:

hosts.<name>.overrides.*.type

The attribute of config.package to pass the override argument to. If null, then data receives and returns the package instead.

If null, data must be a function. If a string, config.package must have the corresponding attribute, and it must be a function.

Type: null or one of “override”, “overrideAttrs” or string

Default: null

Declared by:

hosts.<name>.passthru

Additional attributes to add to the resulting derivation’s passthru. This can be used to add additional metadata or functionality to the wrapped package. Anything added under the attribute name configuration will be ignored, as that value is used internally.

Type: attrsRecursive

Default: { }

Declared by:

hosts.<name>.pkgs

The nixpkgs pkgs instance to use.

Required in order to access .wrapper attribute, either directly, or indirectly.

Type: unspecified value

Declared by:

hosts.<name>.sourceStdenv

Run the enabled stdenv phases on the wrapper derivation.

NOTE: often you may prefer to use things like drv.doDist = true;, or even drv.phases = [ ... "buildPhase" etc ... ]; instead, to override this choice in a more fine-grained manner

Type: boolean

Default: true

Declared by:

hosts.<name>.wrap

Function to extend the current configuration with additional modules. Can accept a single module, or a list of modules. Re-evaluates the configuration with the original settings plus the new module(s).

Returns the updated package.

Type: function that evaluates to a(n) package (read only)

Default: <function>

Declared by:

hosts.<name>.wrapper

The final wrapped package.

You may still call .eval and the rest on the package again afterwards.

Accessing this value without defining pkgs option, either directly, or via some other means like .wrap, will cause an error.

Type: package (read only)

Default: <derivation hello-2.12.2>

Declared by:

hosts.<name>.wrapperFunction

Arguments:

This option takes a function receiving the following arguments:

module arguments + pkgs.callPackage

{
  config,
  wlib,
  ... # <- anything you can get from pkgs.callPackage
}

The result of this function is passed DIRECTLY to the value of the builderFunction function.

The relative path to the thing to wrap from within config.package is config.exePath

You should wrap the package and place the wrapper at "$out/bin/${config.binName}"

Type: null or (function that evaluates to a(n) raw value)

Default: null

Declared by:

info

This wrapper module generates an info plugin.

Add items here to populate require('nix-info').info

You can change the name nix-info with settings.info_plugin_name

You may get the value of settings.info_plugin_name in lua with

vim.g.nix_info_plugin_name

The info plugin also has a safe indexing helper function.

require(vim.g.nix_info_plugin_name)(
  null, -- default value
  "info", -- start indexing!
  "path",
  "to",
  "nested",
  "info",
  "value" -- will return the value, or the specified default
)

Type: lua value

Default: { }

Declared by:

nvim-lib.mkPlugin

A function used to build plugins not in nixpkgs!

If you had a flake input like:

inputs.treesj = {
  url = "github:Wansmer/treesj";
  flake = false;
};

You could install it like:

config.specs.treesj = {
  data = config.nvim-lib.mkPlugin "treesj" inputs.treesj;
  info = { };
  config = "local info = ...; require('treesj').setup(info);";
};

Or, if you wanted to do the config in your main lua files, just install it:

  config.specs.treesj = config.nvim-lib.mkPlugin "treesj" inputs.treesj;

You can use any fetcher, not just flake inputs, but flake inputs are tracked for you!

Type: function that evaluates to a(n) function that evaluates to a(n) package (read only)

Default: <function>

Declared by:

settings

settings for the neovim derivation.

You may pass extra values just like config.info

These will be made available in neovim via

require('nix-info').settings
require('nix-info')("default_value", "settings", "path", "to", "item")
require(vim.g.nix_info_plugin_name).settings

Type: open submodule of lua value

Default: { }

Declared by:

settings.aliases

Aliases for the package to also be added to the PATH

Type: list of string

Default: [ ]

Declared by:

settings.block_normal_config

By default, we block the config in vim.fn.stdpath('config').

The default settings.config_directory is vim.fn.stdpath('config') so we don’t need to run it twice, and when you wrap it, you usually won’t want config from other sources.

But you may make this false, and if you do so, the normal config directory will still be added to the runtimepath.

However, the init.lua of the normal config directory will not be ran.

Type: boolean

Default: true

Declared by:

settings.config_directory

The directory to use as the new config directory.

May be a wlib.types.stringable or a types.luaInline

Type: str|path|drv or inline lua

Default:

{
  _type = "lua-inline";
  expr = "vim.fn.stdpath('config')";
}

Declared by:

Don’t link extra paths from the neovim derivation to the final output.

This, in conjunction with binName option, allows you to install multiple neovims via home.packages or environment.systemPackages without path collisions.

Type: boolean

Default: false

Declared by:

settings.info_plugin_name

The name to use to require the info plugin

May not be empty, may not contain dots or newlines.

Type: non-empty line

Default: "nix-info"

Declared by:

settings.nvim_lua_env

A function that will be supplied to config.package.lua.withPackages

lp: [ lp.inspect ];

Type: function that evaluates to a(n) list of package

Default: <function>

Declared by:

specCollect

contains a function which takes 2 arguments.

config.specCollect = fn: first: builtins.foldl' fn first mappedSpecs;

mappedSpecs in the above snippet is the result after all config.specMaps have been applied.

You will recieve JUST the specs, unlike config.specMaps, which recieves specs wrapped in an outer set with more info

This function offered by this option allows you to use items collected from the final specs, to provide them to other options.

Type: function that evaluates to a(n) function that evaluates to a(n) list of raw value (read only)

Declared by:

specMaps

supply a DAL list of functions

Each function recieves the WHOLE final list of specs, in a particular format.

Each one recieves [ { name = "attrName"; type = "spec" | "parent"; value = spec; } /* ... */ ]

Each one returns the same structure, but with your alterations.

The returned list will REPLACE the original list for the next function in specMaps, and then for the wrapper for sorting.

specCollect gets the final specs after this has ran and everything has been sorted.

You can only have at most 1 parent per attribute name or it errors.

Be VERY careful with this option.

However, this is your chance to have full control to process options you added via specMods

config.specCollect can only collect, and while it is easier and safer than this option, this option has MUCH more control.

This option gets and returns a list of sets with meta info and the spec for each value in config.specs.

Type: list of spec with main field: `data` of function that evaluates to a(n) list of attribute set of raw value

Default: [ ]

Declared by:

specMaps.*.enable

Enable the function to run on the full list of specs

Type: boolean

Default: true

Declared by:

specMods

extra module for the plugin spec submodules (provided to wlib.dag.dagWith or wlib.dag.dalWith)

These modules recieve parentSpec and parentOpts via specialArgs

If the spec is a parent, this will be null

If the spec is a child, it will be the config argument of the parent spec.

You may use this to change defaults and allow parent overrides of the default to propagate default values to child specs.

config.specMods = { parentSpec, ... }: {
  config.collateGrammars = lib.mkDefault (parentSpec.collateGrammars or false);
};

You could also declare entirely new items for the spec to process in specMaps and specCollect.

Type: module

Default: { }

Declared by:

specMods.enable

Enable the value

If this is in the inner list, then the default value from the parent spec will be used.

Type: boolean

Default: true

Declared by:

specMods.config

A snippet of config for this spec.

Type: null or strings concatenated with “\n”

Default: null

Declared by:

specMods.info

Can be received in .config with local info, _, _ = ...

Type: lua value

Default: { }

Declared by:

specMods.lazy

Can be received in .config with local _, _, lazy = ...

If this is in the inner list, then the default value from the parent spec will be used.

Type: boolean

Default: false

Declared by:

specMods.pname

Can be received in .config with local _, pname, _ = ...

Type: null or string

Default: null

Declared by:

specMods.type

The language for the config field of this spec. (Only applies to the config field, not the info field)

If this is in the inner list, then the default value from the parent spec will be used.

Type: one of “lua”, “fnl”, “vim”

Default: "lua"

Declared by:

specs

Plugins are provided via the config.specs option.

It takes a set of plugins, or a set of lists of plugins.

Everything that takes a plugin can instead be a spec

This means you could pass a direct plugin or a spec with the plugin as its .data field.

For the outer attribute set, this means the value or the .data field may be a plugin (a stringable value), null, or a list of specs

And for the contained lists, the values or .data fields may be a plugin or null

Many options when set in the outer set will propagate to the contained lists.

For example, the value for lazy does this, allowing you to specify a list of plugins all to be loaded lazily by default.

This is controlled by the specMods option.

# Direct plugin path
config.specs.gitsigns = pkgs.vimPlugins.gitsigns-nvim;

config.specs.treesj = {
  data = pkgs.vimPlugins.treesj;
  config = "require('treesj').setup({})";
};

# Spec with info values (in fennel!)
config.specs.lualine = {
  data = pkgs.vimPlugins.lualine-nvim;
  type = "fnl";
  info = { # mkLuaInline in info still just makes lua even if its fennel type
    theme = lua.mkLuaInline "[[catppuccin]]";
  };
  # but here we can use fennel!
  config = ''
    (local (opts name) ...)
    ((. (require "lualine") setup) {
      :options { :theme info.theme }
    })
  '';
};

# List of specs (DAL inside the DAG)
config.specs.completion-plugins = {
  lazy = true; # lazy will propagate to the contained specs.
  data = [
    {
      name = "blink-cmp";
      data = pkgs.vimPlugins.blink-cmp;
    }
    # values can be specs or plugins here too!
    # some values will propagate from the parent.
    # you can change this, or add your own options via `config.specMods`!
    pkgs.vimPlugins.fzf-lua-nvim;
  ];
};

Built-in Spec Fields

  • data: The plugin package to install
  • config: Configuration code (lua by default, can be vimscript or fennel)
  • info: Lua values to be accessed in config via local info, pname, lazy = ...
  • lazy: Whether to load the plugin lazily (default: false) Propagates to child specs.
  • type: Language for config field - "lua", "vim", or "fnl" (default: "lua") Propagates to child specs.
  • pname: Optional package name, accessible in config
  • enable: Enable or disable this spec (default: true) Propagates to child specs.
  • name: Allows this spec to be referenced by the before and after fields of other specs
  • before: A list of specs which this spec will run its configuration before
  • after: A list of specs which this spec will run its configuration after

…and also some extra ones set via specMods by default.

  • pluginDeps: Install plugins from .dependencies attribute on this plugin Propagates to child specs. Default "startup", allows "lazy" and false as well.
  • collateGrammars: Collate the grammars of all plugins in this spec into a single grammar Propagates to child specs. Default true.
  • runtimeDeps: Install values from .runtimeDeps attribute on this plugin to the PATH. Propagates to child specs. Default "suffix", allows "prefix" and false as well.
  • autoconfig: Add configuration code from .passthru.initLua attribute on this plugin. Propagates to child specs. Value is of type boolean, with a default of true.

TIP

The main init.lua of your config directory is added to the specs DAG under the name INIT_MAIN.

By default, the specs will run after it. Add before = [ "INIT_MAIN" ] to the spec to run before it.

Using specMods for Customization

The specMods option allows you to define extra options and processing for specs.

It receives parentSpec and parentOpts via specialArgs, allowing child specs to access and inherit from their parents.

These values will be null if the spec is a parent spec, and contain the config and options arguments of their parent spec if they are in a contained list instead.

config.specMods = { parentSpec, ... }: {
  # declare more spec fields you can process either here, or after with other options!
  options.myopt = lib.mkOption {
    type = lib.types.bool;
    default = parentOpts.myopt or false;
    desc = "A description for myopt";
  };
  # Or change a default!
  config.collateGrammars = parentSpec.collateGrammars or false;
  config.type = parentSpec.type or "fnl";
};

These extra modules will be provided to the modules argument that creates the specs from wlib.types.specWith

You may then need to process the backend of these new options via config.specMaps or config.specCollect.

This is more complex, with specCollect being the next simplest option followed by specMaps which is hardest, but it gives amazing flexibility for adding behaviors!

Type: attribute set of spec with main field: `data` of (null or str|path|drv or list of spec with main field: `data` of (null or str|path|drv))

Default: { }

Declared by:

specs.<name>.enable

Enable the value

If this is in the inner list, then the default value from the parent spec will be used.

Type: boolean

Default: true

Declared by:

specs.<name>.config

A snippet of config for this spec.

Type: null or strings concatenated with “\n”

Default: null

Declared by:

specs.<name>.info

Can be received in .config with local info, _, _ = ...

Type: lua value

Default: { }

Declared by:

specs.<name>.lazy

Can be received in .config with local _, _, lazy = ...

If this is in the inner list, then the default value from the parent spec will be used.

Type: boolean

Default: false

Declared by:

specs.<name>.pname

Can be received in .config with local _, pname, _ = ...

Type: null or string

Default: null

Declared by:

specs.<name>.type

The language for the config field of this spec. (Only applies to the config field, not the info field)

If this is in the inner list, then the default value from the parent spec will be used.

Type: one of “lua”, “fnl”, “vim”

Default: "lua"

Declared by:

modules/makeWrapper:

addFlag

Wrapper for

–add-flag ARG

Prepend the single argument ARG to the invocation of the executable, before any command-line arguments.

This option takes a list. To group them more strongly, option may take a list of lists as well.

Any entry can instead be of type { data, name ? null, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG.

If no name is provided, it cannot be targeted.

Type: list of spec with main field: `data` of (str|path|drv or list of str|path|drv)

Default: [ ]

Example:

[
  "-v"
  "-f"
  [
    "--config"
    "\${./storePath.cfg}"
  ]
  [
    "-s"
    "idk"
  ]
]

Declared by:

addFlag.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

appendFlag

–append-flag ARG

Append the single argument ARG to the invocation of the executable, after any command-line arguments.

This option takes a list. To group them more strongly, option may take a list of lists as well.

Any entry can instead be of type { data, name ? null, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG.

If no name is provided, it cannot be targeted.

Type: list of spec with main field: `data` of (str|path|drv or list of str|path|drv)

Default: [ ]

Example:

[
  "-v"
  "-f"
  [
    "--config"
    "\${./storePath.cfg}"
  ]
  [
    "-s"
    "idk"
  ]
]

Declared by:

appendFlag.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

argv0

–argv0 NAME

Set the name of the executed process to NAME. If unset or null, defaults to EXECUTABLE.

overrides the setting from argv0type if set.

Type: null or string

Default: null

Declared by:

argv0type

argv0 overrides this option if not null or unset

Both shell and the nix implementations ignore this option, as the shell always resolves $0

However, the binary implementation will use this option

Values:

  • "inherit":

The executable inherits argv0 from the wrapper. Use instead of --argv0 '$0'.

  • "resolve":

If argv0 does not include a “/” character, resolve it against PATH.

  • Function form: str -> str

This one works only in the nix implementation. The others will treat it as inherit

Rather than calling exec, you get the command plus all its flags supplied, and you can choose how to run it.

e.g. command_string: "eval \"$(${command_string})\";

It will also be added to the end of the overall DAL, with the name NIX_RUN_MAIN_PACKAGE

Thus, you can make things run after it, but by default it is still last.

Type: one of “resolve”, “inherit” or function that evaluates to a(n) string

Default: "inherit"

Declared by:

chdir

–chdir DIR

Change working directory before running the executable. Use instead of --run "cd DIR".

Type: list of spec with main field: `data` of str|path|drv

Default: [ ]

Declared by:

chdir.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

env

Environment variables to set in the wrapper.

This option takes a set.

Any entry can instead be of type { data, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG, which will cause the resulting wrapper argument to be sorted accordingly

Type: attribute set of spec with main field: `data` of str|path|drv

Default: { }

Example:

{
  XDG_DATA_HOME = "/somewhere/on/your/machine";
}

Declared by:

env.<name>.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

envDefault

Environment variables to set in the wrapper.

Like env, but only adds the variable if not already set in the environment.

This option takes a set.

Any entry can instead be of type { data, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG, which will cause the resulting wrapper argument to be sorted accordingly

Type: attribute set of spec with main field: `data` of str|path|drv

Default: { }

Example:

{
  XDG_DATA_HOME = "/only/if/not/set";
}

Declared by:

envDefault.<name>.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

escapingFunction

The function to use to escape shell values

Caution: When using shell or binary implementations, these will be expanded at BUILD time.

You should probably leave this as is when using either of those implementations.

However, when using the nix implementation, they will expand at runtime! Which means wlib.escapeShellArgWithEnv may prove to be a useful substitute!

Type: function that evaluates to a(n) string

Default: "lib.escapeShellArg"

Declared by:

extraPackages

Additional packages to add to the wrapper’s runtime PATH. This is useful if the wrapped program needs additional libraries or tools to function correctly.

Adds all its entries to the DAG under the name NIX_PATH_ADDITIONS

Type: list of package

Default: [ ]

Declared by:

flagSeparator

Separator between flag names and values when generating args from flags. " " for --flag value or "=" for --flag=value

Type: string

Default: " "

Declared by:

flags

Flags to pass to the wrapper. The key is the flag name, the value is the flag value. If the value is true, the flag will be passed without a value. If the value is false or null, the flag will not be passed. If the value is a list, the flag will be passed multiple times with each value.

This option takes a set.

Any entry can instead be of type { data, before ? [], after ? [], esc-fn ? null, sep ? null }

The sep field may be used to override the value of config.flagSeparator

This will cause it to be added to the DAG, which will cause the resulting wrapper argument to be sorted accordingly

Type: attribute set of spec with main field: `data` of (null or boolean or str|path|drv or list of str|path|drv)

Default: { }

Example:

{
  "--config" = "\${./nixPath}";
}

Declared by:

flags.<name>.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

flags.<name>.sep

A per-item override of the default separator used for flags and their values

Type: null or string

Default: null

Declared by:

prefixContent

[
  [ "ENV" "SEP" "FILE" ]
]

Prefix ENV with contents of FILE and SEP at build time.

Also accepts sets like the other options

[
  [ "ENV" "SEP" "FILE" ]
  { data = [ "ENV" "SEP" "FILE" ]; esc-fn = lib.escapeShellArg; /* name, before, after */ }
]

Type: list of spec with main field: `data` of (List of length 3)

Default: [ ]

Declared by:

prefixContent.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

prefixVar

–prefix ENV SEP VAL

Prefix ENV with VAL, separated by SEP.

Type: list of spec with main field: `data` of (List of length 3)

Default: [ ]

Example:

[
  [
    "LD_LIBRARY_PATH"
    ":"
    "\${lib.makeLibraryPath (with pkgs; [ ... ])}"
  ]
  [
    "PATH"
    ":"
    "\${lib.makeBinPath (with pkgs; [ ... ])}"
  ]
]

Declared by:

prefixVar.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

runShell

–run COMMAND

Run COMMAND before executing the main program.

This option takes a list.

Any entry can instead be of type { data, name ? null, before ? [], after ? [], esc-fn ? null }

This will cause it to be added to the DAG.

If no name is provided, it cannot be targeted.

Type: list of spec with main field: `data` of str|path|drv

Default: [ ]

Declared by:

runShell.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

runtimeLibraries

Additional libraries to add to the wrapper’s runtime LD_LIBRARY_PATH. This is useful if the wrapped program needs additional libraries or tools to function correctly.

Adds all its entries to the DAG under the name NIX_LIB_ADDITIONS

Type: list of package

Default: [ ]

Declared by:

suffixContent

[
  [ "ENV" "SEP" "FILE" ]
]

Suffix ENV with SEP and then the contents of FILE at build time.

Also accepts sets like the other options

[
  [ "ENV" "SEP" "FILE" ]
  { data = [ "ENV" "SEP" "FILE" ]; esc-fn = lib.escapeShellArg; /* name, before, after */ }
]

Type: list of spec with main field: `data` of (List of length 3)

Default: [ ]

Declared by:

suffixContent.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

suffixVar

–suffix ENV SEP VAL

Suffix ENV with VAL, separated by SEP.

Type: list of spec with main field: `data` of (List of length 3)

Default: [ ]

Example:

[
  [
    "LD_LIBRARY_PATH"
    ":"
    "\${lib.makeLibraryPath (with pkgs; [ ... ])}"
  ]
  [
    "PATH"
    ":"
    "\${lib.makeBinPath (with pkgs; [ ... ])}"
  ]
]

Declared by:

suffixVar.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

unsetVar

–unset VAR

Remove VAR from the environment.

Type: list of spec with main field: `data` of string

Default: [ ]

Declared by:

unsetVar.*.esc-fn

A per-item override of the default string escape function

Type: null or (function that evaluates to a(n) string)

Default: null

Declared by:

wrapperImplementation

the nix implementation is the default

It makes the escapingFunction most relevant.

This is because the shell and binary implementations use pkgs.makeWrapper or pkgs.makeBinaryWrapper, and arguments to these functions are passed at BUILD time.

So, generally, when not using the nix implementation, you should always prefer to have escapingFunction set to lib.escapeShellArg.

However, if you ARE using the nix implementation, using wlib.escapeShellArgWithEnv will allow you to use $ expansions, which will expand at runtime.

binary implementation is useful for programs which are likely to be used in “shebangs”, as macos will not allow scripts to be used for these.

However, it is more limited. It does not have access to runShell, prefixContent, and suffixContent options.

Chosing binary will thus cause values in those options to be ignored.

Type: one of “nix”, “shell”, “binary”

Default: "nix"

Declared by:

Tips and Tricks:

The main init.lua of your config directory is added to the specs DAG under the name INIT_MAIN.

By default, the specs will run after it. Add before = [ "INIT_MAIN" ] to the spec to run before it.


  • Your config.settings.config_directory can point to an impure path (or lua inline value)

Use this for a quick feedback mode while editing, and then switch it back to the pure path when you are done! (or make an option for it)


The wrapper makes a lot of information available to you in your lua config via the info plugin!

local nixInfo = require(vim.g.nix_info_plugin_name)
local default = nil
local value = nixInfo(default, "path", "to", "value", "in", "plugin")

It is just a table! Run :=require(vim.g.nix_info_plugin_name) to look at it!

A useful function to see if nix installed a plugin for you is:

local nixInfo = require(vim.g.nix_info_plugin_name)
local function get_nix_plugin_path(name)
  return nixInfo(nil, "plugins", "lazy", name) or nixInfo(nil, "plugins", "start", name)
end

For another example, you might want to tell your info plugin about the top-level specs which you have enabled, which you can do like this in your module:

config.info.cats = builtins.mapAttrs (_: v: v.enable) config.specs;

And then get it in lua with:

local nixInfo = require(vim.g.nix_info_plugin_name)
local cat_is_present = nixInfo(false, "info", "cats", "<specs_attribute_name>")

You could also do similar with

options.settings.cats = lib.mkOption {
  readOnly = true;
  type = lib.types.attrsOf lib.types.raw;
  default = builtins.mapAttrs (_: v: v.enable) config.specs;
};
# nixInfo(false, "settings", "cats", "<specs_attribute_name>")

  • lazy loading

If you mark a spec as lazy, (or mark a parent spec and don’t override the value in the child spec by default), it will be placed in pack/myNeovimPackages/opt/<pname> on the runtime path.

It will not be loaded yet. Use vim.cmd.packadd("<pname>") to load it via lua (or vimscript or fennel) at a time of your choosing.

There are great plugins for this.

See lze and lz.n, which work beautifully with this method of installing plugins.

They also work great with the builtin neovim plugin manager, vim.pack.add!

lze can also be used to do some interesting bulk modifications to your plugins. You might want to disable the ones nix didn’t install automatically, for example. You could use the modify field of a handler with set_lazy = false also set within it to do that, using one of the functions from the previous tip.


  • To use a different version of neovim, set config.package to the version you want to use!
config.package = inputs.neovim-nightly-overlay.packages.${pkgs.stdenv.hostPlatform.system}.neovim;

  • In order to prevent path collisions when installing multiple neovim derivations via home.packages or environment.systemPackages
# set this to true
config.settings.dont_link = true;
# and make sure these dont share values:
config.binName = "nvim";
config.settings.aliases = [ ];

  • Use nvim-lib.mkPlugin to build plugins from sources outside nixpkgs (e.g., git flake inputs)
inputs.treesj = {
  url = "github:Wansmer/treesj";
  flake = false;
};
config.specs.treesj = config.nvim-lib.mkPlugin "treesj" inputs.treesj;

  • Building many plugins from outside nixpkgs at once

In your flake inputs, if you named your inputs like so:

inputs.plugins-treesitter-textobjects = {
  url = "github:nvim-treesitter/nvim-treesitter-textobjects/main";
  flake = false;
};

You could identify them and build them as plugins all at once!

Here is a useful module to import which gives you a helper function in config.nvim-lib for that!

{ config, lib, ... }: {
  options.nvim-lib.pluginsFromPrefix = lib.mkOption {
    type = lib.types.raw;
    readOnly = true;
    default =
      prefix: inputs:
      lib.pipe inputs [
        builtins.attrNames
        (builtins.filter (s: lib.hasPrefix prefix s))
        (map (
          input:
          let
            name = lib.removePrefix prefix input;
          in
          {
            inherit name;
            value = config.nvim-lib.mkPlugin name inputs.${input};
          }
        ))
        builtins.listToAttrs
      ];
  };
}

And then you have access to the plugins like this!:

inputs:
{ config, ... }: let
  neovimPlugins = config.nvim-lib.pluginsFromPrefix "plugins-" inputs;
in {
  imports = [ ./the_above_module.nix ];
  specs.treesitter-textobjects = neovimPlugins.treesitter-textobjects;
}

  • Change defaults and allow parent values to propagate default values to child specs:
config.specMods = { parentSpec, ... }: {
  config.collateGrammars = lib.mkDefault (parentSpec.collateGrammars or false);
};

You have full control over them via the module system! This module will apply to the wlib.types.spec type of both specs in both the outer set and inner lists!

In the outer set, parentSpec is null and in the inner lists, it receives the config argument from the outer set!

It also receives parentOpts, which contains the options argument.


  • You may want to move the installation of things like language servers into your specs. You can do that!
{ config, lib, wlib, ... }: {
  config.specMods = {
    options.extraPackages = lib.mkOption {
      type = lib.types.listOf wlib.types.stringable;
      default = [ ];
      description = "a extraPackages spec field to put packages to suffix to the PATH";
    };
  };
  config.extraPackages = config.specCollect (acc: v: acc ++ (v.extraPackages or [ ])) [ ];
}

  • Use specMaps for advanced spec processing only when specMods and specCollect is not flexible enough.

specMaps has free-reign to modify the whole structure of specs provided as desired after the module evaluation, before specCollect runs, and before the wrapper evaluates the builtin fields of the specs.

Be careful with this option, but an advanced user might use this to preprocess the items in truly amazing ways!

This also means items in specCollect may occasionally be missing fields, do not rely on them being there when using it! Use or to catch indexing errors.


  • Make a new host!
# an attribute set of wrapper modules
config.hosts.neovide =
  {
    lib,
    wlib,
    pkgs,
    ...
  }:
  {
    imports = [ wlib.modules.default ];
    config.nvim-host.enable = lib.mkDefault false;
    config.package = pkgs.neovide;
    # also offers nvim-host wrapper arguments which run in the context of the final nvim drv!
    config.nvim-host.flags."--neovim-bin" = "${placeholder "out"}/bin/${config.binName}";
  };

  # This one is included!
  # To add a wrapped $out/bin/${config.binName}-neovide to the resulting neovim derivation
  config.hosts.neovide.nvim-host.enable = true;

  • Non-nix compatibility:

If you always use the fetcher function form to access items in the plugin from nix, then this mostly takes care of non-nix compatibility. Non-nix compatibility meaning, trying to use the same config directory without using nix to install it.

do
  local ok = pcall(require, vim.g.nix_info_plugin_name)
  if not ok then
    package.loaded[vim.g.nix_info_plugin_name] = setmetatable({}, {
      __call = function (_, default) return default end
    })
  end
  require(vim.g.nix_info_plugin_name).isNix = vim.g.nix_info_plugin_name ~= nil
end

You would have to have a file that installs the plugins with vim.pack.add if not nix and install the lsps some other way.

As a reminder, the fetcher function form is:

local nixInfo = require(vim.g.nix_info_plugin_name)
local default = nil
local value = nixInfo(default, "path", "to", "value", "in", "plugin")

  • Stylix colorscheme integration (requires somewhere with stylix installed to get the colors from):
{ pkgs, ... }: {
  config.specs.base16 = {
    # install a plugin to handle the colors
    data = pkgs.vimPlugins.mini-base16;
    # run before the main init.lua
    before = [ "INIT_MAIN" ];

    # get the colors from your system and pass it
    info = pkgs.lib.filterAttrs (
      k: v: builtins.match "base0[0-9A-F]" k != null
    ) your-system-config.lib.stylix.colors.withHashtag;

    # call the plugin with the colors
    config = /* lua */ ''
      local info, pname, lazy = ...
      require("mini.base16").setup({
        palette = info,
      })
    '';
  };
}