How to setup (neo)vim on NixOS
This post is about how to setup vim (vim_configurable
) on NixOS with reusable
parts and the possibility to replace vim with neovim without rewriting the whole
setup.
configuration.nix
What we have in the configuration.nix
file is simple:
environment.systemPackages = let
vimPackages = import ./some/directory/vimPackages.nix pkgs;
# neovimPackages = import ./some/directory/neovimPackages.nix pkgs;
in
vimPackages ++ with pkgs; [ some other packages ];
You can see that we simply import some nix expressions and pass them the pkgs of
from our configuration.nix
file. Everything else is done in this file and some
sub-files.
vimPackages.nix
So here starts the fun. We define a function which gets the pkgs
variable and
the lib
variable from the outside scope (we don't pass the lib
variable,
though nix is smart enough to figure out what we mean). We already know that we
need to return an array of packages, so we can return vim and some things we
use in our vim scripts, like so:
# ...
[ vim pkgs.ctags ]
vim is not prefixed with a pkgs.
here because we define it in the file itself.
But first, we need to know what we want to override. vim_customize
allows us
to compile vim without GUI support, for example (there are a lot more options).
We also want to build our vimrc
and we want to put plugins into our vim setup,
because this is the NixOS way of life.
customization = {
vimrcConfig = (import ./customization.nix { pkgs = pkgs; });
} // { name = "vim"; };
Here we import our customization (vimrc
, plugins, etc) and ensure that the
executable is named "vim"
. We can now use this customization to customize
vim_configurable
:
custom_vim = pkgs.vim_configurable.customize customization;
and afterwards we override this derivation and set variables, what features should be compiled into vim:
vim = lib.overrideDerivation custom_vim (o: {
ftNixSupport = true;
gui = false;
luaSupport = true;
# some more
});
Now we put these parts together and into a function, so the whole file looks like this:
{ pkgs, lib, ... }:
let
customization = {
vimrcConfig = (import ./vim/customization.nix { pkgs = pkgs; });
} // { name = "vim"; };
custom_vim = pkgs.vim_configurable.customize customization;
vim = lib.overrideDerivation custom_vim (o: {
aclSupport = false;
cscopeSupport = true;
darwinSupport = false;
fontsetSupport = true;
ftNixSupport = true;
gpmSupport = true;
gui = false;
hangulinputSupport = false;
luaSupport = true;
multibyteSupport = true;
mzschemeSupport = true;
netbeansSupport = false;
nlsSupport = false;
perlSupport = false;
pythonSupport = true;
rubySupport = true;
sniffSupport = false;
tclSupport = false;
ximSupport = false;
xsmpSupport = false;
xsmp_interactSupport = false;
});
in [
vim
pkgs.python
pkgs.ctags
]
But that's not all to it. The customization.nix
is where the real fun
begins.
customization.nix
So this is the file where we define which plugins we want to use. There are some plugins which are not yet included in the nixpkgs repository, so we have to build these packages ourselves to be able to install them. Luckily, the nixpkgs provide some helper functions to build vim plugin packages rather easily.
Also, we build our vimrc
file here, which consists of another bunch of script
files.
vimrc.nix
The vimrc
itself is written in viml
of course. If you have a lot of vimrc
configuration, you can split this file into several smaller ones.
For example, I have split these files into the following structure:
./
├── colorschemes
│ ├── mm_neverness_share.vim
│ └── razorlight.vim
├── filetypes.vim
├── folding.vim
├── general.vim
├── gui.vim
├── insertmode.vim
├── mappings.vim
├── menuconf.vim
├── plugins
│ ├── # ... some things
│ └── ycm.vim
├── pluginconfigurations.nix
├── scripts.vim
├── searchnreplace.vim
├── statusline.vim
└── textediting.vim
So what we do in our vimrc.nix
file (which later gets included in our
customization.nix
file) is: We include all these .vim
files and put them
into one giant string, which is then our vimrc
file:
{ stdenv, writeText }:
let
generic = builtins.readFile ./vimrc/general.vim;
textediting = builtins.readFile ./vimrc/textediting.vim;
# ... more here
plug = import ./vimrc/pluginconfigurations.nix;
in
''
${generic}
${textediting}
# ... more here
${plug}
''
You might noticed the vimrc/pluginconfigurations.nix
file. This one is just
another layer of abstraction here and is basically the same as the vimrc.nix
file:
let
# some files
ycm = builtins.readFile ./plugins/ycm.vim;
in
''
# some variables
${ycm}
''
plugins.nix
Another file we need is the plugins.nix
file. As said, not all vim plugins are
in nixpkgs (yet). So we have to build some vim plugins ourselves (don't get
scared here. This does not mean you have to run compilejobs yourself, most
plugins don't contain any binaries so rebuilding the system with vim plugins
installed is normally a matter of seconds).
So this one is also rather simple in the end. In the example below you can see
how I build the vim-trailing-whitespace
plugin. The version might be outdated,
though.
{ pkgs, fetchgit }:
let
buildVimPlugin = pkgs.vimUtils.buildVimPluginFrom2Nix;
in {
"vim-trailing-whitespace" = buildVimPlugin {
name = "vim-trailing-whitespace";
src = fetchgit {
url = "https://github.com/bronson/vim-trailing-whitespace";
rev = "d4ad27de051848e544e360482bdf076b154de0c1";
sha256 = "594769a6f901407609b635a5041966456bfd91b13437169a4562857544e1dca3";
};
dependencies = [];
};
# more?
}
Finally... put the parts together.
Now we can write down our customization.nix
file, which imports the
plugins.nix
and vimrc.nix
files and puts them in the right context, so the
nixpkgs infrastructure understands how we want to have our vim package build:
{ pkgs }:
let
# this is the vimrc.nix from above
vimrc = pkgs.callPackage ./vimrc.nix {};
# and the plugins.nix from above
plugins = pkgs.callPackage ./plugins.nix {};
in
{
customRC = vimrc;
vam = {
knownPlugins = pkgs.vimPlugins // plugins;
pluginDictionaries = [
# from pkgs.vimPlugins
{ name = "youcompleteme"; }
# from our own plugin package set
{ name = "vim-trailing-whitespace"; }
];
};
}
Building
After putting these things together you can rebuild your system. The vim
binary might be compiled from source, though this isn't that much of a problem
because there are only few vim updates, so rebuilding vim will happen every four
weeks or something, not on every system rebuild.
nixos-rebuild switch
neovim
As said, you can now use the infrastructure we just wrote to compile the very same configuration into neovim, including your vimrc, plugins and everything.
{ pkgs, lib, ... }:
let
nvim = pkgs.neovim.override {
# don't alias neovim to vim, yet.
vimAlias = false;
configure = (import ./vim/customization.nix { pkgs = pkgs; });
};
in [
nvim
pkgs.python
pkgs.ctags
]
Just make sure your vimrc
settings don't mess with neovim. Some things are not
supported by neovim, so you have to put them into if-else
conditions like so:
if has("nvim")
" we don't need this in nvim
else
set clipboard=exclude:.*
endif
And that's it. tags: #nix #nixos #vim #neovim #programming