Skip to content

Commit

Permalink
Merge branch 'exa-colors'
Browse files Browse the repository at this point in the history
This branch added support for the EXA_COLORS environment variable, and defines a bunch of two-letter configuration settings that allows theming exa.

The next step is to allow custom highlighting based on file names.
  • Loading branch information
ogham committed Aug 26, 2017
2 parents 4cab5b6 + b13b37e commit 075fe80
Show file tree
Hide file tree
Showing 21 changed files with 470 additions and 306 deletions.
37 changes: 23 additions & 14 deletions Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ Vagrant.configure(2) do |config|
echo -e "\033[32;1mt\033[0m or \033[32;1mtest-exa\033[0m to run \033[1mcargo test\033[0m" >> /etc/motd
echo -e "\033[32;1mx\033[0m or \033[32;1mrun-xtests\033[0m to run \033[1m/vagrant/xtests/run.sh\033[0m" >> /etc/motd
echo -e "\033[32;1mc\033[0m or \033[32;1mcompile-exa\033[0m to run all three" >> /etc/motd
echo -e "\033[32;1mdebug on\033[0;32m|\033[1moff\033[0m to toggle printing logs" >> /etc/motd
echo -e "\033[32;1mstrict on\033[0;32m|\033[1moff\033[0m to toggle strict mode" >> /etc/motd
echo -e "\033[32;1mls-colors on\033[0;32m|\033[1moff\033[0m to toggle LS_COLORS\n" >> /etc/motd
echo -e "\033[32;1mdebug\033[0m to toggle printing logs" >> /etc/motd
echo -e "\033[32;1mstrict\033[0m to toggle strict mode" >> /etc/motd
echo -e "\033[32;1mcolors\033[0m to toggle custom colours\n" >> /etc/motd
# help banner
echo 'echo -e "\\033[4mVersions\\033[0m"' > /home/ubuntu/.bash_profile
Expand All @@ -147,31 +147,40 @@ Vagrant.configure(2) do |config|
# cool prompt
echo 'function nonzero_return() { RETVAL=$?; [ $RETVAL -ne 0 ] && echo "$RETVAL "; }' >> /home/ubuntu/.bash_profile
echo 'function debug_mode() { [ -n "$EXA_DEBUG" ] && echo "debug "; }' >> /home/ubuntu/.bash_profile
echo 'function debug_mode() { [ -n "$EXA_DEBUG" ] && echo "debug "; }' >> /home/ubuntu/.bash_profile
echo 'function strict_mode() { [ -n "$EXA_STRICT" ] && echo "strict "; }' >> /home/ubuntu/.bash_profile
echo 'function lsc_mode() { [ -n "$LS_COLORS" ] && echo "lsc "; }' >> /home/ubuntu/.bash_profile
echo 'export PS1="\\[\\e[1;36m\\]\\h \\[\\e[32m\\]\\w \\[\\e[31m\\]\\`nonzero_return\\`\\[\\e[35m\\]\\`debug_mode\\`\\[\\e[32m\\]\\`lsc_mode\\`\\[\\e[33m\\]\\`strict_mode\\`\\[\\e[36m\\]\\\\$\\[\\e[0m\\] "' >> /home/ubuntu/.bash_profile
echo 'function lsc_mode() { [ -n "$LS_COLORS" ] && echo "lsc "; }' >> /home/ubuntu/.bash_profile
echo 'function exac_mode() { [ -n "$EXA_COLORS" ] && echo "exac "; }' >> /home/ubuntu/.bash_profile
echo 'export PS1="\\[\\e[1;36m\\]\\h \\[\\e[32m\\]\\w \\[\\e[31m\\]\\`nonzero_return\\`\\[\\e[35m\\]\\`debug_mode\\`\\[\\e[32m\\]\\`lsc_mode\\`\\[\\e[1;32m\\]\\`exac_mode\\`\\[\\e[33m\\]\\`strict_mode\\`\\[\\e[36m\\]\\\\$\\[\\e[0m\\] "' >> /home/ubuntu/.bash_profile
# environment setting
echo 'function debug () {' >> /home/ubuntu/.bash_profile
echo ' case "$1" in "on") export EXA_DEBUG=1 ;;' >> /home/ubuntu/.bash_profile
echo ' "off") export EXA_DEBUG= ;;' >> /home/ubuntu/.bash_profile
echo ' "") [ -n "$EXA_DEBUG" ] && echo "debug on" || echo "debug off" ;;' >> /home/ubuntu/.bash_profile
echo ' *) echo "Usage: debug on|off"; return 1 ;; esac; }' >> /home/ubuntu/.bash_profile
echo ' "") [ -n "$EXA_DEBUG" ] && echo "debug on" || echo "debug off" ;;' >> /home/ubuntu/.bash_profile
echo ' *) echo "Usage: debug on|off"; return 1 ;; esac; }' >> /home/ubuntu/.bash_profile
echo 'function strict () {' >> /home/ubuntu/.bash_profile
echo ' case "$1" in "on") export EXA_STRICT=1 ;;' >> /home/ubuntu/.bash_profile
echo ' "off") export EXA_STRICT= ;;' >> /home/ubuntu/.bash_profile
echo ' "") [ -n "$EXA_STRICT" ] && echo "strict on" || echo "strict off" ;;' >> /home/ubuntu/.bash_profile
echo ' *) echo "Usage: strict on|off"; return 1 ;; esac; }' >> /home/ubuntu/.bash_profile
echo 'function ls-colors () {' >> /home/ubuntu/.bash_profile
echo 'function colors () {' >> /home/ubuntu/.bash_profile
echo ' case "$1" in ' >> /home/ubuntu/.bash_profile
echo ' "on") export LS_COLORS="di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43:su=30;41:sg=30;46:tw=30;42:ow=30;43" ;;' >> /home/ubuntu/.bash_profile
echo ' "hacker") export LS_COLORS="di=32:ex=32:fi=32:pi=32:so=32:bd=32:cd=32:ln=32:or=32:mi=32" ;;' >> /home/ubuntu/.bash_profile
echo ' "off") export LS_COLORS= ;;' >> /home/ubuntu/.bash_profile
echo ' "") [ -n "$LS_COLORS" ] && echo "LS_COLORS=$LS_COLORS" || echo "ls-colors off" ;;' >> /home/ubuntu/.bash_profile
echo ' *) echo "Usage: ls-colors on|off"; return 1 ;; esac; }' >> /home/ubuntu/.bash_profile
echo ' "ls")' >> /home/ubuntu/.bash_profile
echo ' export LS_COLORS="di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43:su=30;41:sg=30;46:tw=30;42:ow=30;43"' >> /home/ubuntu/.bash_profile
echo ' export EXA_COLORS="" ;;' >> /home/ubuntu/.bash_profile
echo ' "hacker")' >> /home/ubuntu/.bash_profile
echo ' export LS_COLORS="di=32:ex=32:fi=32:pi=32:so=32:bd=32:cd=32:ln=32:or=32:mi=32"' >> /home/ubuntu/.bash_profile
echo ' export EXA_COLORS="ur=32:uw=32:ux=32:ue=32:gr=32:gw=32:gx=32:tr=32:tw=32:tx=32:su=32:sf=32:xa=32:sn=32:sb=32:df=32:ds=32:uu=32:un=32:gu=32:gn=32:lc=32:lm=32:ga=32:gm=32:gd=32:gv=32:gt=32:xx=32:da=32:in=32:bl=32:hd=32:lp=32:cc=32:" ;;' >> /home/ubuntu/.bash_profile
echo ' "off")' >> /home/ubuntu/.bash_profile
echo ' export LS_COLORS=' >> /home/ubuntu/.bash_profile
echo ' export EXA_COLORS= ;;' >> /home/ubuntu/.bash_profile
echo ' "")' >> /home/ubuntu/.bash_profile
echo ' [ -n "$LS_COLORS" ] && echo "LS_COLORS=$LS_COLORS" || echo "ls-colors off"' >> /home/ubuntu/.bash_profile
echo ' [ -n "$EXA_COLORS" ] && echo "EXA_COLORS=$EXA_COLORS" || echo "exa-colors off" ;;' >> /home/ubuntu/.bash_profile
echo ' *) echo "Usage: ls-colors ls|hacker|off"; return 1 ;; esac; }' >> /home/ubuntu/.bash_profile
# Disable last login date in sshd
sed -i '/PrintLastLog yes/c\PrintLastLog no' /etc/ssh/sshd_config
Expand Down
1 change: 1 addition & 0 deletions src/exa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mod fs;
mod info;
mod options;
mod output;
mod style;


/// The main program wrapper.
Expand Down
45 changes: 34 additions & 11 deletions src/info/filetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@
//! those are the only metadata that we have access to without reading the
//! file’s contents.
use ansi_term::Style;

use fs::File;
use output::file_name::FileColours;


#[derive(Debug)]
#[derive(Debug, Default, PartialEq)]
pub struct FileExtensions;

impl FileExtensions {

/// An “immediate” file is something that can be run or activated somehow
/// in order to kick off the build of a project. It’s usually only present
/// in directories full of source code.
pub fn is_immediate(&self, file: &File) -> bool {
fn is_immediate(&self, file: &File) -> bool {
file.name.starts_with("README") || file.name_is_one_of( &[
"Makefile", "Cargo.toml", "SConstruct", "CMakeLists.txt",
"build.gradle", "Rakefile", "Gruntfile.js",
"Gruntfile.coffee",
])
}

pub fn is_image(&self, file: &File) -> bool {
fn is_image(&self, file: &File) -> bool {
file.extension_is_one_of( &[
"png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif",
"ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
Expand All @@ -32,56 +35,56 @@ impl FileExtensions {
])
}

pub fn is_video(&self, file: &File) -> bool {
fn is_video(&self, file: &File) -> bool {
file.extension_is_one_of( &[
"avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg",
"mpg", "ogm", "ogv", "vob", "wmv", "webm", "m2ts",
"ts",
])
}

pub fn is_music(&self, file: &File) -> bool {
fn is_music(&self, file: &File) -> bool {
file.extension_is_one_of( &[
"aac", "m4a", "mp3", "ogg", "wma", "mka", "opus",
])
}

// Lossless music, rather than any other kind of data...
pub fn is_lossless(&self, file: &File) -> bool {
fn is_lossless(&self, file: &File) -> bool {
file.extension_is_one_of( &[
"alac", "ape", "flac", "wav",
])
}

pub fn is_crypto(&self, file: &File) -> bool {
fn is_crypto(&self, file: &File) -> bool {
file.extension_is_one_of( &[
"asc", "enc", "gpg", "pgp", "sig", "signature", "pfx", "p12",
])
}

pub fn is_document(&self, file: &File) -> bool {
fn is_document(&self, file: &File) -> bool {
file.extension_is_one_of( &[
"djvu", "doc", "docx", "dvi", "eml", "eps", "fotd",
"odp", "odt", "pdf", "ppt", "pptx", "rtf",
"xls", "xlsx",
])
}

pub fn is_compressed(&self, file: &File) -> bool {
fn is_compressed(&self, file: &File) -> bool {
file.extension_is_one_of( &[
"zip", "tar", "Z", "z", "gz", "bz2", "a", "ar", "7z",
"iso", "dmg", "tc", "rar", "par", "tgz", "xz", "txz",
"lzma", "deb", "rpm"
])
}

pub fn is_temp(&self, file: &File) -> bool {
fn is_temp(&self, file: &File) -> bool {
file.name.ends_with('~')
|| (file.name.starts_with('#') && file.name.ends_with('#'))
|| file.extension_is_one_of( &[ "tmp", "swp", "swo", "swn", "bak" ])
}

pub fn is_compiled(&self, file: &File) -> bool {
fn is_compiled(&self, file: &File) -> bool {
if file.extension_is_one_of( &[ "class", "elc", "hi", "o", "pyc" ]) {
true
}
Expand All @@ -93,3 +96,23 @@ impl FileExtensions {
}
}
}

impl FileColours for FileExtensions {
fn colour_file(&self, file: &File) -> Option<Style> {
use ansi_term::Colour::*;

Some(match file {
f if self.is_immediate(f) => Yellow.bold().underline(),
f if self.is_image(f) => Fixed(133).normal(),
f if self.is_video(f) => Fixed(135).normal(),
f if self.is_music(f) => Fixed(92).normal(),
f if self.is_lossless(f) => Fixed(93).normal(),
f if self.is_crypto(f) => Fixed(109).normal(),
f if self.is_document(f) => Fixed(105).normal(),
f if self.is_compressed(f) => Red.normal(),
f if self.is_temp(f) => Fixed(244).normal(),
f if self.is_compiled(f) => Fixed(137).normal(),
_ => return None,
})
}
}
33 changes: 15 additions & 18 deletions src/options/colours.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use output::Colours;
use style::Colours;

use options::{flags, Vars, Misfire};
use options::parser::MatchedFlags;
Expand Down Expand Up @@ -63,7 +63,8 @@ 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 output::lsc::LSColors;
use style::LSColors;
use options::vars;

let tc = TerminalColours::deduce(matches)?;
if tc == Never || (tc == Automatic && widther().is_none()) {
Expand All @@ -73,20 +74,14 @@ impl Colours {
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("LS_COLORS") {
if let Some(lsc) = vars.get(vars::LS_COLORS) {
let lsc = lsc.to_string_lossy();
let lsc = LSColors::parse(lsc.as_ref());

if let Some(c) = lsc.get("di") { colours.filekinds.directory = c; }
if let Some(c) = lsc.get("ex") { colours.filekinds.executable = c; }
if let Some(c) = lsc.get("fi") { colours.filekinds.normal = c; }
if let Some(c) = lsc.get("pi") { colours.filekinds.pipe = c; }
if let Some(c) = lsc.get("so") { colours.filekinds.socket = c; }
if let Some(c) = lsc.get("bd") { colours.filekinds.block_device = c; }
if let Some(c) = lsc.get("cd") { colours.filekinds.char_device = c; }
if let Some(c) = lsc.get("ln") { colours.filekinds.symlink = c; }
if let Some(c) = lsc.get("or") { colours.broken_arrow = c; }
if let Some(c) = lsc.get("mi") { colours.broken_filename = c; }
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)
Expand Down Expand Up @@ -251,7 +246,7 @@ mod customs_test {
let vars = MockVars { ls: $ls, exa: $exa };

for result in parse_for_test(&[], &[], Both, |mf| Colours::deduce(mf, &vars, || Some(80))) {
assert_eq!(result, Ok(c));
assert_eq!(result.as_ref(), Ok(&c));
}
}
};
Expand All @@ -265,10 +260,12 @@ mod customs_test {
// Test impl that just returns the value it has.
impl Vars for MockVars {
fn get(&self, name: &'static str) -> Option<OsString> {
if name == "LS_COLORS" && !self.ls.is_empty() {
use options::vars;

if name == vars::LS_COLORS && !self.ls.is_empty() {
OsString::from(self.ls.clone()).into()
}
else if name == "EXA_COLORS" && !self.exa.is_empty() {
else if name == vars::EXA_COLORS && !self.exa.is_empty() {
OsString::from(self.exa.clone()).into()
}
else {
Expand Down
23 changes: 6 additions & 17 deletions src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ use self::version::VersionString;
mod misfire;
pub use self::misfire::Misfire;

pub mod vars;
pub use self::vars::Vars;

mod parser;
mod flags;
use self::parser::MatchedFlags;
Expand Down Expand Up @@ -120,8 +123,9 @@ impl Options {
where I: IntoIterator<Item=&'args OsString>,
V: Vars {
use options::parser::{Matches, Strictness};
use options::vars;

let strictness = match vars.get("EXA_STRICT") {
let strictness = match vars.get(vars::EXA_STRICT) {
None => Strictness::UseLastArguments,
Some(ref t) if t.is_empty() => Strictness::UseLastArguments,
_ => Strictness::ComplainAboutRedundantArguments,
Expand Down Expand Up @@ -162,28 +166,13 @@ impl Options {
}


/// Mockable wrapper for `std::env::var_os`.
pub trait Vars {
fn get(&self, name: &'static str) -> Option<OsString>;
}




#[cfg(test)]
pub mod test {
use super::{Options, Misfire, Vars, flags};
use super::{Options, Misfire, flags};
use options::parser::{Arg, MatchedFlags};
use std::ffi::OsString;

// Test impl that just returns the value it has.
impl Vars for Option<OsString> {
fn get(&self, _name: &'static str) -> Option<OsString> {
self.clone()
}
}


#[derive(PartialEq, Debug)]
pub enum Strictnesses {
Last,
Expand Down
48 changes: 48 additions & 0 deletions src/options/vars.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use std::ffi::OsString;


// General variables

/// Environment variable used to colour files, both by their filesystem type
/// (symlink, socket, directory) and their file name or extension (image,
/// video, archive);
pub static LS_COLORS: &str = "LS_COLORS";

/// Environment variable used to override the width of the terminal, in
/// characters.
pub static COLUMNS: &str = "COLUMNS";


// exa-specific variables

/// Environment variable used to colour exa’s interface when colours are
/// enabled. This includes all the colours that LS_COLORS would recognise,
/// overriding them if necessary. It can also contain exa-specific codes.
pub static EXA_COLORS: &str = "EXA_COLORS";

/// Environment variable used to switch on strict argument checking, such as
/// complaining if an argument was specified twice, or if two conflict.
/// This is meant to be so you don’t accidentally introduce the wrong
/// behaviour in a script, rather than for general command-line use.
pub static EXA_STRICT: &str = "EXA_STRICT";

/// Environment variable used to limit the grid-details view
/// (`--grid --long`) so it’s only activated if there’s at least the given
/// number of rows of output.
pub static EXA_GRID_ROWS: &str = "EXA_GRID_ROWS";



/// Mockable wrapper for `std::env::var_os`.
pub trait Vars {
fn get(&self, name: &'static str) -> Option<OsString>;
}


// Test impl that just returns the value it has.
#[cfg(test)]
impl Vars for Option<OsString> {
fn get(&self, _name: &'static str) -> Option<OsString> {
self.clone()
}
}
Loading

0 comments on commit 075fe80

Please sign in to comment.