From dc2009aef4d71ddb42dffb0fe5f6e62540b3f4cd Mon Sep 17 00:00:00 2001 From: Benjamin Sago Date: Mon, 8 Oct 2018 03:59:51 +0100 Subject: [PATCH] Parse more ANSI codes Including more styles and 24-bit foregrounds and backgrounds. Also a comment about how this is kicking the can down the road. Fixes #347, mostly. --- src/style/lsc.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/style/lsc.rs b/src/style/lsc.rs index aace6dd..b3abba3 100644 --- a/src/style/lsc.rs +++ b/src/style/lsc.rs @@ -4,6 +4,24 @@ use ansi_term::{Colour, Style}; use ansi_term::Colour::*; +// Parsing the LS_COLORS environment variable into a map of names to Style values. +// +// This is sitting around undocumented at the moment because it’s a feature +// that should really be unnecessary! exa highlights its output by creating a +// theme of one Style value per part of the interface that can be coloured, +// then reading styles from that theme. The LS_COLORS variable, on the other +// hand, can contain arbitrary characters that ls is supposed to add to the +// output, without needing to know what they actually do. This puts exa in the +// annoying position of having to parse the ANSI escape codes _back_ into +// Style values before it’s able to use them. Doing this has a lot of +// downsides: if a new terminal feature is added with its own code, exa won’t +// be able to use this without explicit support for parsing the feature, while +// ls would not even need to know it existed. And there are some edge cases in +// ANSI codes, where terminals would accept codes exa is strict about it. It’s +// just not worth doing, and there should really be a way to just use slices +// of the LS_COLORS string without having to parse them. + + pub struct LSColors<'var>(pub &'var str); impl<'var> LSColors<'var> { @@ -37,6 +55,28 @@ where I: Iterator { } } } + Some(&"2") => { + let _2 = iter.next(); + if let Some(hexes) = iter.next() { + // Some terminals support R:G:B instead of R;G;B + // but this clashes with splitting on ':' in each_pair above. + /*if hexes.contains(':') { + let rgb = hexes.splitn(3, ':').collect::>(); + if rgb.len() != 3 { + return None; + } + else if let (Ok(r), Ok(g), Ok(b)) = (rgb[0].parse(), rgb[1].parse(), rgb[2].parse()) { + return Some(RGB(r, g, b)); + } + }*/ + + if let (Some(r), Some(g), Some(b)) = (hexes.parse().ok(), + iter.next().and_then(|s| s.parse().ok()), + iter.next().and_then(|s| s.parse().ok())) { + return Some(RGB(r, g, b)); + } + } + } _ => {}, } @@ -53,7 +93,14 @@ impl<'var> Pair<'var> { // Bold and italic "1" => style = style.bold(), + "2" => style = style.dimmed(), + "3" => style = style.italic(), "4" => style = style.underline(), + "5" => style = style.blink(), + // 6 is supposedly a faster blink + "7" => style = style.reverse(), + "8" => style = style.hidden(), + "9" => style = style.strikethrough(), // Foreground colours "30" => style = style.fg(Black), @@ -126,6 +173,11 @@ mod ansi_test { test!(hibo: "48;5;1;1" => Style::default().on(Fixed(1)).bold()); test!(hiund: "4;48;5;1" => Style::default().on(Fixed(1)).underline()); + test!(rgb: "38;2;255;100;0" => Style::default().fg(RGB(255, 100, 0))); + test!(rgbi: "38;2;255;100;0;3" => Style::default().fg(RGB(255, 100, 0)).italic()); + test!(rgbbg: "48;2;255;100;0" => Style::default().on(RGB(255, 100, 0))); + test!(rgbbi: "48;2;255;100;0;3" => Style::default().on(RGB(255, 100, 0)).italic()); + test!(fgbg: "38;5;121;48;5;212" => Fixed(121).on(Fixed(212))); test!(bgfg: "48;5;121;38;5;212" => Fixed(212).on(Fixed(121))); test!(toohi: "48;5;999" => Style::default());