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
|
||||
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
|
||||
that represented by the rgb value (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
|
||||
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
|
||||
/// 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)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Color {
|
||||
@ -1311,9 +1322,9 @@ pub enum Color {
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl Color {
|
||||
/// Translate this color to a wincolor::Color.
|
||||
#[cfg(windows)]
|
||||
fn to_windows(&self) -> Option<wincolor::Color> {
|
||||
match *self {
|
||||
Color::Black => Some(wincolor::Color::Black),
|
||||
@ -1329,6 +1340,68 @@ impl Color {
|
||||
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.
|
||||
@ -1373,13 +1446,13 @@ impl fmt::Display for ParseColorError {
|
||||
}
|
||||
InvalidAnsi256 => {
|
||||
write!(f, "unrecognized ansi256 color number, \
|
||||
should be '[0-255]', but is '{}'",
|
||||
should be '[0-255]' (or a hex number), but is '{}'",
|
||||
self.given)
|
||||
}
|
||||
InvalidRgb => {
|
||||
write!(f, "unrecognized RGB color triple, \
|
||||
should be '[0-255],[0-255],[0-255]', but is '{}'",
|
||||
self.given)
|
||||
should be '[0-255],[0-255],[0-255]' (or a hex \
|
||||
triple), but is '{}'", self.given)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1398,52 +1471,7 @@ impl FromStr for Color {
|
||||
"magenta" => Ok(Color::Magenta),
|
||||
"yellow" => Ok(Color::Yellow),
|
||||
"white" => Ok(Color::White),
|
||||
_ => {
|
||||
// - 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(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => Color::from_str_numeric(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1550,9 +1578,11 @@ mod tests {
|
||||
let color = "7".parse::<Color>();
|
||||
assert_eq!(color, Ok(Color::Ansi256(7)));
|
||||
|
||||
// In range of standard color codes
|
||||
let color = "32".parse::<Color>();
|
||||
assert_eq!(color, Ok(Color::Ansi256(32)));
|
||||
|
||||
let color = "0xFF".parse::<Color>();
|
||||
assert_eq!(color, Ok(Color::Ansi256(0xFF)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1571,6 +1601,12 @@ mod tests {
|
||||
|
||||
let color = "0,128,255".parse::<Color>();
|
||||
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]
|
||||
|
Loading…
x
Reference in New Issue
Block a user