Setting up zsh completions for tools installed via home-manager
A couple of months ago I switched to using home-manager for managing my home folder. Previously, I had a handmade setup, then switched to chezmoi, explored other dotfile managers, but none of them felt right. Except for home-manager, which is simply a great tool. Anyways, recently I found out how I can source all completion functions for the tools managed by the home-manager.
First, why is this a problem? Well, when tools are installed via home-manager’s
home.packages
, they get included in the environment. But the completion
functions do not. This leaves me in an uncomfortable situation that my nicely
tuned shell (zsh) can’t offer me completions for tools that I do use the
most.
In zsh, the directories from where the completion system reads the
completion functions is set via fpath
. For example, if I use zsh-completions
and nix-zsh-completions
plugins, I would have this in the initExtra
stanza
of home.nix
:
fpath=(${config.xdg.configHome}/zsh/plugins/zsh-completions/src \
${config.xdg.configHome}/zsh/plugins/nix-zsh-completions \
$fpath)
However, listing the paths to all completion functions for all tools in the
fpath
is a Sisyphean task, as some tools put their completion functions
to different places. For example, even though the majority of tools put
their completion functions in share/zsh/site-functions
, some tools don’t
(looking at you niv
and kubectl
). To solve the issue, I decided to collect
all completion functions and place them in vendor-completions
folder:
fpath=(${config.xdg.configHome}/zsh/plugins/zsh-completions/src \
${config.xdg.configHome}/zsh/plugins/nix-zsh-completions \
${config.xdg.configHome}/zsh/vendor-completions \
$fpath)
Now, it’s just a matter of populating this folder. To do so, I created a
derivation, that depends on all packages listed in home.packages
, then finds
all completion functions, and copies them to $out
:
xdg.configFile."zsh/vendor-completions".source = with pkgs;
runCommandNoCC "vendored-zsh-completions" {} ''
mkdir -p $out
${fd}/bin/fd -t f '^_[^.]+$' \
${lib.escapeShellArgs home.packages} \
--exec ${ripgrep}/bin/rg -0l '^#compdef' {} \
| xargs -0 cp -t $out/
'';
And that’s it. The script looks for all files that begin with an underscore
and then picks up those files that contain word compdef
inside.