From 964c7973c777852af6f394855faa75290f420d6f Mon Sep 17 00:00:00 2001 From: Gil Shoshan Date: Fri, 3 May 2024 01:39:15 +0300 Subject: [PATCH] allow customizing styling for inline context [default], [possible values], [env], [aliases] and [short aliases] --- clap_builder/src/builder/styling.rs | 37 +++++++++++++ clap_builder/src/output/help_template.rs | 67 ++++++++++++++++++------ 2 files changed, 89 insertions(+), 15 deletions(-) diff --git a/clap_builder/src/builder/styling.rs b/clap_builder/src/builder/styling.rs index 3a034489b95..55fe1c24718 100644 --- a/clap_builder/src/builder/styling.rs +++ b/clap_builder/src/builder/styling.rs @@ -28,6 +28,8 @@ pub struct Styles { placeholder: anstyle::Style, valid: anstyle::Style, invalid: anstyle::Style, + inline_context: anstyle::Style, + inline_context_value: Option, } impl Styles { @@ -41,6 +43,8 @@ impl Styles { placeholder: anstyle::Style::new(), valid: anstyle::Style::new(), invalid: anstyle::Style::new(), + inline_context: anstyle::Style::new(), + inline_context_value: None, } } @@ -60,6 +64,8 @@ impl Styles { .fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Green))), invalid: anstyle::Style::new() .fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Yellow))), + inline_context: anstyle::Style::new(), + inline_context_value: None, } } #[cfg(not(feature = "color"))] @@ -116,6 +122,20 @@ impl Styles { self.invalid = style; self } + + /// Highlight specified contexts: `[env], [default], [possible values], [aliases] and [short aliases]` + #[inline] + pub const fn inline_context(mut self, style: anstyle::Style) -> Self { + self.inline_context = style; + self + } + + /// Highlight values within specified contexts: `[env], [default], [possible values], [aliases] and [short aliases]` + #[inline] + pub const fn inline_context_value(mut self, style: anstyle::Style) -> Self { + self.inline_context_value = Some(style); + self + } } /// Reflection @@ -161,6 +181,23 @@ impl Styles { pub const fn get_invalid(&self) -> &anstyle::Style { &self.invalid } + + /// Highlight specified contexts: `[env], [default], [possible values], [aliases] and [short aliases]` + #[inline(always)] + pub const fn get_inline_context(&self) -> &anstyle::Style { + &self.inline_context + } + + /// Highlight values within specified contexts: `[env], [default], [possible values], [aliases] and [short aliases]` + /// + /// If `inline_context_value` was not set, defaults to `inline_context` + #[inline(always)] + pub const fn get_inline_context_value(&self) -> &anstyle::Style { + match &self.inline_context_value { + Some(inline_context_value) => inline_context_value, + None => &self.inline_context, + } + } } impl super::AppTag for Styles {} diff --git a/clap_builder/src/output/help_template.rs b/clap_builder/src/output/help_template.rs index dff15ff1279..f5d0c807997 100644 --- a/clap_builder/src/output/help_template.rs +++ b/clap_builder/src/output/help_template.rs @@ -639,6 +639,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { ) { debug!("HelpTemplate::help"); use std::fmt::Write as _; + let inline_context = &self.styles.get_inline_context(); let literal = &self.styles.get_literal(); // Is help on next line, if so then indent @@ -703,7 +704,14 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { if !help_is_empty { let _ = write!(self.writer, "\n\n{:spaces$}", ""); } - self.writer.push_str("Possible values:"); + self.writer.push_str( + format!( + "{}Possible values:{}", + inline_context.render(), + inline_context.render_reset(), + ) + .as_str(), + ); for pv in possible_vals.iter().filter(|pv| !pv.is_hide_set()) { let name = pv.get_name(); @@ -770,6 +778,13 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { fn spec_vals(&self, a: &Arg) -> String { debug!("HelpTemplate::spec_vals: a={a}"); + let inline_context = &self.styles.get_inline_context(); + let i_ctx = inline_context.render(); + let i_ctx_r = inline_context.render_reset(); + let inline_context_value = &self.styles.get_inline_context_value(); + let i_ctx_val = inline_context_value.render(); + let i_ctx_val_r = inline_context_value.render_reset(); + let mut spec_vals = Vec::new(); #[cfg(feature = "env")] if let Some(ref env) = a.env { @@ -789,7 +804,11 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { } else { Default::default() }; - let env_info = format!("[env: {}{}]", env.0.to_string_lossy(), env_val); + let env_info = format!( + "{i_ctx}[env: {i_ctx_r}{i_ctx_val}{}{}{i_ctx_val_r}{i_ctx}]{i_ctx_r}", + env.0.to_string_lossy(), + env_val, + ); spec_vals.push(env_info); } } @@ -813,34 +832,38 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { .collect::>() .join(" "); - spec_vals.push(format!("[default: {pvs}]")); + spec_vals.push(format!( + "{i_ctx}[default: {i_ctx_r}{i_ctx_val}{pvs}{i_ctx_val_r}{i_ctx}]{i_ctx_r}" + )); } let als = a .aliases .iter() .filter(|&als| als.1) // visible - .map(|als| als.0.as_str()) // name + .map(|als| format!("{i_ctx_val}{}{i_ctx_val_r}", als.0.as_str())) // name .collect::>() - .join(", "); + .join(format!("{i_ctx}, {i_ctx_r}").as_str()); if !als.is_empty() { debug!("HelpTemplate::spec_vals: Found aliases...{:?}", a.aliases); - spec_vals.push(format!("[aliases: {als}]")); + spec_vals.push(format!("{i_ctx}[aliases: {i_ctx_r}{als}{i_ctx}]{i_ctx_r}")); } let als = a .short_aliases .iter() .filter(|&als| als.1) // visible - .map(|&als| als.0.to_string()) // name + .map(|&als| format!("{i_ctx_val}{}{i_ctx_val_r}", als.0)) // name .collect::>() - .join(", "); + .join(format!("{i_ctx}, {i_ctx_r}").as_str()); if !als.is_empty() { debug!( "HelpTemplate::spec_vals: Found short aliases...{:?}", a.short_aliases ); - spec_vals.push(format!("[short aliases: {als}]")); + spec_vals.push(format!( + "{i_ctx}[short aliases: {i_ctx_r}{als}{i_ctx}]{i_ctx_r}" + )); } if !a.is_hide_possible_values_set() && !self.use_long_pv(a) { @@ -851,10 +874,13 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { let pvs = possible_vals .iter() .filter_map(PossibleValue::get_visible_quoted_name) + .map(|pvs| format!("{i_ctx_val}{pvs}{i_ctx_val_r}")) .collect::>() - .join(", "); + .join(format!("{i_ctx}, {i_ctx_r}").as_str()); - spec_vals.push(format!("[possible values: {pvs}]")); + spec_vals.push(format!( + "{i_ctx}[possible values: {i_ctx_r}{pvs}{i_ctx}]{i_ctx_r}" + )); } } let connector = if self.use_long { "\n" } else { " " }; @@ -1036,15 +1062,24 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { fn sc_spec_vals(&self, a: &Command) -> String { debug!("HelpTemplate::sc_spec_vals: a={}", a.get_name()); + let inline_context = &self.styles.get_inline_context(); + let i_ctx = inline_context.render(); + let i_ctx_r = inline_context.render_reset(); + let inline_context_value = &self.styles.get_inline_context_value(); + let i_ctx_val = inline_context_value.render(); + let i_ctx_val_r = inline_context_value.render_reset(); + let mut spec_vals = vec![]; let mut short_als = a .get_visible_short_flag_aliases() - .map(|a| format!("-{a}")) + .map(|a| format!("{i_ctx_val}-{a}{i_ctx_val_r}",)) .collect::>(); - let als = a.get_visible_aliases().map(|s| s.to_string()); + let als = a + .get_visible_aliases() + .map(|s| format!("{i_ctx_val}{s}{i_ctx_val_r}")); short_als.extend(als); - let all_als = short_als.join(", "); + let all_als = short_als.join(format!("{i_ctx}, {i_ctx_r}").as_str()); if !all_als.is_empty() { debug!( "HelpTemplate::spec_vals: Found aliases...{:?}", @@ -1054,7 +1089,9 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { "HelpTemplate::spec_vals: Found short flag aliases...{:?}", a.get_all_short_flag_aliases().collect::>() ); - spec_vals.push(format!("[aliases: {all_als}]")); + spec_vals.push(format!( + "{i_ctx}[aliases: {i_ctx_r}{all_als}{i_ctx}]{i_ctx_r}" + )); } spec_vals.join(" ")