diff --git a/doc/nixos.md b/doc/nixos.md new file mode 100644 index 0000000..13ad463 --- /dev/null +++ b/doc/nixos.md @@ -0,0 +1,140 @@ +# NixOS integration + +inshellah provides a NixOS module that automatically generates nushell +completions for all installed packages at system build time. + +## Enabling + +```nix +# In your flake.nix outputs: +{ + nixosConfigurations.myhost = nixpkgs.lib.nixosSystem { + modules = [ + inshellah.nixosModules.default + { + programs.inshellah.enable = true; + } + ]; + }; +} +``` + +Or if importing the module directly: + +```nix +# configuration.nix +{ pkgs, ... }: { + imports = [ ./path/to/inshellah/nix/module.nix ]; + programs.inshellah = { + enable = true; + package = pkgs.inshellah; # or your local build + }; +} +``` + +## What happens at build time + +The module hooks into `environment.extraSetup`, which runs during the +system profile build (the `buildEnv` that creates `/run/current-system/sw`). +At that point, all system packages are merged, so `$out/bin` contains every +executable and `$out/share/man` contains every manpage. + +inshellah runs a single command: + +``` +inshellah generate $out/bin $out/share/man -o $out/share/nushell/vendor/autoload +``` + +This executes a three-phase pipeline: + +### Phase 1: Native completion detection (parallel) + +For each executable, inshellah scans the ELF binary for the string +`completion`. If found, it probes common patterns like +`CMD completions nushell` to see if the program can generate its own +nushell completions. Native output is used verbatim — these are always +higher quality than parsed completions. + +Programs like `niri`, and any Clap/Cobra tool with nushell support, +are handled this way. + +### Phase 2: Manpage parsing (sequential) + +For commands not covered by Phase 1, inshellah parses manpages from +man1 (user commands) and man8 (sysadmin commands). It handles: + +- GNU `.TP` style (coreutils, help2man) +- `.IP` style (curl, hand-written) +- `.PP`+`.RS`/`.RE` style (git, DocBook) +- Nix3 bullet+hyperlink style (`nix run`, `nix build`, etc.) +- Deroff fallback for unusual formats + +SYNOPSIS sections are parsed to detect subcommands: `git-commit.1` +generates `export extern "git commit"`, not `export extern "git-commit"`. + +### Phase 3: --help fallback (parallel) + +Remaining executables without manpages get `--help` (or `-h`) called +with a 200ms timeout. ELF binaries are pre-scanned for the `-h` string +to skip those that don't support help flags. Shell scripts are run +directly (they're fast). Execution is parallelized to available cores. + +### Output + +Each command gets its own `.nu` file in the vendor autoload directory, +module-wrapped: + +```nu +module git-completions { +export extern "git commit" [ + --all(-a) # Automatically stage modified and deleted files + --message(-m): string # Use the given msg as the commit message + ... +] +} + +use git-completions * +``` + +Nushell built-in commands (ls, cd, cp, mv, etc.) are excluded since +nushell provides its own completions. + +### Performance + +On a typical NixOS system (~950 executables, ~1600 manpages): +- Total time: ~4-10 seconds +- Native gzip decompression (camlzip, no process spawning) +- Parallel --help with core-scaled forking +- ELF string scanning to skip ~15% of binaries + +## Module options + +```nix +programs.inshellah = { + enable = true; + + # The inshellah package (set automatically by the flake module) + package = pkgs.inshellah; + + # Where to place generated .nu files + # Default matches nushell's vendor autoload convention + generatedCompletionsPath = "/share/nushell/vendor/autoload"; +}; +``` + +## Troubleshooting + +**Completions not appearing**: Ensure nushell discovers the vendor +autoload path. Check `$env.NU_VENDOR_AUTOLOAD_DIRS` or verify that +`/run/current-system/sw/share` is in `$XDG_DATA_DIRS`. + +**Missing completions for a specific command**: Check if it's a nushell +built-in (`help commands | where name == "thecommand"`). Built-ins are +excluded because nushell serves its own completions for them. + +**Stale completions after update**: Completions regenerate on every +`nixos-rebuild`. If a command changed its flags, rebuild to pick up +the changes. + +**Build-time errors**: Generation failures are non-fatal (`|| true`). +Check `journalctl` for the build log if completions are missing. diff --git a/doc/nushell-integration.md b/doc/nushell-integration.md new file mode 100644 index 0000000..7c6580d --- /dev/null +++ b/doc/nushell-integration.md @@ -0,0 +1,118 @@ +# Using inshellah completions in nushell + +inshellah generates nushell completions from three sources (in priority order): +1. **Native generators** — programs that can emit nushell completions directly +2. **Manpages** — groff/troff manpage parsing +3. **`--help` output** — parsing help text as a fallback + +Each command gets a module-wrapped `.nu` file suitable for nushell's autoload. + +## Quick start + +Generate completions for a single command: + +```sh +# From a manpage +inshellah manpage /usr/share/man/man1/git.1.gz > git.nu + +# From --help output +inshellah help rg > rg.nu + +# Pipe --help text directly +curl --help | inshellah parse-help curl > curl.nu +``` + +## Full system generation + +The `generate` command runs all three strategies across an entire system: + +```sh +mkdir -p ~/completions +inshellah generate /usr/bin /usr/share/man -o ~/completions +``` + +This produces one `.nu` file per command in the output directory. Each file +is module-wrapped: + +```nu +module git-completions { +export extern "git" [ + --all(-a) + --verbose(-v) + ... +] +} + +use git-completions * +``` + +Programs that provide their own nushell completion generators (e.g. +`niri completions nushell`) are detected automatically and their native +output is used instead of parsing. + +## NixOS module + +Enable automatic completion generation at system build time: + +```nix +{ + imports = [ ./path/to/inshellah/nix/module.nix ]; + programs.inshellah.enable = true; +} +``` + +This runs `inshellah generate` during the system profile build, +producing per-command `.nu` files in nushell's vendor autoload path. +Completions are available immediately with no manual configuration. + +The module uses a three-phase pipeline: +1. Scans executables in `$out/bin` for native nushell completion support +2. Parses manpages from `$out/share/man` (man1 and man8 sections) +3. Falls back to `--help` parsing for unmanpaged executables + +All phases run with ELF binary scanning to skip executables that don't +contain help flags, parallel execution scaled to available cores, and +200ms timeouts. + +## Loading completions manually + +### Option A: Autoload directory (recommended) + +Nushell automatically sources `.nu` files from vendor autoload paths +(discovered via `$XDG_DATA_DIRS`) and `~/.config/nushell/autoload/`. + +```sh +mkdir -p ~/.config/nushell/autoload +inshellah generate /usr/bin /usr/share/man -o ~/.config/nushell/autoload +``` + +### Option B: Batch to stdout + +For simpler setups, the `manpage-dir` command writes all completions to +stdout (without module wrapping): + +```sh +inshellah manpage-dir /usr/share/man > ~/.config/nushell/autoload/completions.nu +``` + +## What gets generated + +Each command produces a nushell `extern` block with flags, parameter +types, and descriptions extracted from the source: + +```nu +export extern "rg" [ + --regexp(-e): string # A pattern to search for + --file(-f): path # Search for patterns from the given file + --count(-c) # Only show the count of matching lines + --color: string # Controls when to use color + --max-depth: int # Limit the depth of directory traversal +] +``` + +Subcommand manpages (e.g. `git-commit.1`) are detected via SYNOPSIS +parsing and generate the correct nushell name (`git commit` not +`git-commit`). + +Nushell built-in commands (ls, cd, mv, etc.) are excluded since nushell +provides its own completions for these. diff --git a/doc/runtime-completions.md b/doc/runtime-completions.md new file mode 100644 index 0000000..25373f9 --- /dev/null +++ b/doc/runtime-completions.md @@ -0,0 +1,87 @@ +# Runtime completion caching + +For commands not covered by build-time generation, inshellah can generate +completions on first tab-press and cache the result. + +## Setup + +Add an external completer to your nushell config that calls inshellah +on cache miss: + +```nu +# ~/.config/nushell/autoload/inshellah-completer.nu + +const INSHELLAH_CACHE = ($nu.home-path | path join ".cache" "inshellah") + +def inshellah-complete [spans: list] { + if ($spans | length) == 0 { return [] } + mkdir $INSHELLAH_CACHE + + let max_depth = [($spans | length) 5] | math min + mut cmd_spans = [] + + for depth in (1..($max_depth)) { + let candidate = ($spans | first $depth) + let cache_key = ($candidate | str join "-") + let cache_file = ($INSHELLAH_CACHE | path join $"($cache_key).nu") + + if ($cache_file | path exists) { + $cmd_spans = $candidate + break + } + + try { + let result = (run-external "inshellah" "help" ...($candidate) | complete) + if $result.exit_code == 0 and ($result.stdout | str length) > 10 { + $result.stdout | save -f $cache_file + $cmd_spans = $candidate + break + } + } + } + + if ($cmd_spans | length) > 0 { + let cache_key = ($cmd_spans | str join "-") + let cache_file = ($INSHELLAH_CACHE | path join $"($cache_key).nu") + if ($cache_file | path exists) { source $cache_file } + } +} +``` + +Wire it in: + +```nu +# ~/.config/nushell/config.nu +$env.config.completions.external = { + enable: true + completer: {|spans| inshellah-complete $spans } +} +``` + +## How it works + +When you type `docker compose up --`: + +1. Nushell calls the completer with `spans = ["docker", "compose", "up", "--"]` +2. The completer tries progressively deeper prefixes as cache keys +3. On cache miss, runs `inshellah help docker compose up` +4. Caches the result; all subsequent completions are instant + +First tab-press latency is ~100-200ms. Depth is capped at 5 levels. + +## Cache management + +```sh +rm -rf ~/.cache/inshellah/ # Clear all +ls ~/.cache/inshellah/ # List cached +inshellah help docker run > ~/.cache/inshellah/docker-run.nu # Regenerate one +``` + +## When to use this vs build-time generation + +The NixOS module (`programs.inshellah.enable = true`) handles most commands +at build time. Runtime caching is useful for: + +- Commands installed outside the system profile (cargo, pip, npm, go) +- Subcommand completions at arbitrary depth +- Systems without the NixOS module