diff --git a/Cargo.toml b/Cargo.toml index 2ce487b..7fab803 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ async-trait = "0.1" repos-github = { path = "common/repos-github" } clap = { version = "4.4", features = ["derive"] } +clap_complete = "4.4" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" serde_json = "1.0" diff --git a/README.md b/README.md index 5fe71fb..8fdb652 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,38 @@ cargo build --release sudo cp target/release/repos /usr/local/bin/ ``` +### Shell Completions + +`repos` can generate shell completions for zsh, bash, fish, PowerShell, and elvish. + +#### Zsh + +```bash +# Generate completions +repos completions zsh > ~/.zsh/completions/_repos + +# Or for Oh My Zsh +mkdir -p ~/.oh-my-zsh/custom/plugins/repos-completions +repos completions zsh > ~/.oh-my-zsh/custom/plugins/repos-completions/_repos + +# Add to your .zshrc +fpath=(~/.zsh/completions $fpath) +autoload -Uz compinit && compinit +``` + +#### Bash + +```bash +repos completions bash > ~/.repos-completion.bash +echo 'source ~/.repos-completion.bash' >> ~/.bashrc +``` + +#### Fish + +```bash +repos completions fish > ~/.config/fish/completions/repos.fish +``` + ## Quick Start The easiest way to get started is to let `repos` generate a configuration file diff --git a/src/main.rs b/src/main.rs index 15336b8..dc3da52 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ use anyhow::Result; -use clap::{Parser, Subcommand}; +use clap::{CommandFactory, Parser, Subcommand}; +use clap_complete::{Shell, generate}; use repos::commands::validators; use repos::{commands::*, config::Config, constants, plugins}; -use std::{env, path::PathBuf}; +use std::{env, io, path::PathBuf}; #[derive(Parser)] #[command(name = "repos")] @@ -193,6 +194,13 @@ enum Commands { supplement: bool, }, + /// Generate shell completions + Completions { + /// Shell to generate completions for + #[arg(value_enum)] + shell: Shell, + }, + /// External plugin command #[command(external_subcommand)] External(Vec), @@ -221,6 +229,11 @@ async fn main() -> Result<()> { // Handle commands match cli.command { + Some(Commands::Completions { shell }) => { + let mut cmd = Cli::command(); + generate(shell, &mut cmd, "repos", &mut io::stdout()); + return Ok(()); + } Some(Commands::External(args)) => { if args.is_empty() { anyhow::bail!("External command provided but no arguments given"); @@ -502,6 +515,10 @@ async fn execute_builtin_command(command: Commands) -> Result<()> { .execute(&context) .await?; } + Commands::Completions { .. } => { + // Handled in main(), this should not be reached + unreachable!("Completions command should be handled in main()") + } } Ok(())