From 7709762ddd377a99829419e3ae771bb633b516d2 Mon Sep 17 00:00:00 2001
From: Michael Aaron Murphy <mmstickman@gmail.com>
Date: Wed, 16 Aug 2017 19:41:19 -0400
Subject: [PATCH] Enable setting multiple color parameters

Try the following out in the prompt:

echo ${color::magenta,whitebg}Magenta text with a white background.
---
 src/parser/statement/splitter.rs |   4 +-
 src/shell/colors.rs              | 113 +++++++++++++++++++++++++++++++
 src/shell/mod.rs                 |   1 +
 src/shell/variables.rs           |  56 +--------------
 4 files changed, 118 insertions(+), 56 deletions(-)
 create mode 100644 src/shell/colors.rs

diff --git a/src/parser/statement/splitter.rs b/src/parser/statement/splitter.rs
index 70b71f99..4d637d1f 100644
--- a/src/parser/statement/splitter.rs
+++ b/src/parser/statement/splitter.rs
@@ -94,8 +94,8 @@ impl<'a> Iterator for StatementSplitter<'a> {
                 _ if self.flags.contains(POST_MATHEXPR) => {
                     self.flags -= POST_MATHEXPR;
                 }
-                // [^A-Za-z0-9_:}]
-                0...47 | 59...64 | 91...94 | 96 | 123...124 | 126...127 if self.flags.contains(VBRACE) => {
+                // [^A-Za-z0-9_:,}]
+                0...43 | 45...47 | 59...64 | 91...94 | 96 | 123...124 | 126...127 if self.flags.contains(VBRACE) => {
                     // If we are just ending the braced section continue as normal
                     if error.is_none() {
                         error = Some(StatementError::InvalidCharacter(character as char, self.read))
diff --git a/src/shell/colors.rs b/src/shell/colors.rs
new file mode 100644
index 00000000..127a4f4e
--- /dev/null
+++ b/src/shell/colors.rs
@@ -0,0 +1,113 @@
+use fnv::FnvHashMap;
+
+lazy_static! {
+    static ref ATTRIBUTES: FnvHashMap<&'static str, &'static str> = {
+        let mut map = FnvHashMap::default();
+        map.insert("bold", "1");
+        map.insert("dim", "2");
+        map.insert("underlined", "4");
+        map.insert("blink", "5");
+        map.insert("reverse", "7");
+        map.insert("hidden", "8");
+        map
+    };
+}
+
+lazy_static! {
+    static ref COLORS: FnvHashMap<&'static str, &'static str> = {
+        let mut map = FnvHashMap::default();
+        map.insert("black", "30");
+        map.insert("red", "31");
+        map.insert("green", "32");
+        map.insert("yellow", "33");
+        map.insert("blue", "34");
+        map.insert("magenta", "35");
+        map.insert("cyan", "36");
+        map.insert("light_gray", "37");
+        map.insert("default", "39");
+        map.insert("blackbg", "40");
+        map.insert("dark_gray", "90");
+        map.insert("light_red", "91");
+        map.insert("light_green", "92");
+        map.insert("light_yellow", "93");
+        map.insert("light_blue", "94");
+        map.insert("light_magenta", "95");
+        map.insert("light_cyan", "96");
+        map
+    };
+}
+
+lazy_static! {
+    static ref BG_COLORS: FnvHashMap<&'static str, &'static str> = {
+        let mut map = FnvHashMap::default();
+        map.insert("redbg", "41");
+        map.insert("greenbg", "42");
+        map.insert("yellowbg", "43");
+        map.insert("bluebg", "44");
+        map.insert("magentabg", "45");
+        map.insert("cyanbg", "46");
+        map.insert("light_graybg", "47");
+        map.insert("defaultbg", "49");
+        map.insert("dark_graybg", "100");
+        map.insert("light_redbg", "101");
+        map.insert("light_greenbg", "102");
+        map.insert("light_yellowbg",  "103");
+        map.insert("light_bluebg", "104");
+        map.insert("light_magentabg", "105");
+        map.insert("light_cyanbg", "106");
+        map.insert("whitebg", "107");
+        map
+    };
+}
+
+pub struct Colors {
+    foreground: Option<&'static str>,
+    background: Option<&'static str>,
+    attribute: Option<Vec<&'static str>>
+}
+
+impl Colors {
+    pub fn collect(input: &str) -> Colors {
+        let mut colors = Colors { foreground: None, background: None, attribute: None };
+        for variable in input.split(",") {
+            if variable == "reset" {
+                return Colors { foreground: None, background: None, attribute: Some(vec!["0"]) };
+            } else if let Some(color) = ATTRIBUTES.get(&variable) {
+                let vec_exists = match colors.attribute.as_mut() {
+                    Some(mut vec) => { vec.push(color); true },
+                    None => false
+                };
+
+                if vec_exists {
+                    colors.attribute = Some(vec![color]);
+                }
+            } else if let Some(color) = COLORS.get(&variable) {
+                colors.foreground = Some(*color)
+            } else {
+                match BG_COLORS.get(&variable) {
+                    Some(color) => colors.background = Some(*color),
+                    None => eprintln!("ion: {} is not a valid color", variable),
+                }
+            }
+        }
+        colors
+    }
+
+    pub fn into_string(self) -> Option<String> {
+        let mut output = String::from("\x1b[");
+        if let Some(attr) = self.attribute {
+            output.push_str(&attr.join(";"));
+            match (self.foreground, self.background) {
+                (Some(c), None) | (None, Some(c)) => Some([&output, ";", c, "m"].concat()),
+                (None, None) => Some([&output, "m"].concat()),
+                (Some(fg), Some(bg)) => Some([&output, ";", fg, ";", bg, "m"].concat())
+            }
+        } else {
+            match (self.foreground, self.background) {
+                (Some(c), None) | (None, Some(c)) => Some([&output, c, "m"].concat()),
+                (None, None) => None,
+                (Some(fg), Some(bg)) => Some([&output, fg, ";", bg, "m"].concat())
+            }
+        }
+    }
+}
diff --git a/src/shell/mod.rs b/src/shell/mod.rs
index da734ce6..c2191a2d 100644
--- a/src/shell/mod.rs
+++ b/src/shell/mod.rs
@@ -5,6 +5,7 @@ mod flow;
 mod history;
 mod job;
 mod pipe_exec;
+pub mod colors;
 pub mod directory_stack;
 pub mod flags;
 pub mod plugins;
diff --git a/src/shell/variables.rs b/src/shell/variables.rs
index e08f9690..1e74ca57 100644
--- a/src/shell/variables.rs
+++ b/src/shell/variables.rs
@@ -17,58 +17,12 @@ use sys::getpid;
 
 use sys;
 use sys::variables as self_sys;
+use super::colors::Colors;
 
 lazy_static! {
     static ref STRING_NAMESPACES: FnvHashMap<Identifier, StringNamespace> = namespaces::collect();
 }
 
-lazy_static! {
-    static ref COLORS: FnvHashMap<&'static str, &'static str> = {
-        let mut map = FnvHashMap::default();
-        map.insert("reset", "\x1b[0m");
-        map.insert("bold", "\x1b[1m");
-        map.insert("dim", "\x1b[2m");
-        map.insert("underlined", "\x1b[4m");
-        map.insert("blink", "\x1b[5m");
-        map.insert("reverse", "\x1b[7m");
-        map.insert("hidden", "\x1b[8m");
-        map.insert("black", "\x1b[30m");
-        map.insert("red", "\x1b[31m");
-        map.insert("green", "\x1b[32m");
-        map.insert("yellow", "\x1b[33m");
-        map.insert("blue", "\x1b[34m");
-        map.insert("magenta", "\x1b[35m");
-        map.insert("cyan", "\x1b[36m");
-        map.insert("light_gray", "\x1b[37m");
-        map.insert("default", "\x1b[39m");
-        map.insert("blackbg", "\x1b[40m");
-        map.insert("redbg", "\x1b[41m");
-        map.insert("greenbg", "\x1b[42m");
-        map.insert("yellowbg", "\x1b[43m");
-        map.insert("bluebg", "\x1b[44m");
-        map.insert("magentabg", "\x1b[45m");
-        map.insert("cyanbg", "\x1b[46m");
-        map.insert("light_graybg", "\x1b[47m");
-        map.insert("defaultbg", "\x1b[49m");
-        map.insert("dark_gray", "\x1b[90m");
-        map.insert("light_red", "\x1b[91m");
-        map.insert("light_green", "\x1b[92m");
-        map.insert("light_yellow", "\x1b[93m");
-        map.insert("light_blue", "\x1b[94m");
-        map.insert("light_magenta", "\x1b[95m");
-        map.insert("light_cyan", "\x1b[96m");
-        map.insert("dark_graybg", "\x1b[100m");
-        map.insert("light_redbg", "\x1b[101m");
-        map.insert("light_greenbg", "\x1b[102m");
-        map.insert("light_yellowbg",  "\x1b[103m");
-        map.insert("light_bluebg", "\x1b[104m");
-        map.insert("light_magentabg", "\x1b[105m");
-        map.insert("light_cyanbg", "\x1b[106m");
-        map.insert("white_bg", "\x1b[107m");
-        map
-    };
-}
-
 #[derive(Debug)]
 pub struct Variables {
     pub hashmaps: HashMapVariableContext,
@@ -194,13 +148,7 @@ impl Variables {
         if let Some((name, variable)) = name.find("::").map(|pos| (&name[..pos], &name[pos + 2..])) {
             // If the parsed name contains the '::' pattern, then a namespace was designated. Find it.
             match name {
-                "color" => match COLORS.get(&variable) {
-                    Some(color) => Some((*color).into()),
-                    None => {
-                        eprintln!("ion: {} is not a valid color", name);
-                        None
-                    }
-                }
+                "color" => Colors::collect(variable).into_string(),
                 "env" => env::var(variable).map(Into::into).ok(),
                 _ => {
                     // Attempt to obtain the given namespace from our lazily-generated map of namespaces.
-- 
GitLab