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 \

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 \

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.