Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(AppSettings): Enable user-defined colors for help message #3407

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 207 additions & 4 deletions src/build/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ mod tests;

pub use self::settings::{AppFlags, AppSettings};

#[cfg(feature = "color")]
use crate::output::fmt::Style;
#[cfg(feature = "color")]
use termcolor::Color;
#[cfg(feature = "color")]
use termcolor::ColorSpec;

// Std
use std::{
collections::HashMap,
Expand All @@ -27,7 +34,7 @@ use crate::build::{arg::ArgProvider, Arg, ArgGroup, ArgPredicate, ArgSettings};
use crate::error::ErrorKind;
use crate::error::Result as ClapResult;
use crate::mkeymap::MKeyMap;
use crate::output::{fmt::Colorizer, Help, HelpWriter, Usage};
use crate::output::{fmt::Colorizer, fmt::StyleSpec, Help, HelpWriter, Usage};
use crate::parse::{ArgMatcher, ArgMatches, Input, Parser};
use crate::util::{color::ColorChoice, Id, Key};
use crate::{Error, INTERNAL_ERROR_MSG};
Expand Down Expand Up @@ -97,6 +104,7 @@ pub struct App<'help> {
pub(crate) current_help_heading: Option<&'help str>,
pub(crate) subcommand_value_name: Option<&'help str>,
pub(crate) subcommand_heading: Option<&'help str>,
pub(crate) output_style: StyleSpec,
}

impl<'help> App<'help> {
Expand Down Expand Up @@ -677,8 +685,9 @@ impl<'help> App<'help> {
pub fn print_help(&mut self) -> io::Result<()> {
self._build();
let color = self.get_color();
let style = self.get_style_spec();

let mut c = Colorizer::new(false, color);
let mut c = Colorizer::new(false, color, style);
let parser = Parser::new(self);
let usage = Usage::new(parser.app, &parser.required);
Help::new(HelpWriter::Buffer(&mut c), parser.app, &usage, false).write_help()?;
Expand All @@ -703,8 +712,8 @@ impl<'help> App<'help> {
pub fn print_long_help(&mut self) -> io::Result<()> {
self._build();
let color = self.get_color();

let mut c = Colorizer::new(false, color);
let style = self.get_style_spec();
let mut c = Colorizer::new(false, color, style);
let parser = Parser::new(self);
let usage = Usage::new(parser.app, &parser.required);
Help::new(HelpWriter::Buffer(&mut c), parser.app, &usage, true).write_help()?;
Expand Down Expand Up @@ -1320,6 +1329,196 @@ impl<'help> App<'help> {
}
}

/// Sets the `ColorSpec` for the associated style
///
/// **NOTE:** This style is propagated to all child subcommands
///
/// **NOTE:** Default behavior is to use green for Good, yellow for Warning, b
/// old red for Error, and dimmed for hint_style.
///
/// # Examples
///
/// ```no_run
///
/// # use clap::{App, Style, Color, ColorSpec};
///
/// let mut color = ColorSpec::new();
///
/// color.set_fg(Some(Color::Green)).set_bold(true);
///
/// App::new("myprog")
/// .style(Style::Good, color)
/// .get_matches();
/// ```
#[cfg(feature = "color")]
#[inline]
#[must_use]
pub fn style(mut self, style: Style, spec: ColorSpec) -> Self {
self.output_style[style] = spec;
self
}

/// Sets whether the associated `Style` should be displayed as bold
///
/// **NOTE:** This style is propagated to all child subcommands
///
/// # Examples
///
/// ```no_run
///
/// # use clap::{App, Style};
///
/// // Do stuff to color
/// App::new("myprog")
/// .bold(Style::Good, true)
/// .get_matches();
/// ```
#[cfg(feature = "color")]
#[inline]
#[must_use]
pub fn bold(mut self, style: Style, value: bool) -> Self {
self.output_style[style].set_bold(value);
self
}

/// Sets whether the associated `Style` should be displayed as italic
///
/// **NOTE:** This style is propagated to all child subcommands
///
/// # Examples
///
/// ```no_run
///
/// # use clap::{App, Style};
///
/// // Do stuff to color
/// App::new("myprog")
/// .italic(Style::Good, true)
/// .get_matches();
/// ```
#[cfg(feature = "color")]
#[inline]
#[must_use]
pub fn italic(mut self, style: Style, value: bool) -> Self {
self.output_style[style].set_italic(value);
self
}

/// Sets whether the associated `Style` should be displayed as underlined
///
/// **NOTE:** This style is propagated to all child subcommands
///
/// # Examples
///
/// ```no_run
///
/// # use clap::{App, Style};
///
/// // Do stuff to color
/// App::new("myprog")
/// .underline(Style::Good, true)
/// .get_matches();
/// ```
#[cfg(feature = "color")]
#[inline]
#[must_use]
pub fn underline(mut self, style: Style, value: bool) -> Self {
self.output_style[style].set_underline(value);
self
}

/// Sets whether the associated `Style` should be displayed as dimmed
///
/// **NOTE:** This style is propagated to all child subcommands
///
/// # Examples
///
/// ```no_run
///
/// # use clap::{App, Style};
///
/// // Do stuff to color
/// App::new("myprog")
/// .dimmed(Style::Good, true)
/// .get_matches();
/// ```
#[cfg(feature = "color")]
#[inline]
#[must_use]
pub fn dimmed(mut self, style: Style, value: bool) -> Self {
self.output_style[style].set_dimmed(value);
self
}

/// Sets whether the associated `Style` should be displayed as dimmed
///
/// **NOTE:** This style is propagated to all child subcommands
///
/// # Examples
///
/// ```no_run
///
/// # use clap::{App, Style};
///
/// // Do stuff to color
/// App::new("myprog")
/// .intense(Style::Good, true)
/// .get_matches();
/// ```
#[cfg(feature = "color")]
#[inline]
#[must_use]
pub fn intense(mut self, style: Style, value: bool) -> Self {
self.output_style[style].set_intense(value);
self
}

/// Sets the color for the foreground of a style
///
/// **NOTE:** This style is propagated to all child subcommands
///
/// # Examples
///
/// ```no_run
///
/// # use clap::{App, Style, Color};
///
/// // Do stuff to color
/// App::new("myprog")
/// .foreground(Style::Good, Some(Color::Green))
/// .get_matches();
/// ```
#[cfg(feature = "color")]
#[inline]
#[must_use]
pub fn foreground(mut self, style: Style, color: Option<Color>) -> Self {
self.output_style[style].set_fg(color);
self
}

/// Sets the color for the background of a style
///
/// **NOTE:** This style is propagated to all child subcommands
///
/// # Examples
///
/// ```no_run
///
/// # use clap::{App, Style, Color};
///
/// // Do stuff to color
/// App::new("myprog")
/// .background(Style::Good, Some(Color::Green))
/// .get_matches();
/// ```
#[cfg(feature = "color")]
#[inline]
#[must_use]
pub fn background(mut self, style: Style, color: Option<Color>) -> Self {
self.output_style[style].set_bg(color);
self
}

/// Set the default section heading for future args.
///
/// This will be used for any arg that hasn't had [`Arg::help_heading`] called.
Expand Down Expand Up @@ -2244,6 +2443,10 @@ impl<'help> App<'help> {
}
}

pub(crate) fn get_style_spec(&self) -> StyleSpec {
self.output_style.clone()
}

/// Iterate through the set of subcommands, getting a reference to each.
#[inline]
pub fn get_subcommands(&self) -> impl Iterator<Item = &App<'help>> {
Expand Down
Loading