summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Sago <ogham@bsago.me>2017-09-13 10:12:08 +0100
committerBenjamin Sago <ogham@bsago.me>2017-09-13 10:12:08 +0100
commitaa2e3a5d9e64ea5e02e4b5f2c3dc621da3354205 (patch)
treefc050f0cf78aeae5f4944165f57c9e73d234298f
parent265f93f7cd9b4560af64b606075a623b3fdf2105 (diff)
parentdc45332d7b2ed839f11da00c244dbed198e547c1 (diff)
Merge branch 'ls-colors-finally'
-rw-r--r--Cargo.lock56
-rw-r--r--src/options/colours.rs287
-rw-r--r--src/options/mod.rs4
-rw-r--r--src/options/style.rs528
-rw-r--r--src/options/view.rs32
-rw-r--r--src/output/file_name.rs10
-rw-r--r--src/style/colours.rs21
-rw-r--r--src/style/lsc.rs36
-rwxr-xr-xxtests/run.sh55
-rw-r--r--xtests/themed_compresseds5
-rw-r--r--xtests/themed_compresseds_r5
-rw-r--r--xtests/themed_links3
-rw-r--r--xtests/themed_long22
-rw-r--r--xtests/themed_specials3
-rw-r--r--xtests/themed_un26
15 files changed, 713 insertions, 380 deletions
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<TerminalColours, Misfire> {
-
- 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<V, TW>(matches: &MatchedFlags, vars: &V, widther: TW) -> Result<Colours, Misfire>
- where TW: Fn() -> Option<usize>, 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<OsString> {
- 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<TerminalColours, Misfire> {
+
+ 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<V, TW>(matches: &MatchedFlags, vars: &V, widther: TW) -> Result<Self, Misfire>
+ where TW: Fn() -> Option<usize>, 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<V: 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<Style> {
+ self.mappings
+ .iter()
+ .rev()
+ .find(|t| t.0.matches(&file.name))
+ .map (|t| t.1)
+ }
+}
+
+impl ExtensionMappings {
+ fn is_non_empty(&self) -> bool {
+ !self.mappings.is_empty()
+ }
+
+ fn add(&mut self, pattern: glob::Pattern, style: Style) {
+ self.mappings.push((pattern, style))
+ }
+}
+
+
+
+impl Classify {
+ fn deduce(matches: &MatchedFlags) -> Result<Classify, Misfire> {
+ let flagged = matches.has(&flags::CLASSIFY)?;
+
+ Ok(if flagged { Classify::AddFileIndicators }
+ else { Classify::JustFilenames })
+ }
+}
+
+
+
+#[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,
+