Skip to content

Commit 400dd85

Browse files
committed
clap-rs#5819 - for current version
1 parent 9ebef15 commit 400dd85

File tree

2 files changed

+66
-4
lines changed

2 files changed

+66
-4
lines changed

clap_builder/src/builder/command.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,11 @@ pub struct Command {
101101
subcommands: Vec<Command>,
102102
groups: Vec<ArgGroup>,
103103
current_help_heading: Option<Str>,
104+
current_subcommand_help_heading: Option<Str>,
104105
current_disp_ord: Option<usize>,
105106
subcommand_value_name: Option<Str>,
106107
subcommand_heading: Option<Str>,
108+
subcommand_help_heading: Option<Option<Str>>,
107109
external_value_parser: Option<super::ValueParser>,
108110
long_help_exists: bool,
109111
deferred: Option<fn(Command) -> Command>,
@@ -490,6 +492,7 @@ impl Command {
490492
subcmd.disp_ord.get_or_insert(current);
491493
*current_disp_ord = current + 1;
492494
}
495+
subcmd.subcommand_help_heading.get_or_insert_with(|| self.current_subcommand_help_heading.clone());
493496
self.subcommands.push(subcmd);
494497
self
495498
}
@@ -2295,6 +2298,32 @@ impl Command {
22952298
self
22962299
}
22972300

2301+
/// Set the default section heading for future subcommands.
2302+
///
2303+
/// This will be used for any subcommand that hasn't had [`Command::subcommand_help_heading`] called.
2304+
///
2305+
/// This is useful if the default `Commands` heading is
2306+
/// not specific enough for one's use case.
2307+
///
2308+
/// [`Command::subcommand`]: Command::subcommand()
2309+
/// [`Command::subcommand_help_heading`]: crate::Command::subcommand_help_heading()
2310+
#[inline]
2311+
#[must_use]
2312+
pub fn next_subcommand_help_heading(mut self, heading: impl IntoResettable<Str>) -> Self {
2313+
self.current_subcommand_help_heading = heading.into_resettable().into_option();
2314+
self
2315+
}
2316+
2317+
/// Get the custom section heading specified via [`Command::next_subcommand_help_heading`].
2318+
///
2319+
/// [`Command::subcommand_help_heading`]: Command::subcommand_help_heading()
2320+
#[inline]
2321+
pub fn get_next_subcommand_help_heading(&self) -> Option<&str> {
2322+
self.current_subcommand_help_heading.as_deref()
2323+
}
2324+
2325+
2326+
22982327
/// Change the starting value for assigning future display orders for args.
22992328
///
23002329
/// This will be used for any arg that hasn't had [`Arg::display_order`] called.
@@ -4889,6 +4918,14 @@ impl Command {
48894918
.any(|sc| sc.name != "help" && !sc.is_set(AppSettings::Hidden))
48904919
}
48914920

4921+
#[cfg(any(feature = "usage", feature = "help"))]
4922+
pub(crate) fn needs_commands_header(&self) -> bool {
4923+
self.subcommands
4924+
.iter()
4925+
.filter(|sc| !sc.is_set(AppSettings::Hidden))
4926+
.any(|sc| sc.subcommand_help_heading.is_none())
4927+
}
4928+
48924929
/// Check if this subcommand can be referred to as `name`. In other words,
48934930
/// check if `name` is the name of this subcommand or is one of its aliases.
48944931
#[inline]
@@ -5131,6 +5168,8 @@ impl Default for Command {
51315168
subcommands: Default::default(),
51325169
groups: Default::default(),
51335170
current_help_heading: Default::default(),
5171+
current_subcommand_help_heading: Default::default(),
5172+
subcommand_help_heading: Default::default(),
51345173
current_disp_ord: Some(0),
51355174
subcommand_value_name: Default::default(),
51365175
subcommand_heading: Default::default(),

clap_builder/src/output/help_template.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,10 @@ impl HelpTemplate<'_, '_> {
409409
.cmd
410410
.get_subcommand_help_heading()
411411
.unwrap_or(&default_help_heading);
412-
let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",);
412+
//let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",);
413+
if self.cmd.needs_commands_header() {
414+
let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",);
415+
}
413416

414417
self.write_subcommands(self.cmd);
415418
}
@@ -866,7 +869,7 @@ impl HelpTemplate<'_, '_> {
866869
.filter(|subcommand| should_show_subcommand(subcommand))
867870
{
868871
ord_v.insert(
869-
(subcommand.get_display_order(), subcommand.get_name()),
872+
(subcommand.get_subcommand_help_heading(), subcommand.get_display_order(), subcommand.get_name()),
870873
subcommand,
871874
);
872875
}
@@ -934,18 +937,38 @@ impl HelpTemplate<'_, '_> {
934937
let _ = write!(styled, ", {literal}--{long}{literal:#}",);
935938
}
936939
longest = longest.max(styled.display_width());
937-
ord_v.insert((subcommand.get_display_order(), styled), subcommand);
940+
ord_v.insert((subcommand.get_subcommand_help_heading(), subcommand.get_display_order(), styled), subcommand);
938941
}
939942

940943
debug!("HelpTemplate::write_subcommands longest = {longest}");
941944

942945
let next_line_help = self.will_subcommands_wrap(cmd.get_subcommands(), longest);
946+
let mut current_help_heading: Option<&str> = None;
947+
let mut help_heading_nl_needed = true;
943948

944949
for (i, (sc_str, sc)) in ord_v.into_iter().enumerate() {
945950
if 0 < i {
946951
self.writer.push_str("\n");
947952
}
948-
self.write_subcommand(sc_str.1, sc, next_line_help, longest);
953+
954+
let opt_help_heading = sc_str.0;
955+
956+
if current_help_heading != opt_help_heading {
957+
if let Some(help_heading) = opt_help_heading {
958+
let header = &self.styles.get_header();
959+
if help_heading_nl_needed {
960+
help_heading_nl_needed = false;
961+
} else {
962+
self.writer.push_str("\n");
963+
};
964+
let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",);
965+
}
966+
current_help_heading.replace(sc_str.0.unwrap());
967+
} else {
968+
help_heading_nl_needed = false;
969+
}
970+
971+
self.write_subcommand(sc_str.clone().2, sc, next_line_help, longest);
949972
}
950973
}
951974

0 commit comments

Comments
 (0)