From 7d2a2ed413f5517d45988eef0765cdcd663b6372 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 9 Mar 2016 19:42:22 -0500 Subject: [PATCH] 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 --- src/app/meta.rs | 2 ++ src/app/mod.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++ src/app/parser.rs | 36 +++++++++++++++++++--------------- 3 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/app/meta.rs b/src/app/meta.rs index 973a6a3d12e..335d77f592d 100644 --- a/src/app/meta.rs +++ b/src/app/meta.rs @@ -10,6 +10,7 @@ pub struct AppMeta<'b> { pub usage_str: Option<&'b str>, pub usage: Option, 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, } } } diff --git a/src/app/mod.rs b/src/app/mod.rs index 46497ef0bad..9f92d173420 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -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` /// diff --git a/src/app/parser.rs b/src/app/parser.rs index c6e0404ac16..a50ee70cade 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -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::>() { - 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")); } }