mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-22 20:15:11 +00:00
Merge branch 'ls-colors'
This branch went part of the way towards supporting LS_COLORS in exa. If the variable is set, exa will style certain file kinds, such as sockets or directories or links, according to the style that corresponds with the relevant two-letter code. It doesn’t work with globs for files or extensions yet. That’s coming soon. See #116
This commit is contained in:
commit
adfee28fb9
10
Vagrantfile
vendored
10
Vagrantfile
vendored
@ -159,10 +159,12 @@ Vagrant.configure(2) do |config|
|
||||
echo ' *) echo "Usage: strict on|off"; return 1 ;; esac; }' >> /home/ubuntu/.bash_profile
|
||||
|
||||
echo 'function ls-colors () {' >> /home/ubuntu/.bash_profile
|
||||
echo ' case "$1" in "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 ' "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 ' 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
|
||||
|
||||
# Disable last login date in sshd
|
||||
sed -i '/PrintLastLog yes/c\PrintLastLog no' /etc/ssh/sshd_config
|
||||
|
290
src/options/colours.rs
Normal file
290
src/options/colours.rs
Normal file
@ -0,0 +1,290 @@
|
||||
use output::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 output::lsc::LSColors;
|
||||
|
||||
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("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; }
|
||||
}
|
||||
|
||||
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, 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> {
|
||||
if name == "LS_COLORS" && !self.ls.is_empty() {
|
||||
OsString::from(self.ls.clone()).into()
|
||||
}
|
||||
else if name == "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
|
||||
}
|
@ -75,6 +75,7 @@ use fs::dir_action::DirAction;
|
||||
use fs::filter::FileFilter;
|
||||
use output::{View, Mode, details, grid_details};
|
||||
|
||||
mod colours;
|
||||
mod dir_action;
|
||||
mod filter;
|
||||
mod view;
|
||||
|
@ -11,12 +11,13 @@ use options::parser::MatchedFlags;
|
||||
use fs::feature::xattr;
|
||||
use info::filetype::FileExtensions;
|
||||
|
||||
|
||||
impl View {
|
||||
|
||||
/// Determine which view to use and all of that view’s arguments.
|
||||
pub fn deduce<V: Vars>(matches: &MatchedFlags, vars: &V) -> Result<View, Misfire> {
|
||||
let mode = Mode::deduce(matches, vars)?;
|
||||
let colours = Colours::deduce(matches)?;
|
||||
let colours = Colours::deduce(matches, vars, || *TERM_WIDTH)?;
|
||||
let style = FileStyle::deduce(matches)?;
|
||||
Ok(View { mode, colours, style })
|
||||
}
|
||||
@ -330,77 +331,6 @@ impl TimeTypes {
|
||||
}
|
||||
|
||||
|
||||
/// 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 {
|
||||
fn deduce(matches: &MatchedFlags) -> Result<Colours, Misfire> {
|
||||
use self::TerminalColours::*;
|
||||
|
||||
let tc = TerminalColours::deduce(matches)?;
|
||||
if tc == Always || (tc == Automatic && TERM_WIDTH.is_some()) {
|
||||
let scale = matches.has(&flags::COLOR_SCALE)? || matches.has(&flags::COLOUR_SCALE)?;
|
||||
Ok(Colours::colourful(scale))
|
||||
}
|
||||
else {
|
||||
Ok(Colours::plain())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl FileStyle {
|
||||
fn deduce(matches: &MatchedFlags) -> Result<FileStyle, Misfire> {
|
||||
let classify = Classify::deduce(matches)?;
|
||||
@ -452,7 +382,6 @@ mod test {
|
||||
|
||||
static TEST_ARGS: &[&Arg] = &[ &flags::BINARY, &flags::BYTES, &flags::TIME_STYLE,
|
||||
&flags::TIME, &flags::MODIFIED, &flags::CREATED, &flags::ACCESSED,
|
||||
&flags::COLOR, &flags::COLOUR,
|
||||
&flags::HEADER, &flags::GROUP, &flags::INODE, &flags::GIT,
|
||||
&flags::LINKS, &flags::BLOCKS, &flags::LONG, &flags::LEVEL,
|
||||
&flags::GRID, &flags::ACROSS, &flags::ONE_LINE ];
|
||||
@ -611,39 +540,6 @@ mod test {
|
||||
}
|
||||
|
||||
|
||||
mod colourses {
|
||||
use super::*;
|
||||
|
||||
// Default
|
||||
test!(empty: TerminalColours <- []; Both => Ok(TerminalColours::default()));
|
||||
|
||||
// --colour
|
||||
test!(u_always: TerminalColours <- ["--colour=always"]; Both => Ok(TerminalColours::Always));
|
||||
test!(u_auto: TerminalColours <- ["--colour", "auto"]; Both => Ok(TerminalColours::Automatic));
|
||||
test!(u_never: TerminalColours <- ["--colour=never"]; Both => Ok(TerminalColours::Never));
|
||||
|
||||
// --color
|
||||
test!(no_u_always: TerminalColours <- ["--color", "always"]; Both => Ok(TerminalColours::Always));
|
||||
test!(no_u_auto: TerminalColours <- ["--color=auto"]; Both => Ok(TerminalColours::Automatic));
|
||||
test!(no_u_never: TerminalColours <- ["--color", "never"]; Both => Ok(TerminalColours::Never));
|
||||
|
||||
// Errors
|
||||
test!(no_u_error: TerminalColours <- ["--color=upstream"]; Both => err Misfire::bad_argument(&flags::COLOR, &os("upstream"), super::COLOURS)); // the error is for --color
|
||||
test!(u_error: TerminalColours <- ["--colour=lovers"]; Both => err Misfire::bad_argument(&flags::COLOR, &os("lovers"), super::COLOURS)); // and so is this one!
|
||||
|
||||
// Overriding
|
||||
test!(overridden_1: TerminalColours <- ["--colour=auto", "--colour=never"]; Last => Ok(TerminalColours::Never));
|
||||
test!(overridden_2: TerminalColours <- ["--color=auto", "--colour=never"]; Last => Ok(TerminalColours::Never));
|
||||
test!(overridden_3: TerminalColours <- ["--colour=auto", "--color=never"]; Last => Ok(TerminalColours::Never));
|
||||
test!(overridden_4: TerminalColours <- ["--color=auto", "--color=never"]; Last => Ok(TerminalColours::Never));
|
||||
|
||||
test!(overridden_5: TerminalColours <- ["--colour=auto", "--colour=never"]; Complain => err Misfire::Duplicate(Flag::Long("colour"), Flag::Long("colour")));
|
||||
test!(overridden_6: TerminalColours <- ["--color=auto", "--colour=never"]; Complain => err Misfire::Duplicate(Flag::Long("color"), Flag::Long("colour")));
|
||||
test!(overridden_7: TerminalColours <- ["--colour=auto", "--color=never"]; Complain => err Misfire::Duplicate(Flag::Long("colour"), Flag::Long("color")));
|
||||
test!(overridden_8: TerminalColours <- ["--color=auto", "--color=never"]; Complain => err Misfire::Duplicate(Flag::Long("color"), Flag::Long("color")));
|
||||
}
|
||||
|
||||
|
||||
mod views {
|
||||
use super::*;
|
||||
use output::grid::Options as GridOptions;
|
||||
|
@ -1,11 +1,14 @@
|
||||
use ansi_term::Style;
|
||||
use ansi_term::Colour::{Red, Green, Yellow, Blue, Cyan, Purple, Fixed};
|
||||
|
||||
use output::render;
|
||||
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct Colours {
|
||||
pub scale: bool,
|
||||
|
||||
pub filekinds: FileKinds,
|
||||
pub filetypes: FileTypes,
|
||||
pub perms: Permissions,
|
||||
pub size: Size,
|
||||
@ -25,16 +28,23 @@ pub struct Colours {
|
||||
pub control_char: Style,
|
||||
}
|
||||
|
||||
// Colours for files depending on their filesystem type.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct FileTypes {
|
||||
pub struct FileKinds {
|
||||
pub normal: Style,
|
||||
pub directory: Style,
|
||||
pub symlink: Style,
|
||||
pub pipe: Style,
|
||||
pub device: Style,
|
||||
pub block_device: Style,
|
||||
pub char_device: Style,
|
||||
pub socket: Style,
|
||||
pub special: Style,
|
||||
pub executable: Style,
|
||||
}
|
||||
|
||||
// Colours for files depending on their name or extension.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct FileTypes {
|
||||
pub image: Style,
|
||||
pub video: Style,
|
||||
pub music: Style,
|
||||
@ -115,15 +125,19 @@ impl Colours {
|
||||
Colours {
|
||||
scale: scale,
|
||||
|
||||
filekinds: FileKinds {
|
||||
normal: Style::default(),
|
||||
directory: Blue.bold(),
|
||||
symlink: Cyan.normal(),
|
||||
pipe: Yellow.normal(),
|
||||
block_device: Yellow.bold(),
|
||||
char_device: Yellow.bold(),
|
||||
socket: Red.bold(),
|
||||
special: Yellow.normal(),
|
||||
executable: Green.bold(),
|
||||
},
|
||||
|
||||
filetypes: FileTypes {
|
||||
normal: Style::default(),
|
||||
directory: Blue.bold(),
|
||||
symlink: Cyan.normal(),
|
||||
pipe: Yellow.normal(),
|
||||
device: Yellow.bold(),
|
||||
socket: Red.bold(),
|
||||
special: Yellow.normal(),
|
||||
executable: Green.bold(),
|
||||
image: Fixed(133).normal(),
|
||||
video: Fixed(135).normal(),
|
||||
music: Fixed(92).normal(),
|
||||
@ -202,8 +216,63 @@ impl Colours {
|
||||
control_char: Red.normal(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_size(&self, size: u64) -> Style {
|
||||
|
||||
impl render::BlocksColours for Colours {
|
||||
fn block_count(&self) -> Style { self.blocks }
|
||||
fn no_blocks(&self) -> Style { self.punctuation }
|
||||
}
|
||||
|
||||
impl render::FiletypeColours for Colours {
|
||||
fn normal(&self) -> Style { self.filekinds.normal }
|
||||
fn directory(&self) -> Style { self.filekinds.directory }
|
||||
fn pipe(&self) -> Style { self.filekinds.pipe }
|
||||
fn symlink(&self) -> Style { self.filekinds.symlink }
|
||||
fn block_device(&self) -> Style { self.filekinds.block_device }
|
||||
fn char_device(&self) -> Style { self.filekinds.char_device }
|
||||
fn socket(&self) -> Style { self.filekinds.socket }
|
||||
fn special(&self) -> Style { self.filekinds.special }
|
||||
}
|
||||
|
||||
impl render::GitColours for Colours {
|
||||
fn not_modified(&self) -> Style { self.punctuation }
|
||||
fn new(&self) -> Style { self.git.new }
|
||||
fn modified(&self) -> Style { self.git.modified }
|
||||
fn deleted(&self) -> Style { self.git.deleted }
|
||||
fn renamed(&self) -> Style { self.git.renamed }
|
||||
fn type_change(&self) -> Style { self.git.typechange }
|
||||
}
|
||||
|
||||
impl render::GroupColours for Colours {
|
||||
fn yours(&self) -> Style { self.users.group_yours }
|
||||
fn not_yours(&self) -> Style { self.users.group_not_yours }
|
||||
}
|
||||
|
||||
impl render::LinksColours for Colours {
|
||||
fn normal(&self) -> Style { self.links.normal }
|
||||
fn multi_link_file(&self) -> Style { self.links.multi_link_file }
|
||||
}
|
||||
|
||||
impl render::PermissionsColours for Colours {
|
||||
fn dash(&self) -> Style { self.punctuation }
|
||||
fn user_read(&self) -> Style { self.perms.user_read }
|
||||
fn user_write(&self) -> Style { self.perms.user_write }
|
||||
fn user_execute_file(&self) -> Style { self.perms.user_execute_file }
|
||||
fn user_execute_other(&self) -> Style { self.perms.user_execute_other }
|
||||
fn group_read(&self) -> Style { self.perms.group_read }
|
||||
fn group_write(&self) -> Style { self.perms.group_write }
|
||||
fn group_execute(&self) -> Style { self.perms.group_execute }
|
||||
fn other_read(&self) -> Style { self.perms.other_read }
|
||||
fn other_write(&self) -> Style { self.perms.other_write }
|
||||
fn other_execute(&self) -> Style { self.perms.other_execute }
|
||||
fn special_user_file(&self) -> Style { self.perms.special_user_file }
|
||||
fn special_other(&self) -> Style { self.perms.special_other }
|
||||
fn attribute(&self) -> Style { self.perms.attribute }
|
||||
}
|
||||
|
||||
impl render::SizeColours for Colours {
|
||||
fn size(&self, size: u64) -> Style {
|
||||
if self.scale {
|
||||
if size < 1024 {
|
||||
self.size.scale_byte
|
||||
@ -225,4 +294,16 @@ impl Colours {
|
||||
self.size.numbers
|
||||
}
|
||||
}
|
||||
|
||||
fn unit(&self) -> Style { self.size.unit }
|
||||
fn no_size(&self) -> Style { self.punctuation }
|
||||
fn major(&self) -> Style { self.size.major }
|
||||
fn comma(&self) -> Style { self.punctuation }
|
||||
fn minor(&self) -> Style { self.size.minor }
|
||||
}
|
||||
|
||||
impl render::UserColours for Colours {
|
||||
fn you(&self) -> Style { self.users.user_you }
|
||||
fn someone_else(&self) -> Style { self.users.user_someone_else }
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,8 @@ use std::io::{Write, Error as IOError, Result as IOResult};
|
||||
use std::path::PathBuf;
|
||||
use std::vec::IntoIter as VecIntoIter;
|
||||
|
||||
use ansi_term::Style;
|
||||
|
||||
use fs::{Dir, File};
|
||||
use fs::dir_action::RecurseOptions;
|
||||
use fs::filter::FileFilter;
|
||||
@ -340,15 +342,15 @@ impl<'a> Render<'a> {
|
||||
total_width: table.widths().total(),
|
||||
table: table,
|
||||
inner: rows.into_iter(),
|
||||
colours: self.colours,
|
||||
tree_style: self.colours.punctuation,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iterate(&'a self, rows: Vec<Row>) -> Iter<'a> {
|
||||
pub fn iterate(&'a self, rows: Vec<Row>) -> Iter {
|
||||
Iter {
|
||||
tree_trunk: TreeTrunk::default(),
|
||||
inner: rows.into_iter(),
|
||||
colours: self.colours,
|
||||
tree_style: self.colours.punctuation,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -374,11 +376,12 @@ pub struct Row {
|
||||
|
||||
|
||||
pub struct TableIter<'a> {
|
||||
table: Table<'a>,
|
||||
tree_trunk: TreeTrunk,
|
||||
total_width: usize,
|
||||
colours: &'a Colours,
|
||||
inner: VecIntoIter<Row>,
|
||||
table: Table<'a>,
|
||||
|
||||
total_width: usize,
|
||||
tree_style: Style,
|
||||
tree_trunk: TreeTrunk,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for TableIter<'a> {
|
||||
@ -397,7 +400,7 @@ impl<'a> Iterator for TableIter<'a> {
|
||||
};
|
||||
|
||||
for tree_part in self.tree_trunk.new_row(row.tree) {
|
||||
cell.push(self.colours.punctuation.paint(tree_part.ascii_art()), 4);
|
||||
cell.push(self.tree_style.paint(tree_part.ascii_art()), 4);
|
||||
}
|
||||
|
||||
// If any tree characters have been printed, then add an extra
|
||||
@ -413,13 +416,13 @@ impl<'a> Iterator for TableIter<'a> {
|
||||
}
|
||||
|
||||
|
||||
pub struct Iter<'a> {
|
||||
pub struct Iter {
|
||||
tree_trunk: TreeTrunk,
|
||||
colours: &'a Colours,
|
||||
tree_style: Style,
|
||||
inner: VecIntoIter<Row>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
impl Iterator for Iter {
|
||||
type Item = TextCell;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
@ -427,7 +430,7 @@ impl<'a> Iterator for Iter<'a> {
|
||||
let mut cell = TextCell::default();
|
||||
|
||||
for tree_part in self.tree_trunk.new_row(row.tree) {
|
||||
cell.push(self.colours.punctuation.paint(tree_part.ascii_art()), 4);
|
||||
cell.push(self.tree_style.paint(tree_part.ascii_art()), 4);
|
||||
}
|
||||
|
||||
// If any tree characters have been printed, then add an extra
|
||||
|
@ -247,14 +247,14 @@ impl<'a, 'dir> FileName<'a, 'dir> {
|
||||
// Otherwise, just apply a bunch of rules in order. For example,
|
||||
// executable image files should be executable rather than images.
|
||||
match self.file {
|
||||
f if f.is_directory() => self.colours.filetypes.directory,
|
||||
f if f.is_executable_file() => self.colours.filetypes.executable,
|
||||
f if f.is_link() => self.colours.filetypes.symlink,
|
||||
f if f.is_pipe() => self.colours.filetypes.pipe,
|
||||
f if f.is_char_device()
|
||||
| f.is_block_device() => self.colours.filetypes.device,
|
||||
f if f.is_socket() => self.colours.filetypes.socket,
|
||||
f if !f.is_file() => self.colours.filetypes.special,
|
||||
f if f.is_directory() => self.colours.filekinds.directory,
|
||||
f if f.is_executable_file() => self.colours.filekinds.executable,
|
||||
f if f.is_link() => self.colours.filekinds.symlink,
|
||||
f if f.is_pipe() => self.colours.filekinds.pipe,
|
||||
f if f.is_block_device() => self.colours.filekinds.block_device,
|
||||
f if f.is_char_device() => self.colours.filekinds.char_device,
|
||||
f if f.is_socket() => self.colours.filekinds.socket,
|
||||
f if !f.is_file() => self.colours.filekinds.special,
|
||||
|
||||
f if self.exts.is_immediate(f) => self.colours.filetypes.immediate,
|
||||
f if self.exts.is_image(f) => self.colours.filetypes.image,
|
||||
@ -266,7 +266,8 @@ impl<'a, 'dir> FileName<'a, 'dir> {
|
||||
f if self.exts.is_compressed(f) => self.colours.filetypes.compressed,
|
||||
f if self.exts.is_temp(f) => self.colours.filetypes.temp,
|
||||
f if self.exts.is_compiled(f) => self.colours.filetypes.compiled,
|
||||
_ => self.colours.filetypes.normal,
|
||||
|
||||
_ => self.colours.filekinds.normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
148
src/output/lsc.rs
Normal file
148
src/output/lsc.rs
Normal file
@ -0,0 +1,148 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ansi_term::Style;
|
||||
use ansi_term::Colour::*;
|
||||
|
||||
|
||||
pub struct LSColors<'var> {
|
||||
contents: HashMap<&'var str, &'var str>
|
||||
}
|
||||
|
||||
impl<'var> LSColors<'var> {
|
||||
pub fn parse(input: &'var str) -> LSColors<'var> {
|
||||
let contents = input.split(":")
|
||||
.flat_map(|mapping| {
|
||||
|
||||
let bits = mapping.split("=")
|
||||
.take(3)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if bits.len() != 2 || bits[0].is_empty() || bits[1].is_empty() { None }
|
||||
else { Some( (bits[0], bits[1]) ) }
|
||||
}).collect();
|
||||
LSColors { contents }
|
||||
}
|
||||
|
||||
pub fn get(&self, facet_name: &str) -> Option<Style> {
|
||||
self.contents.get(facet_name).map(ansi_to_style)
|
||||
}
|
||||
}
|
||||
|
||||
fn ansi_to_style(ansi: &&str) -> Style {
|
||||
let mut style = Style::default();
|
||||
|
||||
for num in ansi.split(";") {
|
||||
match num {
|
||||
|
||||
// Bold and italic
|
||||
"1" => style = style.bold(),
|
||||
"4" => style = style.underline(),
|
||||
|
||||
// Foreground colours
|
||||
"30" => style = style.fg(Black),
|
||||
"31" => style = style.fg(Red),
|
||||
"32" => style = style.fg(Green),
|
||||
"33" => style = style.fg(Yellow),
|
||||
"34" => style = style.fg(Blue),
|
||||
"35" => style = style.fg(Purple),
|
||||
"36" => style = style.fg(Cyan),
|
||||
"37" => style = style.fg(White),
|
||||
|
||||
// Background colours
|
||||
"40" => style = style.on(Black),
|
||||
"41" => style = style.on(Red),
|
||||
"42" => style = style.on(Green),
|
||||
"43" => style = style.on(Yellow),
|
||||
"44" => style = style.on(Blue),
|
||||
"45" => style = style.on(Purple),
|
||||
"46" => style = style.on(Cyan),
|
||||
"47" => style = style.on(White),
|
||||
_ => {/* ignore the error and do nothing */},
|
||||
}
|
||||
}
|
||||
|
||||
style
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod ansi_test {
|
||||
use super::*;
|
||||
use ansi_term::Style;
|
||||
|
||||
macro_rules! test {
|
||||
($name:ident: $input:expr => $result:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
assert_eq!(ansi_to_style(&$input), $result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Styles
|
||||
test!(bold: "1" => Style::default().bold());
|
||||
test!(under: "4" => Style::default().underline());
|
||||
test!(both: "1;4" => Style::default().bold().underline());
|
||||
test!(fg: "31" => Red.normal());
|
||||
test!(bg: "43" => Style::default().on(Yellow));
|
||||
test!(bfg: "31;43" => Red.on(Yellow));
|
||||
test!(all: "43;31;1;4" => Red.on(Yellow).bold().underline());
|
||||
test!(again: "1;1;1;1;1" => Style::default().bold());
|
||||
|
||||
// Failure cases
|
||||
test!(empty: "" => Style::default());
|
||||
test!(semis: ";;;;;;" => Style::default());
|
||||
test!(nines: "99999999" => Style::default());
|
||||
test!(word: "GREEN" => Style::default());
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
macro_rules! test {
|
||||
($name:ident: $input:expr, $facet:expr => $result:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let lsc = LSColors::parse($input);
|
||||
assert_eq!(lsc.get($facet), $result.into());
|
||||
assert_eq!(lsc.get(""), None);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Bad parses
|
||||
test!(empty: "", "di" => None);
|
||||
test!(jibber: "blah", "di" => None);
|
||||
|
||||
test!(equals: "=", "di" => None);
|
||||
test!(starts: "=di", "di" => None);
|
||||
test!(ends: "id=", "id" => None);
|
||||
|
||||
// Foreground colours
|
||||
test!(red: "di=31", "di" => Red.normal());
|
||||
test!(green: "cb=32", "cb" => Green.normal());
|
||||
test!(blue: "la=34", "la" => Blue.normal());
|
||||
|
||||
// Background colours
|
||||
test!(yellow: "do=43", "do" => Style::default().on(Yellow));
|
||||
test!(purple: "re=45", "re" => Style::default().on(Purple));
|
||||
test!(cyan: "mi=46", "mi" => Style::default().on(Cyan));
|
||||
|
||||
// Bold and underline
|
||||
test!(bold: "fa=1", "fa" => Style::default().bold());
|
||||
test!(under: "so=4", "so" => Style::default().underline());
|
||||
test!(both: "la=1;4", "la" => Style::default().bold().underline());
|
||||
|
||||
// More and many
|
||||
test!(more_1: "me=43;21;55;34:yu=1;4;1", "me" => Blue.on(Yellow));
|
||||
test!(more_2: "me=43;21;55;34:yu=1;4;1", "yu" => Style::default().bold().underline());
|
||||
|
||||
test!(many_1: "red=31:green=32:blue=34", "red" => Red.normal());
|
||||
test!(many_2: "red=31:green=32:blue=34", "green" => Green.normal());
|
||||
test!(many_3: "red=31:green=32:blue=34", "blue" => Blue.normal());
|
||||
}
|
@ -3,12 +3,14 @@ use output::file_name::FileStyle;
|
||||
pub use self::cell::{TextCell, TextCellContents, DisplayWidth};
|
||||
pub use self::colours::Colours;
|
||||
pub use self::escape::escape;
|
||||
pub use self::lsc::LSColors;
|
||||
|
||||
pub mod details;
|
||||
pub mod file_name;
|
||||
pub mod grid_details;
|
||||
pub mod grid;
|
||||
pub mod lines;
|
||||
pub mod lsc;
|
||||
pub mod table;
|
||||
pub mod time;
|
||||
|
||||
|
@ -1,44 +1,57 @@
|
||||
use ansi_term::Style;
|
||||
|
||||
use output::cell::TextCell;
|
||||
use output::colours::Colours;
|
||||
use fs::fields as f;
|
||||
|
||||
|
||||
impl f::Blocks {
|
||||
pub fn render(&self, colours: &Colours) -> TextCell {
|
||||
pub fn render<C: Colours>(&self, colours: &C) -> TextCell {
|
||||
match *self {
|
||||
f::Blocks::Some(ref blk) => TextCell::paint(colours.blocks, blk.to_string()),
|
||||
f::Blocks::None => TextCell::blank(colours.punctuation),
|
||||
f::Blocks::Some(ref blk) => TextCell::paint(colours.block_count(), blk.to_string()),
|
||||
f::Blocks::None => TextCell::blank(colours.no_blocks()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait Colours {
|
||||
fn block_count(&self) -> Style;
|
||||
fn no_blocks(&self) -> Style;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use output::colours::Colours;
|
||||
use ansi_term::Style;
|
||||
use ansi_term::Colour::*;
|
||||
|
||||
use super::Colours;
|
||||
use output::cell::TextCell;
|
||||
use fs::fields as f;
|
||||
|
||||
use ansi_term::Colour::*;
|
||||
|
||||
struct TestColours;
|
||||
|
||||
impl Colours for TestColours {
|
||||
fn block_count(&self) -> Style { Red.blink() }
|
||||
fn no_blocks(&self) -> Style { Green.italic() }
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn blocklessness() {
|
||||
let mut colours = Colours::default();
|
||||
colours.punctuation = Green.italic();
|
||||
|
||||
let blox = f::Blocks::None;
|
||||
let expected = TextCell::blank(Green.italic());
|
||||
assert_eq!(expected, blox.render(&colours).into());
|
||||
|
||||
assert_eq!(expected, blox.render(&TestColours).into());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn blockfulity() {
|
||||
let mut colours = Colours::default();
|
||||
colours.blocks = Red.blink();
|
||||
|
||||
let blox = f::Blocks::Some(3005);
|
||||
let expected = TextCell::paint_str(Red.blink(), "3005");
|
||||
assert_eq!(expected, blox.render(&colours).into());
|
||||
|
||||
assert_eq!(expected, blox.render(&TestColours).into());
|
||||
}
|
||||
}
|
||||
|
31
src/output/render/filetype.rs
Normal file
31
src/output/render/filetype.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use ansi_term::{ANSIString, Style};
|
||||
|
||||
use fs::fields as f;
|
||||
|
||||
|
||||
impl f::Type {
|
||||
pub fn render<C: Colours>(&self, colours: &C) -> ANSIString<'static> {
|
||||
match *self {
|
||||
f::Type::File => colours.normal().paint("."),
|
||||
f::Type::Directory => colours.directory().paint("d"),
|
||||
f::Type::Pipe => colours.pipe().paint("|"),
|
||||
f::Type::Link => colours.symlink().paint("l"),
|
||||
f::Type::BlockDevice => colours.block_device().paint("b"),
|
||||
f::Type::CharDevice => colours.char_device().paint("c"),
|
||||
f::Type::Socket => colours.socket().paint("s"),
|
||||
f::Type::Special => colours.special().paint("?"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait Colours {
|
||||
fn normal(&self) -> Style;
|
||||
fn directory(&self) -> Style;
|
||||
fn pipe(&self) -> Style;
|
||||
fn symlink(&self) -> Style;
|
||||
fn block_device(&self) -> Style;
|
||||
fn char_device(&self) -> Style;
|
||||
fn socket(&self) -> Style;
|
||||
fn special(&self) -> Style;
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
use ansi_term::ANSIString;
|
||||
use ansi_term::{ANSIString, Style};
|
||||
|
||||
use output::cell::{TextCell, DisplayWidth};
|
||||
use output::colours::Colours;
|
||||
use fs::fields as f;
|
||||
|
||||
|
||||
@ -17,34 +16,55 @@ impl f::Git {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl f::GitStatus {
|
||||
fn render(&self, colours: &Colours) -> ANSIString<'static> {
|
||||
match *self {
|
||||
f::GitStatus::NotModified => colours.punctuation.paint("-"),
|
||||
f::GitStatus::New => colours.git.new.paint("N"),
|
||||
f::GitStatus::Modified => colours.git.modified.paint("M"),
|
||||
f::GitStatus::Deleted => colours.git.deleted.paint("D"),
|
||||
f::GitStatus::Renamed => colours.git.renamed.paint("R"),
|
||||
f::GitStatus::TypeChange => colours.git.typechange.paint("T"),
|
||||
f::GitStatus::NotModified => colours.not_modified().paint("-"),
|
||||
f::GitStatus::New => colours.new().paint("N"),
|
||||
f::GitStatus::Modified => colours.modified().paint("M"),
|
||||
f::GitStatus::Deleted => colours.deleted().paint("D"),
|
||||
f::GitStatus::Renamed => colours.renamed().paint("R"),
|
||||
f::GitStatus::TypeChange => colours.type_change().paint("T"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait Colours {
|
||||
fn not_modified(&self) -> Style;
|
||||
fn new(&self) -> Style;
|
||||
fn modified(&self) -> Style;
|
||||
fn deleted(&self) -> Style;
|
||||
fn renamed(&self) -> Style;
|
||||
fn type_change(&self) -> Style;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use output::colours::Colours;
|
||||
use super::Colours;
|
||||
use output::cell::{TextCell, DisplayWidth};
|
||||
use fs::fields as f;
|
||||
|
||||
use ansi_term::Colour::*;
|
||||
use ansi_term::Style;
|
||||
|
||||
|
||||
struct TestColours;
|
||||
|
||||
impl Colours for TestColours {
|
||||
fn not_modified(&self) -> Style { Fixed(90).normal() }
|
||||
fn new(&self) -> Style { Fixed(91).normal() }
|
||||
fn modified(&self) -> Style { Fixed(92).normal() }
|
||||
fn deleted(&self) -> Style { Fixed(93).normal() }
|
||||
fn renamed(&self) -> Style { Fixed(94).normal() }
|
||||
fn type_change(&self) -> Style { Fixed(95).normal() }
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn git_blank() {
|
||||
let mut colours = Colours::default();
|
||||
colours.punctuation = Fixed(44).normal();
|
||||
|
||||
let stati = f::Git {
|
||||
staged: f::GitStatus::NotModified,
|
||||
unstaged: f::GitStatus::NotModified,
|
||||
@ -53,21 +73,17 @@ pub mod test {
|
||||
let expected = TextCell {
|
||||
width: DisplayWidth::from(2),
|
||||
contents: vec![
|
||||
Fixed(44).paint("-"),
|
||||
Fixed(44).paint("-"),
|
||||
Fixed(90).paint("-"),
|
||||
Fixed(90).paint("-"),
|
||||
].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, stati.render(&colours).into())
|
||||
assert_eq!(expected, stati.render(&TestColours).into())
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn git_new_changed() {
|
||||
let mut colours = Colours::default();
|
||||
colours.git.new = Red.normal();
|
||||
colours.git.modified = Purple.normal();
|
||||
|
||||
let stati = f::Git {
|
||||
staged: f::GitStatus::New,
|
||||
unstaged: f::GitStatus::Modified,
|
||||
@ -76,11 +92,11 @@ pub mod test {
|
||||
let expected = TextCell {
|
||||
width: DisplayWidth::from(2),
|
||||
contents: vec![
|
||||
Red.paint("N"),
|
||||
Purple.paint("M"),
|
||||
Fixed(91).paint("N"),
|
||||
Fixed(92).paint("M"),
|
||||
].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, stati.render(&colours).into())
|
||||
assert_eq!(expected, stati.render(&TestColours).into())
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
use ansi_term::Style;
|
||||
use users::{Users, Groups};
|
||||
|
||||
use fs::fields as f;
|
||||
use output::colours::Colours;
|
||||
use output::cell::TextCell;
|
||||
|
||||
|
||||
impl f::Group {
|
||||
pub fn render<U: Users+Groups>(&self, colours: &Colours, users: &U) -> TextCell {
|
||||
pub fn render<C: Colours, U: Users+Groups>(&self, colours: &C, users: &U) -> TextCell {
|
||||
use users::os::unix::GroupExt;
|
||||
|
||||
let mut style = colours.users.group_not_yours;
|
||||
let mut style = colours.not_yours();
|
||||
|
||||
let group = match users.get_group_by_gid(self.0) {
|
||||
Some(g) => (*g).clone(),
|
||||
@ -20,7 +20,7 @@ impl f::Group {
|
||||
if let Some(current_user) = users.get_user_by_uid(current_uid) {
|
||||
if current_user.primary_group_id() == group.gid()
|
||||
|| group.members().contains(¤t_user.name().to_owned()) {
|
||||
style = colours.users.group_yours;
|
||||
style = colours.yours();
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,63 +29,66 @@ impl f::Group {
|
||||
}
|
||||
|
||||
|
||||
pub trait Colours {
|
||||
fn yours(&self) -> Style;
|
||||
fn not_yours(&self) -> Style;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(unused_results)]
|
||||
pub mod test {
|
||||
use super::Colours;
|
||||
use fs::fields as f;
|
||||
use output::cell::TextCell;
|
||||
use output::colours::Colours;
|
||||
|
||||
use users::{User, Group};
|
||||
use users::mock::MockUsers;
|
||||
use users::os::unix::GroupExt;
|
||||
use ansi_term::Colour::*;
|
||||
use ansi_term::Style;
|
||||
|
||||
|
||||
struct TestColours;
|
||||
|
||||
impl Colours for TestColours {
|
||||
fn yours(&self) -> Style { Fixed(80).normal() }
|
||||
fn not_yours(&self) -> Style { Fixed(81).normal() }
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn named() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.group_not_yours = Fixed(101).normal();
|
||||
|
||||
let mut users = MockUsers::with_current_uid(1000);
|
||||
users.add_group(Group::new(100, "folk"));
|
||||
|
||||
let group = f::Group(100);
|
||||
let expected = TextCell::paint_str(Fixed(101).normal(), "folk");
|
||||
assert_eq!(expected, group.render(&colours, &users))
|
||||
let expected = TextCell::paint_str(Fixed(81).normal(), "folk");
|
||||
assert_eq!(expected, group.render(&TestColours, &users))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unnamed() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.group_not_yours = Fixed(87).normal();
|
||||
|
||||
let users = MockUsers::with_current_uid(1000);
|
||||
|
||||
let group = f::Group(100);
|
||||
let expected = TextCell::paint_str(Fixed(87).normal(), "100");
|
||||
assert_eq!(expected, group.render(&colours, &users));
|
||||
let expected = TextCell::paint_str(Fixed(81).normal(), "100");
|
||||
assert_eq!(expected, group.render(&TestColours, &users));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn primary() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.group_yours = Fixed(64).normal();
|
||||
|
||||
let mut users = MockUsers::with_current_uid(2);
|
||||
users.add_user(User::new(2, "eve", 100));
|
||||
users.add_group(Group::new(100, "folk"));
|
||||
|
||||
let group = f::Group(100);
|
||||
let expected = TextCell::paint_str(Fixed(64).normal(), "folk");
|
||||
assert_eq!(expected, group.render(&colours, &users))
|
||||
let expected = TextCell::paint_str(Fixed(80).normal(), "folk");
|
||||
assert_eq!(expected, group.render(&TestColours, &users))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secondary() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.group_yours = Fixed(31).normal();
|
||||
|
||||
let mut users = MockUsers::with_current_uid(2);
|
||||
users.add_user(User::new(2, "eve", 666));
|
||||
|
||||
@ -93,17 +96,14 @@ pub mod test {
|
||||
users.add_group(test_group);
|
||||
|
||||
let group = f::Group(100);
|
||||
let expected = TextCell::paint_str(Fixed(31).normal(), "folk");
|
||||
assert_eq!(expected, group.render(&colours, &users))
|
||||
let expected = TextCell::paint_str(Fixed(80).normal(), "folk");
|
||||
assert_eq!(expected, group.render(&TestColours, &users))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.group_not_yours = Blue.underline();
|
||||
|
||||
let group = f::Group(2_147_483_648);
|
||||
let expected = TextCell::paint_str(Blue.underline(), "2147483648");
|
||||
assert_eq!(expected, group.render(&colours, &MockUsers::with_current_uid(0)));
|
||||
let expected = TextCell::paint_str(Fixed(81).normal(), "2147483648");
|
||||
assert_eq!(expected, group.render(&TestColours, &MockUsers::with_current_uid(0)));
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
use ansi_term::Style;
|
||||
|
||||
use output::cell::TextCell;
|
||||
use output::colours::Colours;
|
||||
use fs::fields as f;
|
||||
|
||||
|
||||
impl f::Inode {
|
||||
pub fn render(&self, colours: &Colours) -> TextCell {
|
||||
TextCell::paint(colours.inode, self.0.to_string())
|
||||
pub fn render(&self, style: Style) -> TextCell {
|
||||
TextCell::paint(style, self.0.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use output::colours::Colours;
|
||||
use output::cell::TextCell;
|
||||
use fs::fields as f;
|
||||
|
||||
@ -21,11 +21,8 @@ pub mod test {
|
||||
|
||||
#[test]
|
||||
fn blocklessness() {
|
||||
let mut colours = Colours::default();
|
||||
colours.inode = Cyan.underline();
|
||||
|
||||
let io = f::Inode(1414213);
|
||||
let expected = TextCell::paint_str(Cyan.underline(), "1414213");
|
||||
assert_eq!(expected, io.render(&colours).into());
|
||||
assert_eq!(expected, io.render(Cyan.underline()).into());
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,47 @@
|
||||
use output::cell::TextCell;
|
||||
use output::colours::Colours;
|
||||
use fs::fields as f;
|
||||
use ansi_term::Style;
|
||||
use locale::Numeric as NumericLocale;
|
||||
|
||||
use locale;
|
||||
use output::cell::TextCell;
|
||||
use fs::fields as f;
|
||||
|
||||
|
||||
impl f::Links {
|
||||
pub fn render(&self, colours: &Colours, numeric: &locale::Numeric) -> TextCell {
|
||||
let style = if self.multiple { colours.links.multi_link_file }
|
||||
else { colours.links.normal };
|
||||
pub fn render<C: Colours>(&self, colours: &C, numeric: &NumericLocale) -> TextCell {
|
||||
let style = if self.multiple { colours.multi_link_file() }
|
||||
else { colours.normal() };
|
||||
|
||||
TextCell::paint(style, numeric.format_int(self.count))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait Colours {
|
||||
fn normal(&self) -> Style;
|
||||
fn multi_link_file(&self) -> Style;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use output::colours::Colours;
|
||||
use super::Colours;
|
||||
use output::cell::{TextCell, DisplayWidth};
|
||||
use fs::fields as f;
|
||||
|
||||
use ansi_term::Colour::*;
|
||||
use ansi_term::Style;
|
||||
use locale;
|
||||
|
||||
|
||||
struct TestColours;
|
||||
|
||||
impl Colours for TestColours {
|
||||
fn normal(&self) -> Style { Blue.normal() }
|
||||
fn multi_link_file(&self) -> Style { Blue.on(Red) }
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn regular_file() {
|
||||
let mut colours = Colours::default();
|
||||
colours.links.normal = Blue.normal();
|
||||
|
||||
let stati = f::Links {
|
||||
count: 1,
|
||||
multiple: false,
|
||||
@ -40,14 +52,11 @@ pub mod test {
|
||||
contents: vec![ Blue.paint("1") ].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, stati.render(&colours, &locale::Numeric::english()).into());
|
||||
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()).into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regular_directory() {
|
||||
let mut colours = Colours::default();
|
||||
colours.links.normal = Blue.normal();
|
||||
|
||||
let stati = f::Links {
|
||||
count: 3005,
|
||||
multiple: false,
|
||||
@ -58,14 +67,11 @@ pub mod test {
|
||||
contents: vec![ Blue.paint("3,005") ].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, stati.render(&colours, &locale::Numeric::english()).into());
|
||||
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()).into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn popular_file() {
|
||||
let mut colours = Colours::default();
|
||||
colours.links.multi_link_file = Blue.on(Red);
|
||||
|
||||
let stati = f::Links {
|
||||
count: 3005,
|
||||
multiple: true,
|
||||
@ -76,6 +82,6 @@ pub mod test {
|
||||
contents: vec![ Blue.on(Red).paint("3,005") ].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, stati.render(&colours, &locale::Numeric::english()).into());
|
||||
assert_eq!(expected, stati.render(&TestColours, &locale::Numeric::english()).into());
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,29 @@
|
||||
mod blocks;
|
||||
pub use self::blocks::Colours as BlocksColours;
|
||||
|
||||
mod filetype;
|
||||
pub use self::filetype::Colours as FiletypeColours;
|
||||
|
||||
mod git;
|
||||
pub use self::git::Colours as GitColours;
|
||||
|
||||
mod groups;
|
||||
pub use self::groups::Colours as GroupColours;
|
||||
|
||||
mod inode;
|
||||
// inode uses just one colour
|
||||
|
||||
mod links;
|
||||
pub use self::links::Colours as LinksColours;
|
||||
|
||||
mod permissions;
|
||||
pub use self::permissions::Colours as PermissionsColours;
|
||||
|
||||
mod size;
|
||||
pub use self::size::Colours as SizeColours;
|
||||
|
||||
mod times;
|
||||
// times does too
|
||||
|
||||
mod users;
|
||||
pub use self::users::Colours as UserColours;
|
||||
|
@ -1,16 +1,17 @@
|
||||
use fs::fields as f;
|
||||
use output::colours::Colours;
|
||||
use output::cell::{TextCell, DisplayWidth};
|
||||
use ansi_term::{ANSIString, Style};
|
||||
|
||||
use fs::fields as f;
|
||||
use output::cell::{TextCell, DisplayWidth};
|
||||
use output::render::FiletypeColours;
|
||||
|
||||
|
||||
impl f::PermissionsPlus {
|
||||
pub fn render(&self, colours: &Colours) -> TextCell {
|
||||
pub fn render<C: Colours+FiletypeColours>(&self, colours: &C) -> TextCell {
|
||||
let mut chars = vec![ self.file_type.render(colours) ];
|
||||
chars.extend(self.permissions.render(colours, self.file_type.is_regular_file()));
|
||||
|
||||
if self.xattrs {
|
||||
chars.push(colours.perms.attribute.paint("@"));
|
||||
chars.push(colours.attribute().paint("@"));
|
||||
}
|
||||
|
||||
// As these are all ASCII characters, we can guarantee that they’re
|
||||
@ -23,87 +24,115 @@ impl f::PermissionsPlus {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl f::Permissions {
|
||||
pub fn render(&self, colours: &Colours, is_regular_file: bool) -> Vec<ANSIString<'static>> {
|
||||
pub fn render<C: Colours>(&self, colours: &C, is_regular_file: bool) -> Vec<ANSIString<'static>> {
|
||||
|
||||
let bit = |bit, chr: &'static str, style: Style| {
|
||||
if bit { style.paint(chr) } else { colours.punctuation.paint("-") }
|
||||
if bit { style.paint(chr) } else { colours.dash().paint("-") }
|
||||
};
|
||||
|
||||
vec![
|
||||
bit(self.user_read, "r", colours.perms.user_read),
|
||||
bit(self.user_write, "w", colours.perms.user_write),
|
||||
bit(self.user_read, "r", colours.user_read()),
|
||||
bit(self.user_write, "w", colours.user_write()),
|
||||
self.user_execute_bit(colours, is_regular_file),
|
||||
bit(self.group_read, "r", colours.perms.group_read),
|
||||
bit(self.group_write, "w", colours.perms.group_write),
|
||||
bit(self.group_read, "r", colours.group_read()),
|
||||
bit(self.group_write, "w", colours.group_write()),
|
||||
self.group_execute_bit(colours),
|
||||
bit(self.other_read, "r", colours.perms.other_read),
|
||||
bit(self.other_write, "w", colours.perms.other_write),
|
||||
bit(self.other_read, "r", colours.other_read()),
|
||||
bit(self.other_write, "w", colours.other_write()),
|
||||
self.other_execute_bit(colours)
|
||||
]
|
||||
}
|
||||
|
||||
fn user_execute_bit(&self, colours: &Colours, is_regular_file: bool) -> ANSIString<'static> {
|
||||
fn user_execute_bit<C: Colours>(&self, colours: &C, is_regular_file: bool) -> ANSIString<'static> {
|
||||
match (self.user_execute, self.setuid, is_regular_file) {
|
||||
(false, false, _) => colours.punctuation.paint("-"),
|
||||
(true, false, false) => colours.perms.user_execute_other.paint("x"),
|
||||
(true, false, true) => colours.perms.user_execute_file.paint("x"),
|
||||
(false, true, _) => colours.perms.special_other.paint("S"),
|
||||
(true, true, false) => colours.perms.special_other.paint("s"),
|
||||
(true, true, true) => colours.perms.special_user_file.paint("s"),
|
||||
(false, false, _) => colours.dash().paint("-"),
|
||||
(true, false, false) => colours.user_execute_other().paint("x"),
|
||||
(true, false, true) => colours.user_execute_file().paint("x"),
|
||||
(false, true, _) => colours.special_other().paint("S"),
|
||||
(true, true, false) => colours.special_other().paint("s"),
|
||||
(true, true, true) => colours.special_user_file().paint("s"),
|
||||
}
|
||||
}
|
||||
|
||||
fn group_execute_bit(&self, colours: &Colours) -> ANSIString<'static> {
|
||||
fn group_execute_bit<C: Colours>(&self, colours: &C) -> ANSIString<'static> {
|
||||
match (self.group_execute, self.setgid) {
|
||||
(false, false) => colours.punctuation.paint("-"),
|
||||
(true, false) => colours.perms.group_execute.paint("x"),
|
||||
(false, true) => colours.perms.special_other.paint("S"),
|
||||
(true, true) => colours.perms.special_other.paint("s"),
|
||||
(false, false) => colours.dash().paint("-"),
|
||||
(true, false) => colours.group_execute().paint("x"),
|
||||
(false, true) => colours.special_other().paint("S"),
|
||||
(true, true) => colours.special_other().paint("s"),
|
||||
}
|
||||
}
|
||||
|
||||
fn other_execute_bit(&self, colours: &Colours) -> ANSIString<'static> {
|
||||
fn other_execute_bit<C: Colours>(&self, colours: &C) -> ANSIString<'static> {
|
||||
match (self.other_execute, self.sticky) {
|
||||
(false, false) => colours.punctuation.paint("-"),
|
||||
(true, false) => colours.perms.other_execute.paint("x"),
|
||||
(false, true) => colours.perms.special_other.paint("T"),
|
||||
(true, true) => colours.perms.special_other.paint("t"),
|
||||
(false, false) => colours.dash().paint("-"),
|
||||
(true, false) => colours.other_execute().paint("x"),
|
||||
(false, true) => colours.special_other().paint("T"),
|
||||
(true, true) => colours.special_other().paint("t"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl f::Type {
|
||||
pub fn render(&self, colours: &Colours) -> ANSIString<'static> {
|
||||
match *self {
|
||||
f::Type::File => colours.filetypes.normal.paint("."),
|
||||
f::Type::Directory => colours.filetypes.directory.paint("d"),
|
||||
f::Type::Pipe => colours.filetypes.pipe.paint("|"),
|
||||
f::Type::Link => colours.filetypes.symlink.paint("l"),
|
||||
f::Type::CharDevice => colours.filetypes.device.paint("c"),
|
||||
f::Type::BlockDevice => colours.filetypes.device.paint("b"),
|
||||
f::Type::Socket => colours.filetypes.socket.paint("s"),
|
||||
f::Type::Special => colours.filetypes.special.paint("?"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Colours {
|
||||
fn dash(&self) -> Style;
|
||||
|
||||
fn user_read(&self) -> Style;
|
||||
fn user_write(&self) -> Style;
|
||||
fn user_execute_file(&self) -> Style;
|
||||
fn user_execute_other(&self) -> Style;
|
||||
|
||||
fn group_read(&self) -> Style;
|
||||
fn group_write(&self) -> Style;
|
||||
fn group_execute(&self) -> Style;
|
||||
|
||||
fn other_read(&self) -> Style;
|
||||
fn other_write(&self) -> Style;
|
||||
fn other_execute(&self) -> Style;
|
||||
|
||||
fn special_user_file(&self) -> Style;
|
||||
fn special_other(&self) -> Style;
|
||||
|
||||
fn attribute(&self) -> Style;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(unused_results)]
|
||||
pub mod test {
|
||||
use output::colours::Colours;
|
||||
use super::Colours;
|
||||
use output::cell::TextCellContents;
|
||||
use fs::fields as f;
|
||||
|
||||
use ansi_term::Colour::*;
|
||||
use ansi_term::Style;
|
||||
|
||||
|
||||
struct TestColours;
|
||||
|
||||
impl Colours for TestColours {
|
||||
fn dash(&self) -> Style { Fixed(11).normal() }
|
||||
fn user_read(&self) -> Style { Fixed(101).normal() }
|
||||
fn user_write(&self) -> Style { Fixed(102).normal() }
|
||||
fn user_execute_file(&self) -> Style { Fixed(103).normal() }
|
||||
fn user_execute_other(&self) -> Style { Fixed(113).normal() }
|
||||
fn group_read(&self) -> Style { Fixed(104).normal() }
|
||||
fn group_write(&self) -> Style { Fixed(105).normal() }
|
||||
fn group_execute(&self) -> Style { Fixed(106).normal() }
|
||||
fn other_read(&self) -> Style { Fixed(107).normal() }
|
||||
fn other_write(&self) -> Style { Fixed(108).normal() }
|
||||
fn other_execute(&self) -> Style { Fixed(109).normal() }
|
||||
fn special_user_file(&self) -> Style { Fixed(110).normal() }
|
||||
fn special_other(&self) -> Style { Fixed(111).normal() }
|
||||
fn attribute(&self) -> Style { Fixed(112).normal() }
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn negate() {
|
||||
let mut colours = Colours::default();
|
||||
colours.punctuation = Fixed(11).normal();
|
||||
|
||||
let bits = f::Permissions {
|
||||
user_read: false, user_write: false, user_execute: false, setuid: false,
|
||||
group_read: false, group_write: false, group_execute: false, setgid: false,
|
||||
@ -116,25 +145,12 @@ pub mod test {
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(11).paint("-"),
|
||||
]);
|
||||
|
||||
assert_eq!(expected, bits.render(&colours, false).into())
|
||||
assert_eq!(expected, bits.render(&TestColours, false).into())
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn affirm() {
|
||||
let mut colours = Colours::default();
|
||||
colours.perms.user_read = Fixed(101).normal();
|
||||
colours.perms.user_write = Fixed(102).normal();
|
||||
colours.perms.user_execute_file = Fixed(103).normal();
|
||||
|
||||
colours.perms.group_read = Fixed(104).normal();
|
||||
colours.perms.group_write = Fixed(105).normal();
|
||||
colours.perms.group_execute = Fixed(106).normal();
|
||||
|
||||
colours.perms.other_read = Fixed(107).normal();
|
||||
colours.perms.other_write = Fixed(108).normal();
|
||||
colours.perms.other_execute = Fixed(109).normal();
|
||||
|
||||
let bits = f::Permissions {
|
||||
user_read: true, user_write: true, user_execute: true, setuid: false,
|
||||
group_read: true, group_write: true, group_execute: true, setgid: false,
|
||||
@ -147,17 +163,12 @@ pub mod test {
|
||||
Fixed(107).paint("r"), Fixed(108).paint("w"), Fixed(109).paint("x"),
|
||||
]);
|
||||
|
||||
assert_eq!(expected, bits.render(&colours, true).into())
|
||||
assert_eq!(expected, bits.render(&TestColours, true).into())
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn specials() {
|
||||
let mut colours = Colours::default();
|
||||
colours.punctuation = Fixed(11).normal();
|
||||
colours.perms.special_user_file = Fixed(77).normal();
|
||||
colours.perms.special_other = Fixed(88).normal();
|
||||
|
||||
let bits = f::Permissions {
|
||||
user_read: false, user_write: false, user_execute: true, setuid: true,
|
||||
group_read: false, group_write: false, group_execute: true, setgid: true,
|
||||
@ -165,21 +176,17 @@ pub mod test {
|
||||
};
|
||||
|
||||
let expected = TextCellContents::from(vec![
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(77).paint("s"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(88).paint("s"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(88).paint("t"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(110).paint("s"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(111).paint("s"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(111).paint("t"),
|
||||
]);
|
||||
|
||||
assert_eq!(expected, bits.render(&colours, true).into())
|
||||
assert_eq!(expected, bits.render(&TestColours, true).into())
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn extra_specials() {
|
||||
let mut colours = Colours::default();
|
||||
colours.punctuation = Fixed(11).normal();
|
||||
colours.perms.special_other = Fixed(88).normal();
|
||||
|
||||
let bits = f::Permissions {
|
||||
user_read: false, user_write: false, user_execute: false, setuid: true,
|
||||
group_read: false, group_write: false, group_execute: false, setgid: true,
|
||||
@ -187,11 +194,11 @@ pub mod test {
|
||||
};
|
||||
|
||||
let expected = TextCellContents::from(vec![
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(88).paint("S"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(88).paint("S"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(88).paint("T"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(111).paint("S"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(111).paint("S"),
|
||||
Fixed(11).paint("-"), Fixed(11).paint("-"), Fixed(111).paint("T"),
|
||||
]);
|
||||
|
||||
assert_eq!(expected, bits.render(&colours, true).into())
|
||||
assert_eq!(expected, bits.render(&TestColours, true).into())
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
use ansi_term::Style;
|
||||
use locale::Numeric as NumericLocale;
|
||||
|
||||
use fs::fields as f;
|
||||
use output::cell::{TextCell, DisplayWidth};
|
||||
use output::colours::Colours;
|
||||
use output::table::SizeFormat;
|
||||
use locale;
|
||||
|
||||
|
||||
|
||||
impl f::Size {
|
||||
pub fn render(&self, colours: &Colours, size_format: SizeFormat, numerics: &locale::Numeric) -> TextCell {
|
||||
pub fn render<C: Colours>(&self, colours: &C, size_format: SizeFormat, numerics: &NumericLocale) -> TextCell {
|
||||
use number_prefix::{binary_prefix, decimal_prefix};
|
||||
use number_prefix::{Prefixed, Standalone, PrefixNames};
|
||||
|
||||
let size = match *self {
|
||||
f::Size::Some(s) => s,
|
||||
f::Size::None => return TextCell::blank(colours.punctuation),
|
||||
f::Size::None => return TextCell::blank(colours.no_size()),
|
||||
f::Size::DeviceIDs(ref ids) => return ids.render(colours),
|
||||
};
|
||||
|
||||
@ -21,12 +23,12 @@ impl f::Size {
|
||||
SizeFormat::BinaryBytes => binary_prefix(size as f64),
|
||||
SizeFormat::JustBytes => {
|
||||
let string = numerics.format_int(size);
|
||||
return TextCell::paint(colours.file_size(size), string);
|
||||
return TextCell::paint(colours.size(size), string);
|
||||
},
|
||||
};
|
||||
|
||||
let (prefix, n) = match result {
|
||||
Standalone(b) => return TextCell::paint(colours.file_size(b as u64), b.to_string()),
|
||||
Standalone(b) => return TextCell::paint(colours.size(b as u64), b.to_string()),
|
||||
Prefixed(p, n) => (p, n)
|
||||
};
|
||||
|
||||
@ -41,114 +43,121 @@ impl f::Size {
|
||||
TextCell {
|
||||
width: width,
|
||||
contents: vec![
|
||||
colours.file_size(size).paint(number),
|
||||
colours.size.unit.paint(symbol),
|
||||
colours.size(size).paint(number),
|
||||
colours.unit().paint(symbol),
|
||||
].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl f::DeviceIDs {
|
||||
fn render(&self, colours: &Colours) -> TextCell {
|
||||
fn render<C: Colours>(&self, colours: &C) -> TextCell {
|
||||
let major = self.major.to_string();
|
||||
let minor = self.minor.to_string();
|
||||
|
||||
TextCell {
|
||||
width: DisplayWidth::from(major.len() + 1 + minor.len()),
|
||||
contents: vec![
|
||||
colours.size.major.paint(major),
|
||||
colours.punctuation.paint(","),
|
||||
colours.size.minor.paint(minor),
|
||||
colours.major().paint(major),
|
||||
colours.comma().paint(","),
|
||||
colours.minor().paint(minor),
|
||||
].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait Colours {
|
||||
fn size(&self, size: u64) -> Style;
|
||||
fn unit(&self) -> Style;
|
||||
fn no_size(&self) -> Style;
|
||||
|
||||
fn major(&self) -> Style;
|
||||
fn comma(&self) -> Style;
|
||||
fn minor(&self) -> Style;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use output::colours::Colours;
|
||||
use super::Colours;
|
||||
use output::cell::{TextCell, DisplayWidth};
|
||||
use output::table::SizeFormat;
|
||||
use fs::fields as f;
|
||||
|
||||
use locale;
|
||||
use locale::Numeric as NumericLocale;
|
||||
use ansi_term::Colour::*;
|
||||
use ansi_term::Style;
|
||||
|
||||
|
||||
struct TestColours;
|
||||
|
||||
impl Colours for TestColours {
|
||||
fn size(&self, _size: u64) -> Style { Fixed(66).normal() }
|
||||
fn unit(&self) -> Style { Fixed(77).bold() }
|
||||
fn no_size(&self) -> Style { Black.italic() }
|
||||
|
||||
fn major(&self) -> Style { Blue.on(Red) }
|
||||
fn comma(&self) -> Style { Green.italic() }
|
||||
fn minor(&self) -> Style { Cyan.on(Yellow) }
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn directory() {
|
||||
let mut colours = Colours::default();
|
||||
colours.punctuation = Green.italic();
|
||||
|
||||
let directory = f::Size::None;
|
||||
let expected = TextCell::blank(Green.italic());
|
||||
assert_eq!(expected, directory.render(&colours, SizeFormat::JustBytes, &locale::Numeric::english()))
|
||||
let expected = TextCell::blank(Black.italic());
|
||||
assert_eq!(expected, directory.render(&TestColours, SizeFormat::JustBytes, &NumericLocale::english()))
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn file_decimal() {
|
||||
let mut colours = Colours::default();
|
||||
colours.size.numbers = Blue.on(Red);
|
||||
colours.size.unit = Yellow.bold();
|
||||
|
||||
let directory = f::Size::Some(2_100_000);
|
||||
let expected = TextCell {
|
||||
width: DisplayWidth::from(4),
|
||||
contents: vec![
|
||||
Blue.on(Red).paint("2.1"),
|
||||
Yellow.bold().paint("M"),
|
||||
Fixed(66).paint("2.1"),
|
||||
Fixed(77).bold().paint("M"),
|
||||
].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, directory.render(&colours, SizeFormat::DecimalBytes, &locale::Numeric::english()))
|
||||
assert_eq!(expected, directory.render(&TestColours, SizeFormat::DecimalBytes, &NumericLocale::english()))
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn file_binary() {
|
||||
let mut colours = Colours::default();
|
||||
colours.size.numbers = Blue.on(Red);
|
||||
colours.size.unit = Yellow.bold();
|
||||
|
||||
let directory = f::Size::Some(1_048_576);
|
||||
let expected = TextCell {
|
||||
width: DisplayWidth::from(5),
|
||||
contents: vec![
|
||||
Blue.on(Red).paint("1.0"),
|
||||
Yellow.bold().paint("Mi"),
|
||||
Fixed(66).paint("1.0"),
|
||||
Fixed(77).bold().paint("Mi"),
|
||||
].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, directory.render(&colours, SizeFormat::BinaryBytes, &locale::Numeric::english()))
|
||||
assert_eq!(expected, directory.render(&TestColours, SizeFormat::BinaryBytes, &NumericLocale::english()))
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn file_bytes() {
|
||||
let mut colours = Colours::default();
|
||||
colours.size.numbers = Blue.on(Red);
|
||||
|
||||
let directory = f::Size::Some(1048576);
|
||||
let expected = TextCell {
|
||||
width: DisplayWidth::from(9),
|
||||
contents: vec![
|
||||
Blue.on(Red).paint("1,048,576"),
|
||||
Fixed(66).paint("1,048,576"),
|
||||
].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, directory.render(&colours, SizeFormat::JustBytes, &locale::Numeric::english()))
|
||||
assert_eq!(expected, directory.render(&TestColours, SizeFormat::JustBytes, &NumericLocale::english()))
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn device_ids() {
|
||||
let mut colours = Colours::default();
|
||||
colours.size.major = Blue.on(Red);
|
||||
colours.punctuation = Green.italic();
|
||||
colours.size.minor = Cyan.on(Yellow);
|
||||
|
||||
let directory = f::Size::DeviceIDs(f::DeviceIDs { major: 10, minor: 80 });
|
||||
let expected = TextCell {
|
||||
width: DisplayWidth::from(5),
|
||||
@ -159,6 +168,6 @@ pub mod test {
|
||||
].into(),
|
||||
};
|
||||
|
||||
assert_eq!(expected, directory.render(&colours, SizeFormat::JustBytes, &locale::Numeric::english()))
|
||||
assert_eq!(expected, directory.render(&TestColours, SizeFormat::JustBytes, &NumericLocale::english()))
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,23 @@
|
||||
use datetime::TimeZone;
|
||||
use ansi_term::Style;
|
||||
|
||||
use fs::fields as f;
|
||||
use output::cell::TextCell;
|
||||
use output::colours::Colours;
|
||||
use output::time::TimeFormat;
|
||||
|
||||
|
||||
impl f::Time {
|
||||
pub fn render(self, colours: &Colours,
|
||||
tz: &Option<TimeZone>,
|
||||
style: &TimeFormat) -> TextCell {
|
||||
pub fn render(self, style: Style,
|
||||
tz: &Option<TimeZone>,
|
||||
format: &TimeFormat) -> TextCell {
|
||||
|
||||
if let Some(ref tz) = *tz {
|
||||
let datestamp = style.format_zoned(self, tz);
|
||||
TextCell::paint(colours.date, datestamp)
|
||||
let datestamp = format.format_zoned(self, tz);
|
||||
TextCell::paint(style, datestamp)
|
||||
}
|
||||
else {
|
||||
let datestamp = style.format_local(self);
|
||||
TextCell::paint(colours.date, datestamp)
|
||||
let datestamp = format.format_local(self);
|
||||
TextCell::paint(style, datestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,89 +1,92 @@
|
||||
use ansi_term::Style;
|
||||
use users::Users;
|
||||
|
||||
use fs::fields as f;
|
||||
use output::colours::Colours;
|
||||
use output::cell::TextCell;
|
||||
|
||||
|
||||
|
||||
impl f::User {
|
||||
pub fn render(&self, colours: &Colours, users: &Users) -> TextCell {
|
||||
pub fn render<C: Colours, U: Users>(&self, colours: &C, users: &U) -> TextCell {
|
||||
let user_name = match users.get_user_by_uid(self.0) {
|
||||
Some(user) => user.name().to_owned(),
|
||||
None => self.0.to_string(),
|
||||
};
|
||||
|
||||
let style = if users.get_current_uid() == self.0 { colours.users.user_you }
|
||||
else { colours.users.user_someone_else };
|
||||
let style = if users.get_current_uid() == self.0 { colours.you() }
|
||||
else { colours.someone_else() };
|
||||
TextCell::paint(style, user_name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait Colours {
|
||||
fn you(&self) -> Style;
|
||||
fn someone_else(&self) -> Style;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(unused_results)]
|
||||
pub mod test {
|
||||
use super::Colours;
|
||||
use fs::fields as f;
|
||||
use output::cell::TextCell;
|
||||
use output::colours::Colours;
|
||||
|
||||
use users::User;
|
||||
use users::mock::MockUsers;
|
||||
use ansi_term::Colour::*;
|
||||
use ansi_term::Style;
|
||||
|
||||
|
||||
struct TestColours;
|
||||
|
||||
impl Colours for TestColours {
|
||||
fn you(&self) -> Style { Red.bold() }
|
||||
fn someone_else(&self) -> Style { Blue.underline() }
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn named() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.user_you = Red.bold();
|
||||
|
||||
let mut users = MockUsers::with_current_uid(1000);
|
||||
users.add_user(User::new(1000, "enoch", 100));
|
||||
|
||||
let user = f::User(1000);
|
||||
let expected = TextCell::paint_str(Red.bold(), "enoch");
|
||||
assert_eq!(expected, user.render(&colours, &users))
|
||||
assert_eq!(expected, user.render(&TestColours, &users))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unnamed() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.user_you = Cyan.bold();
|
||||
|
||||
let users = MockUsers::with_current_uid(1000);
|
||||
|
||||
let user = f::User(1000);
|
||||
let expected = TextCell::paint_str(Cyan.bold(), "1000");
|
||||
assert_eq!(expected, user.render(&colours, &users));
|
||||
let expected = TextCell::paint_str(Red.bold(), "1000");
|
||||
assert_eq!(expected, user.render(&TestColours, &users));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_named() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.user_someone_else = Green.bold();
|
||||
|
||||
let mut users = MockUsers::with_current_uid(0);
|
||||
users.add_user(User::new(1000, "enoch", 100));
|
||||
|
||||
let user = f::User(1000);
|
||||
let expected = TextCell::paint_str(Green.bold(), "enoch");
|
||||
assert_eq!(expected, user.render(&colours, &users));
|
||||
let expected = TextCell::paint_str(Blue.underline(), "enoch");
|
||||
assert_eq!(expected, user.render(&TestColours, &users));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_unnamed() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.user_someone_else = Red.normal();
|
||||
|
||||
let user = f::User(1000);
|
||||
let expected = TextCell::paint_str(Red.normal(), "1000");
|
||||
assert_eq!(expected, user.render(&colours, &MockUsers::with_current_uid(0)));
|
||||
let expected = TextCell::paint_str(Blue.underline(), "1000");
|
||||
assert_eq!(expected, user.render(&TestColours, &MockUsers::with_current_uid(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow() {
|
||||
let mut colours = Colours::default();
|
||||
colours.users.user_someone_else = Blue.underline();
|
||||
|
||||
let user = f::User(2_147_483_648);
|
||||
let expected = TextCell::paint_str(Blue.underline(), "2147483648");
|
||||
assert_eq!(expected, user.render(&colours, &MockUsers::with_current_uid(0)));
|
||||
assert_eq!(expected, user.render(&TestColours, &MockUsers::with_current_uid(0)));
|
||||
}
|
||||
}
|
||||
|
@ -342,18 +342,18 @@ impl<'a, 'f> Table<'a> {
|
||||
use output::table::TimeType::*;
|
||||
|
||||
match *column {
|
||||
Column::Permissions => self.permissions_plus(file, xattrs).render(&self.colours),
|
||||
Column::FileSize => file.size().render(&self.colours, self.size_format, &self.env.numeric),
|
||||
Column::HardLinks => file.links().render(&self.colours, &self.env.numeric),
|
||||
Column::Inode => file.inode().render(&self.colours),
|
||||
Column::Blocks => file.blocks().render(&self.colours),
|
||||
Column::User => file.user().render(&self.colours, &*self.env.lock_users()),
|
||||
Column::Group => file.group().render(&self.colours, &*self.env.lock_users()),
|
||||
Column::GitStatus => file.git_status().render(&self.colours),
|
||||
Column::Permissions => self.permissions_plus(file, xattrs).render(self.colours),
|
||||
Column::FileSize => file.size().render(self.colours, self.size_format, &self.env.numeric),
|
||||
Column::HardLinks => file.links().render(self.colours, &self.env.numeric),
|
||||
Column::Inode => file.inode().render(self.colours.inode),
|
||||
Column::Blocks => file.blocks().render(self.colours),
|
||||
Column::User => file.user().render(self.colours, &*self.env.lock_users()),
|
||||
Column::Group => file.group().render(self.colours, &*self.env.lock_users()),
|
||||
Column::GitStatus => file.git_status().render(self.colours),
|
||||
|
||||
Column::Timestamp(Modified) => file.modified_time().render(&self.colours, &self.env.tz, &self.time_format),
|
||||
Column::Timestamp(Created) => file.created_time().render( &self.colours, &self.env.tz, &self.time_format),
|
||||
Column::Timestamp(Accessed) => file.accessed_time().render(&self.colours, &self.env.tz, &self.time_format),
|
||||
Column::Timestamp(Modified) => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
|
||||
Column::Timestamp(Created) => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
|
||||
Column::Timestamp(Accessed) => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user