diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6310bc72e47f..537249d90be0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,14 +1,11 @@
-## v2.2.0 (2016-03-14)
+## v2.2.0 (2016-03-15)
-#### Documentation
-
-* **Groups:** explains required ArgGroups better ([4ff0205b](https://github.com/kbknapp/clap-rs/commit/4ff0205b85a45151b59bbaf090a89df13438380f), closes [#439](https://github.com/kbknapp/clap-rs/issues/439))
-
#### Features
* **Help Message:** can auto wrap and aligning help text to term width ([e36af026](https://github.com/kbknapp/clap-rs/commit/e36af0266635f23e85e951b9088d561e9a5d1bf6), closes [#428](https://github.com/kbknapp/clap-rs/issues/428))
+* **Help Subcommand:** adds support passing additional subcommands to help subcommand ([2c12757b](https://github.com/kbknapp/clap-rs/commit/2c12757bbdf34ce481f3446c074e24c09c2e60fd), closes [#416](https://github.com/kbknapp/clap-rs/issues/416))
* **Opts and Flags:** adds support for custom ordering in help messages ([9803b51e](https://github.com/kbknapp/clap-rs/commit/9803b51e799904c0befaac457418ee766ccc1ab9))
* **Settings:** adds support for automatically deriving custom display order of args ([ad86e433](https://github.com/kbknapp/clap-rs/commit/ad86e43334c4f70e86909689a088fb87e26ff95a), closes [#444](https://github.com/kbknapp/clap-rs/issues/444))
* **Subcommands:** adds support for custom ordering in help messages ([7d2a2ed4](https://github.com/kbknapp/clap-rs/commit/7d2a2ed413f5517d45988eef0765cdcd663b6372), closes [#442](https://github.com/kbknapp/clap-rs/issues/442))
@@ -17,7 +14,9 @@
* **From Usage:** fixes a bug where adding empty lines werent ignored ([c5c58c86](https://github.com/kbknapp/clap-rs/commit/c5c58c86b9c503d8de19da356a5a5cffb59fbe84))
+#### Documentation
+* **Groups:** explains required ArgGroups better ([4ff0205b](https://github.com/kbknapp/clap-rs/commit/4ff0205b85a45151b59bbaf090a89df13438380f), closes [#439](https://github.com/kbknapp/clap-rs/issues/439))
### v2.1.2 (2016-02-24)
diff --git a/README.md b/README.md
index 96dd16ca03b2..754dee24aa45 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,8 @@ Here's the highlights from v2.2.0
* **Help text auto wraps and aligns at term width!** - Long help strings will now properly wrap and align to term width on Linux and OSX (and resumably Unix too). This can be turned off as well.
* **Can customize the order of opts, flags, and subcommands in help messages** - Instead of using the default alphabetical order, you can now re-arange the order of your args and subcommands in help message. This helps to emphasize more popular or important options.
* **Can auto-derive the order from declaration order** - Have a bunch of args or subcommmands to re-order? You can now just derive the order from the declaration order!
+* **Help subcommand now accepts other subcommands as arguments!** - Similar to other CLI precedents, the `help` subcommand can now accept other subcommands as arguments to display their help message. i.e. `$ myprog help mysubcmd` (*Note* these can even be nested heavily such as `$ myprog help subcmd1 subcmd2 subcmd3` etc.)
+
* Other minor bug fixes
An example of the help text wrapping at term width:
diff --git a/clap-tests/run_tests.py b/clap-tests/run_tests.py
index 3e55a8eb3593..bbd6140fd9fb 100755
--- a/clap-tests/run_tests.py
+++ b/clap-tests/run_tests.py
@@ -36,7 +36,7 @@
... tests positionals with specific values [values: vi, emacs]
SUBCOMMANDS:
- help Prints this message
+ help With no arguments it prints this message, otherwise it prints help information about other subcommands
subcmd tests subcommands'''
_version = "claptests v1.4.8"
diff --git a/src/app/meta.rs b/src/app/meta.rs
index 335d77f592d1..3ca96e1890b1 100644
--- a/src/app/meta.rs
+++ b/src/app/meta.rs
@@ -39,3 +39,20 @@ impl<'b> AppMeta<'b> {
}
}
}
+
+impl<'b> Clone for AppMeta<'b> {
+ fn clone(&self) -> Self {
+ AppMeta {
+ name: self.name.clone(),
+ author: self.author.clone(),
+ about: self.about.clone(),
+ more_help: self.more_help.clone(),
+ version: self.version.clone(),
+ usage_str: self.usage_str.clone(),
+ usage: self.usage.clone(),
+ bin_name: self.bin_name.clone(),
+ help_str: self.help_str.clone(),
+ disp_ord: self.disp_ord,
+ }
+ }
+}
diff --git a/src/app/mod.rs b/src/app/mod.rs
index cd252ed1f413..1ae13a591a53 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -875,3 +875,11 @@ impl<'a> From<&'a Yaml> for App<'a, 'a> {
a
}
}
+
+impl<'a, 'b> Clone for App<'a, 'b> {
+ fn clone(&self) -> Self {
+ App {
+ p: self.p.clone(),
+ }
+ }
+}
diff --git a/src/app/parser.rs b/src/app/parser.rs
index 05e373d052e0..40ba1124a1d3 100644
--- a/src/app/parser.rs
+++ b/src/app/parser.rs
@@ -481,7 +481,22 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
if pos_sc {
if &*arg_os == "help" &&
self.settings.is_set(AppSettings::NeedsSubcommandHelp) {
- return self._help();
+ let cmds: Vec = it.map(|c| c.into()).collect();
+ let mut sc: &Parser = self;
+ for (i, cmd) in cmds.iter().enumerate() {
+ if let Some(c) = sc.subcommands.iter().filter(|s| &*s.p.meta.name == cmd).next().map(|sc| &sc.p) {
+ sc = c;
+ if i == cmds.len() - 1 {
+ break;
+ }
+ } else {
+ return Err(
+ Error::unrecognized_subcommand(
+ cmd.to_string_lossy().into_owned(),
+ self.meta.bin_name.as_ref().unwrap_or(&self.meta.name)));
+ }
+ }
+ return sc._help();
}
subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned());
break;
@@ -500,7 +515,6 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
parse_positional!(self, p, arg_os, pos_only, pos_counter, matcher);
} else {
if self.settings.is_set(AppSettings::AllowExternalSubcommands) {
- // let arg_str = arg_os.to_str().expect(INVALID_UTF8);
let mut sc_m = ArgMatcher::new();
while let Some(v) = it.next() {
let a = v.into();
@@ -521,7 +535,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
} else {
return Err(Error::unknown_argument(
&*arg_os.to_string_lossy(),
- "", //self.meta.bin_name.as_ref().unwrap_or(&self.meta.name),
+ "",
&*self.create_current_usage(matcher)));
}
}
@@ -800,7 +814,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
.iter()
.any(|s| &s.p.meta.name[..] == "help") {
debugln!("Building help");
- self.subcommands.push(App::new("help").about("Prints this message"));
+ self.subcommands.push(App::new("help").about("With no arguments it prints this message, otherwise it prints help information about other subcommands"));
}
}
@@ -1562,3 +1576,25 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
Ok(())
}
}
+
+impl<'a, 'b> Clone for Parser<'a, 'b> where 'a: 'b {
+ fn clone(&self) -> Self {
+ Parser {
+ required: self.required.clone(),
+ short_list: self.short_list.clone(),
+ long_list: self.long_list.clone(),
+ blacklist: self.blacklist.clone(),
+ flags: self.flags.clone(),
+ opts: self.opts.clone(),
+ positionals: self.positionals.clone(),
+ subcommands: self.subcommands.clone(),
+ groups: self.groups.clone(),
+ global_args: self.global_args.clone(),
+ overrides: self.overrides.clone(),
+ help_short: self.help_short.clone(),
+ version_short: self.version_short.clone(),
+ settings: self.settings.clone(),
+ meta: self.meta.clone(),
+ }
+ }
+}
diff --git a/src/app/settings.rs b/src/app/settings.rs
index 3c08f8f55d97..1c08c7df3071 100644
--- a/src/app/settings.rs
+++ b/src/app/settings.rs
@@ -32,6 +32,12 @@ bitflags! {
#[derive(Debug)]
pub struct AppFlags(Flags);
+impl Clone for AppFlags {
+ fn clone(&self) -> Self {
+ AppFlags(self.0)
+ }
+}
+
impl Default for AppFlags {
fn default() -> Self {
AppFlags(NEEDS_LONG_VERSION | NEEDS_LONG_HELP | NEEDS_SC_HELP | UTF8_NONE)
@@ -480,7 +486,7 @@ impl FromStr for AppSettings {
#[cfg(test)]
mod test {
use super::AppSettings;
-
+
#[test]
fn app_settings_fromstr() {
assert_eq!("subcommandsnegatereqs".parse::().unwrap(), AppSettings::SubcommandsNegateReqs);
diff --git a/src/args/arg.rs b/src/args/arg.rs
index 17bd8ec358ab..caed4db9ef2b 100644
--- a/src/args/arg.rs
+++ b/src/args/arg.rs
@@ -1911,3 +1911,29 @@ impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>>
}
}
}
+
+impl<'a, 'b> Clone for Arg<'a, 'b> {
+ fn clone(&self) -> Self {
+ Arg {
+ name: self.name,
+ short: self.short,
+ long: self.long,
+ help: self.help,
+ index: self.index,
+ possible_vals: self.possible_vals.clone(),
+ blacklist: self.blacklist.clone(),
+ requires: self.requires.clone(),
+ num_vals: self.num_vals,
+ min_vals: self.min_vals,
+ max_vals: self.max_vals,
+ val_names: self.val_names.clone(),
+ group: self.group,
+ validator: self.validator.clone(),
+ overrides: self.overrides.clone(),
+ settings: self.settings,
+ val_delim: self.val_delim,
+ default_val: self.default_val,
+ disp_ord: self.disp_ord,
+ }
+ }
+}
diff --git a/src/args/arg_builder/flag.rs b/src/args/arg_builder/flag.rs
index 452a0e379999..dfbebbbce5d2 100644
--- a/src/args/arg_builder/flag.rs
+++ b/src/args/arg_builder/flag.rs
@@ -93,6 +93,22 @@ impl<'n, 'e> Display for FlagBuilder<'n, 'e> {
}
}
+impl<'n, 'e> Clone for FlagBuilder<'n, 'e> {
+ fn clone(&self) -> Self {
+ FlagBuilder {
+ name: self.name,
+ short: self.short,
+ long: self.long,
+ help: self.help,
+ blacklist: self.blacklist.clone(),
+ overrides: self.overrides.clone(),
+ requires: self.requires.clone(),
+ settings: self.settings,
+ disp_ord: self.disp_ord,
+ }
+ }
+}
+
impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn name(&self) -> &'n str { self.name }
fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) }
diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs
index a8c4fd4b6011..5cdaa6d12928 100644
--- a/src/args/arg_builder/option.rs
+++ b/src/args/arg_builder/option.rs
@@ -146,6 +146,30 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
}
}
+impl<'n, 'e> Clone for OptBuilder<'n, 'e> {
+ fn clone(&self) -> Self {
+ OptBuilder {
+ name: self.name,
+ short: self.short,
+ long: self.long,
+ help: self.help,
+ blacklist: self.blacklist.clone(),
+ overrides: self.overrides.clone(),
+ requires: self.requires.clone(),
+ settings: self.settings,
+ disp_ord: self.disp_ord,
+ num_vals: self.num_vals,
+ min_vals: self.min_vals,
+ max_vals: self.max_vals,
+ val_names: self.val_names.clone(),
+ val_delim: self.val_delim,
+ possible_vals: self.possible_vals.clone(),
+ default_val: self.default_val,
+ validator: self.validator.clone(),
+ }
+ }
+}
+
impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn name(&self) -> &'n str { self.name }
fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) }
diff --git a/src/args/arg_builder/positional.rs b/src/args/arg_builder/positional.rs
index 289af2f713ae..590b934026e6 100644
--- a/src/args/arg_builder/positional.rs
+++ b/src/args/arg_builder/positional.rs
@@ -135,6 +135,29 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> {
}
}
+impl<'n, 'e> Clone for PosBuilder<'n, 'e> {
+ fn clone(&self) -> Self {
+ PosBuilder {
+ name: self.name,
+ help: self.help,
+ blacklist: self.blacklist.clone(),
+ overrides: self.overrides.clone(),
+ requires: self.requires.clone(),
+ settings: self.settings,
+ disp_ord: self.disp_ord,
+ num_vals: self.num_vals,
+ min_vals: self.min_vals,
+ max_vals: self.max_vals,
+ val_names: self.val_names.clone(),
+ val_delim: self.val_delim,
+ possible_vals: self.possible_vals.clone(),
+ default_val: self.default_val,
+ validator: self.validator.clone(),
+ index: self.index,
+ }
+ }
+}
+
impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
fn name(&self) -> &'n str { self.name }
fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) }
diff --git a/src/args/group.rs b/src/args/group.rs
index 726ace6b9a88..5dbc3c66eb77 100644
--- a/src/args/group.rs
+++ b/src/args/group.rs
@@ -486,3 +486,16 @@ requires:
assert_eq!(g.conflicts, Some(confs));
}
}
+
+impl<'a> Clone for ArgGroup<'a> {
+ fn clone(&self) -> Self {
+ ArgGroup {
+ name: self.name,
+ required: self.required,
+ args: self.args.clone(),
+ requires: self.requires.clone(),
+ conflicts: self.conflicts.clone(),
+ }
+ }
+
+}
diff --git a/src/errors.rs b/src/errors.rs
index dc54fd83dfea..60eee79af426 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -64,6 +64,28 @@ pub enum ErrorKind {
/// assert_eq!(result.unwrap_err().kind, ErrorKind::InvalidSubcommand);
/// ```
InvalidSubcommand,
+ /// Occurs when the user provids an unrecognized subcommand which does not meet the threshold
+ /// for being similar enough to an existing subcommand so as to not cause the more detailed
+ /// `InvalidSubcommand` error.
+ ///
+ /// This error typically happens when passing additional subcommand names to the `help`
+ /// subcommand. Otherwise, the more general `UnknownArgument` error is used.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap::{App, Arg, ErrorKind, SubCommand};
+ /// let result = App::new("myprog")
+ /// .subcommand(SubCommand::with_name("config")
+ /// .about("Used for configuration")
+ /// .arg(Arg::with_name("config_file")
+ /// .help("The configuration file to use")
+ /// .index(1)))
+ /// .get_matches_from_safe(vec!["myprog", "help", "nothing"]);
+ /// assert!(result.is_err());
+ /// assert_eq!(result.unwrap_err().kind, ErrorKind::UnrecognizedSubcommand);
+ /// ```
+ UnrecognizedSubcommand,
/// Occurs when the user provides an empty value for an option that does not allow empty
/// values.
///
@@ -441,6 +463,26 @@ impl Error {
}
}
+ #[doc(hidden)]
+ pub fn unrecognized_subcommand(subcmd: S, name: N) -> Self
+ where S: Into,
+ N: Display
+ {
+ let s = subcmd.into();
+ Error {
+ message: format!("{} The subcommand '{}' wasn't recognized\n\n\
+ USAGE:\n\t\
+ {} help ...\n\n\
+ For more information try {}",
+ Format::Error("error:"),
+ Format::Warning(&*s),
+ name,
+ Format::Good("--help")),
+ kind: ErrorKind::UnrecognizedSubcommand,
+ info: Some(vec![s]),
+ }
+ }
+
#[doc(hidden)]
pub fn missing_required_argument(required: R, usage: U) -> Self
where R: Display,