Skip to content

Commit 87740bd

Browse files
committed
refactor(help): Improve subcommand help display
- Refactor subcommand help display to group subcommands under custom headings. - Use a FlatSet to collect unique custom headings from subcommands. - Iterate through the headings and display associated subcommands. - Remove the previous implementation of `write_subcommand_under_heading`.
1 parent b2a7d59 commit 87740bd

File tree

1 file changed

+47
-67
lines changed

1 file changed

+47
-67
lines changed

clap_builder/src/output/help_template.rs

Lines changed: 47 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -938,10 +938,11 @@ impl HelpTemplate<'_, '_> {
938938
debug!("HelpTemplate::write_subcommands");
939939
use std::fmt::Write as _;
940940
let literal = &self.styles.get_literal();
941+
let header = &self.styles.get_header();
941942

942943
// The shortest an arg can legally be is 2 (i.e. '-x')
943944
let mut longest = 2;
944-
let mut has_heading = false;
945+
// let mut has_heading = false;
945946
let mut ord_v = BTreeMap::new();
946947
for subcommand in cmd
947948
.get_subcommands()
@@ -956,9 +957,9 @@ impl HelpTemplate<'_, '_> {
956957
if let Some(long) = subcommand.get_long_flag() {
957958
let _ = write!(styled, ", {literal}--{long}{literal:#}",);
958959
}
959-
if subcommand.get_help_heading().is_some() {
960-
has_heading = true;
961-
}
960+
// if subcommand.get_help_heading().is_some() {
961+
// has_heading = true;
962+
// }
962963
longest = longest.max(styled.display_width());
963964
ord_v.insert((subcommand.get_display_order(), styled), subcommand);
964965
}
@@ -967,6 +968,12 @@ impl HelpTemplate<'_, '_> {
967968

968969
let next_line_help = self.will_subcommands_wrap(cmd.get_subcommands(), longest);
969970

971+
let command_custom_headings = self
972+
.cmd
973+
.get_subcommands()
974+
.filter_map(|cmd| cmd.get_help_heading())
975+
.collect::<FlatSet<_>>();
976+
970977
// First for the commands that don't have a heading
971978
for (i, (sc_str, sc)) in ord_v
972979
.clone()
@@ -981,72 +988,45 @@ impl HelpTemplate<'_, '_> {
981988
self.write_subcommand(sc_str.1, sc, next_line_help, longest);
982989
}
983990

984-
if has_heading {
985-
self.write_subcommand_under_heading(cmd, next_line_help, longest);
986-
}
987-
}
988-
989-
/// Writes help with headings for subcommands of a Parser Object to the wrapped stream.
990-
fn write_subcommand_under_heading(
991-
&mut self,
992-
cmd: &Command,
993-
next_line_help: bool,
994-
longest: usize,
995-
) {
996-
debug!("HelpTemplate::write_subcommands_under_heading");
997-
use std::fmt::Write as _;
998-
let literal = &self.styles.get_literal();
999-
1000-
let mut ord_v = BTreeMap::new();
1001-
let mut headings = BTreeMap::new();
1002-
let mut next_heading_ord = 0_usize;
1003-
for subcommand in cmd
1004-
.get_subcommands()
1005-
.filter(|subcommand| should_show_subcommand(subcommand))
1006-
.filter(|subcommand| subcommand.get_help_heading().is_some())
1007-
{
1008-
let mut styled = StyledStr::new();
1009-
let name = subcommand.get_name();
1010-
let _ = write!(styled, "{literal}{name}{literal:#}",);
1011-
if let Some(short) = subcommand.get_short_flag() {
1012-
let _ = write!(styled, ", {literal}-{short}{literal:#}",);
1013-
}
1014-
if let Some(long) = subcommand.get_long_flag() {
1015-
let _ = write!(styled, ", {literal}--{long}{literal:#}",);
1016-
}
1017-
let heading = subcommand.get_help_heading().unwrap();
1018-
let heading_ord = if let Some(ho) = headings.get(heading) {
1019-
*ho
1020-
} else {
1021-
next_heading_ord += 1;
1022-
headings.insert(heading, next_heading_ord);
1023-
next_heading_ord
1024-
};
1025-
1026-
ord_v.insert(
1027-
(heading_ord, subcommand.get_display_order(), styled),
1028-
subcommand,
1029-
);
1030-
}
1031-
1032-
let header = &self.styles.get_header();
1033-
let mut current_heading = "";
991+
if !command_custom_headings.is_empty() {
992+
for heading in command_custom_headings {
993+
let cmds = self
994+
.cmd
995+
.get_subcommands()
996+
.filter(|sc| {
997+
if let Some(help_heading) = sc.get_help_heading() {
998+
return help_heading == heading;
999+
}
1000+
false
1001+
})
1002+
.filter(|sc| should_show_subcommand(sc))
1003+
.collect::<Vec<_>>();
10341004

1035-
for (sc_str, sc) in ord_v
1036-
.into_iter()
1037-
.filter(|item| item.1.get_help_heading().is_some())
1038-
{
1039-
debug!("Processing with heading {}", sc.get_name());
1040-
self.writer.push_str("\n");
1041-
if sc.get_help_heading().unwrap() != current_heading {
1042-
current_heading = sc.get_help_heading().unwrap();
1043-
debug!("Heading changed; print new heading {}.", current_heading);
1044-
self.writer.push_str("\n");
1045-
let _ = write!(self.writer, "{header}{current_heading}:{header:#}\n",);
1005+
if !cmds.is_empty() {
1006+
self.writer.push_str("\n\n");
1007+
let _ = write!(self.writer, "{header}{heading}:{header:#}\n",);
1008+
for (i, (sc_str, sc)) in ord_v
1009+
.clone()
1010+
.into_iter()
1011+
.filter(|item| item.1.get_help_heading() == Some(heading))
1012+
.enumerate()
1013+
{
1014+
debug!(
1015+
"Processing with heading: name {}, heading {}",
1016+
sc.get_name(),
1017+
heading
1018+
);
1019+
if 0 < i {
1020+
self.writer.push_str("\n");
1021+
}
1022+
self.write_subcommand(sc_str.1, sc, next_line_help, longest);
1023+
}
1024+
}
10461025
}
1047-
1048-
self.write_subcommand(sc_str.2, sc, next_line_help, longest);
10491026
}
1027+
// if has_heading {
1028+
// self.write_subcommand_under_heading(cmd, next_line_help, longest);
1029+
// }
10501030
}
10511031

10521032
/// Will use next line help on writing subcommands.

0 commit comments

Comments
 (0)