14 KiB
Neovim
Install neovim-unwrapped to get a bare-bones Neovim to configure imperatively.
This is the closest to what you encounter on other distributions.
neovim is a wrapper around Neovim with some extra configuration, for
instance, to set the various language providers like Python.
The wrapper can be further configured to include your favorite plugins and
configurations for a reproducible neovim across machines.
See the next section for more details.
Custom configuration
There are two wrappers available to provide additional configuration around the vanilla package pkgs.neovim-unwrapped:
wrapNeovim: the historical one you should usewrapNeovimUnstableintended to replace the former. It has more features but the interface is not stable yet.
You can configure the former via:
neovim.override {
withPython3 = true; # see `:h g:python3_host_prog`
withNodeJs = false;
withRuby = false;
configure = {
customRC = ''
# here your custom viml configuration goes!
'';
packages.myVimPackage = with pkgs.vimPlugins; {
# See examples below on how to use custom packages.
start = [ ];
# If a Vim plugin has a dependency that is not explicitly listed in
# `opt`, that dependency will always be added to `start` to avoid confusion.
opt = [ ];
};
};
}
myVimPackage is an arbitrary name for the generated package. You can choose any name you like.
If you want to use neovim-qt as a graphical editor, you can configure it by overriding Neovim in an overlay
or passing it an overridden Neovim:
neovim-qt.override {
neovim = neovim.override {
configure = {
customRC = ''
# your custom viml configuration
'';
};
};
}
You can use the new unstable wrapper but the interface may change:
autoconfigure: certain plugins need a custom configuration to work with Nix. For instance,sqlite-luaneedsg:sqlite_clib_pathto be set to work. Nixpkgs historically patched these in the plugins with several drawbacks: harder maintenance and making upstream work harder. Per convention, these mandatory bits of configuration are bookmarked in nixpkgs inpassthru.initLua. Enablingautoconfigureautomatically adds the snippets required for the plugins to work.autowrapRuntimeDeps: Appends plugin's runtime dependencies toPATH. For instance,rest.nvimrequirescurlto work. EnablingautowrapRuntimeDepsadds it to thePATHvisible by your Neovim wrapper (but not your globalPATH).luaRcContent: Extra lua code to add to the generatedinit.lua.neovimRcContent: Extra vimL code sourced by the generatedinit.lua.wrapperArgs: Extra arguments forwarded to themakeWrappercall.wrapRc: Nix, not being able to write in your$HOME, loads the generated Neovim configuration via the$VIMINITenvironment variable, i.e. :export VIMINIT='lua dofile("/nix/store/…-init.lua")'. This has side effects like preventing Neovim from sourcing yourinit.luain$XDG_CONFIG_HOME/nvim(see bullet 7 of:help startupin Neovim). Disable it if you want to generate your own wrapper. You can still reuse the generated vimscript init code vianeovim.passthru.initRc.plugins: A list of plugins to add to the wrapper.extraLuaPackages: A function passed on tolua.withPackageswithPython3,withNodeJs,withRubycontrol when to enable neovim providers (see:h provider).
wrapNeovimUnstable neovim-unwrapped {
autoconfigure = true;
autowrapRuntimeDeps = true;
luaRcContent = ''
vim.o.sessionoptions = 'buffers,curdir,help,tabpages,winsize,winpos,localoptions'
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
vim.opt.smoothscroll = true
vim.opt.colorcolumn = { 100 }
vim.opt.termguicolors = true
'';
# plugins accepts a list of either plugins or { plugin = ...; config = ..vimscript.. };
plugins = with vimPlugins; [
{
plugin = vim-obsession;
config = ''
map <Leader>$ <Cmd>Obsession<CR>
'';
}
(nvim-treesitter.withPlugins (p: [ p.nix p.python ]))
hex-nvim
];
extraLuaPackages = lp: [ lp.mpack ];
withPython3 = true;
withNodeJs = false;
withRuby = false;
}
You can explore the configuration withnix repl to discover these options and
override them. For instance:
neovim.overrideAttrs (oldAttrs: {
autowrapRuntimeDeps = false;
})
Specificities for some plugins
Plugin optional configuration
Some plugins require specific configuration to work. We choose not to
patch those plugins but expose the necessary configuration under
PLUGIN.passthru.initLua for neovim plugins. For instance, the unicode-vim plugin
needs the path towards a unicode database so we expose the following snippet vim.g.Unicode_data_directory="${self.unicode-vim}/autoload/unicode" under vimPlugins.unicode-vim.passthru.initLua.
Plugin license overrides
Generated Vim and Neovim plugins get their meta.license from GitHub license metadata when possible.
Some upstream repositories do not expose a license file that GitHub can detect, or only mention the license in a README.
In those cases, add a manual meta.license override in overrides.nix.
For example, if upstream documents that a plugin uses the Vim license but GitHub does not detect it:
{
foo-nvim = super.foo-nvim.overrideAttrs (old: {
meta = old.meta // {
# README says this plugin is distributed under the Vim license.
license = lib.licenses.vim;
};
});
}
LuaRocks based plugins
In order to automatically handle plugin dependencies, several Neovim plugins
upload their package to LuaRocks. This means less work for nixpkgs maintainers in the long term as dependencies get updated automatically.
This means several Neovim plugins are first packaged as nixpkgs lua
packages, and converted via buildNeovimPlugin in
a vim plugin. This conversion is necessary because Neovim expects lua folders to be
top-level while LuaRocks installs them in various subfolders by default.
For instance:
{
rtp-nvim = neovimUtils.buildNeovimPlugin { luaAttr = luaPackages.rtp-nvim; };
}
To update these packages, you should use the lua updater rather than vim's.
Treesitter
Treesitter provides syntax parsing for Neovim, enabling features like: Advanced syntax highlighting, Code folding, Indentation and more.
Most Neovim users manage treesitter through the nvim-treesitter plugin, which provides:
- Commands for managing grammars and queries,
e.g.
:TSInstall, which downloads, compiles and installs them at runtime. - A custom indentation implementation (
:h indentexpr) for languages withindents.scmqueries.
These features build on top of treesitter functionality that is built into Neovim.
In nixpkgs, grammars and queries are precompiled and packaged separately. This means:
- You can use treesitter features without installing
nvim-treesitter. - You only need
nvim-treesitterif you want its custom indentation implementation. - Plugins that depend on grammars can reference them directly.
Treesitter setup using nvim-treesitter
::: {.tip}
Choose this approach if you want to use nvim-treesitter's custom indentation expression.
:::
To install nvim-treesitter combined with a set of precompiled grammars,
you can use the nvim-treesitter.withPlugins function:
(pkgs.neovim.override {
configure = {
packages.myPlugins = with pkgs.vimPlugins; {
start = [
(nvim-treesitter.withPlugins (
plugins: with plugins; [
nix
python
]
))
];
};
};
})
To enable all grammars packaged in nixpkgs, use pkgs.vimPlugins.nvim-treesitter.withAllGrammars.
For how to configure nvim-treesitter and set up syntax highlighting, indentation, folding, etc.,
please refer to the :help nvim-treesitter-quickstart plugin documentation.
::: {.note}
When using Nix-managed grammars, :checkhealth nvim-treesitter will report no installed languages.
This is expected behavior because:
- The
nvim-treesitterhealth check searches its configured install directory. - Nix installs grammars to the Nix store and adds them to the
runtimepathinstead.
To verify Nix-managed parsers and queries, use :checkhealth vim.treesitter instead.
:::
Treesitter setup using standalone grammars and queries
::: {.tip} Choose this approach if you
- Want minimal dependencies.
- Don't need
nvim-treesitter's custom indentation expression. :::
You can install the standalone parsers and queries directly without installing nvim-treesitter:
(pkgs.neovim.override {
configure = {
packages.myPlugins =
with pkgs.vimPlugins;
let
# Select the grammars you need
treesitter-grammars = with nvim-treesitter-parsers; [
nix
python
];
# Queries are needed for treesitter based syntax highlighting and folds.
treesitter-queries = map (p: p.associatedQuery) treesitter-grammars;
in
{
start = [
# regular plugins
]
++ treesitter-grammars
++ treesitter-queries;
};
};
})
You can enable treesitter features for installed grammars in a FileType autocommand
or in an ftplugin/<language>.lua script, e.g.
vim.api.nvim_create_autocmd('FileType', {
pattern = { 'rust', 'javascript', 'zig' },
callback = function(ev)
local bufnr = ev.buf
-- Enable treesitter syntax highlighting and parsing for the current buffer
-- (Requires queries to be installed)
vim.treesitter.start(bufnr)
-- Enable treesitter based code folding
-- (folds are window-scoped, not buffer-scoped)
-- (Requires queries to be installed)
vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
vim.wo.foldmethod = 'expr'
end,
})
Treesitter grammars as plugin dependencies
Some Neovim plugins (like neotest adapters, markdoc-nvim, hurl-nvim) depend on treesitter grammars.
These dependencies are usually declared in plugin overrides.
::: {.important}
Some plugin READMEs may suggest that they depend on nvim-treesitter.
This is almost always not the case.
nvim-treesitter no longer provides a Lua module API for other plugins to use.
In the vast majority of cases, these plugins:
- Depend on parsers (not on
nvim-treesitteror its queries). - Bundle their own queries (either as
*.scmfiles or hardcoded in the Lua sources). :::
To add grammars as a plugin dependency, add an override:
{
foo-nvim = super.foo-nvim.overrideAttrs {
dependencies = with self.nvim-treesitter-parsers; [
markdown
markdown_inline
html
];
};
}
If a plugin actually does depend on the nvim-treesitter legacy module API, you can add
nvim-treesitter-legacy as a dependency:
{
foo-legacy-nvim = super.foo-legacy-nvim.overrideAttrs {
dependencies = with self; [
nvim-treesitter-legacy
nvim-treesitter-parsers.nix
];
};
}
::: {.caution}
nvim-treesitter-legacy exists for the purpose of easing transition and will be removed in 26.11.
If a Neovim configuration contains both nvim-treesitter and nvim-treesitter-legacy, it will fail to evaluate.
:::
Testing Neovim plugins
neovimRequireCheck
neovimRequireCheck is a simple test which checks if Neovim can require lua modules without errors. This is often enough to catch missing dependencies.
It accepts a single string for a module, or a list of module strings to test.
nvimRequireCheck = MODULE;nvimRequireCheck = [ MODULE1 MODULE2 ];
When nvimRequireCheck is not specified, we will search the plugin's directory for lua modules to attempt loading. This quick smoke test can catch obvious dependency errors that might be missed.
The check hook will fail the build if any modules cannot be loaded. This encourages inspecting the logs to identify potential issues.
To only check a specific module, add it manually to the plugin definition overrides.
{
gitsigns-nvim = super.gitsigns-nvim.overrideAttrs {
dependencies = [ self.plenary-nvim ];
nvimRequireCheck = "gitsigns";
};
}
Some plugins will have lua modules that require a user configuration to function properly or can contain optional lua modules that we don't want to test by requiring.
We can skip specific modules using nvimSkipModules. Similar to nvimRequireCheck, it accepts a list of strings.
nvimSkipModules = [ MODULE1 MODULE2 ];
{
asyncrun-vim = super.asyncrun-vim.overrideAttrs {
nvimSkipModules = [
# vim plugin with optional toggleterm integration
"asyncrun.toggleterm"
"asyncrun.toggleterm2"
];
};
}
In rare cases, we might not want to actually test loading lua modules for a plugin. In those cases, we can disable neovimRequireCheck with doCheck = false;.
This can be manually added through plugin definition overrides in the overrides.nix.
{
vim-test = super.vim-test.overrideAttrs {
# Vim plugin with a test lua file
doCheck = false;
};
}