From 3f9a7c3f531398a011ef000713dc44f83be6ab7f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 4 Aug 2025 08:44:19 -0500 Subject: [PATCH 1/2] test(man): Verify styled text behavior --- Cargo.lock | 5 ++-- clap_mangen/Cargo.toml | 3 ++- clap_mangen/tests/snapshots/styled.bash.roff | 28 ++++++++++++++++++++ clap_mangen/tests/testsuite/common.rs | 23 ++++++++++++++++ clap_mangen/tests/testsuite/roff.rs | 7 +++++ 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 clap_mangen/tests/snapshots/styled.bash.roff diff --git a/Cargo.lock b/Cargo.lock index 4ef3bead3cb..6ae05ea4ac4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-lossy" @@ -551,6 +551,7 @@ dependencies = [ name = "clap_mangen" version = "0.2.31" dependencies = [ + "anstyle", "automod", "clap 4.5.54", "roff", diff --git a/clap_mangen/Cargo.toml b/clap_mangen/Cargo.toml index b89f41753b2..010c1ba5f49 100644 --- a/clap_mangen/Cargo.toml +++ b/clap_mangen/Cargo.toml @@ -38,8 +38,9 @@ clap = { path = "../", version = "4.0.0", default-features = false, features = [ [dev-dependencies] snapbox = { version = "0.6.0", features = ["diff"] } -clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "help"] } +clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "help", "string", "color"] } automod = "1.0.14" +anstyle = "1.0.11" [features] default = [] diff --git a/clap_mangen/tests/snapshots/styled.bash.roff b/clap_mangen/tests/snapshots/styled.bash.roff new file mode 100644 index 00000000000..6a6d5e3bb28 --- /dev/null +++ b/clap_mangen/tests/snapshots/styled.bash.roff @@ -0,0 +1,28 @@ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.TH my-app 1 "my-app " +.SH NAME +my\-app \- Hello about! +.SH SYNOPSIS +\fBmy\-app\fR [\fB\-h\fR|\fB\-\-help\fR] [\fIarg\fR] +.SH DESCRIPTION +Hello about! +.SH OPTIONS +.TP +\fB\-h\fR, \fB\-\-help\fR +Print help (see a summary with \*(Aq\-h\*(Aq) +.TP +[\fIarg\fR] +Hello help! +.br + +.br +\fIPossible values:\fR +.RS 14 +.IP \(bu 2 +fast +.IP \(bu 2 +slow: Hello PossibleValue! +.RE +.SH EXTRA +Hello after_help! diff --git a/clap_mangen/tests/testsuite/common.rs b/clap_mangen/tests/testsuite/common.rs index 8ffe406e4b9..3dd8dfbca0e 100644 --- a/clap_mangen/tests/testsuite/common.rs +++ b/clap_mangen/tests/testsuite/common.rs @@ -473,3 +473,26 @@ pub(crate) fn default_subcmd_order(name: &'static str) -> clap::Command { ), ]) } + +pub(crate) fn styled(name: &'static str) -> clap::Command { + const ALL_STYLES: anstyle::Style = anstyle::Style::new() + .fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Red))) + .bg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Blue))) + .bold() + .italic() + .underline(); + clap::Command::new(name) + .about(format!("Hello {ALL_STYLES}about{ALL_STYLES:#}!")) + .before_help(format!("Hello {ALL_STYLES}before_help{ALL_STYLES:#}!")) + .after_help(format!("Hello {ALL_STYLES}after_help{ALL_STYLES:#}!")) + .override_usage(format!("Hello {ALL_STYLES}usage{ALL_STYLES:#}!")) + .arg( + clap::Arg::new("arg") + .value_parser([ + PossibleValue::new("fast"), + PossibleValue::new("slow") + .help(format!("Hello {ALL_STYLES}PossibleValue{ALL_STYLES:#}!")), + ]) + .help(format!("Hello {ALL_STYLES}help{ALL_STYLES:#}!")), + ) +} diff --git a/clap_mangen/tests/testsuite/roff.rs b/clap_mangen/tests/testsuite/roff.rs index 5864870fdb7..b5831299f17 100644 --- a/clap_mangen/tests/testsuite/roff.rs +++ b/clap_mangen/tests/testsuite/roff.rs @@ -189,3 +189,10 @@ fn default_subcmd_order() { cmd, ); } + +#[test] +fn styled() { + let name = "my-app"; + let cmd = common::styled(name); + common::assert_matches(snapbox::file!["../snapshots/styled.bash.roff"], cmd); +} From 3153d8b867c302a0d77f026c1ed1bb5a9a6dad16 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 4 Aug 2025 10:38:04 -0500 Subject: [PATCH 2/2] fix(man): Forward, rather than ignore, user styling --- Cargo.lock | 19 +++++++++++++++++++ clap_mangen/Cargo.toml | 2 ++ clap_mangen/src/render.rs | 27 +++++++++++++++++++++------ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ae05ea4ac4..acaaa4bcafd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anstyle-roff" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af32adb801a280c7ee53c38f2ec7125fdceaf974afe841cda2657450fa74d4e3" +dependencies = [ + "anstyle", + "anstyle-lossy", + "cansi", + "roff", +] + [[package]] name = "anstyle-svg" version = "0.1.7" @@ -325,6 +337,12 @@ dependencies = [ "zip", ] +[[package]] +name = "cansi" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bdcae87153686017415ce77e48c53e6818a0a058f0e21b56640d1e944967ef8" + [[package]] name = "cc" version = "1.1.0" @@ -552,6 +570,7 @@ name = "clap_mangen" version = "0.2.31" dependencies = [ "anstyle", + "anstyle-roff", "automod", "clap 4.5.54", "roff", diff --git a/clap_mangen/Cargo.toml b/clap_mangen/Cargo.toml index 010c1ba5f49..78d4692e507 100644 --- a/clap_mangen/Cargo.toml +++ b/clap_mangen/Cargo.toml @@ -34,6 +34,7 @@ bench = false [dependencies] roff = "0.2.1" +anstyle-roff = { version = "0.3.8", optional = true } clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "env"] } [dev-dependencies] @@ -44,6 +45,7 @@ anstyle = "1.0.11" [features] default = [] +ansi = ["dep:anstyle-roff", "clap/color"] debug = ["clap/debug"] [lints] diff --git a/clap_mangen/src/render.rs b/clap_mangen/src/render.rs index fa43a025325..3e12403ab7c 100644 --- a/clap_mangen/src/render.rs +++ b/clap_mangen/src/render.rs @@ -23,7 +23,7 @@ pub(crate) fn description(roff: &mut Roff, cmd: &clap::Command) { if line.trim().is_empty() { roff.control("PP", []); } else { - roff.text([roman(line)]); + roff.extend([anstyle_roff::to_roff(line)]); } } } @@ -140,7 +140,7 @@ pub(crate) fn options(roff: &mut Roff, items: &[&Arg]) { let mut arg_help_written = false; if let Some(help) = option_help(opt) { arg_help_written = true; - body.push(roman(help.to_string())); + body.push(roman(help.ansi().to_string())); } roff.control("TP", []); @@ -174,7 +174,7 @@ pub(crate) fn options(roff: &mut Roff, items: &[&Arg]) { let mut body = vec![]; let mut arg_help_written = false; if let Some(help) = option_help(pos) { - body.push(roman(help.to_string())); + body.push(roman(help.ansi().to_string())); arg_help_written = true; } @@ -229,7 +229,7 @@ pub(crate) fn subcommands(roff: &mut Roff, cmd: &clap::Command, section: &str) { if let Some(about) = sub.get_about().or_else(|| sub.get_long_about()) { for line in about.to_string().lines() { - roff.text([roman(line)]); + roff.extend([anstyle_roff::to_roff(line)]); } } } @@ -247,7 +247,7 @@ pub(crate) fn version(cmd: &clap::Command) -> String { pub(crate) fn after_help(roff: &mut Roff, cmd: &clap::Command) { if let Some(about) = cmd.get_after_long_help().or_else(|| cmd.get_after_help()) { for line in about.to_string().lines() { - roff.text([roman(line)]); + roff.extend([anstyle_roff::to_roff(line)]); } } } @@ -368,7 +368,8 @@ fn format_possible_values(possibles: &Vec<&clap::builder::PossibleValue>) -> Vec let val_name = value.get_name(); match value.get_help() { Some(help) => { - roff.text([roman(format!("{val_name}: {help}"))]); + roff.text([roman(val_name.to_owned()), roman(": ")]); + roff.extend([anstyle_roff::to_roff(&help.to_string())]); } None => { roff.text([roman(val_name.to_owned())]); @@ -398,3 +399,17 @@ fn option_sort_key(arg: &Arg) -> (usize, String) { }; (arg.get_display_order(), key) } + +#[cfg(feature = "ansi")] +fn style(s: &clap::StyledStr) -> Roff { + #[cfg(feature = "ansi")] + { + anstyle_roff::to_roff(&s.to_string()) + } + #[cfg(not(feature = "ansi"))] + { + let mut roff = Roff::default(); + roff.text([roman(s.to_string())]); + roff + } +}