Browse Source

256-colour support

Add a Fixed(u8) constructor to Colour, which represents the 256 colours
that some terminals support. This means we can:

- stop using black bold to mean grey, which looks weird on terminals
  that haven't been set up to use it;
- support a *lot* more file type colours.

I'm a little suspicious of how much string allocation is being done in
colours.rs, but that's a problem for another time.
Ben S 11 years ago
parent
commit
64770d0a5a
2 changed files with 60 additions and 12 deletions
  1. 52 8
      colours.rs
  2. 8 4
      file.rs

+ 52 - 8
colours.rs

@@ -1,7 +1,49 @@
+// Provide standard values for the eight standard colours and custom
+// values for up to 256. There are terminals that can do the full RGB
+// spectrum, but for something as simple as discerning file types this
+// doesn't really seem worth it.
+
+// Bear in mind that the first eight (and their bold variants) are
+// user-definable and can look different on different terminals, but
+// the other 256 have their values fixed. Prefer using a fixed grey,
+// such as Fixed(244), to bold black, as bold black looks really weird
+// on some terminals.
+
 pub enum Colour {
-    // These are the standard numeric sequences.
-    // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
-    Black = 30, Red = 31, Green = 32, Yellow = 33, Blue = 34, Purple = 35, Cyan = 36, White = 37,
+    Black, Red, Green, Yellow, Blue, Purple, Cyan, White, Fixed(u8),
+}
+
+// These are the standard numeric sequences.
+// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+
+impl Colour {
+    fn foreground_code(&self) -> String {
+        match *self {
+            Black => "30".to_string(),
+            Red => "31".to_string(),
+            Green => "32".to_string(),
+            Yellow => "33".to_string(),
+            Blue => "34".to_string(),
+            Purple => "35".to_string(),
+            Cyan => "36".to_string(),
+            White => "37".to_string(),
+            Fixed(num) => format!("38;5;{}", num),
+        }
+    }
+
+    fn background_code(&self) -> String {
+        match *self {
+            Black => "40".to_string(),
+            Red => "41".to_string(),
+            Green => "42".to_string(),
+            Yellow => "44".to_string(),
+            Blue => "44".to_string(),
+            Purple => "45".to_string(),
+            Cyan => "46".to_string(),
+            White => "47".to_string(),
+            Fixed(num) => format!("48;5;{}", num),
+        }
+    }        
 }
 
 // There are only three different styles: plain (no formatting), only
@@ -33,12 +75,12 @@ impl Style {
             Style(s) => match s {
                 StyleStruct { foreground, background, bold, underline } => {
                     let bg = match background {
-                        Some(c) => format!("{};", c as int + 10),
+                        Some(c) => format!("{};", c.background_code()),
                         None => "".to_string()
                     };
                     let bo = if bold { "1;" } else { "" };
                     let un = if underline { "4;" } else { "" };
-                    let painted = format!("\x1B[{}{}{}{}m{}\x1B[0m", bo, un, bg, foreground as int, input.to_string());
+                    let painted = format!("\x1B[{}{}{}{}m{}\x1B[0m", bo, un, bg, foreground.foreground_code(), input.to_string());
                     return painted.to_string();
                 }
             }
@@ -73,11 +115,13 @@ impl Style {
 }
 
 impl Colour {
-
     // This is a short-cut so you don't have to use Blue.normal() just
-    // to turn Blue into a Style.
+    // to turn Blue into a Style. Annoyingly, this means that Blue and
+    // Blue.normal() aren't of the same type, but this hasn't been an
+    // issue so far.
+    
     pub fn paint(&self, input: &str) -> String {
-        let re = format!("\x1B[{}m{}\x1B[0m", *self as int, input);
+        let re = format!("\x1B[{}m{}\x1B[0m", self.foreground_code(), input);
         return re.to_string();
     }
 

+ 8 - 4
file.rs

@@ -1,7 +1,7 @@
 use std::io::fs;
 use std::io;
 
-use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan};
+use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan, Fixed};
 use column::{Column, Permissions, FileName, FileSize, User, Group};
 use format::{format_metric_bytes, format_IEC_bytes};
 use unix::{get_user_name, get_group_name};
@@ -66,6 +66,10 @@ impl<'a> File<'a> {
         self.name.starts_with(".")
     }
 
+    fn is_tmpfile(&self) -> bool {
+        self.name.ends_with("~") || (self.name.starts_with("#") && self.name.ends_with("#"))
+    }
+
     pub fn display(&self, column: &Column) -> String {
         match *column {
             Permissions => self.permissions_string(),
@@ -112,13 +116,13 @@ impl<'a> File<'a> {
 
     fn file_colour(&self) -> Style {
         if self.stat.kind == io::TypeDirectory {
-            Blue.normal()
+            Blue.bold()
         }
         else if self.stat.perm.contains(io::UserExecute) {
             Green.bold()
         }
-        else if self.name.ends_with("~") {
-            Black.bold()
+        else if self.is_tmpfile() {
+            Fixed(244).normal()  // midway between white and black - should show up as grey on all terminals
         }
         else if self.name.starts_with("README") {
             Yellow.bold().underline()