From 174e004107c433a6569734041cad71471e09b60c Mon Sep 17 00:00:00 2001 From: ModProg Date: Sat, 21 Aug 2021 00:53:10 +0200 Subject: [PATCH 1/3] fix(generate): Hide option/argument description for argenum This returns `"{a\t,b\t}"` instead of `"a b"` for possible_values completion. Therefore fish displays and therefor hides the empty description after the `\t`. Fixes #2727 --- clap_generate/src/generators/shells/fish.rs | 10 +++++++++- clap_generate/tests/value_hints.rs | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/clap_generate/src/generators/shells/fish.rs b/clap_generate/src/generators/shells/fish.rs index dc5a78692b2..38ee57488f5 100644 --- a/clap_generate/src/generators/shells/fish.rs +++ b/clap_generate/src/generators/shells/fish.rs @@ -133,7 +133,15 @@ fn value_completion(option: &Arg) -> String { } if let Some(data) = option.get_possible_values() { - format!(" -r -f -a \"{}\"", data.join(" ")) + // We return the possible values with their own empty description e.g. {a\t,b\t} + // this makes sure that a and b don't get the description of the option or argument + format!( + " -r -f -a \"{{{}}}\"", + data.iter() + .map(|value| format!("{}\t", value)) + .collect::>() + .join(",") + ) } else { // NB! If you change this, please also update the table in `ValueHint` documentation. match option.get_value_hint() { diff --git a/clap_generate/tests/value_hints.rs b/clap_generate/tests/value_hints.rs index 88e1eea3b4b..38ba80f2565 100644 --- a/clap_generate/tests/value_hints.rs +++ b/clap_generate/tests/value_hints.rs @@ -131,7 +131,7 @@ _my_app_commands() { _my_app "$@""#; -static FISH_VALUE_HINTS: &str = r#"complete -c my_app -l choice -r -f -a "bash fish zsh" +static FISH_VALUE_HINTS: &str = r#"complete -c my_app -l choice -r -f -a "{bash ,fish ,zsh }" complete -c my_app -l unknown -r complete -c my_app -l other -r -f complete -c my_app -s p -l path -r -F From f6ccf07de40d99de3647d43b4d1500dbd07eaa13 Mon Sep 17 00:00:00 2001 From: ModProg Date: Sat, 21 Aug 2021 01:00:51 +0200 Subject: [PATCH 2/3] Fix Formatting --- clap_generate/src/generators/shells/fish.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clap_generate/src/generators/shells/fish.rs b/clap_generate/src/generators/shells/fish.rs index 38ee57488f5..7a3f85c63c7 100644 --- a/clap_generate/src/generators/shells/fish.rs +++ b/clap_generate/src/generators/shells/fish.rs @@ -133,7 +133,7 @@ fn value_completion(option: &Arg) -> String { } if let Some(data) = option.get_possible_values() { - // We return the possible values with their own empty description e.g. {a\t,b\t} + // We return the possible values with their own empty description e.g. {a\t,b\t} // this makes sure that a and b don't get the description of the option or argument format!( " -r -f -a \"{{{}}}\"", From c4d846e897dc744cf5a756a89844503659f3456e Mon Sep 17 00:00:00 2001 From: ModProg Date: Sat, 21 Aug 2021 18:04:43 +0200 Subject: [PATCH 3/3] possible_value_about --- clap_generate/src/generators/shells/bash.rs | 5 +++- clap_generate/src/generators/shells/fish.rs | 5 +++- clap_generate/src/generators/shells/zsh.rs | 33 ++++++++++++++++----- src/build/arg/mod.rs | 26 +++++++++++++--- src/output/help.rs | 2 +- src/parse/validator.rs | 9 ++++-- 6 files changed, 62 insertions(+), 18 deletions(-) diff --git a/clap_generate/src/generators/shells/bash.rs b/clap_generate/src/generators/shells/bash.rs index 09d6d1a7bf2..923dc652506 100644 --- a/clap_generate/src/generators/shells/bash.rs +++ b/clap_generate/src/generators/shells/bash.rs @@ -185,7 +185,10 @@ fn vals_for(o: &Arg) -> String { debug!("vals_for: o={}", o.get_name()); if let Some(vals) = o.get_possible_values() { - format!("$(compgen -W \"{}\" -- \"${{cur}}\")", vals.join(" ")) + format!( + "$(compgen -W \"{}\" -- \"${{cur}}\")", + vals.iter().map(|&(pv, _)| pv).collect::>().join(" ") + ) } else { String::from("$(compgen -f \"${cur}\")") } diff --git a/clap_generate/src/generators/shells/fish.rs b/clap_generate/src/generators/shells/fish.rs index dc5a78692b2..81b26a745b6 100644 --- a/clap_generate/src/generators/shells/fish.rs +++ b/clap_generate/src/generators/shells/fish.rs @@ -133,7 +133,10 @@ fn value_completion(option: &Arg) -> String { } if let Some(data) = option.get_possible_values() { - format!(" -r -f -a \"{}\"", data.join(" ")) + format!( + " -r -f -a \"{}\"", + data.iter().map(|&(pv, _)| pv).collect::>().join(" ") + ) } else { // NB! If you change this, please also update the table in `ValueHint` documentation. match option.get_value_hint() { diff --git a/clap_generate/src/generators/shells/zsh.rs b/clap_generate/src/generators/shells/zsh.rs index 0c2e868d189..cf9a7941df4 100644 --- a/clap_generate/src/generators/shells/zsh.rs +++ b/clap_generate/src/generators/shells/zsh.rs @@ -344,14 +344,31 @@ fn get_args_of(parent: &App, p_global: Option<&App>) -> String { // Uses either `possible_vals` or `value_hint` to give hints about possible argument values fn value_completion(arg: &Arg) -> Option { if let Some(values) = &arg.get_possible_values() { - Some(format!( - "({})", - values - .iter() - .map(|&v| escape_value(v)) - .collect::>() - .join(" ") - )) + if values.iter().any(|(_, about)| about.is_some()) { + Some(format!( + "(({}))", + values + .iter() + .map(|&(name, about)| { + format!( + r#"{name}\:"{about}""#, + name = escape_value(name), + about = about.map(|about| escape_help(about)).unwrap_or_default() + ) + }) + .collect::>() + .join("\n") + )) + } else { + Some(format!( + "({})", + values + .iter() + .map(|&(name, _)| name) + .collect::>() + .join(" ") + )) + } } else { // NB! If you change this, please also update the table in `ValueHint` documentation. Some( diff --git a/src/build/arg/mod.rs b/src/build/arg/mod.rs index d46ab28c9bb..19c99c1b2bf 100644 --- a/src/build/arg/mod.rs +++ b/src/build/arg/mod.rs @@ -101,7 +101,7 @@ pub struct Arg<'help> { pub(crate) short_aliases: Vec<(char, bool)>, // (name, visible) pub(crate) disp_ord: usize, pub(crate) unified_ord: usize, - pub(crate) possible_vals: Vec<&'help str>, + pub(crate) possible_vals: Vec<(&'help str, Option<&'help str>)>, pub(crate) val_names: Vec<&'help str>, pub(crate) num_vals: Option, pub(crate) max_occurs: Option, @@ -230,7 +230,7 @@ impl<'help> Arg<'help> { /// Get the list of the possible values for this argument, if any #[inline] - pub fn get_possible_values(&self) -> Option<&[&str]> { + pub fn get_possible_values(&self) -> Option<&[(&str, Option<&str>)]> { if self.possible_vals.is_empty() { None } else { @@ -1904,7 +1904,19 @@ impl<'help> Arg<'help> { /// [options]: Arg::takes_value() /// [positional arguments]: Arg::index() pub fn possible_values(mut self, names: &[&'help str]) -> Self { - self.possible_vals.extend(names); + self.possible_vals + .extend(names.iter().map(|&name| (name, None)).collect::>()); + self.takes_value(true) + } + + /// TODO Docu + pub fn possible_values_about(mut self, values: &[(&'help str, &'help str)]) -> Self { + self.possible_vals.extend( + values + .iter() + .map(|&(name, about)| (name, (!about.is_empty()).then(|| about))) + .collect::>(), + ); self.takes_value(true) } @@ -1962,7 +1974,13 @@ impl<'help> Arg<'help> { /// [options]: Arg::takes_value() /// [positional arguments]: Arg::index() pub fn possible_value(mut self, name: &'help str) -> Self { - self.possible_vals.push(name); + self.possible_vals.push((name, None)); + self.takes_value(true) + } + + /// TODO Docu + pub fn possible_value_about(mut self, name: &'help str, about: &'help str) -> Self { + self.possible_vals.push((name, Some(about))); self.takes_value(true) } diff --git a/src/output/help.rs b/src/output/help.rs index dcfab1249d4..1f2bec2a330 100644 --- a/src/output/help.rs +++ b/src/output/help.rs @@ -596,7 +596,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { let pvs = a .possible_vals .iter() - .map(|&pv| { + .map(|&(pv, _)| { if pv.contains(char::is_whitespace) { format!("{:?}", pv) } else { diff --git a/src/parse/validator.rs b/src/parse/validator.rs index 7dac71dc91e..6ac69543bfc 100644 --- a/src/parse/validator.rs +++ b/src/parse/validator.rs @@ -107,9 +107,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { let ok = if arg.is_set(ArgSettings::IgnoreCase) { arg.possible_vals .iter() - .any(|pv| pv.eq_ignore_ascii_case(&val_str)) + .any(|(pv, _)| pv.eq_ignore_ascii_case(&val_str)) } else { - arg.possible_vals.contains(&&*val_str) + arg.possible_vals.iter().any(|(pv, _)| pv == &&*val_str) }; if !ok { let used: Vec = matcher @@ -123,7 +123,10 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { .collect(); return Err(Error::invalid_value( val_str.to_string(), - &arg.possible_vals, + &arg.possible_vals + .iter() + .map(|(pv, _)| pv) + .collect::>(), arg, Usage::new(self.p).create_usage_with_title(&used), self.p.app.color(),