From c2c7c267e09d343468e29d6a5f197c963988b4a7 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Wed, 16 Oct 2024 17:01:09 +0200 Subject: [PATCH] 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 --- clap_mangen/src/lib.rs | 6 +- clap_mangen/src/render.rs | 74 +++++++++++++++---- clap_mangen/tests/snapshots/flatten_help.roff | 29 ++++++++ clap_mangen/tests/testsuite/roff.rs | 2 +- 4 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 clap_mangen/tests/snapshots/flatten_help.roff diff --git a/clap_mangen/src/lib.rs b/clap_mangen/src/lib.rs index 7b8d8af79004..b9fbdb76d1ad 100644 --- a/clap_mangen/src/lib.rs +++ b/clap_mangen/src/lib.rs @@ -258,7 +258,11 @@ impl Man { fn _render_subcommands_section(&self, roff: &mut Roff) { let heading = subcommand_heading(&self.cmd); roff.control("SH", [heading]); - render::subcommands(roff, &self.cmd, &self.section); + if self.cmd.is_flatten_help_set() { + render::flat_subcommands(roff, &self.cmd); + } else { + render::subcommands(roff, &self.cmd, &self.section); + } } /// Render the EXTRA section into the writer. diff --git a/clap_mangen/src/render.rs b/clap_mangen/src/render.rs index 81d4e8c6b1cd..e9cb8cd942b6 100644 --- a/clap_mangen/src/render.rs +++ b/clap_mangen/src/render.rs @@ -29,10 +29,8 @@ pub(crate) fn description(roff: &mut Roff, cmd: &clap::Command) { } } -pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) { - let name = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name()); +pub(crate) fn command_with_args(cmd: &clap::Command, name: String) -> Vec { let mut line = vec![bold(name), roman(" ")]; - for opt in cmd.get_arguments().filter(|i| !i.is_hide_set()) { let (lhs, rhs) = option_markers(opt); match (opt.get_short(), opt.get_long()) { @@ -74,18 +72,46 @@ pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) { line.push(roman(" ")); } - if cmd.has_subcommands() { - let (lhs, rhs) = subcommand_markers(cmd); - line.push(roman(lhs)); - line.push(italic( - cmd.get_subcommand_value_name() - .unwrap_or_else(|| subcommand_heading(cmd)) - .to_lowercase(), - )); - line.push(roman(rhs)); + line +} + +pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) { + let name = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name()); + let flatten = cmd.is_flatten_help_set(); + + let mut ord_v = Vec::new(); + if flatten { + for subcommand in cmd.get_subcommands() { + ord_v.push(( + subcommand.get_display_order(), + format!("{} {}", name, subcommand.get_name().to_owned()), + subcommand, + )); + } + ord_v.sort_by(|a, b| (a.0, &a.1).cmp(&(b.0, &b.1))); + } else { + ord_v.push((cmd.get_display_order(), name.to_string(), cmd)); } - roff.text(line); + for (_, name, cmd) in ord_v { + let mut line = command_with_args(cmd, name); + + if cmd.has_subcommands() && !flatten { + let (lhs, rhs) = subcommand_markers(cmd); + line.push(roman(lhs)); + line.push(italic( + cmd.get_subcommand_value_name() + .unwrap_or_else(|| subcommand_heading(cmd)) + .to_lowercase(), + )); + line.push(roman(rhs)); + } + roff.text(line); + + if flatten { + roff.control("br", []); + } + } } pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) { @@ -220,6 +246,28 @@ pub(crate) fn subcommands(roff: &mut Roff, cmd: &clap::Command, section: &str) { } } +pub(crate) fn flat_subcommands(roff: &mut Roff, cmd: &clap::Command) { + for sub in cmd.get_subcommands().filter(|s| !s.is_hide_set()) { + roff.control("TP", []); + + let name = sub.get_name().to_string(); + + let mut line = command_with_args(sub, name); + + if let Some(about) = sub.get_long_about().or_else(|| sub.get_about()) { + line.push(roman("\n")); + line.push(roman(about.to_string())); + } + + if let Some(after_help) = sub.get_after_help() { + line.push(roman("\n")); + line.push(roman(after_help.to_string())); + } + + roff.text(line); + } +} + pub(crate) fn version(cmd: &clap::Command) -> String { format!( "v{}", diff --git a/clap_mangen/tests/snapshots/flatten_help.roff b/clap_mangen/tests/snapshots/flatten_help.roff new file mode 100644 index 000000000000..962edb9dcbbf --- /dev/null +++ b/clap_mangen/tests/snapshots/flatten_help.roff @@ -0,0 +1,29 @@ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.TH my-app 1 "my-app " +.SH NAME +my\-app +.SH SYNOPSIS +\fBmy\-app test\fR [\fB\-d \fR]... [\fB\-c \fR] [\fB\-h\fR|\fB\-\-help\fR] +.br +\fBmy\-app help\fR +.br +.SH DESCRIPTION +.SH OPTIONS +.TP +\fB\-c\fR + +.TP +\fB\-v\fR + +.TP +\fB\-h\fR, \fB\-\-help\fR +Print help +.SH SUBCOMMANDS +.TP +\fBtest\fR [\fB\-d \fR]... [\fB\-c \fR] [\fB\-h\fR|\fB\-\-help\fR] +Subcommand +with a second line +.TP +\fBhelp\fR +Print this message or the help of the given subcommand(s) diff --git a/clap_mangen/tests/testsuite/roff.rs b/clap_mangen/tests/testsuite/roff.rs index 278d437eb428..a24e2ba6d491 100644 --- a/clap_mangen/tests/testsuite/roff.rs +++ b/clap_mangen/tests/testsuite/roff.rs @@ -117,5 +117,5 @@ fn flatten_help_false() { fn flatten_help_true() { let name = "my-app"; let cmd = common::basic_command(name).flatten_help(true); - common::assert_matches(snapbox::file!["../snapshots/basic.bash.roff"], cmd); + common::assert_matches(snapbox::file!["../snapshots/flatten_help.roff"], cmd); }