Skip to content

Commit

Permalink
Merge pull request #4843 from epage/style
Browse files Browse the repository at this point in the history
feat(help): Allow customizing terminal styling (unstable)
  • Loading branch information
epage committed Apr 18, 2023
2 parents 7bff552 + cbea23e commit b0e0c59
Show file tree
Hide file tree
Showing 15 changed files with 798 additions and 390 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -94,6 +94,7 @@ string = ["clap_builder/string"] # Allow runtime generated strings

# In-work features
unstable-v5 = ["clap_builder/unstable-v5", "clap_derive?/unstable-v5", "deprecated"]
unstable-styles = ["clap_builder/unstable-styles"]

[lib]
bench = false
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -16,7 +16,7 @@ _FEATURES = minimal default wasm full debug release
_FEATURES_minimal = --no-default-features --features "std"
_FEATURES_default =
_FEATURES_wasm = --no-default-features --features "std help usage error-context suggestions" --features "deprecated derive cargo env unicode string"
_FEATURES_full = --features "deprecated derive cargo env unicode string wrap_help"
_FEATURES_full = --features "deprecated derive cargo env unicode string wrap_help unstable-styles"
_FEATURES_next = ${_FEATURES_full} --features unstable-v5
_FEATURES_debug = ${_FEATURES_full} --features debug --features clap_complete/debug
_FEATURES_release = ${_FEATURES_full} --release
Expand Down
9 changes: 5 additions & 4 deletions clap_builder/Cargo.toml
Expand Up @@ -32,11 +32,11 @@ tag-name = "v{{version}}"
[features]
default = ["std", "color", "help", "usage", "error-context", "suggestions"]
debug = ["dep:backtrace"] # Enables debug messages
unstable-doc = ["cargo", "wrap_help", "env", "unicode", "string"] # for docs.rs
unstable-doc = ["cargo", "wrap_help", "env", "unicode", "string", "unstable-styles"] # for docs.rs

# Used in default
std = [] # support for no_std in a backwards-compatible way
color = ["dep:anstyle", "dep:anstream"]
std = ["anstyle/std"] # support for no_std in a backwards-compatible way
color = ["dep:anstream"]
help = []
usage = []
error-context = []
Expand All @@ -52,6 +52,7 @@ string = [] # Allow runtime generated strings

# In-work features
unstable-v5 = ["deprecated"]
unstable-styles = ["color"]

[lib]
bench = false
Expand All @@ -62,7 +63,7 @@ bitflags = "1.2.0"
unicase = { version = "2.6.0", optional = true }
strsim = { version = "0.10.0", optional = true }
anstream = { version = "0.3.0", optional = true }
anstyle = { version = "1.0.0", features = ["std"], optional = true }
anstyle = "1.0.0"
terminal_size = { version = "0.2.1", optional = true }
backtrace = { version = "0.3.67", optional = true }
unicode-width = { version = "0.1.9", optional = true }
Expand Down
61 changes: 44 additions & 17 deletions clap_builder/src/builder/arg.rs
Expand Up @@ -17,6 +17,7 @@ use crate::builder::OsStr;
use crate::builder::PossibleValue;
use crate::builder::Str;
use crate::builder::StyledStr;
use crate::builder::Styles;
use crate::builder::ValueRange;
use crate::util::AnyValueId;
use crate::ArgAction;
Expand Down Expand Up @@ -4271,49 +4272,74 @@ impl Arg {
}
}

pub(crate) fn stylized(&self, required: Option<bool>) -> StyledStr {
pub(crate) fn stylized(&self, styles: &Styles, required: Option<bool>) -> StyledStr {
use std::fmt::Write as _;
let literal = styles.get_literal();

let mut styled = StyledStr::new();
// Write the name such --long or -l
if let Some(l) = self.get_long() {
styled.literal("--");
styled.literal(l);
let _ = write!(
styled,
"{}--{l}{}",
literal.render(),
literal.render_reset()
);
} else if let Some(s) = self.get_short() {
styled.literal("-");
styled.literal(s);
let _ = write!(styled, "{}-{s}{}", literal.render(), literal.render_reset());
}
styled.push_styled(&self.stylize_arg_suffix(required));
styled.push_styled(&self.stylize_arg_suffix(styles, required));
styled
}

pub(crate) fn stylize_arg_suffix(&self, required: Option<bool>) -> StyledStr {
pub(crate) fn stylize_arg_suffix(&self, styles: &Styles, required: Option<bool>) -> StyledStr {
use std::fmt::Write as _;
let literal = styles.get_literal();
let placeholder = styles.get_placeholder();
let mut styled = StyledStr::new();

let mut need_closing_bracket = false;
if self.is_takes_value_set() && !self.is_positional() {
let is_optional_val = self.get_min_vals() == 0;
if self.is_require_equals_set() {
let (style, start) = if self.is_require_equals_set() {
if is_optional_val {
need_closing_bracket = true;
styled.placeholder("[=");
(placeholder, "[=")
} else {
styled.literal("=");
(literal, "=")
}
} else if is_optional_val {
need_closing_bracket = true;
styled.placeholder(" [");
(placeholder, " [")
} else {
styled.placeholder(" ");
}
(placeholder, " ")
};
let _ = write!(styled, "{}{start}{}", style.render(), style.render_reset());
}
if self.is_takes_value_set() || self.is_positional() {
let required = required.unwrap_or_else(|| self.is_required_set());
let arg_val = self.render_arg_val(required);
styled.placeholder(arg_val);
let _ = write!(
styled,
"{}{arg_val}{}",
placeholder.render(),
placeholder.render_reset()
);
} else if matches!(*self.get_action(), ArgAction::Count) {
styled.placeholder("...");
let _ = write!(
styled,
"{}...{}",
placeholder.render(),
placeholder.render_reset()
);
}
if need_closing_bracket {
styled.placeholder("]");
let _ = write!(
styled,
"{}]{}",
placeholder.render(),
placeholder.render_reset()
);
}

styled
Expand Down Expand Up @@ -4401,7 +4427,8 @@ impl Eq for Arg {}

impl Display for Arg {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.stylized(None).fmt(f)
let plain = Styles::plain();
self.stylized(&plain, None).fmt(f)
}
}

Expand Down
38 changes: 34 additions & 4 deletions clap_builder/src/builder/command.rs
Expand Up @@ -17,6 +17,7 @@ use crate::builder::IntoResettable;
use crate::builder::PossibleValue;
use crate::builder::Str;
use crate::builder::StyledStr;
use crate::builder::Styles;
use crate::builder::{Arg, ArgGroup, ArgPredicate};
use crate::error::ErrorKind;
use crate::error::Result as ClapResult;
Expand Down Expand Up @@ -1080,6 +1081,31 @@ impl Command {
}
}

/// Sets when to color output.
///
/// **NOTE:** This choice is propagated to all child subcommands.
///
/// **NOTE:** Default behaviour is [`ColorChoice::Auto`].
///
/// # Examples
///
/// ```no_run
/// # use clap_builder as clap;
/// # use clap::{Command, ColorChoice};
/// Command::new("myprog")
/// .color(ColorChoice::Never)
/// .get_matches();
/// ```
/// [`ColorChoice::Auto`]: crate::ColorChoice::Auto
#[cfg(feature = "color")]
#[inline]
#[must_use]
#[cfg(feature = "unstable-styles")]
pub fn styles(mut self, styles: Styles) -> Self {
self.app_ext.set(styles);
self
}

/// Sets the terminal width at which to wrap help messages.
///
/// Using `0` will ignore terminal widths and use source formatting.
Expand Down Expand Up @@ -3338,6 +3364,10 @@ impl Command {
}
}

pub(crate) fn get_styles(&self) -> &Styles {
self.app_ext.get().unwrap_or_default()
}

/// Iterate through the set of subcommands, getting a reference to each.
#[inline]
pub fn get_subcommands(&self) -> impl Iterator<Item = &Command> {
Expand Down Expand Up @@ -4321,9 +4351,9 @@ impl Command {
.collect::<Vec<_>>()
.join("|");
let mut styled = StyledStr::new();
styled.none("<");
styled.none(g_string);
styled.none(">");
styled.push_str("<");
styled.push_string(g_string);
styled.push_str(">");
styled
}
}
Expand Down Expand Up @@ -4649,7 +4679,7 @@ impl fmt::Display for Command {
}
}

trait AppTag: crate::builder::ext::Extension {}
pub(crate) trait AppTag: crate::builder::ext::Extension {}

#[derive(Default, Copy, Clone, Debug)]
struct TermWidth(usize);
Expand Down
5 changes: 5 additions & 0 deletions clap_builder/src/builder/mod.rs
Expand Up @@ -35,6 +35,8 @@ pub use range::ValueRange;
pub use resettable::IntoResettable;
pub use resettable::Resettable;
pub use styled_str::StyledStr;
#[cfg(feature = "unstable-styles")]
pub use styled_str::Styles;
pub use value_hint::ValueHint;
pub use value_parser::_AutoValueParser;
pub use value_parser::via_prelude;
Expand All @@ -59,3 +61,6 @@ pub use value_parser::_AnonymousValueParser;
pub(crate) use self::str::Inner as StrInner;
pub(crate) use action::CountType;
pub(crate) use arg_settings::{ArgFlags, ArgSettings};
pub(crate) use command::AppTag;
#[cfg(not(feature = "unstable-styles"))]
pub(crate) use styled_str::Styles;

0 comments on commit b0e0c59

Please sign in to comment.