2851: fix!: Make color settings an enum r=pksunkara a=pksunkara



Co-authored-by: Pavan Kumar Sunkara <pavan.sss1991@gmail.com>
This commit is contained in:
bors[bot] 2021-10-13 13:03:28 +00:00 committed by GitHub
commit 1f17d9e8ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 140 additions and 154 deletions

View file

@ -444,7 +444,7 @@ Disabling optional features can decrease the binary size of `clap` and decrease
* **std**: _Not Currently Used._ Placeholder for supporting `no_std` environments in a backwards compatible manner.
* **derive**: Enables the custom derive (i.e. `#[derive(Parser)]`). Without this you must use one of the other methods of creating a `clap` CLI listed above. (builds dependency `clap_derive`)
* **cargo**: Turns on macros that read values from `CARGO_*` environment variables.
* **color**: Turns on colored error messages. (builds dependency `termcolor`)
* **color**: Turns on colored error messages. (builds dependency `atty`, `termcolor`)
* **env**: Turns on the usage of environment variables during parsing.
* **suggestions**: Turns on the `Did you mean '--myoption'?` feature for when users make typos. (builds dependency `strsim`)
* **unicode**: Turns on support for unicode characters in arguments and help messages. (builds dependency `textwrap`, `unicase`)

View file

@ -16,7 +16,7 @@
#![doc(html_root_url = "https://docs.rs/clap_derive/3.0.0-beta.4")]
//! This crate is custom derive for clap. It should not be used
//! directly. See [clap documentation](clap)
//! directly. See [clap documentation](http://docs.rs/clap)
//! for the usage of `#[derive(Parser)]`.
#![forbid(unsafe_code)]

View file

@ -28,7 +28,7 @@ use crate::{
mkeymap::MKeyMap,
output::{fmt::Colorizer, Help, HelpWriter, Usage},
parse::{ArgMatcher, ArgMatches, Input, Parser},
util::{safe_exit, termcolor::ColorChoice, Id, Key, USAGE_CODE},
util::{color::ColorChoice, safe_exit, Id, Key, USAGE_CODE},
Result as ClapResult, INTERNAL_ERROR_MSG,
};
@ -90,6 +90,7 @@ pub struct App<'help> {
pub(crate) template: Option<&'help str>,
pub(crate) settings: AppFlags,
pub(crate) g_settings: AppFlags,
pub(crate) color: ColorChoice,
pub(crate) args: MKeyMap<'help>,
pub(crate) subcommands: Vec<App<'help>>,
pub(crate) replacers: HashMap<&'help str, &'help [&'help str]>,
@ -985,7 +986,7 @@ impl<'help> App<'help> {
/// ```no_run
/// # use clap::{App, AppSettings};
/// App::new("myprog")
/// .unset_global_setting(AppSettings::ColorAuto)
/// .unset_global_setting(AppSettings::UnifiedHelpMessage)
/// # ;
/// ```
/// [global]: App::global_setting()
@ -996,6 +997,28 @@ impl<'help> App<'help> {
self
}
/// Sets the behaviour of colored output during runtime.
///
/// **NOTE:** This choice is propagated to all child subcommands.
///
/// **NOTE:** Default behaviour is [`ColorChoice::Auto`].
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, ColorChoice};
/// App::new("myprog")
/// .color(ColorChoice::Never)
/// .get_matches();
/// ```
/// [`ColorChoice::Auto`]: crate::ColorChoice::Auto
#[cfg(feature = "color")]
#[inline]
pub fn color(mut self, color: ColorChoice) -> Self {
self.color = color;
self
}
/// Sets the terminal width at which to wrap help messages. Defaults to
/// `100`. Using `0` will ignore terminal widths and use source formatting.
///
@ -1788,7 +1811,7 @@ impl<'help> App<'help> {
/// [`--help` (long)]: Arg::long_about()
pub fn print_help(&mut self) -> io::Result<()> {
self._build();
let color = self.color();
let color = self.get_color();
let p = Parser::new(self);
let mut c = Colorizer::new(false, color);
@ -1815,7 +1838,7 @@ impl<'help> App<'help> {
/// [`--help` (long)]: Arg::long_about()
pub fn print_long_help(&mut self) -> io::Result<()> {
self._build();
let color = self.color();
let color = self.get_color();
let p = Parser::new(self);
let mut c = Colorizer::new(false, color);
@ -2656,20 +2679,8 @@ impl<'help> App<'help> {
}
#[inline]
// Should we color the output?
pub(crate) fn color(&self) -> ColorChoice {
debug!("App::color: Color setting...");
if self.is_set(AppSettings::ColorNever) {
debug!("Never");
ColorChoice::Never
} else if self.is_set(AppSettings::ColorAlways) {
debug!("Always");
ColorChoice::Always
} else {
debug!("Auto");
ColorChoice::Auto
}
pub(crate) fn get_color(&self) -> ColorChoice {
self.color
}
#[inline]

View file

@ -26,10 +26,6 @@ bitflags! {
const NO_POS_VALUES = 1 << 17;
const NEXT_LINE_HELP = 1 << 18;
const DERIVE_DISP_ORDER = 1 << 19;
const COLORED_HELP = 1 << 20;
const COLOR_ALWAYS = 1 << 21;
const COLOR_AUTO = 1 << 22;
const COLOR_NEVER = 1 << 23;
const DONT_DELIM_TRAIL = 1 << 24;
const ALLOW_NEG_NUMS = 1 << 25;
const DISABLE_HELP_SC = 1 << 27;
@ -59,7 +55,7 @@ pub struct AppFlags(Flags);
impl Default for AppFlags {
fn default() -> Self {
AppFlags(Flags::COLOR_AUTO)
Self::empty()
}
}
@ -80,12 +76,6 @@ impl_settings! { AppSettings, AppFlags,
=> Flags::ALLOW_NEG_NUMS,
AllowMissingPositional("allowmissingpositional")
=> Flags::ALLOW_MISSING_POS,
ColorAlways("coloralways")
=> Flags::COLOR_ALWAYS,
ColorAuto("colorauto")
=> Flags::COLOR_AUTO,
ColorNever("colornever")
=> Flags::COLOR_NEVER,
DontDelimitTrailingValues("dontdelimittrailingvalues")
=> Flags::DONT_DELIM_TRAIL,
DontCollapseArgsInUsage("dontcollapseargsinusage")
@ -491,62 +481,6 @@ pub enum AppSettings {
/// ```
SubcommandPrecedenceOverArg,
/// Enables colored output only when the output is going to a terminal or TTY.
///
/// **NOTE:** This is the default behavior of `clap`.
///
/// **NOTE:** Must be compiled with the `color` cargo feature.
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms).
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::ColorAuto)
/// .get_matches();
/// ```
ColorAuto,
/// Enables colored output regardless of whether or not the output is going to a terminal/TTY.
///
/// **NOTE:** Must be compiled with the `color` cargo feature.
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms).
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::ColorAlways)
/// .get_matches();
/// ```
ColorAlways,
/// Disables colored output no matter if the output is going to a terminal/TTY, or not.
///
/// **NOTE:** Must be compiled with the `color` cargo feature
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and OSX (i.e. non-Windows platforms)
///
/// # Examples
///
/// ```no_run
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::ColorNever)
/// .get_matches();
/// ```
ColorNever,
/// Disables the automatic collapsing of positional args into `[ARGS]` inside the usage string
///
/// # Examples
@ -1086,18 +1020,6 @@ mod test {
"allownegativenumbers".parse::<AppSettings>().unwrap(),
AppSettings::AllowNegativeNumbers
);
assert_eq!(
"colorauto".parse::<AppSettings>().unwrap(),
AppSettings::ColorAuto
);
assert_eq!(
"coloralways".parse::<AppSettings>().unwrap(),
AppSettings::ColorAlways
);
assert_eq!(
"colornever".parse::<AppSettings>().unwrap(),
AppSettings::ColorNever
);
assert_eq!(
"disablehelpsubcommand".parse::<AppSettings>().unwrap(),
AppSettings::DisableHelpSubcommand

View file

@ -29,6 +29,7 @@ pub use crate::{
},
parse::errors::{Error, ErrorKind, Result},
parse::{ArgMatches, Indices, OsValues, Values},
util::color::ColorChoice,
};
pub use crate::derive::{ArgEnum, Args, FromArgMatches, IntoApp, Parser, Subcommand};

View file

@ -1,7 +1,4 @@
#[cfg(not(feature = "color"))]
use crate::util::termcolor::{Color, ColorChoice};
#[cfg(feature = "color")]
use termcolor::{Color, ColorChoice};
use crate::util::color::{Color, ColorChoice};
use std::{
fmt::{self, Display, Formatter},
@ -63,12 +60,12 @@ impl Colorizer {
impl Colorizer {
#[cfg(feature = "color")]
pub(crate) fn print(&self) -> io::Result<()> {
use termcolor::{BufferWriter, ColorSpec, WriteColor};
use termcolor::{BufferWriter, ColorChoice as DepColorChoice, ColorSpec, WriteColor};
let color_when = match self.color_when {
always @ ColorChoice::Always => always,
choice if is_a_tty(self.use_stderr) => choice,
_ => ColorChoice::Never,
ColorChoice::Always => DepColorChoice::Always,
ColorChoice::Auto if is_a_tty(self.use_stderr) => DepColorChoice::Auto,
_ => DepColorChoice::Never,
};
let writer = if self.use_stderr {

View file

@ -12,7 +12,7 @@ use crate::{
build::Arg,
output::fmt::Colorizer,
parse::features::suggestions,
util::{safe_exit, termcolor::ColorChoice, SUCCESS_CODE, USAGE_CODE},
util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE},
App, AppSettings,
};
@ -546,7 +546,7 @@ impl Error {
other: Option<String>,
usage: String,
) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
let arg = arg.to_string();
start_error(&mut c, "The argument '");
@ -582,7 +582,7 @@ impl Error {
}
pub(crate) fn empty_value(app: &App, arg: &Arg, usage: String) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
let arg = arg.to_string();
start_error(&mut c, "The argument '");
@ -601,7 +601,7 @@ impl Error {
}
pub(crate) fn no_equals(app: &App, arg: String, usage: String) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(&mut c, "Equal sign is needed when assigning values to '");
c.warning(&arg);
@ -629,7 +629,7 @@ impl Error {
where
G: AsRef<str> + Display,
{
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
let suffix = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop();
let mut sorted: Vec<String> = good_vals
@ -690,7 +690,7 @@ impl Error {
name: String,
usage: String,
) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(&mut c, "The subcommand '");
c.warning(subcmd.clone());
@ -716,7 +716,7 @@ impl Error {
}
pub(crate) fn unrecognized_subcommand(app: &App, subcmd: String, name: String) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(&mut c, " The subcommand '");
c.warning(subcmd.clone());
@ -739,7 +739,7 @@ impl Error {
required: Vec<String>,
usage: String,
) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(
&mut c,
@ -766,7 +766,7 @@ impl Error {
}
pub(crate) fn missing_subcommand(app: &App, name: String, usage: String) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(&mut c, "'");
c.warning(name);
@ -784,7 +784,7 @@ impl Error {
}
pub(crate) fn invalid_utf8(app: &App, usage: String) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(
&mut c,
@ -809,7 +809,7 @@ impl Error {
curr_occurs: usize,
usage: String,
) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
let verb = Error::singular_or_plural(curr_occurs);
start_error(&mut c, "The argument '");
@ -836,7 +836,7 @@ impl Error {
}
pub(crate) fn too_many_values(app: &App, val: String, arg: String, usage: String) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(&mut c, "The value '");
c.warning(val.clone());
@ -862,7 +862,7 @@ impl Error {
curr_vals: usize,
usage: String,
) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
let verb = Error::singular_or_plural(curr_vals);
start_error(&mut c, "The argument '");
@ -896,7 +896,7 @@ impl Error {
info,
source,
backtrace,
} = Self::value_validation_with_color(arg, val, err, app.color());
} = Self::value_validation_with_color(arg, val, err, app.get_color());
try_help(app, &mut message);
Self {
message,
@ -947,7 +947,7 @@ impl Error {
curr_vals: usize,
usage: String,
) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
let verb = Error::singular_or_plural(curr_vals);
start_error(&mut c, "The argument '");
@ -970,7 +970,7 @@ impl Error {
}
pub(crate) fn unexpected_multiple_usage(app: &App, arg: &Arg, usage: String) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
let arg = arg.to_string();
start_error(&mut c, "The argument '");
@ -994,7 +994,7 @@ impl Error {
did_you_mean: Option<(String, Option<String>)>,
usage: String,
) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(&mut c, "Found argument '");
c.warning(arg.clone());
@ -1039,7 +1039,7 @@ impl Error {
}
pub(crate) fn unnecessary_double_dash(app: &App, arg: String, usage: String) -> Self {
let mut c = Colorizer::new(true, app.color());
let mut c = Colorizer::new(true, app.get_color());
start_error(&mut c, "Found argument '");
c.warning(arg.clone());

View file

@ -1874,7 +1874,7 @@ impl<'help, 'app> Parser<'help, 'app> {
}
pub(crate) fn write_help_err(&self) -> ClapResult<Colorizer> {
let mut c = Colorizer::new(true, self.app.color());
let mut c = Colorizer::new(true, self.app.get_color());
Help::new(HelpWriter::Buffer(&mut c), self, false).write_help()?;
Ok(c)
}
@ -1886,7 +1886,7 @@ impl<'help, 'app> Parser<'help, 'app> {
);
use_long = use_long && self.use_long_help();
let mut c = Colorizer::new(false, self.app.color());
let mut c = Colorizer::new(false, self.app.get_color());
match Help::new(HelpWriter::Buffer(&mut c), self, use_long).write_help() {
Err(e) => e.into(),
@ -1898,7 +1898,7 @@ impl<'help, 'app> Parser<'help, 'app> {
debug!("Parser::version_err");
let msg = self.app._render_version(use_long);
let mut c = Colorizer::new(false, self.app.color());
let mut c = Colorizer::new(false, self.app.get_color());
c.none(msg);
ClapError::new(c, ErrorKind::DisplayVersion)
}

73
src/util/color.rs Normal file
View file

@ -0,0 +1,73 @@
/// Represents the color preferences for program output
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ColorChoice {
/// Enables colored output only when the output is going to a terminal or TTY.
///
/// **NOTE:** This is the default behavior of `clap`.
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms).
///
/// # Examples
///
#[cfg_attr(not(feature = "color"), doc = " ```ignore")]
#[cfg_attr(feature = "color", doc = " ```no_run")]
/// # use clap::{App, ColorChoice};
/// App::new("myprog")
/// .color(ColorChoice::Auto)
/// .get_matches();
/// ```
Auto,
/// Enables colored output regardless of whether or not the output is going to a terminal/TTY.
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms).
///
/// # Examples
///
#[cfg_attr(not(feature = "color"), doc = " ```ignore")]
#[cfg_attr(feature = "color", doc = " ```no_run")]
/// # use clap::{App, ColorChoice};
/// App::new("myprog")
/// .color(ColorChoice::Always)
/// .get_matches();
/// ```
Always,
/// Disables colored output no matter if the output is going to a terminal/TTY, or not.
///
/// # Platform Specific
///
/// This setting only applies to Unix, Linux, and macOS (i.e. non-Windows platforms)
///
/// # Examples
///
#[cfg_attr(not(feature = "color"), doc = " ```ignore")]
#[cfg_attr(feature = "color", doc = " ```no_run")]
/// # use clap::{App, ColorChoice};
/// App::new("myprog")
/// .color(ColorChoice::Never)
/// .get_matches();
/// ```
Never,
}
impl Default for ColorChoice {
fn default() -> Self {
Self::Auto
}
}
#[cfg(feature = "color")]
pub(crate) use termcolor::Color;
#[cfg(not(feature = "color"))]
#[derive(Debug)]
pub(crate) enum Color {
Green,
Yellow,
Red,
}

View file

@ -12,11 +12,7 @@ pub use self::fnv::Key;
pub(crate) use self::str_to_bool::str_to_bool;
pub(crate) use self::{graph::ChildGraph, id::Id};
#[cfg(feature = "color")]
pub(crate) use termcolor;
#[cfg(not(feature = "color"))]
pub(crate) mod termcolor;
pub(crate) mod color;
pub(crate) const SUCCESS_CODE: i32 = 0;
// While sysexists.h defines EX_USAGE as 64, this doesn't seem to be used much in practice but
@ -39,5 +35,6 @@ pub(crate) fn safe_exit(code: i32) -> ! {
pub(crate) fn eq_ignore_case(left: &str, right: &str) -> bool {
left.eq_ignore_ascii_case(right)
}
#[cfg(feature = "unicode")]
pub(crate) use unicase::eq as eq_ignore_case;

View file

@ -1,13 +0,0 @@
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum ColorChoice {
Auto,
Always,
Never,
}
#[derive(Debug)]
pub(crate) enum Color {
Green,
Yellow,
Red,
}

View file

@ -2263,13 +2263,11 @@ fn option_usage_order() {
#[test]
fn about_in_subcommands_list() {
let app = App::new("about-in-subcommands-list")
.setting(AppSettings::ColorNever)
.subcommand(
App::new("sub")
.long_about("long about sub")
.about("short about sub"),
);
let app = App::new("about-in-subcommands-list").subcommand(
App::new("sub")
.long_about("long about sub")
.about("short about sub"),
);
assert!(utils::compare_output(
app,

View file

@ -32,7 +32,7 @@ fn basic() {
(version: "0.1")
(about: "tests clap library")
(author: "Kevin K. <kbknapp@gmail.com>")
(@global_setting ColorNever)
(@global_setting UnifiedHelpMessage)
(@arg opt: -o --option +takes_value ... "tests options")
(@arg positional: index(1) "tests positionals")
(@arg flag: -f --flag ... +global "tests flags")