inshellah/nix/module.nix
2026-03-18 18:19:37 +11:00

109 lines
3.6 KiB
Nix

# NixOS module: automatic nushell completion generation from manpages
#
# Modeled on nixpkgs' programs/fish.nix completion generator.
# For each package in environment.systemPackages, a small derivation runs
# `inshellah manpage-dir` against its share/man directory. Results are merged
# into a single directory and placed in nushell's vendor autoload path.
#
# Usage in your NixOS configuration:
#
# { pkgs, ... }: {
# imports = [ ./path/to/inshellah/nix/module.nix ];
# programs.inshellah.enable = true;
# # Optionally add packages not in systemPackages:
# # programs.inshellah.extraPackages = [ pkgs.kubectl ];
# }
{ config, lib, pkgs, ... }:
let
cfg = config.programs.inshellah;
# The inshellah binary — override this if you build from the local flake
inshellahPkg = cfg.package;
# Per-package derivation: run inshellah manpage-dir against a package's manpages
generateCompletions = package:
pkgs.runCommandLocal
(let
inherit (lib.strings) stringLength substring storeDir;
storeLength = stringLength storeDir + 34;
pathName = substring storeLength (stringLength package - storeLength) package;
in
(package.name or pathName) + "_nu-completions"
)
({
inherit package;
nativeBuildInputs = [ inshellahPkg ];
} // lib.optionalAttrs (package ? meta.priority) {
meta.priority = package.meta.priority;
})
''
mkdir -p $out
if [ -d "$package/share/man" ]; then
inshellah manpage-dir "$package/share/man" > "$out/completions.nu" 2>/dev/null || true
# Remove empty files
find $out -empty -delete
fi
'';
in {
options.programs.inshellah = {
enable = lib.mkEnableOption "nushell completion generation from manpages via inshellah";
package = lib.mkOption {
type = lib.types.package;
description = "The inshellah package to use for generating completions.";
};
extraPackages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [];
description = ''
Additional packages to generate nushell completions from, beyond
those in {option}`environment.systemPackages`.
'';
};
generatedCompletionsPath = lib.mkOption {
type = lib.types.str;
default = "/share/nushell/vendor/autoload";
description = ''
Subdirectory within the merged environment where completion files
are placed. The default matches nushell's vendor autoload convention
(discovered via XDG_DATA_DIRS).
'';
};
};
config = lib.mkIf cfg.enable {
# Merge all per-package completions into a single directory.
# This path ends up in the system profile, and nushell discovers it
# via XDG_DATA_DIRS -> $prefix/share/nushell/vendor/autoload/
environment.pathsToLink = [ cfg.generatedCompletionsPath ];
environment.systemPackages = [
(pkgs.buildEnv {
name = "nushell-generated-completions";
ignoreCollisions = true;
paths = map generateCompletions (
config.environment.systemPackages ++ cfg.extraPackages
);
pathsToLink = [ "/" ];
# Nest everything under the vendor autoload path
postBuild = ''
if [ -d "$out" ]; then
tmp=$(mktemp -d)
cp -r "$out/"* "$tmp/" 2>/dev/null || true
rm -rf "$out/"*
mkdir -p "$out${cfg.generatedCompletionsPath}"
for f in "$tmp"/*.nu; do
[ -f "$f" ] && cp "$f" "$out${cfg.generatedCompletionsPath}/"
done
rm -rf "$tmp"
fi
'';
})
];
};
}