feat(Subcommands): adds support for custom ordering in help messages

Allows custom ordering of subcommands within the help message. Subcommands with a lower
value will be displayed first in the help message. This is helpful when one would like to
emphasise frequently used subcommands, or prioritize those towards the top of the list.
Duplicate values **are** allowed. Subcommands with duplicate display orders will be
displayed in alphabetical order.

**NOTE:** The default is 999 for all subcommands.

```rust
use clap::{App, SubCommand};
let m = App::new("cust-ord")
    .subcommand(SubCommand::with_name("alpha") // typically subcommands are grouped
                                               // alphabetically by name. Subcommands
                                               // without a display_order have a value of
                                               // 999 and are displayed alphabetically with
                                               // all other 999 subcommands
        .about("Some help and text"))
    .subcommand(SubCommand::with_name("beta")
        .display_order(1)   // In order to force this subcommand to appear *first*
                            // all we have to do is give it a value lower than 999.
                            // Any other subcommands with a value of 1 will be displayed
                            // alphabetically with this one...then 2 values, then 3, etc.
        .about("I should be first!"))
    .get_matches_from(vec![
        "cust-ord", "--help"
    ]);
```

The above example displays the following help message

```
cust-ord

USAGE:
    cust-ord [FLAGS] [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

SUBCOMMANDS:
    beta    I should be first!
    alpha   Some help and text
```

Closes #442
This commit is contained in:
Kevin K 2016-03-09 19:42:22 -05:00
parent 9803b51e79
commit 7d2a2ed413
3 changed files with 72 additions and 16 deletions

View file

@ -10,6 +10,7 @@ pub struct AppMeta<'b> {
pub usage_str: Option<&'b str>,
pub usage: Option<String>,
pub help_str: Option<&'b str>,
pub disp_ord: usize,
}
impl<'b> Default for AppMeta<'b> {
@ -24,6 +25,7 @@ impl<'b> Default for AppMeta<'b> {
usage: None,
bin_name: None,
help_str: None,
disp_ord: 999,
}
}
}

View file

@ -545,6 +545,56 @@ impl<'a, 'b> App<'a, 'b> {
self
}
/// Allows custom ordering of subcommands within the help message. Subcommands with a lower
/// value will be displayed first in the help message. This is helpful when one would like to
/// emphasise frequently used subcommands, or prioritize those towards the top of the list.
/// Duplicate values **are** allowed. Subcommands with duplicate display orders will be
/// displayed in alphabetical order.
///
/// **NOTE:** The default is 999 for all subcommands.
///
/// # Examples
///
/// ```rust
/// # use clap::{App, SubCommand};
/// let m = App::new("cust-ord")
/// .subcommand(SubCommand::with_name("alpha") // typically subcommands are grouped
/// // alphabetically by name. Subcommands
/// // without a display_order have a value of
/// // 999 and are displayed alphabetically with
/// // all other 999 subcommands
/// .about("Some help and text"))
/// .subcommand(SubCommand::with_name("beta")
/// .display_order(1) // In order to force this subcommand to appear *first*
/// // all we have to do is give it a value lower than 999.
/// // Any other subcommands with a value of 1 will be displayed
/// // alphabetically with this one...then 2 values, then 3, etc.
/// .about("I should be first!"))
/// .get_matches_from(vec![
/// "cust-ord", "--help"
/// ]);
/// ```
///
/// The above example displays the following help message
///
/// ```ignore
/// cust-ord
///
/// USAGE:
/// cust-ord [FLAGS] [OPTIONS]
///
/// FLAGS:
/// -h, --help Prints help information
/// -V, --version Prints version information
///
/// SUBCOMMANDS:
/// beta I should be first!
/// alpha Some help and text
/// ```
pub fn display_order(mut self, ord: usize) -> Self {
self.p.meta.disp_ord = ord;
self
}
/// Prints the full help message to `io::stdout()` using a `BufWriter`
///

View file

@ -1500,26 +1500,30 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
}
}
if subcmds {
let mut ord_m = VecMap::new();
try!(write!(w, "\nSUBCOMMANDS:\n"));
for (name, sc) in self.subcommands.iter()
.filter(|s| !s.p.is_set(AppSettings::Hidden))
.map(|s| (&s.p.meta.name[..], s))
.collect::<BTreeMap<_, _>>() {
try!(write!(w, "{}{}", tab, 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("")));
for sc in self.subcommands.iter().filter(|s| !s.p.is_set(AppSettings::Hidden)) {
let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new());
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, "{}{}", tab, 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));
}
} else {
try!(write!(w, "{}", a));
}
try!(write!(w, "\n"));
}
try!(write!(w, "\n"));
}
}