Stop collecting ls_colors into a hashmap

LSColors used to be built up from an iterator, and then queried later. But because the resulting HashMap gets queried in serial anyway, we might as well pass in a callback instead, saving the allocation.

This is also technically a little faster because styles that don’t map to anything (like `zz`) are no longer parsed.
This commit is contained in:
Benjamin Sago 2017-08-26 22:33:16 +01:00
parent dd838c2dc1
commit 504a0dd6b7
2 changed files with 82 additions and 86 deletions

View File

@ -76,18 +76,21 @@ impl Colours {
if let Some(lsc) = vars.get(vars::LS_COLORS) { if let Some(lsc) = vars.get(vars::LS_COLORS) {
let lsc = lsc.to_string_lossy(); let lsc = lsc.to_string_lossy();
let lsc = LSColors::parse(lsc.as_ref()); LSColors(lsc.as_ref()).each_pair(|pair| {
match pair.key {
if let Some(c) = lsc.get("di") { colours.filekinds.directory = c; } "di" => colours.filekinds.directory = pair.to_style(),
if let Some(c) = lsc.get("ex") { colours.filekinds.executable = c; } "ex" => colours.filekinds.executable = pair.to_style(),
if let Some(c) = lsc.get("fi") { colours.filekinds.normal = c; } "fi" => colours.filekinds.normal = pair.to_style(),
if let Some(c) = lsc.get("pi") { colours.filekinds.pipe = c; } "pi" => colours.filekinds.pipe = pair.to_style(),
if let Some(c) = lsc.get("so") { colours.filekinds.socket = c; } "so" => colours.filekinds.socket = pair.to_style(),
if let Some(c) = lsc.get("bd") { colours.filekinds.block_device = c; } "bd" => colours.filekinds.block_device = pair.to_style(),
if let Some(c) = lsc.get("cd") { colours.filekinds.char_device = c; } "cd" => colours.filekinds.char_device = pair.to_style(),
if let Some(c) = lsc.get("ln") { colours.filekinds.symlink = c; } "ln" => colours.filekinds.symlink = pair.to_style(),
if let Some(c) = lsc.get("or") { colours.broken_arrow = c; } "or" => colours.broken_arrow = pair.to_style(),
if let Some(c) = lsc.get("mi") { colours.broken_filename = c; } "mi" => colours.broken_filename = pair.to_style(),
_ => {/* dont change anything */},
}
})
} }
Ok(colours) Ok(colours)

View File

@ -1,69 +1,66 @@
#![allow(dead_code)] use std::ops::FnMut;
use std::collections::HashMap;
use ansi_term::Style; use ansi_term::Style;
use ansi_term::Colour::*; use ansi_term::Colour::*;
pub struct LSColors<'var> { pub struct LSColors<'var>(pub &'var str);
contents: HashMap<&'var str, &'var str>
}
impl<'var> LSColors<'var> { impl<'var> LSColors<'var> {
pub fn parse(input: &'var str) -> LSColors<'var> { pub fn each_pair<C>(&mut self, mut callback: C) where C: FnMut(Pair<'var>) -> () {
let contents = input.split(":") for next in self.0.split(":") {
.flat_map(|mapping| { let bits = next.split("=")
.take(3)
.collect::<Vec<_>>();
let bits = mapping.split("=") if bits.len() == 2 && !bits[0].is_empty() && !bits[1].is_empty() {
.take(3) callback(Pair { key: bits[0], value: bits[1] });
.collect::<Vec<_>>(); }
}
if bits.len() != 2 || bits[0].is_empty() || bits[1].is_empty() { None }
else { Some( (bits[0], bits[1]) ) }
}).collect();
LSColors { contents }
}
pub fn get(&self, facet_name: &str) -> Option<Style> {
self.contents.get(facet_name).map(ansi_to_style)
} }
} }
fn ansi_to_style(ansi: &&str) -> Style { pub struct Pair<'var> {
let mut style = Style::default(); pub key: &'var str,
pub value: &'var str,
}
for num in ansi.split(";") { impl<'var> Pair<'var> {
match num { pub fn to_style(&self) -> Style {
let mut style = Style::default();
// Bold and italic for num in self.value.split(";") {
"1" => style = style.bold(), match num {
"4" => style = style.underline(),
// Foreground colours // Bold and italic
"30" => style = style.fg(Black), "1" => style = style.bold(),
"31" => style = style.fg(Red), "4" => style = style.underline(),
"32" => style = style.fg(Green),
"33" => style = style.fg(Yellow),
"34" => style = style.fg(Blue),
"35" => style = style.fg(Purple),
"36" => style = style.fg(Cyan),
"37" => style = style.fg(White),
// Background colours // Foreground colours
"40" => style = style.on(Black), "30" => style = style.fg(Black),
"41" => style = style.on(Red), "31" => style = style.fg(Red),
"42" => style = style.on(Green), "32" => style = style.fg(Green),
"43" => style = style.on(Yellow), "33" => style = style.fg(Yellow),
"44" => style = style.on(Blue), "34" => style = style.fg(Blue),
"45" => style = style.on(Purple), "35" => style = style.fg(Purple),
"46" => style = style.on(Cyan), "36" => style = style.fg(Cyan),
"47" => style = style.on(White), "37" => style = style.fg(White),
_ => {/* ignore the error and do nothing */},
// Background colours
"40" => style = style.on(Black),
"41" => style = style.on(Red),
"42" => style = style.on(Green),
"43" => style = style.on(Yellow),
"44" => style = style.on(Blue),
"45" => style = style.on(Purple),
"46" => style = style.on(Cyan),
"47" => style = style.on(White),
_ => {/* ignore the error and do nothing */},
}
} }
}
style style
}
} }
@ -76,7 +73,7 @@ mod ansi_test {
($name:ident: $input:expr => $result:expr) => { ($name:ident: $input:expr => $result:expr) => {
#[test] #[test]
fn $name() { fn $name() {
assert_eq!(ansi_to_style(&$input), $result); assert_eq!(Pair { key: "", value: $input }.to_style(), $result);
} }
}; };
} }
@ -105,44 +102,40 @@ mod test {
use super::*; use super::*;
macro_rules! test { macro_rules! test {
($name:ident: $input:expr, $facet:expr => $result:expr) => { ($name:ident: $input:expr => $result:expr) => {
#[test] #[test]
fn $name() { fn $name() {
let lsc = LSColors::parse($input); let mut lscs = Vec::new();
assert_eq!(lsc.get($facet), $result.into()); LSColors($input).each_pair(|p| lscs.push( (p.key.clone(), p.to_style()) ));
assert_eq!(lsc.get(""), None); assert_eq!(lscs, $result.to_vec());
} }
}; };
} }
// Bad parses // Bad parses
test!(empty: "", "di" => None); test!(empty: "" => []);
test!(jibber: "blah", "di" => None); test!(jibber: "blah" => []);
test!(equals: "=", "di" => None); test!(equals: "=" => []);
test!(starts: "=di", "di" => None); test!(starts: "=di" => []);
test!(ends: "id=", "id" => None); test!(ends: "id=" => []);
// Foreground colours // Foreground colours
test!(red: "di=31", "di" => Red.normal()); test!(green: "cb=32" => [ ("cb", Green.normal()) ]);
test!(green: "cb=32", "cb" => Green.normal()); test!(red: "di=31" => [ ("di", Red.normal()) ]);
test!(blue: "la=34", "la" => Blue.normal()); test!(blue: "la=34" => [ ("la", Blue.normal()) ]);
// Background colours // Background colours
test!(yellow: "do=43", "do" => Style::default().on(Yellow)); test!(yellow: "do=43" => [ ("do", Style::default().on(Yellow)) ]);
test!(purple: "re=45", "re" => Style::default().on(Purple)); test!(purple: "re=45" => [ ("re", Style::default().on(Purple)) ]);
test!(cyan: "mi=46", "mi" => Style::default().on(Cyan)); test!(cyan: "mi=46" => [ ("mi", Style::default().on(Cyan)) ]);
// Bold and underline // Bold and underline
test!(bold: "fa=1", "fa" => Style::default().bold()); test!(bold: "fa=1" => [ ("fa", Style::default().bold()) ]);
test!(under: "so=4", "so" => Style::default().underline()); test!(under: "so=4" => [ ("so", Style::default().underline()) ]);
test!(both: "la=1;4", "la" => Style::default().bold().underline()); test!(both: "la=1;4" => [ ("la", Style::default().bold().underline()) ]);
// More and many // More and many
test!(more_1: "me=43;21;55;34:yu=1;4;1", "me" => Blue.on(Yellow)); test!(more: "me=43;21;55;34:yu=1;4;1" => [ ("me", Blue.on(Yellow)), ("yu", Style::default().bold().underline()) ]);
test!(more_2: "me=43;21;55;34:yu=1;4;1", "yu" => Style::default().bold().underline()); test!(many: "red=31:green=32:blue=34" => [ ("red", Red.normal()), ("green", Green.normal()), ("blue", Blue.normal()) ]);
test!(many_1: "red=31:green=32:blue=34", "red" => Red.normal());
test!(many_2: "red=31:green=32:blue=34", "green" => Green.normal());
test!(many_3: "red=31:green=32:blue=34", "blue" => Blue.normal());
} }