diff --git a/Cargo.lock b/Cargo.lock index 101a285..cbc3e01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,10 +6,10 @@ dependencies = [ "datetime 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "git2 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "git2 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", "locale 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "natord 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -53,10 +53,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cmake" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -78,7 +78,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iso8601 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", "locale 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -95,7 +95,7 @@ dependencies = [ [[package]] name = "gcc" -version = "0.3.51" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -105,12 +105,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "git2" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", - "libgit2-sys 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -153,17 +153,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.29" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libgit2-sys" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", - "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -173,8 +173,8 @@ name = "libz-sys" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -184,7 +184,7 @@ name = "locale" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -219,7 +219,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -303,7 +303,7 @@ name = "num_cpus" version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -337,7 +337,7 @@ name = "rand" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", "magenta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -382,7 +382,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -392,7 +392,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -436,7 +436,7 @@ name = "users" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -474,21 +474,21 @@ dependencies = [ "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" -"checksum cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ebbb35d3dc9cd09497168f33de1acb79b265d350ab0ac34133b98f8509af1f" +"checksum cmake 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "0c8a6541a55bcd72d3de4faee2d101a5a66df29790282c7f797082a7228a9b3d" "checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" "checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" "checksum datetime 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2d425bf1f6bbd57cf833081c1e60ac294fd74e7edd66acc91c3fca2e496bcee9" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" -"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a" +"checksum gcc 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)" = "e8310f7e9c890398b0e80e301c4f474e9918d2b27fca8f48486ca775fa9ffc5a" "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685" -"checksum git2 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa01936ac96555c083c0e8553f672616274408d9d3fc5b8696603fbf63ff43ee" +"checksum git2 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1c0203d653f4140241da0c1375a404f0a397249ec818cd2076c6280c50f6fa" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum iso8601 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "11dc464f8c6f17595d191447c9c6559298b2d023d6f846a4a23ac7ea3c46c477" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" -"checksum libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "8a014d9226c2cc402676fbe9ea2e15dd5222cd1dd57f576b5b283178c944a264" -"checksum libgit2-sys 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "0f1641ccb55181967a3e5ee4ae2911c0563492f016383ea67a27886181de088c" +"checksum libc 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)" = "2370ca07ec338939e356443dac2296f581453c35fe1e3a3ed06023c49435f915" +"checksum libgit2-sys 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "c00f6e5bc3fb2b5f87e75e8d0fd4ae6720d55f3ee23d389b7c6cae30f8db8db1" "checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8" "checksum locale 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5fdbe492a9c0238da900a1165c42fc5067161ce292678a6fe80921f30fe307fd" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" diff --git a/src/options/colours.rs b/src/options/colours.rs deleted file mode 100644 index cbea537..0000000 --- a/src/options/colours.rs +++ /dev/null @@ -1,287 +0,0 @@ -use style::Colours; - -use options::{flags, Vars, Misfire}; -use options::parser::MatchedFlags; - - - -/// Under what circumstances we should display coloured, rather than plain, -/// output to the terminal. -/// -/// By default, we want to display the colours when stdout can display them. -/// Turning them on when output is going to, say, a pipe, would make programs -/// such as `grep` or `more` not work properly. So the `Automatic` mode does -/// this check and only displays colours when they can be truly appreciated. -#[derive(PartialEq, Debug)] -enum TerminalColours { - - /// Display them even when output isn’t going to a terminal. - Always, - - /// Display them when output is going to a terminal, but not otherwise. - Automatic, - - /// Never display them, even when output is going to a terminal. - Never, -} - -impl Default for TerminalColours { - fn default() -> TerminalColours { - TerminalColours::Automatic - } -} - -const COLOURS: &[&str] = &["always", "auto", "never"]; - -impl TerminalColours { - - /// Determine which terminal colour conditions to use. - fn deduce(matches: &MatchedFlags) -> Result { - - let word = match matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? { - Some(w) => w, - None => return Ok(TerminalColours::default()), - }; - - if word == "always" { - Ok(TerminalColours::Always) - } - else if word == "auto" || word == "automatic" { - Ok(TerminalColours::Automatic) - } - else if word == "never" { - Ok(TerminalColours::Never) - } - else { - Err(Misfire::bad_argument(&flags::COLOR, word, COLOURS)) - } - } -} - - -impl Colours { - pub fn deduce(matches: &MatchedFlags, vars: &V, widther: TW) -> Result - where TW: Fn() -> Option, V: Vars { - use self::TerminalColours::*; - use style::LSColors; - use options::vars; - - let tc = TerminalColours::deduce(matches)?; - if tc == Never || (tc == Automatic && widther().is_none()) { - return Ok(Colours::plain()); - } - - 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)); - } - - 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)); - } - - Ok(colours) - } -} - - -#[cfg(test)] -mod terminal_test { - use super::*; - use std::ffi::OsString; - use options::flags; - use options::parser::{Flag, Arg}; - - use options::test::parse_for_test; - use options::test::Strictnesses::*; - - pub fn os(input: &'static str) -> OsString { - let mut os = OsString::new(); - os.push(input); - os - } - - static TEST_ARGS: &[&Arg] = &[ &flags::COLOR, &flags::COLOUR ]; - - macro_rules! test { - ($name:ident: $inputs:expr; $stricts:expr => $result:expr) => { - #[test] - fn $name() { - for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| TerminalColours::deduce(mf)) { - assert_eq!(result, $result); - } - } - }; - - ($name:ident: $inputs:expr; $stricts:expr => err $result:expr) => { - #[test] - fn $name() { - for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| TerminalColours::deduce(mf)) { - assert_eq!(result.unwrap_err(), $result); - } - } - }; - } - - - // Default - test!(empty: []; Both => Ok(TerminalColours::default())); - - // --colour - test!(u_always: ["--colour=always"]; Both => Ok(TerminalColours::Always)); - test!(u_auto: ["--colour", "auto"]; Both => Ok(TerminalColours::Automatic)); - test!(u_never: ["--colour=never"]; Both => Ok(TerminalColours::Never)); - - // --color - test!(no_u_always: ["--color", "always"]; Both => Ok(TerminalColours::Always)); - test!(no_u_auto: ["--color=auto"]; Both => Ok(TerminalColours::Automatic)); - test!(no_u_never: ["--color", "never"]; Both => Ok(TerminalColours::Never)); - - // Errors - test!(no_u_error: ["--color=upstream"]; Both => err Misfire::bad_argument(&flags::COLOR, &os("upstream"), super::COLOURS)); // the error is for --color - test!(u_error: ["--colour=lovers"]; Both => err Misfire::bad_argument(&flags::COLOR, &os("lovers"), super::COLOURS)); // and so is this one! - - // Overriding - test!(overridden_1: ["--colour=auto", "--colour=never"]; Last => Ok(TerminalColours::Never)); - test!(overridden_2: ["--color=auto", "--colour=never"]; Last => Ok(TerminalColours::Never)); - test!(overridden_3: ["--colour=auto", "--color=never"]; Last => Ok(TerminalColours::Never)); - test!(overridden_4: ["--color=auto", "--color=never"]; Last => Ok(TerminalColours::Never)); - - test!(overridden_5: ["--colour=auto", "--colour=never"]; Complain => err Misfire::Duplicate(Flag::Long("colour"), Flag::Long("colour"))); - test!(overridden_6: ["--color=auto", "--colour=never"]; Complain => err Misfire::Duplicate(Flag::Long("color"), Flag::Long("colour"))); - test!(overridden_7: ["--colour=auto", "--color=never"]; Complain => err Misfire::Duplicate(Flag::Long("colour"), Flag::Long("color"))); - test!(overridden_8: ["--color=auto", "--color=never"]; Complain => err Misfire::Duplicate(Flag::Long("color"), Flag::Long("color"))); -} - - -#[cfg(test)] -mod colour_test { - use super::*; - use options::flags; - use options::parser::{Flag, Arg}; - - use options::test::parse_for_test; - use options::test::Strictnesses::*; - - static TEST_ARGS: &[&Arg] = &[ &flags::COLOR, &flags::COLOUR, - &flags::COLOR_SCALE, &flags::COLOUR_SCALE ]; - - macro_rules! test { - ($name:ident: $inputs:expr, $widther:expr; $stricts:expr => $result:expr) => { - #[test] - fn $name() { - for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| Colours::deduce(mf, &None, &$widther)) { - assert_eq!(result, $result); - } - } - }; - - ($name:ident: $inputs:expr, $widther:expr; $stricts:expr => err $result:expr) => { - #[test] - fn $name() { - for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| Colours::deduce(mf, &None, &$widther)) { - assert_eq!(result.unwrap_err(), $result); - } - } - }; - - ($name:ident: $inputs:expr, $widther:expr; $stricts:expr => like $pat:pat) => { - #[test] - fn $name() { - for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| Colours::deduce(mf, &None, &$widther)) { - println!("Testing {:?}", result); - match result { - $pat => assert!(true), - _ => assert!(false), - } - } - } - }; - } - - test!(width_1: ["--colour", "always"], || Some(80); Both => Ok(Colours::colourful(false))); - test!(width_2: ["--colour", "always"], || None; Both => Ok(Colours::colourful(false))); - test!(width_3: ["--colour", "never"], || Some(80); Both => Ok(Colours::plain())); - test!(width_4: ["--colour", "never"], || None; Both => Ok(Colours::plain())); - test!(width_5: ["--colour", "automatic"], || Some(80); Both => Ok(Colours::colourful(false))); - test!(width_6: ["--colour", "automatic"], || None; Both => Ok(Colours::plain())); - test!(width_7: [], || Some(80); Both => Ok(Colours::colourful(false))); - test!(width_8: [], || None; Both => Ok(Colours::plain())); - - test!(scale_1: ["--color=always", "--color-scale", "--colour-scale"], || None; Last => like Ok(Colours { scale: true, .. })); - test!(scale_2: ["--color=always", "--color-scale", ], || None; Last => like Ok(Colours { scale: true, .. })); - test!(scale_3: ["--color=always", "--colour-scale"], || None; Last => like Ok(Colours { scale: true, .. })); - test!(scale_4: ["--color=always", ], || None; Last => like Ok(Colours { scale: false, .. })); - - test!(scale_5: ["--color=always", "--color-scale", "--colour-scale"], || None; Complain => err Misfire::Duplicate(Flag::Long("color-scale"), Flag::Long("colour-scale"))); - test!(scale_6: ["--color=always", "--color-scale", ], || None; Complain => like Ok(Colours { scale: true, .. })); - test!(scale_7: ["--color=always", "--colour-scale"], || None; Complain => like Ok(Colours { scale: true, .. })); - test!(scale_8: ["--color=always", ], || None; Complain => like Ok(Colours { scale: false, .. })); -} - - - -#[cfg(test)] -mod customs_test { - use std::ffi::OsString; - - use super::*; - use options::Vars; - use options::test::parse_for_test; - use options::test::Strictnesses::Both; - - use ansi_term::Colour::*; - - macro_rules! test { - ($name:ident: ls $ls:expr, exa $exa:expr => $resulter:expr) => { - #[test] - fn $name() { - let mut c = Colours::colourful(false); - $resulter(&mut c); - - let vars = MockVars { ls: $ls, exa: $exa }; - - for result in parse_for_test(&[], &[], Both, |mf| Colours::deduce(mf, &vars, || Some(80))) { - assert_eq!(result.as_ref(), Ok(&c)); - } - } - }; - } - - struct MockVars { - ls: &'static str, - exa: &'static str, - } - - // Test impl that just returns the value it has. - impl Vars for MockVars { - fn get(&self, name: &'static str) -> Option { - use options::vars; - - if name == vars::LS_COLORS && !self.ls.is_empty() { - OsString::from(self.ls.clone()).into() - } - else if name == vars::EXA_COLORS && !self.exa.is_empty() { - OsString::from(self.exa.clone()).into() - } - else { - None - } - } - } - - test!(ls_di: ls "di=31", exa "" => |c: &mut Colours| { c.filekinds.directory = Red.normal(); }); // Directory - test!(ls_ex: ls "ex=32", exa "" => |c: &mut Colours| { c.filekinds.executable = Green.normal(); }); // Executable file - test!(ls_fi: ls "fi=33", exa "" => |c: &mut Colours| { c.filekinds.normal = Yellow.normal(); }); // Regular file - test!(ls_pi: ls "pi=34", exa "" => |c: &mut Colours| { c.filekinds.pipe = Blue.normal(); }); // FIFO - test!(ls_so: ls "so=35", exa "" => |c: &mut Colours| { c.filekinds.socket = Purple.normal(); }); // Socket - test!(ls_bd: ls "bd=36", exa "" => |c: &mut Colours| { c.filekinds.block_device = Cyan.normal(); }); // Block device - test!(ls_cd: ls "cd=35", exa "" => |c: &mut Colours| { c.filekinds.char_device = Purple.normal(); }); // Character device - test!(ls_ln: ls "ln=34", exa "" => |c: &mut Colours| { c.filekinds.symlink = Blue.normal(); }); // Symlink - test!(ls_or: ls "or=33", exa "" => |c: &mut Colours| { c.broken_arrow = Yellow.normal(); }); // Broken link - test!(ls_mi: ls "mi=32", exa "" => |c: &mut Colours| { c.broken_filename = Green.normal(); }); // Broken link target -} diff --git a/src/options/mod.rs b/src/options/mod.rs index 074416d..97366d7 100644 --- a/src/options/mod.rs +++ b/src/options/mod.rs @@ -64,7 +64,7 @@ //! if no aliases are being used! //! //! Finally, this isn’t just useful when options could override each other. -//! Creating an alias `exal=”exa --long --inode --header”` then invoking `exal +//! Creating an alias `exal="exa --long --inode --header"` then invoking `exal //! --grid --long` shouldn’t complain about `--long` being given twice when //! it’s clear what the user wants. @@ -75,7 +75,7 @@ use fs::dir_action::DirAction; use fs::filter::FileFilter; use output::{View, Mode, details, grid_details}; -mod colours; +mod style; mod dir_action; mod filter; mod view; diff --git a/src/options/style.rs b/src/options/style.rs new file mode 100644 index 0000000..78970f0 --- /dev/null +++ b/src/options/style.rs @@ -0,0 +1,528 @@ +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, +/// output to the terminal. +/// +/// By default, we want to display the colours when stdout can display them. +/// Turning them on when output is going to, say, a pipe, would make programs +/// such as `grep` or `more` not work properly. So the `Automatic` mode does +/// this check and only displays colours when they can be truly appreciated. +#[derive(PartialEq, Debug)] +enum TerminalColours { + + /// Display them even when output isn’t going to a terminal. + Always, + + /// Display them when output is going to a terminal, but not otherwise. + Automatic, + + /// Never display them, even when output is going to a terminal. + Never, +} + +impl Default for TerminalColours { + fn default() -> TerminalColours { + TerminalColours::Automatic + } +} + +const COLOURS: &[&str] = &["always", "auto", "never"]; + +impl TerminalColours { + + /// Determine which terminal colour conditions to use. + fn deduce(matches: &MatchedFlags) -> Result { + + let word = match matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? { + Some(w) => w, + None => return Ok(TerminalColours::default()), + }; + + if word == "always" { + Ok(TerminalColours::Always) + } + else if word == "auto" || word == "automatic" { + Ok(TerminalColours::Automatic) + } + else if word == "never" { + Ok(TerminalColours::Never) + } + else { + Err(Misfire::bad_argument(&flags::COLOR, word, COLOURS)) + } + } +} + + +/// **Styles**, which is already an overloaded term, is a pair of view option +/// sets that happen to both be affected by `LS_COLORS` and `EXA_COLORS`. +/// Because it’s better to only iterate through that once, the two are deduced +/// together. +pub struct Styles { + + /// The colours to paint user interface elements, like the date column, + /// and file kinds, such as directories. + pub colours: Colours, + + /// The colours to paint the names of files that match glob patterns + /// (and the classify option). + pub style: FileStyle, +} + +impl Styles { + + #[allow(trivial_casts)] // the "as Box<_>" stuff below warns about this for some reason + pub fn deduce(matches: &MatchedFlags, vars: &V, widther: TW) -> Result + where TW: Fn() -> Option, V: Vars { + use self::TerminalColours::*; + use info::filetype::FileExtensions; + 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 { + colours: Colours::plain(), + style: FileStyle { classify, exts: Box::new(NoFileColours) }, + }); + } + + // 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()); + + 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<_>, + }; + + 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; + } + + 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