🎓 A1Learner
🔍
0 of 149 lessons completed

Rust Shell Completions

Shell completions are suggestions the shell offers when Tab is pressed in a terminal. The shell can also finish a tool's flags.

A completions subcommand

clap_complete is a companion crate. It writes the script for the shell you name. This match arm is the whole engine:

Some(Cmd::Completions { shell }) => {
    let mut cmd = Cli::command();
    generate(shell, &mut cmd, "greet", &mut io::stdout());
}

The script opens with your name

Run the subcommand for bash. The first line names a function after your tool:

./greet completions bash | head -1

Every shell gets its own dialect

Ask for fish instead. The tool name stays. The script language changes completely:

./greet completions fish | head -1

Example

// Cargo.toml: clap = { version = "4", features = ["derive"] }, clap_complete = "4"
use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{generate, Shell};
use std::io;

/// Greet someone, and print shell completions on request
#[derive(Parser)]
#[command(name = "greet")]
struct Cli {
    #[command(subcommand)]
    command: Option<Cmd>,
    /// Who to greet
    name: Option<String>,
}

#[derive(Subcommand)]
enum Cmd {
    /// Print completions for a shell
    Completions {
        /// Which shell
        shell: Shell,
    },
}

fn main() {
    let cli = Cli::parse();
    match cli.command {
        Some(Cmd::Completions { shell }) => {
            let mut cmd = Cli::command();
            generate(shell, &mut cmd, "greet", &mut io::stdout());
        }
        None => {
            let name = cli.name.as_deref().unwrap_or("Ferris");
            println!("Hello, {name}!");
        }
    }
}

Key points

  • Shell completions come from a script that describes the tool's flags.
  • Add clap_complete = "4" beside clap.
  • The two versions move together.
  • The script comes from the same struct as the parsing.
  • Completions cannot drift away from --help.
  • Each shell loads scripts from its own completions folder.
  • The user saves yours there once.
  • Regenerate the script on every release.
  • A stale script completes flags that no longer exist.
  • The shell finishes file names the same way it finishes a tool's flags.

In this example, the run is cargo run -- Ada. No subcommand matches, so main greets Ada by name. Run it as greet with no words and it greets Ferris, the fallback. Run it as greet completions bash and the same binary prints the bash script instead. One struct drives all three runs. The parser, the help text, and the completions can never disagree.

Note

This lesson generates at run time, so one binary does it all.

Some projects generate in build.rs instead, and ship the script beside the binary.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Why generate completions from the clap struct instead of writing the script by hand?