diff --git a/clap_builder/src/builder/arg.rs b/clap_builder/src/builder/arg.rs index f83b4642c16..1a1bdb134a3 100644 --- a/clap_builder/src/builder/arg.rs +++ b/clap_builder/src/builder/arg.rs @@ -4078,7 +4078,9 @@ impl Arg { } pub(crate) fn is_takes_value_set(&self) -> bool { - self.get_action().takes_values() + self.get_num_args() + .unwrap_or_else(|| 1.into()) + .takes_values() } /// Report whether [`Arg::allow_hyphen_values`] is set diff --git a/clap_builder/src/builder/debug_asserts.rs b/clap_builder/src/builder/debug_asserts.rs index 2b633849fbe..bfb491f5475 100644 --- a/clap_builder/src/builder/debug_asserts.rs +++ b/clap_builder/src/builder/debug_asserts.rs @@ -704,13 +704,14 @@ fn assert_arg(arg: &Arg) { arg.get_id(), ); - assert_eq!( - arg.get_action().takes_values(), - arg.is_takes_value_set(), - "Argument `{}`'s selected action {:?} contradicts `takes_value`", - arg.get_id(), - arg.get_action() - ); + if arg.is_takes_value_set() { + assert!( + arg.get_action().takes_values(), + "Argument `{}`'s selected action {:?} contradicts `takes_value`", + arg.get_id(), + arg.get_action() + ); + } if let Some(action_type_id) = arg.get_action().value_type_id() { assert_eq!( action_type_id, @@ -774,13 +775,6 @@ fn assert_arg(arg: &Arg) { } } - assert_eq!( - num_vals.takes_values(), - arg.is_takes_value_set(), - "Argument {}: mismatch between `num_args` ({}) and `takes_value`", - arg.get_id(), - num_vals, - ); assert_eq!( num_vals.is_multiple(), arg.is_multiple_values_set(), diff --git a/clap_mangen/src/render.rs b/clap_mangen/src/render.rs index ef4981d80d6..11b8c5dec41 100644 --- a/clap_mangen/src/render.rs +++ b/clap_mangen/src/render.rs @@ -99,7 +99,7 @@ pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) { (None, None) => vec![], }; - if opt.get_action().takes_values() { + if opt.get_num_args().expect("built").takes_values() { if let Some(value) = &opt.get_value_names() { header.push(roman("=")); header.push(italic(value.join(" "))); @@ -288,7 +288,7 @@ fn option_environment(opt: &clap::Arg) -> Option> { } fn option_default_values(opt: &clap::Arg) -> Option { - if opt.is_hide_default_value_set() || !opt.get_action().takes_values() { + if opt.is_hide_default_value_set() || !opt.get_num_args().expect("built").takes_values() { return None; } else if !opt.get_default_values().is_empty() { let values = opt diff --git a/examples/find.md b/examples/find.md index e9b1a86f89a..c52cbd391ce 100644 --- a/examples/find.md +++ b/examples/find.md @@ -12,7 +12,7 @@ Options: TESTS: --empty File is empty and is either a regular file or a directory - --name Base of file name (the path with the leading directories removed) matches shell + --name Base of file name (the path with the leading directories removed) matches shell pattern pattern OPERATORS: @@ -41,5 +41,39 @@ $ find --empty -o --name .keep ), ] +$ find --empty -o --name .keep -o --name foo +[ + ( + "empty", + Bool( + true, + ), + ), + ( + "or", + Bool( + true, + ), + ), + ( + "name", + String( + ".keep", + ), + ), + ( + "or", + Bool( + true, + ), + ), + ( + "name", + String( + "foo", + ), + ), +] + ``` diff --git a/examples/find.rs b/examples/find.rs index 0b7f2c70000..61ce066f4dc 100644 --- a/examples/find.rs +++ b/examples/find.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use clap::{arg, command, ArgGroup, ArgMatches, Command}; +use clap::{command, value_parser, Arg, ArgAction, ArgGroup, ArgMatches, Command}; fn main() { let matches = cli().get_matches(); @@ -13,17 +13,44 @@ fn cli() -> Command { .group(ArgGroup::new("tests").multiple(true)) .next_help_heading("TESTS") .args([ - arg!(--empty "File is empty and is either a regular file or a directory").group("tests"), - arg!(--name "Base of file name (the path with the leading directories removed) matches shell pattern pattern").group("tests"), + position_sensitive_flag(Arg::new("empty")) + .long("empty") + .action(ArgAction::Append) + .help("File is empty and is either a regular file or a directory") + .group("tests"), + Arg::new("name") + .long("name") + .action(ArgAction::Append) + .help("Base of file name (the path with the leading directories removed) matches shell pattern pattern") + .group("tests") ]) .group(ArgGroup::new("operators").multiple(true)) .next_help_heading("OPERATORS") .args([ - arg!(-o - -or "expr2 is not evaluate if exp1 is true").group("operators"), - arg!(-a - -and "Same as `expr1 expr1`").group("operators"), + position_sensitive_flag(Arg::new("or")) + .short('o') + .long("or") + .action(ArgAction::Append) + .help("expr2 is not evaluate if exp1 is true") + .group("operators"), + position_sensitive_flag(Arg::new("and")) + .short('a') + .long("and") + .action(ArgAction::Append) + .help("Same as `expr1 expr1`") + .group("operators"), ]) } +fn position_sensitive_flag(arg: Arg) -> Arg { + // Flags don't track the position of each occurrence, so we need to emulate flags with + // value-less options to get the same result + arg.num_args(0) + .value_parser(value_parser!(bool)) + .default_missing_value("true") + .default_value("false") +} + #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum Value { Bool(bool),