diff --git a/clap_complete/examples/completion-derive.rs b/clap_complete/examples/completion-derive.rs index bbaaf25fa46..baf142fb24a 100644 --- a/clap_complete/examples/completion-derive.rs +++ b/clap_complete/examples/completion-derive.rs @@ -12,18 +12,30 @@ //! . ./value_hints_derive.fish //! ./target/debug/examples/value_hints_derive -- //! ``` -use clap::{Command, CommandFactory, Parser, ValueHint}; +use clap::{Args, Command, CommandFactory, Parser, Subcommand, ValueHint}; use clap_complete::{generate, Generator, Shell}; use std::ffi::OsString; use std::io; use std::path::PathBuf; #[derive(Parser, Debug, PartialEq)] -#[command(name = "value_hints_derive")] +#[command(name = "completion-derive")] struct Opt { - /// If provided, outputs the completion file for given shell + // If provided, outputs the completion file for given shell #[arg(long = "generate", value_enum)] generator: Option, + #[clap(subcommand)] + command: Option, +} + +#[derive(Subcommand, Debug, PartialEq)] +enum Commands { + #[clap(visible_alias = "hint")] + ValueHint(ValueHintOpt), +} + +#[derive(Args, Debug, PartialEq)] +struct ValueHintOpt { // Showcasing all possible ValueHints: #[arg(long, value_hint = ValueHint::Unknown)] unknown: Option, diff --git a/clap_complete/examples/completion.rs b/clap_complete/examples/completion.rs index cf3d315a9f1..0455d4f9c1f 100644 --- a/clap_complete/examples/completion.rs +++ b/clap_complete/examples/completion.rs @@ -17,12 +17,8 @@ use clap_complete::{generate, Generator, Shell}; use std::io; fn build_cli() -> Command { - Command::new("value_hints") - .arg( - Arg::new("generator") - .long("generate") - .value_parser(value_parser!(Shell)), - ) + let value_hint_command = Command::new("value-hint") + .visible_alias("hint") .arg( Arg::new("unknown") .long("unknown") @@ -87,7 +83,15 @@ fn build_cli() -> Command { Arg::new("email") .long("email") .value_hint(ValueHint::EmailAddress), + ); + + Command::new("completion") + .arg( + Arg::new("generator") + .long("generate") + .value_parser(value_parser!(Shell)), ) + .subcommand(value_hint_command) } fn print_completions(gen: G, cmd: &mut Command) { diff --git a/clap_complete/src/generator/utils.rs b/clap_complete/src/generator/utils.rs index 0a2c5620c2b..ca76d189baa 100644 --- a/clap_complete/src/generator/utils.rs +++ b/clap_complete/src/generator/utils.rs @@ -39,10 +39,6 @@ pub fn subcommands(p: &Command) -> Vec<(String, String)> { let mut subcmds = vec![]; - if !p.has_subcommands() { - return subcmds; - } - for sc in p.get_subcommands() { let sc_bin_name = sc.get_bin_name().unwrap(); diff --git a/clap_complete/src/shells/bash.rs b/clap_complete/src/shells/bash.rs index e1429c77ba9..e110537e5f6 100644 --- a/clap_complete/src/shells/bash.rs +++ b/clap_complete/src/shells/bash.rs @@ -31,8 +31,8 @@ impl Generator for Bash { for i in ${{COMP_WORDS[@]}} do - case \"${{i}}\" in - \"$1\") + case \"${{cmd}},${{i}}\" in + \",$1\") cmd=\"{cmd}\" ;;{subcmds} *) @@ -75,26 +75,52 @@ complete -F _{name} -o bashdefault -o default {name} fn all_subcommands(cmd: &Command) -> String { debug!("all_subcommands"); - let mut subcmds = vec![String::new()]; - let mut scs = utils::all_subcommands(cmd) - .iter() - .map(|x| x.0.clone()) - .collect::>(); - - scs.sort(); - scs.dedup(); + fn add_command( + parent_fn_name: &str, + cmd: &Command, + subcmds: &mut Vec<(String, String, String)>, + ) { + let fn_name = format!( + "{parent_fn_name}__{cmd_name}", + parent_fn_name = parent_fn_name, + cmd_name = cmd.get_name().to_string().replace('-', "__") + ); + subcmds.push(( + parent_fn_name.to_string(), + cmd.get_name().to_string(), + fn_name.clone(), + )); + for alias in cmd.get_visible_aliases() { + subcmds.push(( + parent_fn_name.to_string(), + alias.to_string(), + fn_name.clone(), + )); + } + for subcmd in cmd.get_subcommands() { + add_command(&fn_name, subcmd, subcmds); + } + } + let mut subcmds = vec![]; + let fn_name = cmd.get_name().replace('-', "__"); + for subcmd in cmd.get_subcommands() { + add_command(&fn_name, subcmd, &mut subcmds); + } + subcmds.sort(); - subcmds.extend(scs.iter().map(|sc| { - format!( - "{name}) - cmd+=\"__{fn_name}\" + let mut cases = vec![String::new()]; + for (parent_fn_name, name, fn_name) in subcmds { + cases.push(format!( + "{parent_fn_name},{name}) + cmd=\"{fn_name}\" ;;", - name = sc, - fn_name = sc.replace('-', "__") - ) - })); + parent_fn_name = parent_fn_name, + name = name, + fn_name = fn_name, + )); + } - subcmds.join("\n ") + cases.join("\n ") } fn subcommand_details(cmd: &Command) -> String { diff --git a/clap_complete/src/shells/zsh.rs b/clap_complete/src/shells/zsh.rs index d4fc63ba711..9730229a7fc 100644 --- a/clap_complete/src/shells/zsh.rs +++ b/clap_complete/src/shells/zsh.rs @@ -156,9 +156,7 @@ fn subcommands_of(p: &Command) -> String { help = escape_help(&subcommand.get_about().unwrap_or_default().to_string()) ); - if !text.is_empty() { - ret.push(text); - } + ret.push(text); } // The subcommands diff --git a/clap_complete/tests/common.rs b/clap_complete/tests/common.rs index 1e029923f60..ab52fe93a71 100644 --- a/clap_complete/tests/common.rs +++ b/clap_complete/tests/common.rs @@ -151,6 +151,7 @@ pub fn sub_subcommands_command(name: &'static str) -> clap::Command { feature_sample_command(name).subcommand( clap::Command::new("some_cmd") .about("top level subcommand") + .visible_alias("some_cmd_alias") .subcommand( clap::Command::new("sub_cmd").about("sub-subcommand").arg( clap::Arg::new("config") diff --git a/clap_complete/tests/snapshots/aliases.bash b/clap_complete/tests/snapshots/aliases.bash index a9949102be5..0ca8e493f5d 100644 --- a/clap_complete/tests/snapshots/aliases.bash +++ b/clap_complete/tests/snapshots/aliases.bash @@ -8,8 +8,8 @@ _my-app() { for i in ${COMP_WORDS[@]} do - case "${i}" in - "$1") + case "${cmd},${i}" in + ",$1") cmd="my__app" ;; *) diff --git a/clap_complete/tests/snapshots/basic.bash b/clap_complete/tests/snapshots/basic.bash index 08b27e0bbde..70946e79ca7 100644 --- a/clap_complete/tests/snapshots/basic.bash +++ b/clap_complete/tests/snapshots/basic.bash @@ -8,15 +8,21 @@ _my-app() { for i in ${COMP_WORDS[@]} do - case "${i}" in - "$1") + case "${cmd},${i}" in + ",$1") cmd="my__app" ;; - help) - cmd+="__help" + my__app,help) + cmd="my__app__help" ;; - test) - cmd+="__test" + my__app,test) + cmd="my__app__test" + ;; + my__app__help,help) + cmd="my__app__help__help" + ;; + my__app__help,test) + cmd="my__app__help__test" ;; *) ;; diff --git a/clap_complete/tests/snapshots/feature_sample.bash b/clap_complete/tests/snapshots/feature_sample.bash index 2d1ec386ce4..8ea49145b5a 100644 --- a/clap_complete/tests/snapshots/feature_sample.bash +++ b/clap_complete/tests/snapshots/feature_sample.bash @@ -8,15 +8,21 @@ _my-app() { for i in ${COMP_WORDS[@]} do - case "${i}" in - "$1") + case "${cmd},${i}" in + ",$1") cmd="my__app" ;; - help) - cmd+="__help" + my__app,help) + cmd="my__app__help" ;; - test) - cmd+="__test" + my__app,test) + cmd="my__app__test" + ;; + my__app__help,help) + cmd="my__app__help__help" + ;; + my__app__help,test) + cmd="my__app__help__test" ;; *) ;; diff --git a/clap_complete/tests/snapshots/quoting.bash b/clap_complete/tests/snapshots/quoting.bash index b831012b5d3..e387c0bc52a 100644 --- a/clap_complete/tests/snapshots/quoting.bash +++ b/clap_complete/tests/snapshots/quoting.bash @@ -8,30 +8,51 @@ _my-app() { for i in ${COMP_WORDS[@]} do - case "${i}" in - "$1") + case "${cmd},${i}" in + ",$1") cmd="my__app" ;; - cmd-backslash) - cmd+="__cmd__backslash" + my__app,cmd-backslash) + cmd="my__app__cmd__backslash" ;; - cmd-backticks) - cmd+="__cmd__backticks" + my__app,cmd-backticks) + cmd="my__app__cmd__backticks" ;; - cmd-brackets) - cmd+="__cmd__brackets" + my__app,cmd-brackets) + cmd="my__app__cmd__brackets" ;; - cmd-double-quotes) - cmd+="__cmd__double__quotes" + my__app,cmd-double-quotes) + cmd="my__app__cmd__double__quotes" ;; - cmd-expansions) - cmd+="__cmd__expansions" + my__app,cmd-expansions) + cmd="my__app__cmd__expansions" ;; - cmd-single-quotes) - cmd+="__cmd__single__quotes" + my__app,cmd-single-quotes) + cmd="my__app__cmd__single__quotes" ;; - help) - cmd+="__help" + my__app,help) + cmd="my__app__help" + ;; + my__app__help,cmd-backslash) + cmd="my__app__help__cmd__backslash" + ;; + my__app__help,cmd-backticks) + cmd="my__app__help__cmd__backticks" + ;; + my__app__help,cmd-brackets) + cmd="my__app__help__cmd__brackets" + ;; + my__app__help,cmd-double-quotes) + cmd="my__app__help__cmd__double__quotes" + ;; + my__app__help,cmd-expansions) + cmd="my__app__help__cmd__expansions" + ;; + my__app__help,cmd-single-quotes) + cmd="my__app__help__cmd__single__quotes" + ;; + my__app__help,help) + cmd="my__app__help__help" ;; *) ;; diff --git a/clap_complete/tests/snapshots/special_commands.bash b/clap_complete/tests/snapshots/special_commands.bash index 11f8c186c36..31b5354aa7b 100644 --- a/clap_complete/tests/snapshots/special_commands.bash +++ b/clap_complete/tests/snapshots/special_commands.bash @@ -8,24 +8,39 @@ _my-app() { for i in ${COMP_WORDS[@]} do - case "${i}" in - "$1") + case "${cmd},${i}" in + ",$1") cmd="my__app" ;; - help) - cmd+="__help" + my__app,help) + cmd="my__app__help" ;; - some-cmd-with-hyphens) - cmd+="__some__cmd__with__hyphens" + my__app,some-cmd-with-hyphens) + cmd="my__app__some__cmd__with__hyphens" ;; - some-hidden-cmd) - cmd+="__some__hidden__cmd" + my__app,some-hidden-cmd) + cmd="my__app__some__hidden__cmd" ;; - some_cmd) - cmd+="__some_cmd" + my__app,some_cmd) + cmd="my__app__some_cmd" ;; - test) - cmd+="__test" + my__app,test) + cmd="my__app__test" + ;; + my__app__help,help) + cmd="my__app__help__help" + ;; + my__app__help,some-cmd-with-hyphens) + cmd="my__app__help__some__cmd__with__hyphens" + ;; + my__app__help,some-hidden-cmd) + cmd="my__app__help__some__hidden__cmd" + ;; + my__app__help,some_cmd) + cmd="my__app__help__some_cmd" + ;; + my__app__help,test) + cmd="my__app__help__test" ;; *) ;; diff --git a/clap_complete/tests/snapshots/sub_subcommands.bash b/clap_complete/tests/snapshots/sub_subcommands.bash index 69793078c62..4c2573a77ab 100644 --- a/clap_complete/tests/snapshots/sub_subcommands.bash +++ b/clap_complete/tests/snapshots/sub_subcommands.bash @@ -8,21 +8,45 @@ _my-app() { for i in ${COMP_WORDS[@]} do - case "${i}" in - "$1") + case "${cmd},${i}" in + ",$1") cmd="my__app" ;; - help) - cmd+="__help" + my__app,help) + cmd="my__app__help" ;; - some_cmd) - cmd+="__some_cmd" + my__app,some_cmd) + cmd="my__app__some_cmd" ;; - sub_cmd) - cmd+="__sub_cmd" + my__app,some_cmd_alias) + cmd="my__app__some_cmd" ;; - test) - cmd+="__test" + my__app,test) + cmd="my__app__test" + ;; + my__app__help,help) + cmd="my__app__help__help" + ;; + my__app__help,some_cmd) + cmd="my__app__help__some_cmd" + ;; + my__app__help,test) + cmd="my__app__help__test" + ;; + my__app__help__some_cmd,sub_cmd) + cmd="my__app__help__some_cmd__sub_cmd" + ;; + my__app__some_cmd,help) + cmd="my__app__some_cmd__help" + ;; + my__app__some_cmd,sub_cmd) + cmd="my__app__some_cmd__sub_cmd" + ;; + my__app__some_cmd__help,help) + cmd="my__app__some_cmd__help__help" + ;; + my__app__some_cmd__help,sub_cmd) + cmd="my__app__some_cmd__help__sub_cmd" ;; *) ;; diff --git a/clap_complete/tests/snapshots/sub_subcommands.zsh b/clap_complete/tests/snapshots/sub_subcommands.zsh index 922613ba209..7d853d5d4e8 100644 --- a/clap_complete/tests/snapshots/sub_subcommands.zsh +++ b/clap_complete/tests/snapshots/sub_subcommands.zsh @@ -151,6 +151,7 @@ _my-app_commands() { local commands; commands=( 'test:tests things' \ 'some_cmd:top level subcommand' \ +'some_cmd_alias:top level subcommand' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'my-app commands' commands "$@" diff --git a/clap_complete/tests/snapshots/value_hint.bash b/clap_complete/tests/snapshots/value_hint.bash index 2d3d88bae86..11fb39624fc 100644 --- a/clap_complete/tests/snapshots/value_hint.bash +++ b/clap_complete/tests/snapshots/value_hint.bash @@ -8,8 +8,8 @@ _my-app() { for i in ${COMP_WORDS[@]} do - case "${i}" in - "$1") + case "${cmd},${i}" in + ",$1") cmd="my__app" ;; *)