From dc45332d7b2ed839f11da00c244dbed198e547c1 Mon Sep 17 00:00:00 2001 From: Benjamin Sago Date: Wed, 13 Sep 2017 08:51:57 +0100 Subject: [PATCH] Implement file name colouring in {exa,ls}_colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds to the parsing of the LS_COLORS and EXA_COLORS variables so that non-two-letter codes (keys other than things like ‘di’ or ‘ln’ or ‘ex’) will be treated as file name globs, and get used to colour files accordingly. Fixes #116 for good. --- src/options/style.rs | 282 ++++++++++++++++++++++++++++++------ src/output/file_name.rs | 10 ++ xtests/run.sh | 55 ++++--- xtests/themed_compresseds | 5 + xtests/themed_compresseds_r | 5 + xtests/themed_links | 3 + xtests/themed_long | 22 +++ xtests/themed_specials | 3 + xtests/themed_un | 26 ++++ 9 files changed, 351 insertions(+), 60 deletions(-) create mode 100644 xtests/themed_compresseds create mode 100644 xtests/themed_compresseds_r create mode 100644 xtests/themed_links create mode 100644 xtests/themed_long create mode 100644 xtests/themed_specials create mode 100644 xtests/themed_un diff --git a/src/options/style.rs b/src/options/style.rs index 061fef2..78970f0 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,32 +98,103 @@ 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 }) - } - - let classify = Classify::deduce(matches)?; - let exts = if colours.colourful { Box::new(FileExtensions) as Box<_> } - else { Box::new(NoFileColours) as Box<_> }; - } } +/// 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; + } + + 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