diff --git a/src/app/mod.rs b/src/app/mod.rs index f8006adf64c..0227102c885 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -895,12 +895,13 @@ impl<'a, 'b> App<'a, 'b> { /// /// ```rust /// # use clap::App; - /// let app = App::new("myprog"); + /// let mut app = App::new("myprog"); /// app.print_help(); /// ``` /// [`io::stdout()`]: https://doc.rust-lang.org/std/io/fn.stdout.html /// [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html - pub fn print_help(&self) -> ClapResult<()> { + pub fn print_help(&mut self) -> ClapResult<()> { + self.p.create_help_and_version(); let out = io::stdout(); let mut buf_w = BufWriter::new(out.lock()); self.write_help(&mut buf_w) diff --git a/src/app/parser.rs b/src/app/parser.rs index e194b154aa7..00f61ed7b5b 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -195,7 +195,7 @@ impl<'a, 'b> Parser<'a, 'b> pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) { debugln!("fn=Parser::add_subcommand;"); - debugln!("Term widnth...{:?}", self.p.meta.term_w); + debugln!("Term widnth...{:?}", self.meta.term_w); subcmd.p.meta.term_w = self.meta.term_w; debug!("Is help..."); if subcmd.p.meta.name == "help" { @@ -363,14 +363,81 @@ impl<'a, 'b> Parser<'a, 'b> ret_val } - pub fn has_flags(&self) -> bool { - !self.flags.is_empty() + pub fn get_args_tag(&self) -> Option { + let mut count = 0; + 'outer: for p in self.positionals.values().filter(|p| !p.is_set(ArgSettings::Required)) { + if let Some(g_vec) = self.groups_for_arg(p.name) { + for grp_s in &g_vec { + debugln!("iter;grp_s={};", grp_s); + if let Some(grp) = self.groups.get(grp_s) { + debug!("Is group required..."); + if grp.required { + sdebugln!("Yes (continuing)"); + continue 'outer; + } else { + sdebugln!("No (breaking)"); + } + } + } + debugln!("Arg not required..."); + count +=1; + } else { + debugln!("Arg not required..."); + count +=1; + } + } + if count > 1 { + return None; + } else if count == 1 && self.positionals.len() > 1 { + return None; + } else if count == 1 { + let p = self.positionals.values().next().expect(INTERNAL_ERROR_MSG); + return Some(format!(" [{}]{}", p.name_no_brackets(), p.multiple_str())); + } + Some("".into()) + } + + pub fn needs_flags_tag(&self) -> bool { + debugln!("exec=needs_flags_tag;"); + 'outer: for f in &self.flags { + debugln!("iter;f={};", f.name); + if let Some(l) = f.long { + if l == "help" || l == "version" { + continue; + } + } + if let Some(g_vec) = self.groups_for_arg(f.name) { + for grp_s in &g_vec { + debugln!("iter;grp_s={};", grp_s); + if let Some(grp) = self.groups.get(grp_s) { + debug!("Is group required..."); + if grp.required { + sdebugln!("Yes (continuing)"); + continue 'outer; + } else { + sdebugln!("No (breaking)"); + } + } + } + debugln!("Flag not required...(returning true)"); + return true; + } else { + debugln!("Flag not required...(returning true)"); + return true; + } + } + + false } pub fn has_opts(&self) -> bool { !self.opts.is_empty() } + pub fn has_flags(&self) -> bool { + !self.flags.is_empty() + } + pub fn has_positionals(&self) -> bool { !self.positionals.is_empty() } @@ -520,9 +587,13 @@ impl<'a, 'b> Parser<'a, 'b> if &*arg_os == "help" && self.settings.is_set(AppSettings::NeedsSubcommandHelp) { let cmds: Vec = it.map(|c| c.into()).collect(); + let mut help_help = false; let mut sc = { let mut sc: &Parser = self; for (i, cmd) in cmds.iter().enumerate() { + if &*cmd.to_string_lossy() == "help" { // cmd help help + help_help = true; + } if let Some(c) = sc.subcommands .iter() .filter(|s| &*s.p.meta.name == cmd) @@ -563,7 +634,30 @@ impl<'a, 'b> Parser<'a, 'b> } sc.clone() }; - sc.create_help_and_version(); + if help_help { + let mut pb = PosBuilder::new("subcommand", 1); + pb.help = Some("The subcommand whose help message to display"); + pb.set(ArgSettings::Multiple); + sc.positionals.insert(1, pb); + for s in self.g_settings.clone() { + sc.set(s); + } + } else { + sc.create_help_and_version(); + } + if sc.meta.bin_name != self.meta.bin_name { + sc.meta.bin_name = Some(format!("{}{}{}", + self.meta + .bin_name + .as_ref() + .unwrap_or(&String::new()), + if self.meta.bin_name.is_some() { + " " + } else { + "" + }, + &*sc.meta.name)); + } return sc._help(); } subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned()); @@ -873,10 +967,10 @@ impl<'a, 'b> Parser<'a, 'b> args.iter().map(|s| *s).collect() } - fn create_help_and_version(&mut self) { + pub fn create_help_and_version(&mut self) { debugln!("fn=create_help_and_version;"); // name is "hclap_help" because flags are sorted by name - if !self.flags.iter().any(|a| a.long.is_some() && a.long.unwrap() == "help") { + if self.is_set(AppSettings::NeedsLongHelp) { debugln!("Building --help"); if self.help_short.is_none() && !self.short_list.contains(&'h') { self.help_short = Some('h'); @@ -892,7 +986,7 @@ impl<'a, 'b> Parser<'a, 'b> self.flags.push(arg); } if !self.settings.is_set(AppSettings::DisableVersion) && - !self.flags.iter().any(|a| a.long.is_some() && a.long.unwrap() == "version") { + self.is_set(AppSettings::NeedsLongVersion) { debugln!("Building --version"); if self.version_short.is_none() && !self.short_list.contains(&'V') { self.version_short = Some('V'); @@ -908,10 +1002,7 @@ impl<'a, 'b> Parser<'a, 'b> self.long_list.push("version"); self.flags.push(arg); } - if !self.subcommands.is_empty() && - !self.subcommands - .iter() - .any(|s| &s.p.meta.name[..] == "help") { + if !self.subcommands.is_empty() && self.is_set(AppSettings::NeedsSubcommandHelp) { debugln!("Building help"); self.subcommands .push(App::new("help") @@ -1516,9 +1607,10 @@ impl<'a, 'b> Parser<'a, 'b> .iter() .fold(String::new(), |a, s| a + &format!(" {}", s)[..]); - if self.has_flags() && !self.is_set(AppSettings::UnifiedHelpMessage) { + let flags = self.needs_flags_tag(); + if flags && !self.is_set(AppSettings::UnifiedHelpMessage) { usage.push_str(" [FLAGS]"); - } else { + } else if flags { usage.push_str(" [OPTIONS]"); } if !self.is_set(AppSettings::UnifiedHelpMessage) && self.has_opts() && @@ -1538,14 +1630,8 @@ impl<'a, 'b> Parser<'a, 'b> } if self.has_positionals() && self.positionals.values().any(|a| !a.settings.is_set(ArgSettings::Required)) { - if self.positionals.len() == 1 { - let p = self.positionals.values().next().expect(INTERNAL_ERROR_MSG); - if !self.groups.values().any(|g| g.args.iter().any(|a| a == &p.name)) { - usage.push_str(&*format!(" [{}]{}", p.name_no_brackets(), - p.multiple_str())); - } else { - usage.push_str(" [ARGS]"); - } + if let Some(args_tag) = self.get_args_tag() { + usage.push_str(&*args_tag); } else { usage.push_str(" [ARGS]"); } diff --git a/tests/app_settings.rs b/tests/app_settings.rs index 4b1497b2aa6..8b2ed5529b5 100644 --- a/tests/app_settings.rs +++ b/tests/app_settings.rs @@ -125,7 +125,7 @@ Kevin K. tests stuff USAGE: - test [FLAGS] [OPTIONS] [arg1] + test [OPTIONS] [arg1] FLAGS: -h, --help Prints help information diff --git a/tests/groups.rs b/tests/groups.rs index 3f48cf2ccc2..27088f709a2 100644 --- a/tests/groups.rs +++ b/tests/groups.rs @@ -107,7 +107,7 @@ fn req_group_usage_string() { USAGE: - clap-test [FLAGS] [ARGS] + clap-test For more information try --help", true); @@ -179,4 +179,4 @@ fn group_multiple_args_error() { let err = result.unwrap_err(); assert_eq!(err.kind, ErrorKind::ArgumentConflict); -} \ No newline at end of file +} diff --git a/tests/help.rs b/tests/help.rs index 9925340278b..0c73498dca5 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -42,7 +42,7 @@ clap-test v1.4.8 tests clap library USAGE: - clap-test [FLAGS] + clap-test FLAGS: -h, --help Prints help information diff --git a/tests/positionals.rs b/tests/positionals.rs index ae98c922bb6..c3c02010c3f 100644 --- a/tests/positionals.rs +++ b/tests/positionals.rs @@ -180,13 +180,13 @@ fn default_values_user_value() { #[test] fn single_positional_usage_string() { let m = App::new("test").arg_from_usage("[FILE] 'some file'").get_matches_from(vec!["test"]); - assert_eq!(m.usage(), "USAGE:\n test [FLAGS] [FILE]"); + assert_eq!(m.usage(), "USAGE:\n test [FILE]"); } #[test] fn single_positional_multiple_usage_string() { let m = App::new("test").arg_from_usage("[FILE]... 'some file'").get_matches_from(vec!["test"]); - assert_eq!(m.usage(), "USAGE:\n test [FLAGS] [FILE]..."); + assert_eq!(m.usage(), "USAGE:\n test [FILE]..."); } #[test] @@ -195,7 +195,7 @@ fn multiple_positional_usage_string() { .arg_from_usage("[FILE] 'some file'") .arg_from_usage("[FILES]... 'some file'") .get_matches_from(vec!["test"]); - assert_eq!(m.usage(), "USAGE:\n test [FLAGS] [ARGS]"); + assert_eq!(m.usage(), "USAGE:\n test [ARGS]"); } #[test] @@ -204,7 +204,7 @@ fn multiple_positional_one_required_usage_string() { .arg_from_usage(" 'some file'") .arg_from_usage("[FILES]... 'some file'") .get_matches_from(vec!["test", "file"]); - assert_eq!(m.usage(), "USAGE:\n test [FLAGS] [ARGS]"); + assert_eq!(m.usage(), "USAGE:\n test [ARGS]"); } #[test] @@ -212,5 +212,5 @@ fn single_positional_required_usage_string() { let m = App::new("test") .arg_from_usage(" 'some file'") .get_matches_from(vec!["test", "file"]); - assert_eq!(m.usage(), "USAGE:\n test [FLAGS] "); + assert_eq!(m.usage(), "USAGE:\n test "); } diff --git a/tests/subcommands.rs b/tests/subcommands.rs index 8592ee96ca5..96815524890 100644 --- a/tests/subcommands.rs +++ b/tests/subcommands.rs @@ -8,7 +8,7 @@ use clap::{App, Arg, SubCommand, ErrorKind}; static VISIBLE_ALIAS_HELP: &'static str = "clap-test 2.6 USAGE: - clap-test [FLAGS] [SUBCOMMAND] + clap-test [SUBCOMMAND] FLAGS: -h, --help Prints help information @@ -21,7 +21,7 @@ SUBCOMMANDS: static INVISIBLE_ALIAS_HELP: &'static str = "clap-test 2.6 USAGE: - clap-test [FLAGS] [SUBCOMMAND] + clap-test [SUBCOMMAND] FLAGS: -h, --help Prints help information @@ -150,4 +150,4 @@ fn invisible_aliases_help_output() { .about("Some help") .alias("invisible")); test::check_help(app, INVISIBLE_ALIAS_HELP); -} \ No newline at end of file +}