Skip to content

Commit 743527d

Browse files
committed
feat: add custom help headings
- Allow setting custom help headings for subcommands to group them. - Add `help_heading` method to `Command` to set the custom heading. - Update help template to display subcommands under their headings. - Add `get_help_heading` method to retrieve the custom heading. - Add `is_help_heading_set` method to check a custom heading is set. - Update tests to verify display of subcommands with custom headings.
1 parent 282d1bc commit 743527d

File tree

3 files changed

+123
-26
lines changed

3 files changed

+123
-26
lines changed

clap_builder/src/builder/command.rs

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3703,23 +3703,81 @@ impl Command {
37033703
self
37043704
}
37053705

3706-
3707-
/// Set a custom help heading
3706+
/// Set a custom help heading for the command
3707+
///
3708+
/// To place the `help` subcommand under a custom heading amend the default
3709+
/// heading using [`Command::subcommand_help_heading`].
3710+
///
3711+
/// # Examples
3712+
///
3713+
/// ```rust
3714+
/// # use clap_builder as clap;
3715+
/// # use clap::{Command, Arg};
3716+
/// Command::new("myprog")
3717+
/// .version("2.6")
3718+
/// .subcommand(
3719+
/// Command::new("show")
3720+
/// .about("Help for show")
3721+
/// )
3722+
/// .print_help()
3723+
/// # ;
3724+
/// ```
3725+
///
3726+
/// will produce
3727+
///
3728+
/// ```text
3729+
/// myprog
3730+
///
3731+
/// Usage: myprog [COMMAND]
3732+
///
3733+
/// Commands:
3734+
/// help Print this message or the help of the given subcommand(s)
3735+
/// show Help for show
3736+
///
3737+
/// Options:
3738+
/// -h, --help Print help
3739+
/// -V, --version Print version
3740+
/// ```
3741+
///
3742+
/// but usage of `help_heading`
3743+
///
3744+
/// ```rust
3745+
/// # use clap_builder as clap;
3746+
/// # use clap::{Command, Arg};
3747+
/// Command::new("myprog")
3748+
/// .version("2.6")
3749+
/// .subcommand(
3750+
/// Command::new("show")
3751+
/// .about("Help for show")
3752+
/// .help_heading("Custom heading"),
3753+
/// )
3754+
/// .print_help()
3755+
/// # ;
3756+
/// ```
3757+
///
3758+
/// will produce
3759+
///
3760+
/// ```text
3761+
/// myprog
3762+
///
3763+
/// Usage: myprog [COMMAND]
3764+
///
3765+
/// Commands:
3766+
/// help Print this message or the help of the given subcommand(s)
3767+
///
3768+
/// Custom heading:
3769+
/// show Help for show
3770+
///
3771+
/// Options:
3772+
/// -h, --help Print help
3773+
/// -V, --version Print version
3774+
/// ```
37083775
#[inline]
37093776
#[must_use]
37103777
pub fn help_heading(mut self, heading: impl IntoResettable<Str>) -> Self {
37113778
self.help_heading = Some(heading.into_resettable().into_option());
37123779
self
37133780
}
3714-
3715-
/// Get the help heading specified for this argument, if any
3716-
#[inline]
3717-
pub fn get_help_heading(&self) -> Option<&str> {
3718-
self.help_heading
3719-
.as_ref()
3720-
.map(|s| s.as_deref())
3721-
.unwrap_or_default()
3722-
}
37233781
}
37243782

37253783
/// # Reflection
@@ -3959,6 +4017,15 @@ impl Command {
39594017
self.subcommand_heading.as_deref()
39604018
}
39614019

4020+
/// Get the help heading specified for this command, if any
4021+
#[inline]
4022+
pub fn get_help_heading(&self) -> Option<&str> {
4023+
self.help_heading
4024+
.as_ref()
4025+
.map(|s| s.as_deref())
4026+
.unwrap_or_default()
4027+
}
4028+
39624029
/// Returns the subcommand value name.
39634030
#[inline]
39644031
pub fn get_subcommand_value_name(&self) -> Option<&str> {
@@ -4253,7 +4320,7 @@ impl Command {
42534320
pub fn is_help_heading_set(&self) -> bool {
42544321
self.help_heading.is_some()
42554322
}
4256-
4323+
42574324
/// Report whether [`Command::subcommand_required`] is set
42584325
pub fn is_subcommand_required_set(&self) -> bool {
42594326
self.is_set(AppSettings::SubcommandRequired)

clap_builder/src/output/help_template.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -963,12 +963,39 @@ impl HelpTemplate<'_, '_> {
963963

964964
let next_line_help = self.will_subcommands_wrap(cmd.get_subcommands(), longest);
965965

966-
for (i, (sc_str, sc)) in ord_v.into_iter().enumerate() {
966+
// First for the commands that don't have a heading
967+
for (i, (sc_str, sc)) in ord_v
968+
.clone()
969+
.into_iter()
970+
.filter(|item| !should_show_help_heading(item.1))
971+
.enumerate()
972+
{
973+
debug!("Processing without heading {}", sc.get_name());
967974
if 0 < i {
968975
self.writer.push_str("\n");
969976
}
970977
self.write_subcommand(sc_str.1, sc, next_line_help, longest);
971978
}
979+
980+
let header = &self.styles.get_header();
981+
let mut current_heading = "";
982+
983+
// Then for the commands that do have a heading
984+
for (sc_str, sc) in ord_v
985+
.into_iter()
986+
.filter(|item| should_show_help_heading(item.1))
987+
{
988+
debug!("Processing with heading {}", sc.get_name());
989+
self.writer.push_str("\n");
990+
if sc.get_help_heading().unwrap() != current_heading {
991+
current_heading = sc.get_help_heading().unwrap();
992+
debug!("Heading changed; print new heading {}.", current_heading);
993+
self.writer.push_str("\n");
994+
let _ = write!(self.writer, "{header}{current_heading}:{header:#}\n",);
995+
}
996+
997+
self.write_subcommand(sc_str.1, sc, next_line_help, longest);
998+
}
972999
}
9731000

9741001
/// Will use next line help on writing subcommands.
@@ -1142,6 +1169,10 @@ fn should_show_subcommand(subcommand: &Command) -> bool {
11421169
!subcommand.is_hide_set()
11431170
}
11441171

1172+
fn should_show_help_heading(subcommand: &Command) -> bool {
1173+
subcommand.is_help_heading_set()
1174+
}
1175+
11451176
#[cfg(test)]
11461177
mod test {
11471178
#[test]

tests/builder/subcommands.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -639,19 +639,18 @@ fn duplicate_subcommand_alias() {
639639
#[test]
640640
fn test_help_header() {
641641
static VISIBLE_ALIAS_HELP: &str = "\
642-
Usage: clap-test [COMMAND]
643-
644-
Commands:
645-
help Print this message or the help of the given subcommand(s)
646-
647-
Test commands:
648-
test Some help
649-
650-
Options:
651-
-h, --help Print help
652-
-V, --version Print version
653-
";
654-
642+
Usage: clap-test [COMMAND]
643+
644+
Commands:
645+
help Print this message or the help of the given subcommand(s)
646+
647+
Test commands:
648+
test Some help
649+
650+
Options:
651+
-h, --help Print help
652+
-V, --version Print version
653+
";
655654
let cmd = Command::new("clap-test")
656655
.version("2.6")
657656
.subcommand(

0 commit comments

Comments
 (0)