From c6155f62d549ac08f6ff6faecd3ec1a9327dbf17 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 24 Aug 2022 10:24:15 -0500 Subject: [PATCH 01/22] feat: Open the door for user styling in the future This added about 10 KiB to the `.text` which I cannot explain why --- clap_complete/src/shells/elvish.rs | 5 +- clap_complete/src/shells/fish.rs | 8 +-- clap_complete/src/shells/powershell.rs | 5 +- clap_complete/src/shells/zsh.rs | 12 +++-- clap_complete_fig/src/fig.rs | 10 ++-- clap_mangen/src/render.rs | 8 +-- src/builder/arg.rs | 19 +++---- src/builder/command.rs | 72 +++++++++++++------------- src/builder/debug_asserts.rs | 4 +- src/builder/possible_value.rs | 13 ++--- src/builder/resettable.rs | 16 ++++++ src/builder/styled_str.rs | 43 ++++++++++++++- src/output/help.rs | 32 ++++++++---- src/output/usage.rs | 2 +- tests/builder/tests.rs | 12 ++++- tests/derive/doc_comments_help.rs | 5 +- tests/derive/flags.rs | 12 ++++- tests/macros.rs | 48 ++++++++++------- 18 files changed, 215 insertions(+), 111 deletions(-) diff --git a/clap_complete/src/shells/elvish.rs b/clap_complete/src/shells/elvish.rs index 83406776505..d2a267f6f79 100644 --- a/clap_complete/src/shells/elvish.rs +++ b/clap_complete/src/shells/elvish.rs @@ -1,5 +1,6 @@ use std::io::Write; +use clap::builder::StyledStr; use clap::*; use crate::generator::{utils, Generator}; @@ -59,9 +60,9 @@ fn escape_string(string: &str) -> String { string.replace('\'', "''") } -fn get_tooltip(help: Option<&str>, data: T) -> String { +fn get_tooltip(help: Option<&StyledStr>, data: T) -> String { match help { - Some(help) => escape_string(help), + Some(help) => escape_string(&help.to_string()), _ => data.to_string(), } } diff --git a/clap_complete/src/shells/fish.rs b/clap_complete/src/shells/fish.rs index 2550a22989b..b37cda89614 100644 --- a/clap_complete/src/shells/fish.rs +++ b/clap_complete/src/shells/fish.rs @@ -93,7 +93,7 @@ fn gen_fish_inner( } if let Some(data) = option.get_help() { - template.push_str(format!(" -d '{}'", escape_string(data)).as_str()); + template.push_str(format!(" -d '{}'", escape_string(&data.to_string())).as_str()); } template.push_str(value_completion(option).as_str()); @@ -118,7 +118,7 @@ fn gen_fish_inner( } if let Some(data) = flag.get_help() { - template.push_str(format!(" -d '{}'", escape_string(data)).as_str()); + template.push_str(format!(" -d '{}'", escape_string(&data.to_string())).as_str()); } buffer.push_str(template.as_str()); @@ -132,7 +132,7 @@ fn gen_fish_inner( template.push_str(format!(" -a \"{}\"", &subcommand.get_name()).as_str()); if let Some(data) = subcommand.get_about() { - template.push_str(format!(" -d '{}'", escape_string(data)).as_str()) + template.push_str(format!(" -d '{}'", escape_string(&data.to_string())).as_str()) } buffer.push_str(template.as_str()); @@ -164,7 +164,7 @@ fn value_completion(option: &Arg) -> String { Some(format!( "{}\t{}", escape_string(value.get_name()).as_str(), - escape_string(value.get_help().unwrap_or_default()).as_str() + escape_string(&value.get_help().unwrap_or_default().to_string()) )) }) .collect::>() diff --git a/clap_complete/src/shells/powershell.rs b/clap_complete/src/shells/powershell.rs index ec12bd28e3c..ae7e4a750cf 100644 --- a/clap_complete/src/shells/powershell.rs +++ b/clap_complete/src/shells/powershell.rs @@ -1,5 +1,6 @@ use std::io::Write; +use clap::builder::StyledStr; use clap::*; use crate::generator::{utils, Generator}; @@ -64,9 +65,9 @@ fn escape_string(string: &str) -> String { string.replace('\'', "''") } -fn get_tooltip(help: Option<&str>, data: T) -> String { +fn get_tooltip(help: Option<&StyledStr>, data: T) -> String { match help { - Some(help) => escape_string(help), + Some(help) => escape_string(&help.to_string()), _ => data.to_string(), } } diff --git a/clap_complete/src/shells/zsh.rs b/clap_complete/src/shells/zsh.rs index 8aa032a9549..894b5f1487f 100644 --- a/clap_complete/src/shells/zsh.rs +++ b/clap_complete/src/shells/zsh.rs @@ -153,7 +153,7 @@ fn subcommands_of(p: &Command) -> String { let text = format!( "'{name}:{help}' \\", name = name, - help = escape_help(subcommand.get_about().unwrap_or_default()) + help = escape_help(&subcommand.get_about().unwrap_or_default().to_string()) ); if !text.is_empty() { @@ -372,7 +372,8 @@ fn value_completion(arg: &Arg) -> Option { Some(format!( r#"{name}\:"{tooltip}""#, name = escape_value(value.get_name()), - tooltip = value.get_help().map(escape_help).unwrap_or_default() + tooltip = + escape_help(&value.get_help().unwrap_or_default().to_string()), )) } }) @@ -445,7 +446,7 @@ fn write_opts_of(p: &Command, p_global: Option<&Command>) -> String { for o in p.get_opts() { debug!("write_opts_of:iter: o={}", o.get_id()); - let help = o.get_help().map(escape_help).unwrap_or_default(); + let help = escape_help(&o.get_help().unwrap_or_default().to_string()); let conflicts = arg_conflicts(p, o, p_global); let multiple = "*"; @@ -541,7 +542,7 @@ fn write_flags_of(p: &Command, p_global: Option<&Command>) -> String { for f in utils::flags(p) { debug!("write_flags_of:iter: f={}", f.get_id()); - let help = f.get_help().map(escape_help).unwrap_or_default(); + let help = escape_help(&f.get_help().unwrap_or_default().to_string()); let conflicts = arg_conflicts(p, &f, p_global); let multiple = "*"; @@ -634,7 +635,8 @@ fn write_positionals_of(p: &Command) -> String { name = arg.get_id(), help = arg .get_help() - .map_or("".to_owned(), |v| " -- ".to_owned() + v) + .map(|s| s.to_string()) + .map_or("".to_owned(), |v| " -- ".to_owned() + &v) .replace('[', "\\[") .replace(']', "\\]") .replace('\'', "'\\''") diff --git a/clap_complete_fig/src/fig.rs b/clap_complete_fig/src/fig.rs index e9d9186d13a..135bbd337f4 100644 --- a/clap_complete_fig/src/fig.rs +++ b/clap_complete_fig/src/fig.rs @@ -28,7 +28,7 @@ impl Generator for Fig { write!( &mut buffer, " description: \"{}\",\n", - escape_string(cmd.get_about().unwrap_or_default()) + escape_string(&cmd.get_about().unwrap_or_default().to_string()) ) .unwrap(); @@ -101,7 +101,7 @@ fn gen_fig_inner( buffer, "{:indent$}description: \"{}\",\n", "", - escape_string(data), + escape_string(&data.to_string()), indent = indent + 4 ) .unwrap(); @@ -205,7 +205,7 @@ fn gen_options(cmd: &Command, indent: usize) -> String { &mut buffer, "{:indent$}description: \"{}\",\n", "", - escape_string(data), + escape_string(&data.to_string()), indent = indent + 4 ) .unwrap(); @@ -314,7 +314,7 @@ fn gen_options(cmd: &Command, indent: usize) -> String { &mut buffer, "{:indent$}description: \"{}\",\n", "", - escape_string(data).as_str(), + escape_string(&data.to_string()).as_str(), indent = indent + 4 ) .unwrap(); @@ -426,7 +426,7 @@ fn gen_args(arg: &Arg, indent: usize) -> String { &mut buffer, "{:indent$}description: \"{}\",\n", "", - escape_string(help), + escape_string(&help.to_string()), indent = indent + 6 ) .unwrap(); diff --git a/clap_mangen/src/render.rs b/clap_mangen/src/render.rs index 0548cef42e0..8a378fd5836 100644 --- a/clap_mangen/src/render.rs +++ b/clap_mangen/src/render.rs @@ -17,7 +17,7 @@ pub(crate) fn about(roff: &mut Roff, cmd: &clap::Command) { pub(crate) fn description(roff: &mut Roff, cmd: &clap::Command) { if let Some(about) = cmd.get_long_about().or_else(|| cmd.get_about()) { - for line in about.lines() { + for line in about.to_string().lines() { if line.trim().is_empty() { roff.control("PP", []); } else { @@ -110,7 +110,7 @@ pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) { let mut arg_help_written = false; if let Some(help) = opt.get_long_help().or_else(|| opt.get_help()) { arg_help_written = true; - body.push(roman(help)); + body.push(roman(help.to_string())); } roff.control("TP", []); @@ -224,7 +224,7 @@ pub(crate) fn subcommands(roff: &mut Roff, cmd: &clap::Command, section: &str) { roff.text([roman(&name)]); if let Some(about) = sub.get_about().or_else(|| sub.get_long_about()) { - for line in about.lines() { + for line in about.to_string().lines() { roff.text([roman(line)]); } } @@ -242,7 +242,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.lines() { + for line in about.to_string().lines() { roff.text([roman(line)]); } } diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 7fbd0b320ef..3879373ab55 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -17,6 +17,7 @@ use crate::builder::IntoResettable; use crate::builder::OsStr; use crate::builder::PossibleValue; use crate::builder::Str; +use crate::builder::StyledStr; use crate::builder::ValueRange; use crate::ArgAction; use crate::Id; @@ -54,8 +55,8 @@ use crate::INTERNAL_ERROR_MSG; #[derive(Default, Clone)] pub struct Arg { pub(crate) id: Id, - pub(crate) help: Option, - pub(crate) long_help: Option, + pub(crate) help: Option, + pub(crate) long_help: Option, pub(crate) action: Option, pub(crate) value_parser: Option, pub(crate) blacklist: Vec, @@ -1900,7 +1901,7 @@ impl Arg { /// [`Arg::long_help`]: Arg::long_help() #[inline] #[must_use] - pub fn help(mut self, h: impl IntoResettable) -> Self { + pub fn help(mut self, h: impl IntoResettable) -> Self { self.help = h.into_resettable().into_option(); self } @@ -1962,7 +1963,7 @@ impl Arg { /// [`Arg::help`]: Arg::help() #[inline] #[must_use] - pub fn long_help(mut self, h: impl IntoResettable) -> Self { + pub fn long_help(mut self, h: impl IntoResettable) -> Self { self.long_help = h.into_resettable().into_option(); self } @@ -3539,8 +3540,8 @@ impl Arg { /// Get the help specified for this argument, if any #[inline] - pub fn get_help(&self) -> Option<&str> { - self.help.as_deref() + pub fn get_help(&self) -> Option<&StyledStr> { + self.help.as_ref() } /// Get the long help specified for this argument, if any @@ -3550,12 +3551,12 @@ impl Arg { /// ```rust /// # use clap::Arg; /// let arg = Arg::new("foo").long_help("long help"); - /// assert_eq!(Some("long help"), arg.get_long_help()); + /// assert_eq!(Some("long help".to_owned()), arg.get_long_help().map(|s| s.to_string())); /// ``` /// #[inline] - pub fn get_long_help(&self) -> Option<&str> { - self.long_help.as_deref() + pub fn get_long_help(&self) -> Option<&StyledStr> { + self.long_help.as_ref() } /// Get the help heading specified for this argument, if any diff --git a/src/builder/command.rs b/src/builder/command.rs index 461cc8974ad..163d1687e6b 100644 --- a/src/builder/command.rs +++ b/src/builder/command.rs @@ -75,22 +75,22 @@ pub struct Command { author: Option, version: Option, long_version: Option, - about: Option, - long_about: Option, - before_help: Option, - before_long_help: Option, - after_help: Option, - after_long_help: Option, + about: Option, + long_about: Option, + before_help: Option, + before_long_help: Option, + after_help: Option, + after_long_help: Option, aliases: Vec<(Str, bool)>, // (name, visible) short_flag_aliases: Vec<(char, bool)>, // (name, visible) long_flag_aliases: Vec<(Str, bool)>, // (name, visible) - usage_str: Option, + usage_str: Option, usage_name: Option, - help_str: Option, + help_str: Option, disp_ord: Option, term_w: Option, max_w: Option, - template: Option, + template: Option, settings: AppFlags, g_settings: AppFlags, args: MKeyMap, @@ -1442,7 +1442,7 @@ impl Command { /// # ; /// ``` #[must_use] - pub fn about(mut self, about: impl IntoResettable) -> Self { + pub fn about(mut self, about: impl IntoResettable) -> Self { self.about = about.into_resettable().into_option(); self } @@ -1467,7 +1467,7 @@ impl Command { /// ``` /// [`Command::about`]: Command::about() #[must_use] - pub fn long_about(mut self, long_about: impl IntoResettable) -> Self { + pub fn long_about(mut self, long_about: impl IntoResettable) -> Self { self.long_about = long_about.into_resettable().into_option(); self } @@ -1489,7 +1489,7 @@ impl Command { /// ``` /// #[must_use] - pub fn after_help(mut self, help: impl Into) -> Self { + pub fn after_help(mut self, help: impl Into) -> Self { self.after_help = Some(help.into()); self } @@ -1511,7 +1511,7 @@ impl Command { /// # ; /// ``` #[must_use] - pub fn after_long_help(mut self, help: impl Into) -> Self { + pub fn after_long_help(mut self, help: impl Into) -> Self { self.after_long_help = Some(help.into()); self } @@ -1531,7 +1531,7 @@ impl Command { /// # ; /// ``` #[must_use] - pub fn before_help(mut self, help: impl Into) -> Self { + pub fn before_help(mut self, help: impl Into) -> Self { self.before_help = Some(help.into()); self } @@ -1551,7 +1551,7 @@ impl Command { /// # ; /// ``` #[must_use] - pub fn before_long_help(mut self, help: impl Into) -> Self { + pub fn before_long_help(mut self, help: impl Into) -> Self { self.before_long_help = Some(help.into()); self } @@ -1645,7 +1645,7 @@ impl Command { /// /// [`ArgMatches::usage`]: ArgMatches::usage() #[must_use] - pub fn override_usage(mut self, usage: impl Into) -> Self { + pub fn override_usage(mut self, usage: impl Into) -> Self { self.usage_str = Some(usage.into()); self } @@ -1682,7 +1682,7 @@ impl Command { /// # ; /// ``` #[must_use] - pub fn override_help(mut self, help: impl Into) -> Self { + pub fn override_help(mut self, help: impl Into) -> Self { self.help_str = Some(help.into()); self } @@ -1732,7 +1732,7 @@ impl Command { /// [`Command::before_help`]: Command::before_help() /// [`Command::before_long_help`]: Command::before_long_help() #[must_use] - pub fn help_template(mut self, s: impl Into) -> Self { + pub fn help_template(mut self, s: impl Into) -> Self { self.template = Some(s.into()); self } @@ -3242,16 +3242,16 @@ impl Command { /// /// [`Command::about`]: Command::about() #[inline] - pub fn get_about(&self) -> Option<&str> { - self.about.as_deref() + pub fn get_about(&self) -> Option<&StyledStr> { + self.about.as_ref() } /// Get the help message specified via [`Command::long_about`]. /// /// [`Command::long_about`]: Command::long_about() #[inline] - pub fn get_long_about(&self) -> Option<&str> { - self.long_about.as_deref() + pub fn get_long_about(&self) -> Option<&StyledStr> { + self.long_about.as_ref() } /// Get the custom section heading specified via [`Command::next_help_heading`]. @@ -3364,26 +3364,26 @@ impl Command { /// Returns the help heading for listing subcommands. #[inline] - pub fn get_before_help(&self) -> Option<&str> { - self.before_help.as_deref() + pub fn get_before_help(&self) -> Option<&StyledStr> { + self.before_help.as_ref() } /// Returns the help heading for listing subcommands. #[inline] - pub fn get_before_long_help(&self) -> Option<&str> { - self.before_long_help.as_deref() + pub fn get_before_long_help(&self) -> Option<&StyledStr> { + self.before_long_help.as_ref() } /// Returns the help heading for listing subcommands. #[inline] - pub fn get_after_help(&self) -> Option<&str> { - self.after_help.as_deref() + pub fn get_after_help(&self) -> Option<&StyledStr> { + self.after_help.as_ref() } /// Returns the help heading for listing subcommands. #[inline] - pub fn get_after_long_help(&self) -> Option<&str> { - self.after_long_help.as_deref() + pub fn get_after_long_help(&self) -> Option<&StyledStr> { + self.after_long_help.as_ref() } /// Find subcommand such that its name or one of aliases equals `name`. @@ -3667,16 +3667,16 @@ impl Command { // Internally used only impl Command { - pub(crate) fn get_override_usage(&self) -> Option<&str> { - self.usage_str.as_deref() + pub(crate) fn get_override_usage(&self) -> Option<&StyledStr> { + self.usage_str.as_ref() } - pub(crate) fn get_override_help(&self) -> Option<&str> { - self.help_str.as_deref() + pub(crate) fn get_override_help(&self) -> Option<&StyledStr> { + self.help_str.as_ref() } - pub(crate) fn get_help_template(&self) -> Option<&str> { - self.template.as_deref() + pub(crate) fn get_help_template(&self) -> Option<&StyledStr> { + self.template.as_ref() } pub(crate) fn get_term_width(&self) -> Option { diff --git a/src/builder/debug_asserts.rs b/src/builder/debug_asserts.rs index 140a5ad9344..afb4aa5ab3f 100644 --- a/src/builder/debug_asserts.rs +++ b/src/builder/debug_asserts.rs @@ -339,13 +339,13 @@ pub(crate) fn assert_app(cmd: &Command) { if let Some(help_template) = cmd.get_help_template() { assert!( - !help_template.contains("{flags}"), + !help_template.to_string().contains("{flags}"), "Command {}: {}", cmd.get_name(), "`{flags}` template variable was removed in clap3, they are now included in `{options}`", ); assert!( - !help_template.contains("{unified}"), + !help_template.to_string().contains("{unified}"), "Command {}: {}", cmd.get_name(), "`{unified}` template variable was removed in clap3, use `{options}` instead" diff --git a/src/builder/possible_value.rs b/src/builder/possible_value.rs index acf671b29f6..93f012e89d4 100644 --- a/src/builder/possible_value.rs +++ b/src/builder/possible_value.rs @@ -1,6 +1,7 @@ use std::{borrow::Cow, iter}; use crate::builder::Str; +use crate::builder::StyledStr; use crate::util::eq_ignore_case; /// A possible value of an argument. @@ -30,7 +31,7 @@ use crate::util::eq_ignore_case; #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct PossibleValue { name: Str, - help: Option, + help: Option, aliases: Vec, // (name, visible) hide: bool, } @@ -75,7 +76,7 @@ impl PossibleValue { /// ``` #[inline] #[must_use] - pub fn help(mut self, help: impl Into) -> Self { + pub fn help(mut self, help: impl Into) -> Self { self.help = Some(help.into()); self } @@ -144,16 +145,16 @@ impl PossibleValue { /// Get the help specified for this argument, if any #[inline] - pub fn get_help(&self) -> Option<&str> { - self.help.as_deref() + pub fn get_help(&self) -> Option<&StyledStr> { + self.help.as_ref() } /// Get the help specified for this argument, if any and the argument /// value is not hidden #[inline] - pub(crate) fn get_visible_help(&self) -> Option<&str> { + pub(crate) fn get_visible_help(&self) -> Option<&StyledStr> { if !self.hide { - self.help.as_deref() + self.get_help() } else { None } diff --git a/src/builder/resettable.rs b/src/builder/resettable.rs index e7b111d041f..158ff059407 100644 --- a/src/builder/resettable.rs +++ b/src/builder/resettable.rs @@ -3,6 +3,7 @@ use crate::builder::OsStr; use crate::builder::Str; +use crate::builder::StyledStr; /// Clearable builder value #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -43,6 +44,15 @@ pub trait IntoResettable { fn into_resettable(self) -> Resettable; } +impl IntoResettable for Option<&'static str> { + fn into_resettable(self) -> Resettable { + match self { + Some(s) => Resettable::Value(s.into()), + None => Resettable::Reset, + } + } +} + impl IntoResettable for Option<&'static str> { fn into_resettable(self) -> Resettable { match self { @@ -67,6 +77,12 @@ impl IntoResettable for Resettable { } } +impl> IntoResettable for I { + fn into_resettable(self) -> Resettable { + Resettable::Value(self.into()) + } +} + impl> IntoResettable for I { fn into_resettable(self) -> Resettable { Resettable::Value(self.into()) diff --git a/src/builder/styled_str.rs b/src/builder/styled_str.rs index afe2545a3ff..d315e7e1bab 100644 --- a/src/builder/styled_str.rs +++ b/src/builder/styled_str.rs @@ -32,7 +32,41 @@ impl StyledStr { } fn stylize(&mut self, style: Option