diff --git a/src/options/style.rs b/src/options/style.rs index 061fef23..78970f03 100644 --- a/src/options/style.rs +++ b/src/options/style.rs @@ -1,9 +1,11 @@ -use style::Colours; -use output::file_name::{FileStyle, Classify}; +use ansi_term::Style; +use glob; +use fs::File; use options::{flags, Vars, Misfire}; use options::parser::MatchedFlags; - +use output::file_name::{FileStyle, Classify}; +use style::Colours; /// Under what circumstances we should display coloured, rather than plain, @@ -82,12 +84,12 @@ impl Styles { where TW: Fn() -> Option, V: Vars { use self::TerminalColours::*; use info::filetype::FileExtensions; - use style::LSColors; - use options::vars; use output::file_name::NoFileColours; let classify = Classify::deduce(matches)?; + // Before we do anything else, figure out if we need to consider + // custom colours at all let tc = TerminalColours::deduce(matches)?; if tc == Never || (tc == Automatic && widther().is_none()) { return Ok(Styles { @@ -96,33 +98,104 @@ impl Styles { }); } + // Parse the environment variables into colours and extension mappings let scale = matches.has_where(|f| f.matches(&flags::COLOR_SCALE) || f.matches(&flags::COLOUR_SCALE))?; let mut colours = Colours::colourful(scale.is_some()); - if let Some(lsc) = vars.get(vars::LS_COLORS) { - let lsc = lsc.to_string_lossy(); - LSColors(lsc.as_ref()).each_pair(|pair| { - colours.set_ls(&pair); - }); - } + let (exts, use_default_filetypes) = parse_color_vars(vars, &mut colours); + + // Use between 0 and 2 file name highlighters + let exts = match (exts.is_non_empty(), use_default_filetypes) { + (false, false) => Box::new(NoFileColours) as Box<_>, + (false, true) => Box::new(FileExtensions) as Box<_>, + ( true, false) => Box::new(exts) as Box<_>, + ( true, true) => Box::new((exts, FileExtensions)) as Box<_>, + }; - if let Some(exa) = vars.get(vars::EXA_COLORS) { - let exa = exa.to_string_lossy(); - LSColors(exa.as_ref()).each_pair(|pair| { - colours.set_exa(&pair); - }); let style = FileStyle { classify, exts }; Ok(Styles { colours, style }) + } +} + +/// Parse the environment variables into LS_COLORS pairs, putting file glob +/// colours into the `ExtensionMappings` that gets returned, and using the +/// two-character UI codes to modify the mutable `Colours`. +/// +/// Also returns if the EXA_COLORS variable should reset the existing file +/// type mappings or not. The `reset` code needs to be the first one. +fn parse_color_vars(vars: &V, colours: &mut Colours) -> (ExtensionMappings, bool) { + use options::vars; + use style::LSColors; + + let mut exts = ExtensionMappings::default(); + + if let Some(lsc) = vars.get(vars::LS_COLORS) { + let lsc = lsc.to_string_lossy(); + LSColors(lsc.as_ref()).each_pair(|pair| { + if !colours.set_ls(&pair) { + match glob::Pattern::new(pair.key) { + Ok(pat) => exts.add(pat, pair.to_style()), + Err(e) => warn!("Couldn't parse glob pattern {:?}: {}", pair.key, e), + } + } + }); + } + + let mut use_default_filetypes = true; + + if let Some(exa) = vars.get(vars::EXA_COLORS) { + let exa = exa.to_string_lossy(); + + // Is this hacky? Yes. + if exa == "reset" || exa.starts_with("reset:") { + use_default_filetypes = false; } - let classify = Classify::deduce(matches)?; - let exts = if colours.colourful { Box::new(FileExtensions) as Box<_> } - else { Box::new(NoFileColours) as Box<_> }; + LSColors(exa.as_ref()).each_pair(|pair| { + if !colours.set_ls(&pair) && !colours.set_exa(&pair) { + match glob::Pattern::new(pair.key) { + Ok(pat) => exts.add(pat, pair.to_style()), + Err(e) => warn!("Couldn't parse glob pattern {:?}: {}", pair.key, e), + } + }; + }); + } + + (exts, use_default_filetypes) +} + +#[derive(PartialEq, Debug, Default)] +struct ExtensionMappings { + mappings: Vec<(glob::Pattern, Style)> +} + +// Loop through backwards so that colours specified later in the list override +// colours specified earlier, like we do with options and strict mode + +use output::file_name::FileColours; +impl FileColours for ExtensionMappings { + fn colour_file(&self, file: &File) -> Option