Skip to content

Improve shell completion script generation (bash, zsh, fish) #399

@stalep

Description

@stalep

Summary

Improve aesh's shell completion script generation to support bash, zsh (native), and fish shells with full command model coverage. The existing FileCompleterGenerator provides basic bash support but has significant gaps that prevent real-world CLI tools (like jbang) from generating usable completion scripts.

Current State

FileCompleterGenerator exists and generates basic bash completion scripts. It:

  • Generates bash functions per command
  • Handles group commands (subcommands) at one level
  • Completes option names (long and short)
  • Includes boolean and default-value completions
  • Has basic zsh compatibility shims

Gaps

Multi-shell support

  • No fish completion generation — fish uses complete -c commands with conditions, a completely different format
  • No native zsh completion — zsh's compdef/_arguments system is far more powerful than the bash compatibility shim currently used

Missing features in bash generation

  1. Nested subcommand hierarchies — deep command trees (e.g., jbang alias add, jbang template list) need proper function nesting
  2. Argument (positional parameter) completionProcessedOption with type ARGUMENTS is not handled
  3. Negatable options--no-{name} forms not generated despite ProcessedOption.isNegatable() support
  4. Option aliases — when Add alias/alternative names support for @Option #398 is implemented, aliases should appear in completion
  5. Hidden options/commands — should be excluded from completion output
  6. OptionActivator awareness — conditionally available options should ideally not be suggested when inactive
  7. File path completion — options with FileOptionCompleter should trigger file completion in the script rather than static values

Architecture

  • No plugin/strategy pattern — adding a new shell requires modifying core code
  • CompleterCommand is the only entry point; there's no easy programmatic API for one-shot CLI tools using AeshRuntimeRunner

Proposed Design

Shell-specific generators

Create a strategy pattern with a common interface:

public interface ShellCompletionGenerator {
    String generate(CommandLineParser<?> parser, String programName);
}

Implementations:

  • BashCompletionGenerator — improved version of current FileCompleterGenerator
  • ZshCompletionGenerator — native zsh compdef/_arguments format
  • FishCompletionGenerator — fish complete -c format

Fish completion format (reference)

Fish completions use a flat list of complete commands with conditions:

# Subcommands at root level
complete -c jbang -f -n "__fish_use_subcommand" -a "run" -d "Builds and runs provided script"
complete -c jbang -f -n "__fish_use_subcommand" -a "build" -d "Compiles a script"

# Options for a specific subcommand
complete -c jbang -f -n "__fish_seen_subcommand_from run" -l "java" -s "j" -d "JDK version"
complete -c jbang -f -n "__fish_seen_subcommand_from run" -l "debug" -s "d" -d "Launch with debug"

# Nested subcommands
complete -c jbang -f -n "__fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from add list remove" -a "add" -d "Add alias"

Zsh completion format (reference)

#compdef jbang
_jbang() {
    local -a commands
    commands=(
        'run:Builds and runs provided script'
        'build:Compiles a script'
    )
    _arguments -C \
        '1:command:->cmd' \
        '*::arg:->args'
    case $state in
        cmd) _describe 'command' commands ;;
        args) _jbang_${words[1]} ;;
    esac
}

Public API for one-shot tools

Add a utility method accessible without interactive mode:

String script = ShellCompletionGenerator.generate(
    ShellType.FISH, commandClass, "jbang");

Or integrate with AeshRuntimeRunner:

AeshRuntimeRunner.builder()
    .command(JBang.class)
    .generateCompletion(ShellType.BASH)  // outputs script and exits
    .execute();

Motivation

jbang migrated from picocli to aesh. picocli provided AutoComplete.bash() for bash/zsh completion. jbang also had a custom fish completion generator (~260 lines). Both were lost in the migration because aesh's FileCompleterGenerator doesn't cover the needed use cases. Shell completion is a critical user-facing feature for CLI tools.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions