Follow xparsecolor spec in escape sequences

Escape sequences in xterm are parsed according to xparsecolor.
xparsecolor supports 1, 2, 3, and 4 digit hex colors.
Previously, only 2 digits were supported.

This also fixes a bug where "fX" was parsed as "0xf", where X is an invalid character.

The response to a request for fg/bg must be a valid escape sequence.
The current response uses 4-digit hex, which was previously invalid.
This commit is contained in:
rbong 2019-08-19 15:56:27 -04:00 committed by Christian Duerr
parent a8692983f5
commit 629ea247cd
2 changed files with 71 additions and 59 deletions

View File

@ -65,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
and `debug.ref_test`
- Select until next matching bracket when double-clicking a bracket
- Added foreground/background escape code request sequences
- Escape sequences now support 1, 3, and 4 digit hex colors
### Changed

View File

@ -24,63 +24,56 @@ use vte;
use crate::term::color::Rgb;
// Parse color arguments
//
// Expect that color argument looks like "rgb:xx/xx/xx" or "#xxxxxx"
// Parse colors in XParseColor format
fn xparse_color(color: &[u8]) -> Option<Rgb> {
if !color.is_empty() && color[0] == b'#' {
let len = color.len().saturating_sub(1);
parse_legacy_color(&color[1..len])
} else if color.len() >= 4 && &color[..4] == b"rgb:" {
let len = color.len().saturating_sub(1);
parse_rgb_color(&color[4..len])
} else {
None
}
}
// Parse colors in `rgb:r(rrr)/g(ggg)/b(bbb)` format
fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
let mut iter = color.iter();
let colors = str::from_utf8(color).ok()?.split('/').collect::<Vec<_>>();
macro_rules! next {
() => {
iter.next().map(|v| *v as char)
};
if colors.len() != 3 {
return None;
}
macro_rules! parse_hex {
() => {{
let mut digit: u8 = 0;
let next = next!().and_then(|v| v.to_digit(16));
if let Some(value) = next {
digit = value as u8;
}
// Scale values instead of filling with `0`s
let scale = |input: &str| {
let max = u32::pow(16, input.len() as u32) - 1;
let value = u32::from_str_radix(input, 16).ok()?;
Some((255 * value / max) as u8)
};
let next = next!().and_then(|v| v.to_digit(16));
if let Some(value) = next {
digit <<= 4;
digit += value as u8;
}
digit
}};
}
Some(Rgb {
r: scale(colors[0])?,
g: scale(colors[1])?,
b: scale(colors[2])?,
})
}
match next!() {
Some('r') => {
if next!() != Some('g') {
return None;
}
if next!() != Some('b') {
return None;
}
if next!() != Some(':') {
return None;
}
// Parse colors in `#r(rrr)g(ggg)b(bbb)` format
fn parse_legacy_color(color: &[u8]) -> Option<Rgb> {
let item_len = color.len() / 3;
let r = parse_hex!();
let val = next!();
if val != Some('/') {
return None;
}
let g = parse_hex!();
if next!() != Some('/') {
return None;
}
let b = parse_hex!();
// Truncate/Fill to two byte precision
let color_from_slice = |slice: &[u8]| {
let col = usize::from_str_radix(str::from_utf8(slice).ok()?, 16).ok()? << 4;
Some((col >> (4 * slice.len().saturating_sub(1))) as u8)
};
Some(Rgb { r, g, b })
},
Some('#') => Some(Rgb { r: parse_hex!(), g: parse_hex!(), b: parse_hex!() }),
_ => None,
}
Some(Rgb {
r: color_from_slice(&color[0..item_len])?,
g: color_from_slice(&color[item_len..item_len * 2])?,
b: color_from_slice(&color[item_len * 2..])?,
})
}
fn parse_number(input: &[u8]) -> Option<u8> {
@ -781,7 +774,7 @@ where
if params.len() > 1 && params.len() % 2 != 0 {
for chunk in params[1..].chunks(2) {
let index = parse_number(chunk[0]);
let color = parse_rgb_color(chunk[1]);
let color = xparse_color(chunk[1]);
if let (Some(i), Some(c)) = (index, color) {
self.handler.set_color(i as usize, c);
return;
@ -806,7 +799,7 @@ where
break;
}
if let Some(color) = parse_rgb_color(param) {
if let Some(color) = xparse_color(param) {
self.handler.set_color(index, color);
} else if param == b"?" {
self.handler.dynamic_color_sequence(writer, dynamic_code, index);
@ -1203,7 +1196,7 @@ fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> {
37 => Some(Attr::Foreground(Color::Named(NamedColor::White))),
38 => {
let mut start = 0;
if let Some(color) = parse_color(&parameters[i..], &mut start) {
if let Some(color) = parse_sgr_color(&parameters[i..], &mut start) {
i += start;
Some(Attr::Foreground(color))
} else {
@ -1221,7 +1214,7 @@ fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> {
47 => Some(Attr::Background(Color::Named(NamedColor::White))),
48 => {
let mut start = 0;
if let Some(color) = parse_color(&parameters[i..], &mut start) {
if let Some(color) = parse_sgr_color(&parameters[i..], &mut start) {
i += start;
Some(Attr::Background(color))
} else {
@ -1256,7 +1249,7 @@ fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> {
}
/// Parse a color specifier from list of attributes
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
fn parse_sgr_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
if attrs.len() < 2 {
return None;
}
@ -1456,7 +1449,7 @@ pub mod C1 {
#[cfg(test)]
mod tests {
use super::{
parse_number, parse_rgb_color, Attr, CharsetIndex, Color, Handler, Processor,
parse_number, xparse_color, Attr, CharsetIndex, Color, Handler, Processor,
StandardCharset, TermInfo,
};
use crate::index::{Column, Line};
@ -1623,13 +1616,31 @@ mod tests {
}
#[test]
fn parse_valid_rgb_color() {
assert_eq!(parse_rgb_color(b"rgb:11/aa/ff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
fn parse_valid_rgb_colors() {
assert_eq!(xparse_color(b"rgb:f/e/d\x07"), Some(Rgb { r: 0xff, g: 0xee, b: 0xdd }));
assert_eq!(xparse_color(b"rgb:11/aa/ff\x07"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
assert_eq!(xparse_color(b"rgb:f/ed1/cb23\x07"), Some(Rgb { r: 0xff, g: 0xec, b: 0xca }));
assert_eq!(xparse_color(b"rgb:ffff/0/0\x07"), Some(Rgb { r: 0xff, g: 0x0, b: 0x0 }));
}
#[test]
fn parse_valid_rgb_color2() {
assert_eq!(parse_rgb_color(b"#11aaff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
fn parse_valid_legacy_rgb_colors() {
assert_eq!(xparse_color(b"#1af\x07"), Some(Rgb { r: 0x10, g: 0xa0, b: 0xf0 }));
assert_eq!(xparse_color(b"#11aaff\x07"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
assert_eq!(xparse_color(b"#110aa0ff0\x07"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
assert_eq!(xparse_color(b"#1100aa00ff00\x07"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
}
#[test]
fn parse_invalid_rgb_colors() {
assert_eq!(xparse_color(b"rgb:0//\x07"), None);
assert_eq!(xparse_color(b"rgb://///\x07"), None);
}
#[test]
fn parse_invalid_legacy_rgb_colors() {
assert_eq!(xparse_color(b"#\x07"), None);
assert_eq!(xparse_color(b"#f\x07"), None);
}
#[test]