inshellah/doc/runtime-completions.md
2026-03-23 17:03:20 +11:00

3.1 KiB

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:

# ~/.config/nushell/autoload/inshellah-completer.nu

const INSHELLAH_CACHE = ($nu.home-path | path join ".cache" "inshellah")

def inshellah-complete [spans: list<string>] {
    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" "--iterative" ...($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:

# ~/.config/nushell/config.nu
$env.config.completions.external = {
    enable: true
    completer: {|spans| inshellah-complete $spans }
}

How it works

When you type docker compose up --<TAB>:

  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 --iterative docker compose up
  4. Caches the result; all subsequent completions are instant

The --iterative flag tells inshellah to resolve only the immediate level without recursing into subcommands. This keeps each cache miss fast (~100-200ms) and lets the completer progressively resolve deeper levels on demand. Without --iterative, inshellah help recursively resolves all subcommands, which is better for upfront generation but too slow for interactive tab-completion.

Depth is capped at 5 levels.

Cache management

rm -rf ~/.cache/inshellah/          # Clear all
ls ~/.cache/inshellah/              # List cached
inshellah help --iterative 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

For one-off upfront generation (not runtime caching), use inshellah help without --iterative to recursively resolve all subcommands at once:

inshellah help docker > docker.nu  # Resolves all subcommands recursively