diff --git a/Cargo.lock b/Cargo.lock index 53a802305b..bcb4aa2bb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,6 +118,15 @@ dependencies = [ "textwrap", ] +[[package]] +name = "clap_complete" +version = "3.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f7a2e0a962c45ce25afce14220bc24f9dade0a1787f185cecf96bfba7847cd8" +dependencies = [ + "clap", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -217,6 +226,7 @@ dependencies = [ "bumpalo", "cc", "clap", + "clap_complete", "const_format", "crossterm", "glob", diff --git a/Cargo.toml b/Cargo.toml index 0f4b54ecee..ca3e55b1ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" line-numbers = "0.3.0" smallvec = "1.13.2" +clap_complete = "3.2.5" [dev-dependencies] # assert_cmd 2.0.10 requires predicates 3. diff --git a/src/options.rs b/src/options.rs index 774da35869..0b6685f02d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -8,6 +8,7 @@ use std::{ }; use clap::{crate_authors, crate_description, Arg, Command}; +use clap_complete::{generate, Shell}; use const_format::formatcp; use crossterm::tty::IsTty; use itertools::Itertools; @@ -292,6 +293,13 @@ When multiple overrides are specified, the first matching override wins.")) .validator(|s| s.parse::()) .required(false), ) + .arg( + Arg::new("completion").long("completion") + .takes_value(true) + .value_name("SHELL") + .value_parser(clap::value_parser!(Shell)) + .help("Generate completion for a given shell") + ) .arg( Arg::new("paths") .value_name("PATHS") @@ -603,7 +611,11 @@ fn parse_overrides_or_die(raw_overrides: &[String]) -> Vec<(LanguageOverride, Ve /// Parse CLI arguments passed to the binary. pub(crate) fn parse_args() -> Mode { let matches = app().get_matches(); - + if let Some(shell) = matches.get_one::("completion") { + generate(*shell, &mut app(), "difft", &mut std::io::stdout()); + // TODO: What should the exit code be? Should it be defined in src/exit_codes.rs + std::process::exit(0); + } let color_output = match matches.value_of("color").expect("color has a default") { "always" => ColorOutput::Always, "never" => ColorOutput::Never,