update documentation for generate command and NixOS module
Rewrote nushell-integration.md to cover the three-strategy pipeline, module wrapping, and the generate command. Simplified runtime-completions.md. Added nixos.md with detailed explanation of the build-time generation phases, module options, and troubleshooting.
This commit is contained in:
parent
7f0ec8ab4d
commit
e1974a7c1a
3 changed files with 345 additions and 0 deletions
140
doc/nixos.md
Normal file
140
doc/nixos.md
Normal file
|
|
@ -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.
|
||||
118
doc/nushell-integration.md
Normal file
118
doc/nushell-integration.md
Normal file
|
|
@ -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.
|
||||
87
doc/runtime-completions.md
Normal file
87
doc/runtime-completions.md
Normal file
|
|
@ -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<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" ...($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 --<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 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue