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 -1Every shell gets its own dialect
Ask for fish instead. The tool name stays. The script language changes completely:
./greet completions fish | head -1Example
// 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?