Skip to content

Commit 813d75d

Browse files
committed
feat(Help Message): wraps and aligns the help message of subcommands
Subcommand's help strings are now automatically wrapped and aligned just like other arguments. Closes #452
1 parent 1d73b03 commit 813d75d

File tree

5 files changed

+78
-32
lines changed

5 files changed

+78
-32
lines changed

src/app/mod.rs

+61-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ use std::path::Path;
1313
use std::process;
1414
use std::ffi::OsString;
1515
use std::borrow::Borrow;
16+
use std::result::Result as StdResult;
17+
use std::rc::Rc;
18+
use std::fmt;
1619

1720
#[cfg(feature = "yaml")]
1821
use yaml_rust::Yaml;
22+
use vec_map::VecMap;
1923

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

799803
e.exit()
800804
}
805+
806+
#[doc(hidden)]
807+
pub fn write_self_help<W>(&self, w: &mut W, longest: usize, nlh: bool) -> io::Result<()>
808+
where W: Write
809+
{
810+
let hw = HelpWriter::new(self, longest, nlh);
811+
hw.write_to(w)
812+
813+
// try!(write!(w, " {}", self.p.meta.name));
814+
// write_spaces!((longest_sc + 4) - (self.p.meta.name.len()), w);
815+
// if let Some(a) = self.p.meta.about {
816+
// if a.contains("{n}") {
817+
// let mut ab = a.split("{n}");
818+
// while let Some(part) = ab.next() {
819+
// try!(write!(w, "{}\n", part));
820+
// write_spaces!(longest_sc + 8, w);
821+
// try!(write!(w, "{}", ab.next().unwrap_or("")));
822+
// }
823+
// } else {
824+
// try!(write!(w, "{}", a));
825+
// }
826+
// }
827+
// write!(w, "\n")
828+
}
801829
}
802830

803831
#[cfg(feature = "yaml")]
@@ -883,3 +911,35 @@ impl<'a, 'b> Clone for App<'a, 'b> {
883911
}
884912
}
885913
}
914+
915+
impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
916+
fn name(&self) -> &'n str {
917+
unreachable!("App struct does not support AnyArg::name, this is a bug!")
918+
}
919+
fn overrides(&self) -> Option<&[&'e str]> { None }
920+
fn requires(&self) -> Option<&[&'e str]> { None }
921+
fn blacklist(&self) -> Option<&[&'e str]> { None }
922+
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
923+
fn is_set(&self, _: ArgSettings) -> bool { false }
924+
fn set(&mut self, _: ArgSettings) {
925+
unreachable!("App struct does not support AnyArg::set, this is a bug!")
926+
}
927+
fn has_switch(&self) -> bool { false }
928+
fn max_vals(&self) -> Option<u64> { None }
929+
fn num_vals(&self) -> Option<u64> { None }
930+
fn possible_vals(&self) -> Option<&[&'e str]> { None }
931+
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
932+
fn min_vals(&self) -> Option<u64> { None }
933+
fn short(&self) -> Option<char> { None }
934+
fn long(&self) -> Option<&'e str> { None }
935+
fn val_delim(&self) -> Option<char> { None }
936+
fn takes_value(&self) -> bool { true }
937+
fn help(&self) -> Option<&'e str> { self.p.meta.about }
938+
fn default_val(&self) -> Option<&'n str> { None }
939+
}
940+
941+
impl<'n, 'e> fmt::Display for App<'n, 'e> {
942+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
943+
write!(f, "{}", self.p.meta.name)
944+
}
945+
}

src/app/parser.rs

+6-20
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
814814
.iter()
815815
.any(|s| &s.p.meta.name[..] == "help") {
816816
debugln!("Building help");
817-
self.subcommands.push(App::new("help").about("With no arguments it prints this message, otherwise it prints help information about other subcommands"));
817+
self.subcommands.push(App::new("help").about("Prints this message or the help message of the given subcommand(s)"));
818818
}
819819
}
820820

@@ -1034,7 +1034,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
10341034
}
10351035

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

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

11671167
fn _validate_num_vals<A>(&self, a: &A, ma: &MatchedArg, matcher: &ArgMatcher) -> ClapResult<()>
1168-
where A: AnyArg<'a, 'b>
1168+
where A: AnyArg<'a, 'b> + Display
11691169
{
11701170
debugln!("fn=_validate_num_vals;");
11711171
if let Some(num) = a.num_vals() {
@@ -1532,22 +1532,8 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
15321532
btm.insert(sc.p.meta.name.clone(), sc);
15331533
}
15341534
for (_, btm) in ord_m.into_iter() {
1535-
for (name, sc) in btm.into_iter() {
1536-
try!(write!(w, " {}", name));
1537-
write_spaces!((longest_sc + 4) - (name.len()), w);
1538-
if let Some(a) = sc.p.meta.about {
1539-
if a.contains("{n}") {
1540-
let mut ab = a.split("{n}");
1541-
while let Some(part) = ab.next() {
1542-
try!(write!(w, "{}\n", part));
1543-
write_spaces!(longest_sc + 8, w);
1544-
try!(write!(w, "{}", ab.next().unwrap_or("")));
1545-
}
1546-
} else {
1547-
try!(write!(w, "{}", a));
1548-
}
1549-
}
1550-
try!(write!(w, "\n"));
1535+
for (_, sc) in btm.into_iter() {
1536+
try!(sc.write_self_help(w, longest_sc, nlh));
15511537
}
15521538
}
15531539
}

src/args/any_arg.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use std::rc::Rc;
2-
use std::fmt::Display;
32

43
use vec_map::VecMap;
54

65
use args::settings::ArgSettings;
76

87
#[doc(hidden)]
9-
pub trait AnyArg<'n, 'e>: Display {
8+
pub trait AnyArg<'n, 'e> {
109
fn name(&self) -> &'n str;
1110
fn overrides(&self) -> Option<&[&'e str]>;
1211
fn requires(&self) -> Option<&[&'e str]>;

src/args/help_writer.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::io;
2+
use std::fmt::Display;
23

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

17-
impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
18+
impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> + Display {
1819
pub fn new(a: &'a A, l: usize, nlh: bool) -> Self {
1920
HelpWriter {
2021
a: a,
@@ -99,7 +100,7 @@ impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
99100
if it.peek().is_some() { try!(write!(w, " ")); }
100101
}
101102
} else {
102-
try!(write!(w, "<{}>{}", self.a.name(), if self.a.is_set(ArgSettings::Multiple) { "..." } else { "" }));
103+
try!(write!(w, "{}", self.a));
103104
}
104105
if self.a.has_switch() {
105106
if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {

src/errors.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ impl Error {
355355

356356
#[doc(hidden)]
357357
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A, other: Option<O>, usage: U) -> Self
358-
where A: AnyArg<'a, 'b>,
358+
where A: AnyArg<'a, 'b> + Display,
359359
O: Into<String>,
360360
U: Display
361361
{
@@ -383,7 +383,7 @@ impl Error {
383383

384384
#[doc(hidden)]
385385
pub fn empty_value<'a, 'b, A, U>(arg: &A, usage: U) -> Self
386-
where A: AnyArg<'a, 'b>,
386+
where A: AnyArg<'a, 'b> + Display,
387387
U: Display
388388
{
389389
Error {
@@ -404,7 +404,7 @@ impl Error {
404404
pub fn invalid_value<'a, 'b, B, G, A, U>(bad_val: B, good_vals: &[G], arg: &A, usage: U) -> Self
405405
where B: AsRef<str>,
406406
G: AsRef<str> + Display,
407-
A: AnyArg<'a, 'b>,
407+
A: AnyArg<'a, 'b> + Display,
408408
U: Display
409409
{
410410
let suffix = suggestions::did_you_mean_suffix(bad_val.as_ref(),
@@ -539,7 +539,7 @@ impl Error {
539539
#[doc(hidden)]
540540
pub fn too_many_values<'a, 'b, V, A, U>(val: V, arg: &A, usage: U) -> Self
541541
where V: AsRef<str> + Display + ToOwned,
542-
A: AnyArg<'a, 'b>,
542+
A: AnyArg<'a, 'b> + Display,
543543
U: Display
544544
{
545545
let v = val.as_ref();
@@ -560,7 +560,7 @@ impl Error {
560560

561561
#[doc(hidden)]
562562
pub fn too_few_values<'a, 'b, A, U>(arg: &A, min_vals: u64, curr_vals: usize, usage: U) -> Self
563-
where A: AnyArg<'a, 'b>,
563+
where A: AnyArg<'a, 'b> + Display,
564564
U: Display
565565
{
566566
Error {
@@ -595,7 +595,7 @@ impl Error {
595595

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

619619
#[doc(hidden)]
620620
pub fn unexpected_multiple_usage<'a, 'b, A, U>(arg: &A, usage: U) -> Self
621-
where A: AnyArg<'a, 'b>,
621+
where A: AnyArg<'a, 'b> + Display,
622622
U: Display
623623
{
624624
Error {

0 commit comments

Comments
 (0)