From 64770d0a5a3cf0c13a1e039583bb27b867091334 Mon Sep 17 00:00:00 2001 From: Ben S Date: Mon, 16 Jun 2014 12:43:34 +0100 Subject: [PATCH] 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. --- colours.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++-------- file.rs | 12 +++++++---- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/colours.rs b/colours.rs index 1a58d0e..4494315 100644 --- a/colours.rs +++ b/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(); } diff --git a/file.rs b/file.rs index 41f3245..901ae1b 100644 --- a/file.rs +++ b/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()