Skip to content

Commit e2f78c8

Browse files
committed
added docs for dynamic completions
1 parent 28d38ad commit e2f78c8

2 files changed

Lines changed: 163 additions & 5 deletions

File tree

content/docs/aesh/completers.md

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,19 @@ private List<String> commandNames;
158158

159159
## Shell Completion Script Generation
160160

161-
Aesh can generate static completion scripts for **bash**, **zsh**, and **fish** shells. These scripts provide tab completion for your CLI tool's command names, option names, option aliases, negated forms, and default values without requiring a running JVM.
161+
Aesh can generate completion scripts for **bash**, **zsh**, and **fish** shells. There are two approaches:
162+
163+
- **Static scripts** — complete option names, subcommand names, and default values without a running JVM. Fast and zero-overhead, but cannot run custom `OptionCompleter` logic.
164+
- **Dynamic callback scripts** — thin shell shims that call back to your program at tab-time via `--aesh-complete`, running the full aesh completion engine (including custom completers). Ideal for GraalVM native images where startup is near-instant.
165+
166+
### Static vs Dynamic
167+
168+
| | Static | Dynamic |
169+
|---|---|---|
170+
| Custom `OptionCompleter` support | No | Yes |
171+
| JVM required at tab-time | No | Yes |
172+
| Startup cost per Tab press | None | ~10ms (native) / ~200ms (JVM) |
173+
| Best for | Option names, default values | Runtime-dependent completions |
162174

163175
### Supported Shells
164176

@@ -168,9 +180,11 @@ Aesh can generate static completion scripts for **bash**, **zsh**, and **fish**
168180
| Zsh | `ZshCompletionGenerator` | Native `compdef`/`_arguments` format |
169181
| Fish | `FishCompletionGenerator` | `complete -c` commands with conditions |
170182

171-
### Quick Start with AeshRuntimeRunner
183+
### Static Completion Scripts
184+
185+
#### Quick Start with AeshRuntimeRunner
172186

173-
The simplest way to add completion generation to your CLI tool:
187+
The simplest way to add static completion generation to your CLI tool:
174188

175189
```java
176190
public class MyApp {
@@ -285,3 +299,93 @@ AeshRuntimeRunner.builder()
285299
```
286300

287301
This writes the completion script to a file named `mycommand.fish` (or `.bash`/`.zsh`).
302+
303+
### Dynamic Callback Completion Scripts
304+
305+
Dynamic scripts generate thin shell shims that call back to your Java program at tab-time. When the user presses Tab, the shell invokes `myapp --aesh-complete -- <partial-command-line>`, and aesh's full completion engine runs — including all custom `OptionCompleter` implementations.
306+
307+
#### Quick Start
308+
309+
The simplest approach uses the `handleDynamicCompletion()` helper:
310+
311+
```java
312+
public class MyApp {
313+
public static void main(String[] args) {
314+
// Handle dynamic completion requests (called by the shell script)
315+
if (AeshRuntimeRunner.handleDynamicCompletion(args, MyCommand.class)) {
316+
return;
317+
}
318+
319+
// Check for completion script generation flag
320+
if (args.length > 0 && args[0].equals("--generate-completion")) {
321+
ShellType shell = ShellType.valueOf(args.length > 1 ? args[1] : "BASH");
322+
AeshRuntimeRunner.builder()
323+
.command(MyCommand.class)
324+
.generateDynamicCompletion(shell)
325+
.execute();
326+
return;
327+
}
328+
329+
// Normal execution
330+
AeshRuntimeRunner.builder()
331+
.command(MyCommand.class)
332+
.args(args)
333+
.execute();
334+
}
335+
}
336+
```
337+
338+
```bash
339+
# Generate and install dynamic bash completion
340+
$ myapp --generate-completion BASH > ~/.local/share/bash-completion/completions/myapp
341+
342+
# Generate dynamic zsh completion
343+
$ myapp --generate-completion ZSH > ~/.zsh/completions/_myapp
344+
345+
# Generate dynamic fish completion
346+
$ myapp --generate-completion FISH > ~/.config/fish/completions/myapp.fish
347+
```
348+
349+
#### How It Works
350+
351+
1. You generate a dynamic completion script and install it in your shell
352+
2. When the user presses Tab, the shell script calls `myapp --aesh-complete -- <partial-args>`
353+
3. `handleDynamicCompletion()` detects the `--aesh-complete` flag, runs the completion engine, and prints one candidate per line to stdout
354+
4. The shell script captures the output and presents it as completion candidates
355+
356+
#### One-Shot Dynamic API
357+
358+
Generate a dynamic script from a command class without building a runner:
359+
360+
```java
361+
String bashScript = ShellCompletionGenerator.generateDynamic(
362+
ShellType.BASH, MyCommand.class, "myapp");
363+
364+
String zshScript = ShellCompletionGenerator.generateDynamic(
365+
ShellType.ZSH, MyCommand.class, "myapp");
366+
367+
String fishScript = ShellCompletionGenerator.generateDynamic(
368+
ShellType.FISH, MyCommand.class, "myapp");
369+
```
370+
371+
#### GraalVM Native Images
372+
373+
Dynamic callback completion is ideal for GraalVM native images. Since native binaries start in ~10ms, tab completion feels instant. The generated scripts use the program name directly — no special configuration needed:
374+
375+
```bash
376+
# Build native image (e.g., with Maven)
377+
$ mvn package -Pnative
378+
379+
# Generate and install completion — the native binary handles --aesh-complete directly
380+
$ ./target/myapp --generate-completion BASH > ~/.local/share/bash-completion/completions/myapp
381+
```
382+
383+
For JVM-based tools where users run via `java -jar`, you can use a wrapper script and set `completionProgramName()` to match the wrapper name:
384+
385+
```java
386+
AeshRuntimeRunner.builder()
387+
.command(MyCommand.class)
388+
.generateDynamicCompletion(ShellType.BASH)
389+
.completionProgramName("myapp") // matches the wrapper script name
390+
.execute();
391+
```

content/docs/aesh/runners.md

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,14 @@ public CommandResult execute(CommandInvocation invocation) throws InterruptedExc
470470

471471
### Generating Shell Completion Scripts
472472

473-
`AeshRuntimeRunner` can generate shell completion scripts (bash, zsh, fish) instead of executing the command. When `generateCompletion()` is set, `execute()` prints the completion script to stdout and returns without running the command.
473+
`AeshRuntimeRunner` can generate shell completion scripts (bash, zsh, fish) instead of executing the command. There are two modes:
474+
475+
- **Static** (`generateCompletion()`) — scripts that complete option names, subcommand names, and default values without a running JVM
476+
- **Dynamic** (`generateDynamicCompletion()`) — scripts that call back to the program at tab-time via `--aesh-complete`, running custom `OptionCompleter` logic
477+
478+
#### Static Completion Scripts
479+
480+
When `generateCompletion()` is set, `execute()` prints the static completion script to stdout and returns without running the command.
474481

475482
```java
476483
import org.aesh.AeshRuntimeRunner;
@@ -514,7 +521,54 @@ AeshRuntimeRunner.builder()
514521
.execute();
515522
```
516523

517-
See [Completers - Shell Completion Script Generation](../completers#shell-completion-script-generation) for full details on what gets generated and the supported features.
524+
#### Dynamic Callback Completion
525+
526+
Dynamic scripts call back to your program at tab-time, running the full aesh completion engine including custom `OptionCompleter` implementations. This is ideal for GraalVM native images where startup is near-instant (~10ms).
527+
528+
Use `handleDynamicCompletion()` to detect and handle the `--aesh-complete` callback, and `generateDynamicCompletion()` to generate the shell script:
529+
530+
```java
531+
import org.aesh.AeshRuntimeRunner;
532+
import org.aesh.util.completer.ShellCompletionGenerator.ShellType;
533+
534+
public class MyTool {
535+
public static void main(String[] args) {
536+
// Handle dynamic completion callbacks from the shell
537+
if (AeshRuntimeRunner.handleDynamicCompletion(args, MyCommand.class)) {
538+
return;
539+
}
540+
541+
// Generate dynamic completion script
542+
if (args.length > 0 && args[0].equals("--completions")) {
543+
ShellType shell = args.length > 1
544+
? ShellType.valueOf(args[1].toUpperCase())
545+
: ShellType.BASH;
546+
AeshRuntimeRunner.builder()
547+
.command(MyCommand.class)
548+
.generateDynamicCompletion(shell)
549+
.execute();
550+
return;
551+
}
552+
553+
// Normal execution
554+
AeshRuntimeRunner.builder()
555+
.command(MyCommand.class)
556+
.args(args)
557+
.execute();
558+
}
559+
}
560+
```
561+
562+
```bash
563+
# Generate and install dynamic completions
564+
$ mytool --completions bash > /etc/bash_completion.d/mytool
565+
$ mytool --completions zsh > ~/.zsh/completions/_mytool
566+
$ mytool --completions fish > ~/.config/fish/completions/mytool.fish
567+
```
568+
569+
When the user presses Tab, the shell calls `mytool --aesh-complete -- <partial-args>`, the completion engine runs, and candidates are printed one per line to stdout.
570+
571+
See [Completers - Shell Completion Script Generation](../completers#shell-completion-script-generation) for full details on static vs dynamic scripts, what gets generated, and GraalVM native image support.
518572

519573
### Complete Example
520574

0 commit comments

Comments
 (0)