Skip to content

Commit

Permalink
feat(Help Message): wraps and aligns the help message of subcommands
Browse files Browse the repository at this point in the history
Subcommand's help strings are now automatically wrapped and aligned just
like other arguments.

Closes #452
  • Loading branch information
kbknapp committed Mar 16, 2016
1 parent 1d73b03 commit 813d75d
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 32 deletions.
62 changes: 61 additions & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ use std::path::Path;
use std::process;
use std::ffi::OsString;
use std::borrow::Borrow;
use std::result::Result as StdResult;
use std::rc::Rc;
use std::fmt;

#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
use vec_map::VecMap;

use args::{Arg, AnyArg, ArgGroup, ArgMatches, ArgMatcher};
use args::{Arg, HelpWriter, ArgSettings, AnyArg, ArgGroup, ArgMatches, ArgMatcher};
use app::parser::Parser;
use errors::Error;
use errors::Result as ClapResult;
Expand Down Expand Up @@ -798,6 +802,30 @@ impl<'a, 'b> App<'a, 'b> {

e.exit()
}

#[doc(hidden)]
pub fn write_self_help<W>(&self, w: &mut W, longest: usize, nlh: bool) -> io::Result<()>
where W: Write
{
let hw = HelpWriter::new(self, longest, nlh);
hw.write_to(w)

// try!(write!(w, " {}", self.p.meta.name));
// write_spaces!((longest_sc + 4) - (self.p.meta.name.len()), w);
// if let Some(a) = self.p.meta.about {
// if a.contains("{n}") {
// let mut ab = a.split("{n}");
// while let Some(part) = ab.next() {
// try!(write!(w, "{}\n", part));
// write_spaces!(longest_sc + 8, w);
// try!(write!(w, "{}", ab.next().unwrap_or("")));
// }
// } else {
// try!(write!(w, "{}", a));
// }
// }
// write!(w, "\n")
}
}

#[cfg(feature = "yaml")]
Expand Down Expand Up @@ -883,3 +911,35 @@ impl<'a, 'b> Clone for App<'a, 'b> {
}
}
}

impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
fn name(&self) -> &'n str {
unreachable!("App struct does not support AnyArg::name, this is a bug!")
}
fn overrides(&self) -> Option<&[&'e str]> { None }
fn requires(&self) -> Option<&[&'e str]> { None }
fn blacklist(&self) -> Option<&[&'e str]> { None }
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
fn is_set(&self, _: ArgSettings) -> bool { false }
fn set(&mut self, _: ArgSettings) {
unreachable!("App struct does not support AnyArg::set, this is a bug!")
}
fn has_switch(&self) -> bool { false }
fn max_vals(&self) -> Option<u64> { None }
fn num_vals(&self) -> Option<u64> { None }
fn possible_vals(&self) -> Option<&[&'e str]> { None }
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
fn min_vals(&self) -> Option<u64> { None }
fn short(&self) -> Option<char> { None }
fn long(&self) -> Option<&'e str> { None }
fn val_delim(&self) -> Option<char> { None }
fn takes_value(&self) -> bool { true }
fn help(&self) -> Option<&'e str> { self.p.meta.about }
fn default_val(&self) -> Option<&'n str> { None }
}

impl<'n, 'e> fmt::Display for App<'n, 'e> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.p.meta.name)
}
}
26 changes: 6 additions & 20 deletions src/app/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -814,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("With no arguments it prints this message, otherwise it prints help information about other subcommands"));
self.subcommands.push(App::new("help").about("Prints this message or the help message of the given subcommand(s)"));
}
}

Expand Down Expand Up @@ -1034,7 +1034,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
}

fn add_single_val_to_arg<A>(&self, arg: &A, v: &OsStr, matcher: &mut ArgMatcher<'a>) -> ClapResult<Option<&'a str>>
where A: AnyArg<'a, 'b>
where A: AnyArg<'a, 'b> + Display
{
debugln!("adding val: {:?}", v);
matcher.add_val_to(arg.name(), v);
Expand All @@ -1052,7 +1052,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
}

fn validate_value<A>(&self, arg: &A, val: &OsStr, matcher: &ArgMatcher<'a>) -> ClapResult<Option<&'a str>>
where A: AnyArg<'a, 'b> {
where A: AnyArg<'a, 'b> + Display {
debugln!("fn=validate_value; val={:?}", val);
if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() {
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher)));
Expand Down Expand Up @@ -1165,7 +1165,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
}

fn _validate_num_vals<A>(&self, a: &A, ma: &MatchedArg, matcher: &ArgMatcher) -> ClapResult<()>
where A: AnyArg<'a, 'b>
where A: AnyArg<'a, 'b> + Display
{
debugln!("fn=_validate_num_vals;");
if let Some(num) = a.num_vals() {
Expand Down Expand Up @@ -1532,22 +1532,8 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
btm.insert(sc.p.meta.name.clone(), sc);
}
for (_, btm) in ord_m.into_iter() {
for (name, sc) in btm.into_iter() {
try!(write!(w, " {}", name));
write_spaces!((longest_sc + 4) - (name.len()), w);
if let Some(a) = sc.p.meta.about {
if a.contains("{n}") {
let mut ab = a.split("{n}");
while let Some(part) = ab.next() {
try!(write!(w, "{}\n", part));
write_spaces!(longest_sc + 8, w);
try!(write!(w, "{}", ab.next().unwrap_or("")));
}
} else {
try!(write!(w, "{}", a));
}
}
try!(write!(w, "\n"));
for (_, sc) in btm.into_iter() {
try!(sc.write_self_help(w, longest_sc, nlh));
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/args/any_arg.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::rc::Rc;
use std::fmt::Display;

use vec_map::VecMap;

use args::settings::ArgSettings;

#[doc(hidden)]
pub trait AnyArg<'n, 'e>: Display {
pub trait AnyArg<'n, 'e> {
fn name(&self) -> &'n str;
fn overrides(&self) -> Option<&[&'e str]>;
fn requires(&self) -> Option<&[&'e str]>;
Expand Down
5 changes: 3 additions & 2 deletions src/args/help_writer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::io;
use std::fmt::Display;

use args::AnyArg;
use args::settings::ArgSettings;
Expand All @@ -14,7 +15,7 @@ pub struct HelpWriter<'a, A> where A: 'a {
term_w: Option<usize>,
}

impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
pub fn new(a: &'a A, l: usize, nlh: bool) -> Self {
HelpWriter {
a: a,
Expand Down Expand Up @@ -99,7 +100,7 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
if it.peek().is_some() { try!(write!(w, " ")); }
}
} else {
try!(write!(w, "<{}>{}", self.a.name(), if self.a.is_set(ArgSettings::Multiple) { "..." } else { "" }));
try!(write!(w, "{}", self.a));
}
if self.a.has_switch() {
if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
Expand Down
14 changes: 7 additions & 7 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ impl Error {

#[doc(hidden)]
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A, other: Option<O>, usage: U) -> Self
where A: AnyArg<'a, 'b>,
where A: AnyArg<'a, 'b> + Display,
O: Into<String>,
U: Display
{
Expand Down Expand Up @@ -383,7 +383,7 @@ impl Error {

#[doc(hidden)]
pub fn empty_value<'a, 'b, A, U>(arg: &A, usage: U) -> Self
where A: AnyArg<'a, 'b>,
where A: AnyArg<'a, 'b> + Display,
U: Display
{
Error {
Expand All @@ -404,7 +404,7 @@ impl Error {
pub fn invalid_value<'a, 'b, B, G, A, U>(bad_val: B, good_vals: &[G], arg: &A, usage: U) -> Self
where B: AsRef<str>,
G: AsRef<str> + Display,
A: AnyArg<'a, 'b>,
A: AnyArg<'a, 'b> + Display,
U: Display
{
let suffix = suggestions::did_you_mean_suffix(bad_val.as_ref(),
Expand Down Expand Up @@ -539,7 +539,7 @@ impl Error {
#[doc(hidden)]
pub fn too_many_values<'a, 'b, V, A, U>(val: V, arg: &A, usage: U) -> Self
where V: AsRef<str> + Display + ToOwned,
A: AnyArg<'a, 'b>,
A: AnyArg<'a, 'b> + Display,
U: Display
{
let v = val.as_ref();
Expand All @@ -560,7 +560,7 @@ impl Error {

#[doc(hidden)]
pub fn too_few_values<'a, 'b, A, U>(arg: &A, min_vals: u64, curr_vals: usize, usage: U) -> Self
where A: AnyArg<'a, 'b>,
where A: AnyArg<'a, 'b> + Display,
U: Display
{
Error {
Expand Down Expand Up @@ -595,7 +595,7 @@ impl Error {

#[doc(hidden)]
pub fn wrong_number_of_values<'a, 'b, A, S, U>(arg: &A, num_vals: u64, curr_vals: usize, suffix: S, usage: U) -> Self
where A: AnyArg<'a, 'b>,
where A: AnyArg<'a, 'b> + Display,
S: Display,
U: Display
{
Expand All @@ -618,7 +618,7 @@ impl Error {

#[doc(hidden)]
pub fn unexpected_multiple_usage<'a, 'b, A, U>(arg: &A, usage: U) -> Self
where A: AnyArg<'a, 'b>,
where A: AnyArg<'a, 'b> + Display,
U: Display
{
Error {
Expand Down

0 comments on commit 813d75d

Please sign in to comment.