# NixOS module: automatic nushell completion indexing # # Indexes completions using three strategies in priority order: # 1. Native completion generators (e.g. CMD completions nushell) # 2. Manpage parsing # 3. --help output parsing # # Produces a directory of .json/.nu files at build time. # The `complete` command reads from this directory as a system overlay. # # Usage: # { pkgs, ... }: { # imports = [ ./path/to/inshellah/nix/module.nix ]; # programs.inshellah.enable = true; # } { config, lib, pkgs, ... }: let cfg = config.programs.inshellah; in { options.programs.inshellah = { enable = lib.mkEnableOption "nushell completion indexing via inshellah"; package = lib.mkOption { type = lib.types.package; description = "package to use for indexing completions"; }; completionsPath = lib.mkOption { type = lib.types.str; default = "/share/inshellah"; description = '' subdirectory within the system profile where completion files are placed. used as --dir for the completer. ''; }; extraDirs = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "/etc/profiles/per-user/alice/share/inshellah" ]; description = '' additional read-only completion directories to search. these are appended (colon-separated) to the --dir path alongside the system completions path. ''; }; ignoreCommands = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "problematic-tool" ]; description = '' list of command names to skip during completion indexing ''; }; helpOnlyCommands = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; example = [ "nix" ]; description = '' list of command names to skip manpage parsing for, using --help scraping instead ''; }; snippet = lib.mkOption { type = lib.types.str; readOnly = true; }; }; config = lib.mkIf cfg.enable { environment.systemPackages = let systemDir = "/run/current-system/sw${cfg.completionsPath}"; dirPaths = lib.concatStringsSep ":" ([ systemDir ] ++ cfg.extraDirs); wrapped = pkgs.writeShellScriptBin "inshellah" '' case "''${1:-}" in complete|query|dump) exec ${cfg.package}/bin/inshellah "$@" --dir "''${XDG_CACHE_HOME:-$HOME/.cache}/inshellah:${dirPaths}" ;; *) exec ${cfg.package}/bin/inshellah "$@" ;; esac ''; in [ (lib.hiPrio wrapped) cfg.package ]; environment.pathsToLink = [ "/share/nushell/autoload" ]; environment.extraSetup = let inshellah = "${cfg.package}/bin/inshellah"; destDir = "$out${cfg.completionsPath}"; ignoreFile = pkgs.writeText "inshellah-ignore" (lib.concatStringsSep "\n" cfg.ignoreCommands); ignoreFlag = lib.optionalString (cfg.ignoreCommands != [ ]) " --ignore ${ignoreFile}"; helpOnlyFile = pkgs.writeText "inshellah-help-only" ( lib.concatStringsSep "\n" cfg.helpOnlyCommands ); helpOnlyFlag = lib.optionalString (cfg.helpOnlyCommands != [ ]) " --help-only ${helpOnlyFile}"; in '' mkdir -p ${destDir} if [ -d "$out/bin" ] && [ -d "$out/share/man" ]; then ${inshellah} index "$out" --dir ${destDir}${ignoreFlag}${helpOnlyFlag} \ 2>/dev/null || true fi find ${destDir} -maxdepth 1 -empty -delete # nushell hardcodes sudo and doas to bypass the external completer, # returning command-name completion instead of calling inshellah. # these @complete external stubs override that so inshellah handles # their flags and elevation stripping. placed in the nushell autoload # dir so they are sourced automatically at shell startup. mkdir -p $out/share/nushell/vendor/autoload cat > $out/share/nushell/vendor/autoload/inshellah-elevation.nu << 'NUSHELL' @complete external extern "sudo" [] @complete external extern "doas" [] NUSHELL ''; }; }