diff --git a/src/app/help.rs b/src/app/help.rs index 1b400ef4..c79142fe 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -89,6 +89,7 @@ pub struct Help<'a> { cizer: Colorizer, longest: usize, force_next_line: bool, + use_long: bool, } // Public Functions @@ -100,7 +101,8 @@ impl<'a> Help<'a> { color: bool, cizer: Colorizer, term_w: Option, - max_w: Option) + max_w: Option, + use_long: bool) -> Self { debugln!("Help::new;"); Help { @@ -121,21 +123,22 @@ impl<'a> Help<'a> { cizer: cizer, longest: 0, force_next_line: false, + use_long: use_long, } } /// Reads help settings from an App /// and write its help to the wrapped stream. - pub fn write_app_help(w: &'a mut Write, app: &App) -> ClapResult<()> { + pub fn write_app_help(w: &'a mut Write, app: &App, use_long: bool) -> ClapResult<()> { debugln!("Help::write_app_help;"); - Self::write_parser_help(w, &app.p) + Self::write_parser_help(w, &app.p, use_long) } /// Reads help settings from a Parser /// and write its help to the wrapped stream. - pub fn write_parser_help(w: &'a mut Write, parser: &Parser) -> ClapResult<()> { + pub fn write_parser_help(w: &'a mut Write, parser: &Parser, use_long: bool) -> ClapResult<()> { debugln!("Help::write_parser_help;"); - Self::_write_parser_help(w, parser, false) + Self::_write_parser_help(w, parser, false, use_long) } /// Reads help settings from a Parser @@ -143,11 +146,11 @@ impl<'a> Help<'a> { /// formatting when required. pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> { debugln!("Help::write_parser_help;"); - Self::_write_parser_help(w, parser, true) + Self::_write_parser_help(w, parser, true, false) } #[doc(hidden)] - pub fn _write_parser_help(w: &'a mut Write, parser: &Parser, stderr: bool) -> ClapResult<()> { + pub fn _write_parser_help(w: &'a mut Write, parser: &Parser, stderr: bool, use_long: bool) -> ClapResult<()> { debugln!("Help::write_parser_help;"); let nlh = parser.is_set(AppSettings::NextLineHelp); let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp); @@ -162,8 +165,9 @@ impl<'a> Help<'a> { color, cizer, parser.meta.term_w, - parser.meta.max_w) - .write_help(parser) + parser.meta.max_w, + use_long) + .write_help(parser) } /// Writes the parser help to the wrapped stream. @@ -191,8 +195,9 @@ impl<'a> Help<'a> { self.longest = 2; let mut arg_v = Vec::with_capacity(10); for arg in args.filter(|arg| { - !(arg.is_set(ArgSettings::Hidden)) || arg.is_set(ArgSettings::NextLineHelp) - }) { + !(arg.is_set(ArgSettings::Hidden)) || + arg.is_set(ArgSettings::NextLineHelp) + }) { if arg.longest_filter() { self.longest = cmp::max(self.longest, arg.to_string().len()); } @@ -432,8 +437,12 @@ impl<'a> Help<'a> { fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, spec_vals: &str) -> io::Result<()> { debugln!("Help::help;"); let mut help = String::new(); - let h = arg.help().unwrap_or(""); - let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp); + let h = if self.use_long { + arg.long_help().unwrap_or(arg.help().unwrap_or("")) + } else { + arg.help().unwrap_or(arg.long_help().unwrap_or("")) + }; + let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) || self.use_long; debugln!("Help::help: Next Line...{:?}", nlh); let spcs = if nlh || self.force_next_line { @@ -513,7 +522,8 @@ impl<'a> Help<'a> { debugln!("Help::spec_vals: Found aliases...{:?}", aliases); spec_vals.push(format!(" [aliases: {}]", if self.color { - aliases.iter() + aliases + .iter() .map(|v| format!("{}", self.cizer.good(v))) .collect::>() .join(", ") @@ -525,14 +535,14 @@ impl<'a> Help<'a> { if let Some(pv) = a.possible_vals() { debugln!("Help::spec_vals: Found possible vals...{:?}", pv); spec_vals.push(if self.color { - format!(" [values: {}]", - pv.iter() - .map(|v| format!("{}", self.cizer.good(v))) - .collect::>() - .join(", ")) - } else { - format!(" [values: {}]", pv.join(", ")) - }); + format!(" [values: {}]", + pv.iter() + .map(|v| format!("{}", self.cizer.good(v))) + .collect::>() + .join(", ")) + } else { + format!(" [values: {}]", pv.join(", ")) + }); } } spec_vals.join(" ") @@ -548,7 +558,10 @@ impl<'a> Help<'a> { pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> { debugln!("Help::write_all_args;"); let flags = parser.has_flags(); - let pos = parser.positionals().filter(|arg| !arg.is_set(ArgSettings::Hidden)).count() > 0; + let pos = parser + .positionals() + .filter(|arg| !arg.is_set(ArgSettings::Hidden)) + .count() > 0; let opts = parser.has_opts(); let subcmds = parser.has_subcommands(); @@ -557,7 +570,8 @@ impl<'a> Help<'a> { let mut first = true; if unified_help && (flags || opts) { - let opts_flags = parser.flags() + let opts_flags = parser + .flags() .map(as_arg_trait) .chain(parser.opts().map(as_arg_trait)); try!(color!(self, "OPTIONS:\n", warning)); @@ -566,8 +580,7 @@ impl<'a> Help<'a> { } else { if flags { try!(color!(self, "FLAGS:\n", warning)); - try!(self.write_args(parser.flags() - .map(as_arg_trait))); + try!(self.write_args(parser.flags().map(as_arg_trait))); first = false; } if opts { @@ -606,8 +619,13 @@ impl<'a> Help<'a> { // The shortest an arg can legally be is 2 (i.e. '-x') self.longest = 2; let mut ord_m = VecMap::new(); - for sc in parser.subcommands.iter().filter(|s| !s.p.is_set(AppSettings::Hidden)) { - let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new()); + for sc in parser + .subcommands + .iter() + .filter(|s| !s.p.is_set(AppSettings::Hidden)) { + let btm = ord_m + .entry(sc.p.meta.disp_ord) + .or_insert(BTreeMap::new()); self.longest = cmp::max(self.longest, sc.p.meta.name.len()); btm.insert(sc.p.meta.name.clone(), sc.clone()); } @@ -861,9 +879,9 @@ impl<'a> Help<'a> { debugln!("Help::write_template_help:iter: tag_buf={};", unsafe { String::from_utf8_unchecked(tag_buf.get_ref()[0..tag_length] - .iter() - .map(|&i| i) - .collect::>()) + .iter() + .map(|&i| i) + .collect::>()) }); match &tag_buf.get_ref()[0..tag_length] { b"?" => { @@ -894,22 +912,20 @@ impl<'a> Help<'a> { try!(self.write_all_args(&parser)); } b"unified" => { - let opts_flags = parser.flags() + let opts_flags = parser + .flags() .map(as_arg_trait) .chain(parser.opts().map(as_arg_trait)); try!(self.write_args(opts_flags)); } b"flags" => { - try!(self.write_args(parser.flags() - .map(as_arg_trait))); + try!(self.write_args(parser.flags().map(as_arg_trait))); } b"options" => { - try!(self.write_args(parser.opts() - .map(as_arg_trait))); + try!(self.write_args(parser.opts().map(as_arg_trait))); } b"positionals" => { - try!(self.write_args(parser.positionals() - .map(as_arg_trait))); + try!(self.write_args(parser.positionals().map(as_arg_trait))); } b"subcommands" => { try!(self.write_subcommands(&parser)); diff --git a/src/app/meta.rs b/src/app/meta.rs index 33b2f355..0985e999 100644 --- a/src/app/meta.rs +++ b/src/app/meta.rs @@ -1,11 +1,13 @@ #[doc(hidden)] #[allow(missing_debug_implementations)] +#[derive(Default, Clone)] pub struct AppMeta<'b> { pub name: String, pub bin_name: Option, pub author: Option<&'b str>, pub version: Option<&'b str>, pub about: Option<&'b str>, + pub long_about: Option<&'b str>, pub more_help: Option<&'b str>, pub pre_help: Option<&'b str>, pub aliases: Option>, // (name, visible) @@ -18,51 +20,7 @@ pub struct AppMeta<'b> { pub template: Option<&'b str>, } -impl<'b> Default for AppMeta<'b> { - fn default() -> Self { - AppMeta { - name: String::new(), - author: None, - about: None, - more_help: None, - pre_help: None, - version: None, - usage_str: None, - usage: None, - bin_name: None, - help_str: None, - disp_ord: 999, - template: None, - aliases: None, - term_w: None, - max_w: None, - } - } -} - impl<'b> AppMeta<'b> { pub fn new() -> Self { Default::default() } - pub fn with_name(s: String) -> Self { AppMeta { name: s, ..Default::default() } } -} - -impl<'b> Clone for AppMeta<'b> { - fn clone(&self) -> Self { - AppMeta { - name: self.name.clone(), - author: self.author, - about: self.about, - more_help: self.more_help, - pre_help: self.pre_help, - version: self.version, - usage_str: self.usage_str, - usage: self.usage.clone(), - bin_name: self.bin_name.clone(), - help_str: self.help_str, - disp_ord: self.disp_ord, - template: self.template, - aliases: self.aliases.clone(), - term_w: self.term_w, - max_w: self.max_w, - } - } -} + pub fn with_name(s: String) -> Self { AppMeta { name: s, disp_ord: 999, ..Default::default() } } +} \ No newline at end of file diff --git a/src/app/mod.rs b/src/app/mod.rs index 428bcdbd..f4226ccc 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1108,7 +1108,7 @@ impl<'a, 'b> App<'a, 'b> { // self.p.derive_display_order(); // self.p.create_help_and_version(); - Help::write_app_help(w, self) + Help::write_app_help(w, self, false) } /// Writes the version message to the user to a [`io::Write`] object @@ -1622,6 +1622,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> { fn val_delim(&self) -> Option { None } fn takes_value(&self) -> bool { true } fn help(&self) -> Option<&'e str> { self.p.meta.about } + fn long_help(&self) -> Option<&'e str> { self.p.meta.long_about } fn default_val(&self) -> Option<&'e OsStr> { None } fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { None diff --git a/src/app/parser.rs b/src/app/parser.rs index e26cb0f8..f8082dab 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -680,7 +680,7 @@ impl<'a, 'b> Parser<'a, 'b> if sc.meta.bin_name != self.meta.bin_name { sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.name)); } - sc._help() + sc._help(false) } // allow wrong self convention due to self.valid_neg_num = true and it's a private method @@ -1246,7 +1246,7 @@ impl<'a, 'b> Parser<'a, 'b> arg.to_str().unwrap()); if arg == "help" && self.is_set(AS::NeedsLongHelp) { sdebugln!("Help"); - try!(self._help()); + try!(self._help(true)); } if arg == "version" && self.is_set(AS::NeedsLongVersion) { sdebugln!("Version"); @@ -1264,7 +1264,7 @@ impl<'a, 'b> Parser<'a, 'b> if let Some(h) = self.help_short { if arg == h && self.is_set(AS::NeedsLongHelp) { sdebugln!("Help"); - try!(self._help()); + try!(self._help(false)); } } if let Some(v) = self.version_short { @@ -1277,9 +1277,20 @@ impl<'a, 'b> Parser<'a, 'b> Ok(()) } - fn _help(&self) -> ClapResult<()> { + fn use_long_help(&self) -> bool { + let ul = self.flags.iter().any(|f| f.b.long_help.is_some()) || + self.opts.iter().any(|o| o.b.long_help.is_some()) || + self.positionals.values().any(|p| p.b.long_help.is_some()) || + self.subcommands.iter().any(|s| s.p.meta.long_about.is_some()); + debugln!("Parser::use_long_help: ret={:?}", ul); + ul + } + + fn _help(&self, mut use_long: bool) -> ClapResult<()> { + debugln!("Parser::_help: use_long={:?}", use_long); + use_long = use_long && self.use_long_help(); let mut buf = vec![]; - try!(Help::write_parser_help(&mut buf, self)); + try!(Help::write_parser_help(&mut buf, self, use_long)); Err(Error { message: unsafe { String::from_utf8_unchecked(buf) }, kind: ErrorKind::HelpDisplayed, @@ -1288,6 +1299,7 @@ impl<'a, 'b> Parser<'a, 'b> } fn _version(&self) -> ClapResult<()> { + debugln!("Parser::_version: "); let out = io::stdout(); let mut buf_w = BufWriter::new(out.lock()); try!(self.print_version(&mut buf_w)); @@ -1630,7 +1642,11 @@ impl<'a, 'b> Parser<'a, 'b> } pub fn write_help(&self, w: &mut W) -> ClapResult<()> { - Help::write_parser_help(w, self) + Help::write_parser_help(w, self, false) + } + + pub fn write_long_help(&self, w: &mut W) -> ClapResult<()> { + Help::write_parser_help(w, self, true) } pub fn write_help_err(&self, w: &mut W) -> ClapResult<()> { diff --git a/src/args/any_arg.rs b/src/args/any_arg.rs index 55245dba..5f8615fc 100644 --- a/src/args/any_arg.rs +++ b/src/args/any_arg.rs @@ -32,6 +32,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display { fn takes_value(&self) -> bool; fn val_names(&self) -> Option<&VecMap<&'e str>>; fn help(&self) -> Option<&'e str>; + fn long_help(&self) -> Option<&'e str>; fn default_val(&self) -> Option<&'e OsStr>; fn default_vals_ifs(&self) -> Option, &'e OsStr)>>; fn longest_filter(&self) -> bool; diff --git a/src/args/arg.rs b/src/args/arg.rs index 9f57a883..860477c6 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -103,6 +103,7 @@ impl<'a, 'b> Arg<'a, 'b> { "long" => yaml_to_str!(a, v, long), "aliases" => yaml_vec_or_str!(v, a, alias), "help" => yaml_to_str!(a, v, help), + "long_help" => yaml_to_str!(a, v, long_help), "required" => yaml_to_bool!(a, v, required), "required_if" => yaml_tuple2!(a, v, required_if), "required_ifs" => yaml_tuple2!(a, v, required_if), @@ -489,8 +490,14 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Sets the help text of the argument that will be displayed to the user when they print the - /// usage/help information. + /// Sets the short help text of the argument that will be displayed to the user when they print + /// the help information with `-h`. Typically, this is a short (one line) description of the + /// arg. + /// + /// **NOTE:** If only `Arg::help` is provided, and not [`Arg::long_help`] but the user requests + /// `--help` clap will still display the contents of `help` appropriately + /// + /// **NOTE:** Only `Arg::help` is used in completion script generation in order to be concise /// /// # Examples /// @@ -532,11 +539,83 @@ impl<'a, 'b> Arg<'a, 'b> { /// -h, --help Prints help information /// -V, --version Prints version information /// ``` + /// [`Arg::long_help`]: ./struct.Arg.html#method.long_help pub fn help(mut self, h: &'b str) -> Self { self.b.help = Some(h); self } + /// Sets the long help text of the argument that will be displayed to the user when they print + /// the help information with `--help`. Typically this a more detailed (multi-line) message + /// that describes the arg. + /// + /// **NOTE:** If only `long_help` is provided, and not [`Arg::help`] but the user requests `-h` + /// clap will still display the contents of `long_help` appropriately + /// + /// **NOTE:** Only [`Arg::help`] is used in completion script generation in order to be concise + /// + /// # Examples + /// + /// Any valid UTF-8 is allowed in the help text. The one exception is when one wishes to + /// include a newline in the help text and have the following text be properly aligned with all + /// the other help text. + /// + /// ```rust + /// # use clap::{App, Arg}; + /// Arg::with_name("config") + /// .long_help( + /// "The config file used by the myprog must be in JSON format + /// with only valid keys and may not contain other nonsense + /// that cannot be read by this program. Obviously I'm going on + /// and on, so I'll stop now.") + /// # ; + /// ``` + /// + /// Setting `help` displays a short message to the side of the argument when the user passes + /// `-h` or `--help` (by default). + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .long("config") + /// .long_help( + /// "The config file used by the myprog must be in JSON format + /// with only valid keys and may not contain other nonsense + /// that cannot be read by this program. Obviously I'm going on + /// and on, so I'll stop now.")) + /// .get_matches_from(vec![ + /// "prog", "--help" + /// ]); + /// ``` + /// + /// The above example displays + /// + /// ```notrust + /// helptest + /// + /// USAGE: + /// helptest [FLAGS] + /// + /// FLAGS: + /// --config + /// The config file used by the myprog must be in JSON format + /// with only valid keys and may not contain other nonsense + /// that cannot be read by this program. Obviously I'm going on + /// and on, so I'll stop now. + /// + /// -h, --help + /// Prints help information + /// + /// -V, --version + /// Prints version information + /// ``` + /// [`Arg::help`]: ./struct.Arg.html#method.help + pub fn long_help(mut self, h: &'b str) -> Self { + self.b.long_help = Some(h); + self + } + /// Specifies that this arg is the last, or final, positional argument (i.e. has the highest /// index) and is *only* able to be accessed via the `--` syntax (i.e. `$ prog args -- /// last_arg`). Even, if no other arguments are left to parse, if the user omits the `--` syntax diff --git a/src/args/arg_builder/base.rs b/src/args/arg_builder/base.rs index 8b566e38..5990bc08 100644 --- a/src/args/arg_builder/base.rs +++ b/src/args/arg_builder/base.rs @@ -7,6 +7,7 @@ pub struct Base<'a, 'b> { pub name: &'a str, pub help: Option<&'b str>, + pub long_help: Option<&'b str>, pub blacklist: Option>, pub settings: ArgFlags, pub r_unless: Option>, diff --git a/src/args/arg_builder/flag.rs b/src/args/arg_builder/flag.rs index d1c16690..e3e9dd01 100644 --- a/src/args/arg_builder/flag.rs +++ b/src/args/arg_builder/flag.rs @@ -79,6 +79,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> { fn long(&self) -> Option<&'e str> { self.s.long } fn val_delim(&self) -> Option { None } fn help(&self) -> Option<&'e str> { self.b.help } + fn long_help(&self) -> Option<&'e str> { self.b.long_help } fn val_terminator(&self) -> Option<&'e str> { None } fn default_val(&self) -> Option<&'e OsStr> { None } fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs index 342ccbdc..64dd9c46 100644 --- a/src/args/arg_builder/option.rs +++ b/src/args/arg_builder/option.rs @@ -129,6 +129,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> { fn val_delim(&self) -> Option { self.v.val_delim } fn takes_value(&self) -> bool { true } fn help(&self) -> Option<&'e str> { self.b.help } + fn long_help(&self) -> Option<&'e str> { self.b.long_help } fn default_val(&self) -> Option<&'e OsStr> { self.v.default_val } fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { self.v.default_vals_ifs.as_ref().map(|vm| vm.values()) diff --git a/src/args/arg_builder/positional.rs b/src/args/arg_builder/positional.rs index ddbd8964..c77976b1 100644 --- a/src/args/arg_builder/positional.rs +++ b/src/args/arg_builder/positional.rs @@ -126,6 +126,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> { fn val_delim(&self) -> Option { self.v.val_delim } fn takes_value(&self) -> bool { true } fn help(&self) -> Option<&'e str> { self.b.help } + fn long_help(&self) -> Option<&'e str> { self.b.long_help } fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { self.v.default_vals_ifs.as_ref().map(|vm| vm.values()) }