From 41e29417b5e349a6afee046b763da8651aa89bec Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 3 Apr 2018 23:01:45 -0400 Subject: [PATCH] fix: fixes hiding args from short or long help in v3 context Fixes the implementation of the hiding of arguments form the short or long help on v3 --- src/app/help.rs | 129 +++++++++++++++++++++++++++++++++++++------ src/app/parser.rs | 77 +++++++++++++------------- src/args/arg.rs | 3 +- tests/hidden_args.rs | 2 +- 4 files changed, 152 insertions(+), 59 deletions(-) diff --git a/src/app/help.rs b/src/app/help.rs index 33d40915..2ed1e0db 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -174,9 +174,9 @@ impl<'w> Help<'w> { impl<'w> Help<'w> { /// Writes help for each argument in the order they were declared to the wrapped stream. fn write_args_unsorted<'a, 'b, I>(&mut self, args: I) -> io::Result<()> - where - 'a: 'b, - I: Iterator>, + where + 'a: 'b, + I: Iterator>, { debugln!("Help::write_args_unsorted;"); // The shortest an arg can legally be is 2 (i.e. '-x') @@ -208,9 +208,9 @@ impl<'w> Help<'w> { /// Sorts arguments by length and display order and write their help to the wrapped stream. fn write_args<'a, 'b, I>(&mut self, args: I) -> io::Result<()> - where - 'a: 'b, - I: Iterator>, + where + 'a: 'b, + I: Iterator>, { debugln!("Help::write_args;"); // The shortest an arg can legally be is 2 (i.e. '-x') @@ -558,14 +558,17 @@ impl<'w> Help<'w> { } spec_vals.join(" ") } - - fn should_show_arg(use_long: bool, arg: &Arg) -> bool { - if arg.is_set(ArgSettings::Hidden) { - return false; - } - (!arg.is_set(ArgSettings::HiddenLongHelp) && use_long) || - (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long) || - arg.is_set(ArgSettings::NextLineHelp) +} + +/// Methods to write a single subcommand +impl<'w> Help<'w> { + fn write_subcommand<'a, 'b>(&mut self, app: &App<'a, 'b>) -> io::Result<()> { + debugln!("Help::write_subcommand;"); + write!(self.writer, "{}", TAB)?; + color!(self, "{}", app.name, good)?; + let spec_vals = self.sc_val(app)?; + self.sc_help(app, &*spec_vals)?; + Ok(()) } fn sc_val<'a, 'b>(&mut self, app: &App<'a, 'b>) -> Result { @@ -587,6 +590,87 @@ impl<'w> Help<'w> { } Ok(spec_vals) } + + fn sc_spec_vals(&self, a: &App) -> String { + debugln!("Help::sc_spec_vals: a={}", a.name); + let mut spec_vals = vec![]; + if let Some(ref aliases) = a.aliases { + debugln!("Help::spec_vals: Found aliases...{:?}", aliases); + let als = if self.color { + aliases + .iter() + .filter(|&als| als.1) // visible + .map(|&als| format!("{}", self.cizer.good(als.0))) // name + .collect::>() + .join(", ") + } else { + aliases + .iter() + .filter(|&als| als.1) + .map(|&als| als.0) + .collect::>() + .join(", ") + }; + if !als.is_empty() { + spec_vals.push(format!(" [aliases: {}]", als)); + } + } + spec_vals.join(" ") + } + + fn sc_help<'a, 'b>(&mut self, app: &App<'a, 'b>, spec_vals: &str) -> io::Result<()> { + debugln!("Help::sc_help;"); + let h = if self.use_long { + app.long_about.unwrap_or_else(|| app.about.unwrap_or("")) + } else { + app.about.unwrap_or_else(|| app.long_about.unwrap_or("")) + }; + let mut help = String::from(h) + spec_vals; + let nlh = self.next_line_help || self.use_long; + debugln!("Help::sc_help: Next Line...{:?}", nlh); + + let spcs = if nlh || self.force_next_line { + 12 // "tab" * 3 + } else { + self.longest + 12 + }; + + let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= self.term_w; + + // Is help on next line, if so then indent + if nlh || self.force_next_line { + write!(self.writer, "\n{}{}{}", TAB, TAB, TAB)?; + } + + debug!("Help::sc_help: Too long..."); + if too_long && spcs <= self.term_w || h.contains("{n}") { + sdebugln!("Yes"); + debugln!("Help::sc_help: help...{}", help); + debugln!("Help::sc_help: help width...{}", str_width(&*help)); + // Determine how many newlines we need to insert + let avail_chars = self.term_w - spcs; + debugln!("Help::sc_help: Usable space...{}", avail_chars); + help = wrap_help(&help.replace("{n}", "\n"), avail_chars); + } else { + sdebugln!("No"); + } + if let Some(part) = help.lines().next() { + write!(self.writer, "{}", part)?; + } + for part in help.lines().skip(1) { + write!(self.writer, "\n")?; + if nlh || self.force_next_line { + write!(self.writer, "{}{}{}", TAB, TAB, TAB)?; + } else { + write_nspaces!(self.writer, self.longest + 8); + } + write!(self.writer, "{}", part)?; + } + if !help.contains('\n') && (nlh || self.force_next_line) { + write!(self.writer, "\n")?; + } + Ok(()) + } } // Methods to write Parser help. @@ -600,10 +684,10 @@ impl<'w> Help<'w> { let flags = parser.has_flags(); // Strange filter/count vs fold... https://github.com/rust-lang/rust/issues/33038 let pos = positionals!(parser.app).fold(0, |acc, arg| { - if arg.is_set(ArgSettings::Hidden) { - acc - } else { + if should_show_arg(self.use_long, arg) { acc + 1 + } else { + acc } }) > 0; let opts = parser.has_opts(); @@ -1017,6 +1101,17 @@ impl<'w> Help<'w> { } } +fn should_show_arg(use_long: bool, arg: &Arg) -> bool { + debugln!("Help::should_show_arg: use_long={:?}, arg={}", use_long, arg.name); + if arg.is_set(ArgSettings::Hidden) { + return false; + } + (!arg.is_set(ArgSettings::HiddenLongHelp) && use_long) || + (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long) || + arg.is_set(ArgSettings::NextLineHelp) +} + + fn wrap_help(help: &str, avail_chars: usize) -> String { let wrapper = textwrap::Wrapper::new(avail_chars).break_words(false); help.lines() diff --git a/src/app/parser.rs b/src/app/parser.rs index 1727dc64..1d5bc63e 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -8,8 +8,8 @@ use std::iter::Peekable; use std::mem; use std::cell::Cell; -// Third party -use map::{self, VecMap}; +// Third party facade +use map::VecMap; // Internal use INTERNAL_ERROR_MSG; @@ -927,52 +927,49 @@ where } fn use_long_help(&self) -> bool { + debugln!("Parser::use_long_help;"); // In this case, both must be checked. This allows the retention of // original formatting, but also ensures that the actual -h or --help // specified by the user is sent through. If HiddenShortHelp is not included, // then items specified with hidden_short_help will also be hidden. - let should_long = |v: &Base| { + let should_long = |v: &Arg| { v.long_help.is_some() || v.is_set(ArgSettings::HiddenLongHelp) || v.is_set(ArgSettings::HiddenShortHelp) }; - self.meta.long_about.is_some() - || self.flags.iter().any(|f| should_long(&f.b)) - || self.opts.iter().any(|o| should_long(&o.b)) - || self.positionals.values().any(|p| should_long(&p.b)) - || self.subcommands - .iter() - .any(|s| s.p.meta.long_about.is_some()) + self.app.long_about.is_some() + || args!(self.app).any(|f| should_long(&f)) + || subcommands!(self.app).any(|s| s.long_about.is_some()) } - fn _help(&self, mut use_long: bool) -> Error { - debugln!("Parser::_help: use_long={:?}", use_long); - use_long = use_long && self.use_long_help(); - let mut buf = vec![]; - match Help::write_parser_help(&mut buf, self, use_long) { - Err(e) => e, - _ => Error { - message: String::from_utf8(buf).unwrap_or_default(), - kind: ErrorKind::HelpDisplayed, - info: None, - }, - } - } - - fn _version(&self, use_long: bool) -> Error { - debugln!("Parser::_version: "); - let out = io::stdout(); - let mut buf_w = BufWriter::new(out.lock()); - match self.print_version(&mut buf_w, use_long) { - Err(e) => e, - _ => Error { - message: String::new(), - kind: ErrorKind::VersionDisplayed, - info: None, - }, - } - } +// fn _help(&self, mut use_long: bool) -> ClapError { +// debugln!("Parser::_help: use_long={:?}", use_long && self.use_long_help()); +// use_long = use_long && self.use_long_help(); +// let mut buf = vec![]; +// match Help::write_parser_help(&mut buf, self, use_long) { +// Err(e) => e, +// _ => ClapError { +// message: String::from_utf8(buf).unwrap_or_default(), +// kind: ErrorKind::HelpDisplayed, +// info: None, +// }, +// } +// } +// +// fn _version(&self, use_long: bool) -> ClapError { +// debugln!("Parser::_version: "); +// let out = io::stdout(); +// let mut buf_w = BufWriter::new(out.lock()); +// match self.print_version(&mut buf_w, use_long) { +// Err(e) => e, +// _ => ClapError { +// message: String::new(), +// kind: ErrorKind::VersionDisplayed, +// info: None, +// }, +// } +// } fn parse_long_arg( &mut self, @@ -1512,8 +1509,8 @@ where } fn help_err(&self, mut use_long: bool) -> ClapError { - debugln!("Parser::_help: use_long={:?}", use_long); - use_long = use_long && self.app.use_long_help(); + debugln!("Parser::help_err: use_long={:?}", use_long && self.use_long_help()); + use_long = use_long && self.use_long_help(); let mut buf = vec![]; match Help::write_parser_help(&mut buf, self, use_long) { Err(e) => e, @@ -1526,7 +1523,7 @@ where } fn version_err(&self, use_long: bool) -> ClapError { - debugln!("Parser::_version: "); + debugln!("Parser::version_err: "); let out = io::stdout(); let mut buf_w = BufWriter::new(out.lock()); match self.print_version(&mut buf_w, use_long) { diff --git a/src/args/arg.rs b/src/args/arg.rs index 9d0242f1..57e6c7bc 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -10,6 +10,7 @@ use osstringext::OsStrExt3; use std::os::unix::ffi::OsStrExt; use std::env; use std::cmp::{Ord, Ordering}; +use std::str; #[cfg(feature = "yaml")] use yaml_rust::Yaml; @@ -4250,7 +4251,7 @@ mod test { f.long = Some("flag"); f.aliases = Some(vec![("als", true)]); - assert_eq!(&*format!("{}", f), "--flag"); + assert_eq!(&*format!("{}", f), "--flag") } #[test] diff --git a/tests/hidden_args.rs b/tests/hidden_args.rs index 581da23e..88dd0cd0 100644 --- a/tests/hidden_args.rs +++ b/tests/hidden_args.rs @@ -30,7 +30,7 @@ fn hidden_args() { Arg::from_usage("-f, --flag 'some flag'").hidden(true), Arg::from_usage("-F, --flag2 'some other flag'"), Arg::from_usage("--option [opt] 'some option'"), - Arg::with_name("DUMMY").required(false).hidden(true), + Arg::with_name("DUMMY").hidden(true), ]); assert!(test::compare_output(app, "test --help", HIDDEN_ARGS, false)); }