Skip to content

Commit 52a7a4c

Browse files
committed
feat(mangen): Support flatten_help
The `flatten_help` argument combines all subcommands on a single page. Until now this wasn't supported for `mangen`. With this command the sections `SYNOPSIS` as well as `SUBCOMMANDS` are changed to imitate the style of `git stash --help`. Signed-off-by: Paul Spooren <[email protected]>
1 parent 89dea44 commit 52a7a4c

File tree

5 files changed

+119
-17
lines changed

5 files changed

+119
-17
lines changed

clap_mangen/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,11 @@ impl Man {
258258
fn _render_subcommands_section(&self, roff: &mut Roff) {
259259
let heading = subcommand_heading(&self.cmd);
260260
roff.control("SH", [heading]);
261-
render::subcommands(roff, &self.cmd, &self.section);
261+
if self.cmd.is_flatten_help_set() {
262+
render::flat_subcommands(roff, &self.cmd);
263+
} else {
264+
render::subcommands(roff, &self.cmd, &self.section);
265+
}
262266
}
263267

264268
/// Render the EXTRA section into the writer.

clap_mangen/src/render.rs

+57-13
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,45 @@ pub(crate) fn description(roff: &mut Roff, cmd: &clap::Command) {
3030
}
3131

3232
pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) {
33-
let name = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name());
34-
let mut line = vec![bold(name), roman(" ")];
35-
let mut line = usage(cmd, name);
33+
let flatten = cmd.is_flatten_help_set();
34+
let mut ord_v = Vec::new();
35+
let mut first = true;
36+
37+
if !cmd.is_subcommand_required_set() || cmd.is_args_conflicts_with_subcommands_set() {
38+
let mut line = usage(cmd, cmd.get_bin_name().unwrap_or_else(|| cmd.get_name()));
39+
if cmd.has_subcommands() && !flatten {
40+
let (lhs, rhs) = subcommand_markers(cmd);
41+
line.push(roman(lhs));
42+
line.push(italic(
43+
cmd.get_subcommand_value_name()
44+
.unwrap_or_else(|| subcommand_heading(cmd))
45+
.to_lowercase(),
46+
));
47+
line.push(roman(rhs));
48+
}
49+
roff.text(line);
3650

37-
if cmd.has_subcommands() && !flatten {
38-
let (lhs, rhs) = subcommand_markers(cmd);
39-
line.push(roman(lhs));
40-
line.push(italic(
41-
cmd.get_subcommand_value_name()
42-
.unwrap_or_else(|| subcommand_heading(cmd))
43-
.to_lowercase(),
44-
));
45-
line.push(roman(rhs));
51+
first = false;
4652
}
47-
roff.text(line);
53+
54+
if flatten {
55+
for subcommand in cmd.get_subcommands() {
56+
ord_v.push((
57+
subcommand.get_display_order(),
58+
subcommand.get_bin_name().unwrap_or_else(|| cmd.get_name()),
59+
subcommand,
60+
));
61+
}
62+
ord_v.sort_by(|a, b| (a.0, &a.1).cmp(&(b.0, &b.1)));
63+
}
64+
65+
for (_, name, cmd) in ord_v {
66+
if !first {
67+
roff.control("br", []);
68+
} else {
69+
first = false;
70+
}
71+
roff.text(usage(cmd, name));
4872
}
4973
}
5074

@@ -226,6 +250,26 @@ pub(crate) fn subcommands(roff: &mut Roff, cmd: &clap::Command, section: &str) {
226250
}
227251
}
228252

253+
pub(crate) fn flat_subcommands(roff: &mut Roff, cmd: &clap::Command) {
254+
for sub in cmd.get_subcommands().filter(|s| !s.is_hide_set()) {
255+
roff.control("TP", []);
256+
257+
let mut line = usage(sub, sub.get_name());
258+
259+
if let Some(about) = sub.get_long_about().or_else(|| sub.get_about()) {
260+
line.push(roman("\n"));
261+
line.push(roman(about.to_string()));
262+
}
263+
264+
if let Some(after_help) = sub.get_after_help() {
265+
line.push(roman("\n"));
266+
line.push(roman(after_help.to_string()));
267+
}
268+
269+
roff.text(line);
270+
}
271+
}
272+
229273
pub(crate) fn version(cmd: &clap::Command) -> String {
230274
format!(
231275
"v{}",

clap_mangen/tests/snapshots/flatten_help.roff

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
.SH NAME
55
my\-app
66
.SH SYNOPSIS
7-
\fBmy\-app\fR [\fB\-c \fR] [\fB\-v \fR] [\fB\-h\fR|\fB\-\-help\fR] [\fIsubcommands\fR]
7+
\fBmy\-app\fR [\fB\-c \fR] [\fB\-v \fR] [\fB\-h\fR|\fB\-\-help\fR]
8+
.br
9+
\fBmy\-app test\fR [\fB\-d \fR]... [\fB\-c \fR] [\fB\-h\fR|\fB\-\-help\fR]
10+
.br
11+
\fBmy\-app help\fR
812
.SH DESCRIPTION
913
.SH OPTIONS
1014
.TP
@@ -18,9 +22,9 @@ my\-app
1822
Print help
1923
.SH SUBCOMMANDS
2024
.TP
21-
my\-app\-test(1)
25+
\fBtest\fR [\fB\-d \fR]... [\fB\-c \fR] [\fB\-h\fR|\fB\-\-help\fR]
2226
Subcommand
2327
with a second line
2428
.TP
25-
my\-app\-help(1)
29+
\fBhelp\fR
2630
Print this message or the help of the given subcommand(s)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.ie \n(.g .ds Aq \(aq
2+
.el .ds Aq '
3+
.TH my-app 1 "my-app "
4+
.SH NAME
5+
my\-app
6+
.SH SYNOPSIS
7+
\fBmy\-app test\fR [\fB\-d \fR]... [\fB\-c \fR] [\fB\-h\fR|\fB\-\-help\fR]
8+
.br
9+
\fBmy\-app help\fR
10+
.SH DESCRIPTION
11+
.SH OPTIONS
12+
.TP
13+
\fB\-c\fR
14+
15+
.TP
16+
\fB\-v\fR
17+
18+
.TP
19+
\fB\-h\fR, \fB\-\-help\fR
20+
Print help
21+
.SH SUBCOMMANDS
22+
.TP
23+
\fBtest\fR [\fB\-d \fR]... [\fB\-c \fR] [\fB\-h\fR|\fB\-\-help\fR]
24+
Subcommand
25+
with a second line
26+
.TP
27+
\fBhelp\fR
28+
Print this message or the help of the given subcommand(s)

clap_mangen/tests/testsuite/roff.rs

+22
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,25 @@ fn flatten_help_true() {
119119
let cmd = common::basic_command(name).flatten_help(true);
120120
common::assert_matches(snapbox::file!["../snapshots/flatten_help.roff"], cmd);
121121
}
122+
123+
#[test]
124+
fn flatten_help_true_subcommand_required_true() {
125+
let name = "my-app";
126+
let cmd = common::basic_command(name)
127+
.flatten_help(true)
128+
.subcommand_required(true);
129+
common::assert_matches(
130+
snapbox::file!["../snapshots/flatten_help_subcommand_required.roff"],
131+
cmd,
132+
);
133+
}
134+
135+
#[test]
136+
fn flatten_help_true_subcommand_args_conflicts_with_subcommands() {
137+
let name = "my-app";
138+
let cmd = common::basic_command(name)
139+
.flatten_help(true)
140+
.subcommand_required(false)
141+
.args_conflicts_with_subcommands(false);
142+
common::assert_matches(snapbox::file!["../snapshots/flatten_help.roff"], cmd);
143+
}

0 commit comments

Comments
 (0)