mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-05-19 09:40:22 -07:00
termcolor: permit hex colors
This commit adds support for specifying Ansi256 or RGB colors using hexadecimal notation.
This commit is contained in:
parent
09c5b2c4ea
commit
7e5589f07d
@ -696,13 +696,18 @@ the background color for line numbers to yellow:
|
|||||||
|
|
||||||
Extended colors can be used for {value} when the terminal supports ANSI color
|
Extended colors can be used for {value} when the terminal supports ANSI color
|
||||||
sequences. These are specified as either 'x' (256-color) or 'x,x,x' (24-bit
|
sequences. These are specified as either 'x' (256-color) or 'x,x,x' (24-bit
|
||||||
truecolor) where x is a number between 0 and 255 inclusive.
|
truecolor) where x is a number between 0 and 255 inclusive. x may be given as
|
||||||
|
a normal decimal number or a hexadecimal number, which is prefixed by `0x`.
|
||||||
|
|
||||||
For example, the following command will change the match background color to
|
For example, the following command will change the match background color to
|
||||||
that represented by the rgb value (0,128,255):
|
that represented by the rgb value (0,128,255):
|
||||||
|
|
||||||
rg --colors 'match:bg:0,128,255'
|
rg --colors 'match:bg:0,128,255'
|
||||||
|
|
||||||
|
or, equivalently,
|
||||||
|
|
||||||
|
rg --colors 'match:bg:0x0,0x80,0xFF'
|
||||||
|
|
||||||
Note that the the intense and nointense style flags will have no effect when
|
Note that the the intense and nointense style flags will have no effect when
|
||||||
used alongside these extended color codes.
|
used alongside these extended color codes.
|
||||||
");
|
");
|
||||||
|
@ -1293,7 +1293,18 @@ impl ColorSpec {
|
|||||||
/// on Windows using the console. If they are used on Windows, then they are
|
/// on Windows using the console. If they are used on Windows, then they are
|
||||||
/// silently ignored and no colors will be emitted.
|
/// silently ignored and no colors will be emitted.
|
||||||
///
|
///
|
||||||
/// Note that this set may expand over time.
|
/// This set may expand over time.
|
||||||
|
///
|
||||||
|
/// This type has a `FromStr` impl that can parse colors from their human
|
||||||
|
/// readable form. The format is as follows:
|
||||||
|
///
|
||||||
|
/// 1. Any of the explicitly listed colors in English. They are matched
|
||||||
|
/// case insensitively.
|
||||||
|
/// 2. A single 8-bit integer, in either decimal or hexadecimal format.
|
||||||
|
/// 3. A triple of 8-bit integers separated by a comma, where each integer is
|
||||||
|
/// in decimal or hexadecimal format.
|
||||||
|
///
|
||||||
|
/// Hexadecimal numbers are written with a `0x` prefix.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
@ -1311,9 +1322,9 @@ pub enum Color {
|
|||||||
__Nonexhaustive,
|
__Nonexhaustive,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
impl Color {
|
impl Color {
|
||||||
/// Translate this color to a wincolor::Color.
|
/// Translate this color to a wincolor::Color.
|
||||||
|
#[cfg(windows)]
|
||||||
fn to_windows(&self) -> Option<wincolor::Color> {
|
fn to_windows(&self) -> Option<wincolor::Color> {
|
||||||
match *self {
|
match *self {
|
||||||
Color::Black => Some(wincolor::Color::Black),
|
Color::Black => Some(wincolor::Color::Black),
|
||||||
@ -1329,6 +1340,68 @@ impl Color {
|
|||||||
Color::__Nonexhaustive => unreachable!(),
|
Color::__Nonexhaustive => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a numeric color string, either ANSI or RGB.
|
||||||
|
fn from_str_numeric(s: &str) -> Result<Color, ParseColorError> {
|
||||||
|
// The "ansi256" format is a single number (decimal or hex)
|
||||||
|
// corresponding to one of 256 colors.
|
||||||
|
//
|
||||||
|
// The "rgb" format is a triple of numbers (decimal or hex) delimited
|
||||||
|
// by a comma corresponding to one of 256^3 colors.
|
||||||
|
|
||||||
|
fn parse_number(s: &str) -> Option<u8> {
|
||||||
|
use std::u8;
|
||||||
|
|
||||||
|
if s.starts_with("0x") {
|
||||||
|
u8::from_str_radix(&s[2..], 16).ok()
|
||||||
|
} else {
|
||||||
|
u8::from_str_radix(s, 10).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let codes: Vec<&str> = s.split(',').collect();
|
||||||
|
if codes.len() == 1 {
|
||||||
|
if let Some(n) = parse_number(&codes[0]) {
|
||||||
|
Ok(Color::Ansi256(n))
|
||||||
|
} else {
|
||||||
|
if s.chars().all(|c| c.is_digit(16)) {
|
||||||
|
Err(ParseColorError {
|
||||||
|
kind: ParseColorErrorKind::InvalidAnsi256,
|
||||||
|
given: s.to_string(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(ParseColorError {
|
||||||
|
kind: ParseColorErrorKind::InvalidName,
|
||||||
|
given: s.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if codes.len() == 3 {
|
||||||
|
let mut v = vec![];
|
||||||
|
for code in codes {
|
||||||
|
let n = parse_number(code).ok_or_else(|| {
|
||||||
|
ParseColorError {
|
||||||
|
kind: ParseColorErrorKind::InvalidRgb,
|
||||||
|
given: s.to_string(),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
v.push(n);
|
||||||
|
}
|
||||||
|
Ok(Color::Rgb(v[0], v[1], v[2]))
|
||||||
|
} else {
|
||||||
|
Err(if s.contains(",") {
|
||||||
|
ParseColorError {
|
||||||
|
kind: ParseColorErrorKind::InvalidRgb,
|
||||||
|
given: s.to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ParseColorError {
|
||||||
|
kind: ParseColorErrorKind::InvalidName,
|
||||||
|
given: s.to_string(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error from parsing an invalid color specification.
|
/// An error from parsing an invalid color specification.
|
||||||
@ -1373,13 +1446,13 @@ impl fmt::Display for ParseColorError {
|
|||||||
}
|
}
|
||||||
InvalidAnsi256 => {
|
InvalidAnsi256 => {
|
||||||
write!(f, "unrecognized ansi256 color number, \
|
write!(f, "unrecognized ansi256 color number, \
|
||||||
should be '[0-255]', but is '{}'",
|
should be '[0-255]' (or a hex number), but is '{}'",
|
||||||
self.given)
|
self.given)
|
||||||
}
|
}
|
||||||
InvalidRgb => {
|
InvalidRgb => {
|
||||||
write!(f, "unrecognized RGB color triple, \
|
write!(f, "unrecognized RGB color triple, \
|
||||||
should be '[0-255],[0-255],[0-255]', but is '{}'",
|
should be '[0-255],[0-255],[0-255]' (or a hex \
|
||||||
self.given)
|
triple), but is '{}'", self.given)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1398,52 +1471,7 @@ impl FromStr for Color {
|
|||||||
"magenta" => Ok(Color::Magenta),
|
"magenta" => Ok(Color::Magenta),
|
||||||
"yellow" => Ok(Color::Yellow),
|
"yellow" => Ok(Color::Yellow),
|
||||||
"white" => Ok(Color::White),
|
"white" => Ok(Color::White),
|
||||||
_ => {
|
_ => Color::from_str_numeric(s),
|
||||||
// - Ansi256: '[0-255]'
|
|
||||||
// - Rgb: '[0-255],[0-255],[0-255]'
|
|
||||||
let codes: Vec<&str> = s.split(',').collect();
|
|
||||||
if codes.len() == 1 {
|
|
||||||
if let Ok(n) = codes[0].parse::<u8>() {
|
|
||||||
Ok(Color::Ansi256(n))
|
|
||||||
} else {
|
|
||||||
if s.chars().all(|c| c.is_digit(10)) {
|
|
||||||
Err(ParseColorError {
|
|
||||||
kind: ParseColorErrorKind::InvalidAnsi256,
|
|
||||||
given: s.to_string(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(ParseColorError {
|
|
||||||
kind: ParseColorErrorKind::InvalidName,
|
|
||||||
given: s.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if codes.len() == 3 {
|
|
||||||
let mut v = vec![];
|
|
||||||
for code in codes {
|
|
||||||
let n = code.parse::<u8>().map_err(|_| {
|
|
||||||
ParseColorError {
|
|
||||||
kind: ParseColorErrorKind::InvalidRgb,
|
|
||||||
given: s.to_string(),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
v.push(n);
|
|
||||||
}
|
|
||||||
Ok(Color::Rgb(v[0], v[1], v[2]))
|
|
||||||
} else {
|
|
||||||
Err(if s.contains(",") {
|
|
||||||
ParseColorError {
|
|
||||||
kind: ParseColorErrorKind::InvalidRgb,
|
|
||||||
given: s.to_string(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ParseColorError {
|
|
||||||
kind: ParseColorErrorKind::InvalidName,
|
|
||||||
given: s.to_string(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1550,9 +1578,11 @@ mod tests {
|
|||||||
let color = "7".parse::<Color>();
|
let color = "7".parse::<Color>();
|
||||||
assert_eq!(color, Ok(Color::Ansi256(7)));
|
assert_eq!(color, Ok(Color::Ansi256(7)));
|
||||||
|
|
||||||
// In range of standard color codes
|
|
||||||
let color = "32".parse::<Color>();
|
let color = "32".parse::<Color>();
|
||||||
assert_eq!(color, Ok(Color::Ansi256(32)));
|
assert_eq!(color, Ok(Color::Ansi256(32)));
|
||||||
|
|
||||||
|
let color = "0xFF".parse::<Color>();
|
||||||
|
assert_eq!(color, Ok(Color::Ansi256(0xFF)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1571,6 +1601,12 @@ mod tests {
|
|||||||
|
|
||||||
let color = "0,128,255".parse::<Color>();
|
let color = "0,128,255".parse::<Color>();
|
||||||
assert_eq!(color, Ok(Color::Rgb(0, 128, 255)));
|
assert_eq!(color, Ok(Color::Rgb(0, 128, 255)));
|
||||||
|
|
||||||
|
let color = "0x0,0x0,0x0".parse::<Color>();
|
||||||
|
assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
|
||||||
|
|
||||||
|
let color = "0x33,0x66,0xFF".parse::<Color>();
|
||||||
|
assert_eq!(color, Ok(Color::Rgb(0x33, 0x66, 0xFF)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user