My experience when switching to NixOS – Part 4
So I promised I will explain my vim setup in this part of the series. And of course, I will do this.
The requirements
The requirements are fairly easy here. I wanted to be able to use YouCompleteMe with vim, so I need a vim with Python support enabled. So, I absolutely need the configurable vim package from the NixOS package tree and I clearly must override it. And when doing this step, I can remove all the stuff from the vim package I do not need.
Another requirement was that I can replace vim with neovim. I've not done this yet, but I guess it should be fairly easy.
Because not all vim plugins are in the NixOS packages, I need to be able to build my own packages outside of the NixOS package tree and pack them into my store as well.
And, because vim needs to know all these plugins, I must be able to override
the runtimepath variable in the vim configuration, the vimrc. So, I need to
be able to compile the vimrc out of the package definition as well.
The setup
The directory setup here is fairly easy. I have my normal configuration folder
whereas I created a folder vim inside the pkgs folder:
config/nixos/ | +– ./pkgs/ | +– vim.nix +– vim | +– override.nix +– pkgPlugins.nix +– plugins.nix +– vimrc/ | +– colorscheme.vim +– filetypes.vim +– ... +– vimrc.nix
Let me elaborate what is located where here.
vim.nix
The top-level file for the vim packaging. This file is actually included in my
machine-specific configuration or in the pkgs/basePackages.nix file, so my
vim is always installed on every system. The latter is the way I want to do
this, so I have the same vim on all systems.
This file returns a list.
vim/override.nix
This file simply contains the overridden vim package.
This file returns a package / derivation.
vim/pkgPlugins.nix
This file contains the already packaged plugins. I do not have to compile them, as they are already in the NixOS package tree, which makes certain things simple but others more complex, we will see why.
This file returns a list.
vim/plugins.nix
Self-packaged plugins. Period.
This file returns a list.
vim/vimrc.nix
This file uses all files in pkgs/vim/vimrc/*.vim and compiles them into one
giant string and writes this one as vimrc file to the store. Actually, there
is a pkgs/vim/vimrc/plugins.nix file in the directory which compiles
pkgs/vim/vimrc/plugins/*.vim files into a string and returns that string,
which simply gets included in the vimrc. I did this for abstraction, so the
configuration for each file lives into the appropriate subdirectory.
The How
Lets start with the easy parts here first. To override vim, you don't really have to do much things. What you have to do can be explained best with the appropriate nix file:
Override vim
{ pkgs, lib, vimrc }:
let vim = lib.overrideDerivation pkgs.vimconfigurable (o: { aclSupport = false; cscopeSupport = true; darwinSupport = false; fontsetSupport = true; ftNixSupport = true; gpmSupport = true; 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; xsmpinteractSupport = false;
postInstall = (o.postInstall or “”) + '' ln -sf “${vimrc}” “$out/share/vim/vimrc” ''; }); in vim
Of course, you have to make sure that this is the only occurance of vim in your package setup. And if you install vim in a user environment, it will override this as well, so be warned!
So I ripped out everything I don't need here, which is for example tcl
support. I don't even know what tcl is, actually.
You also need to override the postInstall hook to ensure you link the
vimrc to the appropriate path. The vimrc file must be passed to this
function, as you can see in the first line.
Packaged plugins
The second thing which is fairly easy is the file for the plugins which are already in the NixOS package repository. What I do here is simple:
{ stdenv , pkgs }:
[ pkgs.vimPlugins.youcompleteme # pkgs.vimPlugins. # ... ]
And yes, it is that simple. I will not share my entire config files here, of course. You don't need to know what kind of plugins I use in my vim setup!
More plugins!
Now comes the more difficult part. I actually copied these definitins from aszlig:
{ stdenv , pkgs , lib , writeTextFile , writeText , buildEnv , fetchgit , vim_configurable }:
with stdenv;
let
# ... here were some things I did not need
mkVimPlugins = plugins: buildEnv { name = “vim-plugins”; paths = with lib; mapAttrsToList (const id) plugins; ignoreCollisions = true; postBuild = '' find -L “$out” -mindepth 1 -maxdepth 1 -type f -delete ''; };
pluginDeps = { };
plugins = mkVimPlugins (pluginDeps // {
vimvim-fugitive = fetchgit { url = “https://github.com/tpope/vim-fugitive"; rev = “”; sha256 = “”; };
# ...
});
in [plugins]
As you can see, this file is a bit longer than the previous ones. I only
inserted the fugitive plugin for posting here, obviousely. So how does this
work? Each plugin gets fetched with git (for fetching from vimscripts or other
methods of fetching, look at aszligs repo,
this file, actually).
After it was fetched from git, it is inserted in a set which is afterwards fed
to the mkVimPlugins function, which adds all the plugins to one package. All
plugin pathes are added to the package then. Why you need the
ignoreCollisions = true; line... I'm not sure but I guess it ensures that
two plugins can have files with same names. I'm also not sure about the
postBuild hook.
But that's how it is supposed to work. It returns a list of plugins, then.
Lets build a vimrc!
Yes, I actually compile my vimrc out of nix expressions. That's not that hard,
it is actually string concatenation, because I do write .vim files.
{ stdenv, writeText, additionalRTP }:
let
generic = builtins.readFile ./vimrc/general.vim; # ... statusline = builtins.readFile ./vimrc/statusline.vim; # ...
plugins = import ./vimrc/plugins.nix;
in
writeText “vimrc” '' ${generic}
set runtimepath+=${additionalRTP} set runtimepath+=${additionalRTP}/after set runtimepath+=${additionalRTP}/share/vim-plugins
${statusline}
${plugins} ''
That's really easy: I read the files which contain the vim configurations (general configuration, statusline, etc) and put them all together.
What I add as well: The additional runtime path. This is actually required because when installing vim packages which are not in the package tree of NixOS by now, vim cannot find these. When adding the additional runtime path here, I tell vim to look in these directories as well, ensuring that the plugins can be found.
The last point is obviousely one point I have to clean up, because I guess there should be a better way how to do this. As you might notice when setting up your vim configuration on NixOS, there is also an plugin manager for vim you can use from within NixOS/the nix packages. It gives you lazy loading of vim plugins and all these nice things you want to have. I'm not sure how this fits into the way how NixOS packages vim plugins, I guess I have to dig around here a bit.
Anyhow, it works. On my testing machine, which is an rather old machine (not that old, but also not the new shit, you know) vim starts up in about 1 second. This is obviousely way to much for a machine I want to use for developing software, but for this machine it is okay.
Maybe this will get better after cleaning up the setup, I'm not sure.
In the next part of the series, I will explain my bash(rc) setup, which is handled by nix, of course. Maybe I can also elaborate on my vim setup, if I managed to clean up the mentioned parts. tags: #linux #nixos