done use crossterm to impl theme, functionally works

Signed-off-by: zwPapEr <zw.paper@gmail.com>
This commit is contained in:
zwPapEr
2021-07-06 14:21:22 +08:00
committed by Abin Simon
parent e29966978e
commit 5e40a1c2cb
8 changed files with 236 additions and 196 deletions

View File

@@ -1,10 +1,11 @@
mod theme;
use crossterm::style::{Attribute, ContentStyle, StyledContent, Stylize};
use theme::Theme;
pub use crate::flags::color::ThemeOption;
use ansi_term::{ANSIString, Colour, Style};
use crossterm::style::Color;
use lscolors::{Indicator, LsColors};
use std::path::Path;
@@ -65,7 +66,7 @@ impl Elem {
pub fn has_suid(&self) -> bool {
matches!(self, Elem::Dir { uid: true } | Elem::File { uid: true, .. })
}
pub fn get_color(&self, theme: &theme::Theme) -> Colour {
pub fn get_color(&self, theme: &theme::Theme) -> Color {
match self {
Elem::File {
exec: true,
@@ -113,11 +114,15 @@ impl Elem {
Elem::INode { valid: false } => theme.inode.valid,
Elem::INode { valid: true } => theme.inode.invalid,
Elem::TreeEdge => theme.inode.invalid,
Elem::Links { valid: false } => theme.inode.invalid,
Elem::Links { valid: true } => theme.inode.invalid,
}
}
}
pub type ColoredString<'a> = ANSIString<'a>;
pub type ColoredString = StyledContent<String>;
pub struct Colors {
theme: Option<Theme>,
@@ -142,40 +147,35 @@ impl Colors {
Self { theme, lscolors }
}
pub fn colorize<'a>(&self, input: String, elem: &Elem) -> ColoredString<'a> {
self.style(elem).paint(input)
pub fn colorize(&self, input: String, elem: &Elem) -> ColoredString {
self.style(elem).apply(input)
}
pub fn colorize_using_path<'a>(
&self,
input: String,
path: &Path,
elem: &Elem,
) -> ColoredString<'a> {
pub fn colorize_using_path(&self, input: String, path: &Path, elem: &Elem) -> ColoredString {
let style_from_path = self.style_from_path(path);
match style_from_path {
Some(style_from_path) => style_from_path.paint(input),
Some(style_from_path) => style_from_path.apply(input),
None => self.colorize(input, elem),
}
}
fn style_from_path(&self, path: &Path) -> Option<Style> {
pub fn default_style() -> ContentStyle {
ContentStyle::default()
}
fn style_from_path(&self, path: &Path) -> Option<ContentStyle> {
match &self.lscolors {
Some(lscolors) => lscolors
.style_for_path(path)
.map(lscolors::Style::to_ansi_term_style),
Some(lscolors) => lscolors.style_for_path(path).map(to_content_style),
None => None,
}
}
fn style(&self, elem: &Elem) -> Style {
fn style(&self, elem: &Elem) -> ContentStyle {
match &self.lscolors {
Some(lscolors) => match self.get_indicator_from_elem(elem) {
Some(style) => {
let style = lscolors.style_for_indicator(style);
style
.map(lscolors::Style::to_ansi_term_style)
.unwrap_or_default()
style.map(to_content_style).unwrap_or_default()
}
None => self.style_default(elem),
},
@@ -183,16 +183,16 @@ impl Colors {
}
}
fn style_default(&self, elem: &Elem) -> Style {
fn style_default(&self, elem: &Elem) -> ContentStyle {
if let Some(t) = &self.theme {
let style_fg = Style::default().fg(elem.get_color(&t));
let style_fg = ContentStyle::default().with(elem.get_color(&t));
if elem.has_suid() {
style_fg.on(Colour::Fixed(124)) // Red3
style_fg.on(Color::AnsiValue(124)) // Red3
} else {
style_fg
}
} else {
Style::default()
ContentStyle::default()
}
}
@@ -234,6 +234,59 @@ impl Colors {
}
}
fn to_content_style(ls: &lscolors::Style) -> ContentStyle {
let to_crossterm_color = |c: &lscolors::Color| match c {
lscolors::style::Color::RGB(r, g, b) => Color::Rgb {
r: *r,
g: *g,
b: *b,
},
lscolors::style::Color::Fixed(n) => Color::AnsiValue(*n),
lscolors::style::Color::Black => Color::Black,
lscolors::style::Color::Red => Color::Red,
lscolors::style::Color::Green => Color::Green,
lscolors::style::Color::Yellow => Color::Yellow,
lscolors::style::Color::Blue => Color::Blue,
lscolors::style::Color::Magenta => Color::Magenta,
lscolors::style::Color::Cyan => Color::Cyan,
lscolors::style::Color::White => Color::White,
};
let mut style = ContentStyle::default();
style.foreground_color = ls.foreground.as_ref().map(to_crossterm_color);
style.background_color = ls.background.as_ref().map(to_crossterm_color);
if ls.font_style.bold {
style.attributes.set(Attribute::Bold);
}
if ls.font_style.dimmed {
style.attributes.set(Attribute::Dim);
}
if ls.font_style.italic {
style.attributes.set(Attribute::Italic);
}
if ls.font_style.underline {
style.attributes.set(Attribute::Underlined);
}
if ls.font_style.rapid_blink {
style.attributes.set(Attribute::RapidBlink);
}
if ls.font_style.slow_blink {
style.attributes.set(Attribute::SlowBlink);
}
if ls.font_style.reverse {
style.attributes.set(Attribute::Reverse);
}
if ls.font_style.hidden {
style.attributes.set(Attribute::Hidden);
}
if ls.font_style.strikethrough {
style.attributes.set(Attribute::CrossedOut);
}
style
}
#[cfg(test)]
mod tests {
use super::Colors;
@@ -265,55 +318,55 @@ mod tests {
mod elem {
use super::Elem;
use crate::color::{theme, Theme};
use ansi_term::Colour;
use crossterm::style::Color;
#[cfg(test)]
fn test_theme() -> Theme {
Theme {
user: Colour::Fixed(230), // Cornsilk1
group: Colour::Fixed(187), // LightYellow3
user: Color::AnsiValue(230), // Cornsilk1
group: Color::AnsiValue(187), // LightYellow3
permissions: theme::Permissions {
read: Colour::Green,
write: Colour::Yellow,
exec: Colour::Red,
exec_sticky: Colour::Purple,
no_access: Colour::Fixed(245), // Grey
no_access: Color::AnsiValue(245), // Grey
},
file_type: theme::FileType {
file: theme::File {
exec_uid: Colour::Fixed(40), // Green3
uid_no_exec: Colour::Fixed(184), // Yellow3
exec_no_uid: Colour::Fixed(40), // Green3
no_exec_no_uid: Colour::Fixed(184), // Yellow3
exec_uid: Color::AnsiValue(40), // Green3
uid_no_exec: Color::AnsiValue(184), // Yellow3
exec_no_uid: Color::AnsiValue(40), // Green3
no_exec_no_uid: Color::AnsiValue(184), // Yellow3
},
dir: theme::Dir {
uid: Colour::Fixed(33), // DodgerBlue1
no_uid: Colour::Fixed(33), // DodgerBlue1
uid: Color::AnsiValue(33), // DodgerBlue1
no_uid: Color::AnsiValue(33), // DodgerBlue1
},
pipe: Colour::Fixed(44), // DarkTurquoise
pipe: Color::AnsiValue(44), // DarkTurquoise
symlink: theme::Symlink {
default: Colour::Fixed(44), // DarkTurquoise
broken: Colour::Fixed(124), // Red3
default: Color::AnsiValue(44), // DarkTurquoise
broken: Color::AnsiValue(124), // Red3
},
block_device: Colour::Fixed(44), // DarkTurquoise
char_device: Colour::Fixed(172), // Orange3
socket: Colour::Fixed(44), // DarkTurquoise
special: Colour::Fixed(44), // DarkTurquoise
block_device: Color::AnsiValue(44), // DarkTurquoise
char_device: Color::AnsiValue(172), // Orange3
socket: Color::AnsiValue(44), // DarkTurquoise
special: Color::AnsiValue(44), // DarkTurquoise
},
modified: theme::Modified {
hour_old: Colour::Fixed(40), // Green3
day_old: Colour::Fixed(42), // SpringGreen2
older: Colour::Fixed(36), // DarkCyan
hour_old: Color::AnsiValue(40), // Green3
day_old: Color::AnsiValue(42), // SpringGreen2
older: Color::AnsiValue(36), // DarkCyan
},
size: theme::Size {
none: Colour::Fixed(245), // Grey
small: Colour::Fixed(229), // Wheat1
medium: Colour::Fixed(216), // LightSalmon1
large: Colour::Fixed(172), // Orange3
none: Color::AnsiValue(245), // Grey
small: Color::AnsiValue(229), // Wheat1
medium: Color::AnsiValue(216), // LightSalmon1
large: Color::AnsiValue(172), // Orange3
},
inode: theme::INode {
valid: Colour::Fixed(13), // Pink
invalid: Colour::Fixed(245), // Grey
valid: Color::AnsiValue(13), // Pink
invalid: Color::AnsiValue(245), // Grey
},
}
}

View File

@@ -3,7 +3,7 @@
use crate::config_file;
use crate::print_error;
use ansi_term::Colour;
use crossterm::style::Color;
use serde::Deserialize;
use std::fs;
use std::path::Path;
@@ -14,8 +14,8 @@ use std::path::Path;
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct Theme {
pub user: Colour,
pub group: Colour,
pub user: Color,
pub group: Color,
pub permissions: Permissions,
pub file_type: FileType,
pub modified: Modified,
@@ -27,11 +27,11 @@ pub struct Theme {
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct Permissions {
pub read: Colour,
pub write: Colour,
pub exec: Colour,
pub exec_sticky: Colour,
pub no_access: Colour,
pub read: Color,
pub write: Color,
pub exec: Color,
pub exec_sticky: Color,
pub no_access: Color,
}
#[derive(Debug, Deserialize, PartialEq)]
@@ -40,65 +40,65 @@ pub struct Permissions {
pub struct FileType {
pub file: File,
pub dir: Dir,
pub pipe: Colour,
pub pipe: Color,
pub symlink: Symlink,
pub block_device: Colour,
pub char_device: Colour,
pub socket: Colour,
pub special: Colour,
pub block_device: Color,
pub char_device: Color,
pub socket: Color,
pub special: Color,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct File {
pub exec_uid: Colour,
pub uid_no_exec: Colour,
pub exec_no_uid: Colour,
pub no_exec_no_uid: Colour,
pub exec_uid: Color,
pub uid_no_exec: Color,
pub exec_no_uid: Color,
pub no_exec_no_uid: Color,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct Dir {
pub uid: Colour,
pub no_uid: Colour,
pub uid: Color,
pub no_uid: Color,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct Symlink {
pub default: Colour,
pub broken: Colour,
pub default: Color,
pub broken: Color,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct Modified {
pub hour_old: Colour,
pub day_old: Colour,
pub older: Colour,
pub hour_old: Color,
pub day_old: Color,
pub older: Color,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct Size {
pub none: Colour,
pub small: Colour,
pub medium: Colour,
pub large: Colour,
pub none: Color,
pub small: Color,
pub medium: Color,
pub large: Color,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)]
pub struct INode {
pub valid: Colour,
pub invalid: Colour,
pub valid: Color,
pub invalid: Color,
}
impl Default for Theme {
@@ -146,50 +146,50 @@ impl Theme {
pub fn default_dark() -> Self {
Theme {
user: Colour::Fixed(230), // Cornsilk1
group: Colour::Fixed(187), // LightYellow3
user: Color::AnsiValue(230), // Cornsilk1
group: Color::AnsiValue(187), // LightYellow3
permissions: Permissions {
read: Colour::Green,
write: Colour::Yellow,
exec: Colour::Red,
exec_sticky: Colour::Purple,
no_access: Colour::Fixed(245), // Grey
read: Color::Green,
write: Color::Yellow,
exec: Color::Red,
exec_sticky: Color::AnsiValue(5),
no_access: Color::AnsiValue(245), // Grey
},
file_type: FileType {
file: File {
exec_uid: Colour::Fixed(40), // Green3
uid_no_exec: Colour::Fixed(184), // Yellow3
exec_no_uid: Colour::Fixed(40), // Green3
no_exec_no_uid: Colour::Fixed(184), // Yellow3
exec_uid: Color::AnsiValue(40), // Green3
uid_no_exec: Color::AnsiValue(184), // Yellow3
exec_no_uid: Color::AnsiValue(40), // Green3
no_exec_no_uid: Color::AnsiValue(184), // Yellow3
},
dir: Dir {
uid: Colour::Fixed(33), // DodgerBlue1
no_uid: Colour::Fixed(33), // DodgerBlue1
uid: Color::AnsiValue(33), // DodgerBlue1
no_uid: Color::AnsiValue(33), // DodgerBlue1
},
pipe: Colour::Fixed(44), // DarkTurquoise
pipe: Color::AnsiValue(44), // DarkTurquoise
symlink: Symlink {
default: Colour::Fixed(44), // DarkTurquoise
broken: Colour::Fixed(124), // Red3
default: Color::AnsiValue(44), // DarkTurquoise
broken: Color::AnsiValue(124), // Red3
},
block_device: Colour::Fixed(44), // DarkTurquoise
char_device: Colour::Fixed(172), // Orange3
socket: Colour::Fixed(44), // DarkTurquoise
special: Colour::Fixed(44), // DarkTurquoise
block_device: Color::AnsiValue(44), // DarkTurquoise
char_device: Color::AnsiValue(172), // Orange3
socket: Color::AnsiValue(44), // DarkTurquoise
special: Color::AnsiValue(44), // DarkTurquoise
},
modified: Modified {
hour_old: Colour::Fixed(40), // Green3
day_old: Colour::Fixed(42), // SpringGreen2
older: Colour::Fixed(36), // DarkCyan
hour_old: Color::AnsiValue(40), // Green3
day_old: Color::AnsiValue(42), // SpringGreen2
older: Color::AnsiValue(36), // DarkCyan
},
size: Size {
none: Colour::Fixed(245), // Grey
small: Colour::Fixed(229), // Wheat1
medium: Colour::Fixed(216), // LightSalmon1
large: Colour::Fixed(172), // Orange3
none: Color::AnsiValue(245), // Grey
small: Color::AnsiValue(229), // Wheat1
medium: Color::AnsiValue(216), // LightSalmon1
large: Color::AnsiValue(172), // Orange3
},
inode: INode {
valid: Colour::Fixed(13), // Pink
invalid: Colour::Fixed(245), // Grey
valid: Color::AnsiValue(13), // Pink
invalid: Color::AnsiValue(245), // Grey
},
}
}
@@ -197,68 +197,43 @@ impl Theme {
#[cfg(test)]
pub fn default_yaml() -> &'static str {
r#"---
user:
Fixed: 230
group:
Fixed: 187
user: 230
group: 187
permissions:
read: Green
write: Yellow
exec: Red
exec-sticky: Purple
no-access:
Fixed: 245
no-access: 245
file-type:
file:
exec-uid:
Fixed: 40
uid-no-exec:
Fixed: 184
exec-no-uid:
Fixed: 40
no-exec-no-uid:
Fixed: 184
exec-uid: 40
uid-no-exec: 184
exec-no-uid: 40
no-exec-no-uid: 184
dir:
uid:
Fixed: 33
no-uid:
Fixed: 33
pipe:
Fixed: 44
uid: 33
no-uid: 33
pipe: 44
symlink:
default:
Fixed: 44
broken:
Fixed: 124
block-device:
Fixed: 44
char-device:
Fixed: 172
socket:
Fixed: 44
special:
Fixed: 44
default: 44
broken: 124
block-device: 44
char-device: 172
socket: 44
special: 44
modified:
hour-old:
Fixed: 40
day-old:
Fixed: 42
older:
Fixed: 36
hour-old: 40
day-old: 42
older: 36
size:
none:
Fixed: 245
small:
Fixed: 229
medium:
Fixed: 216
large:
Fixed: 172
none: 245
small: 229
medium: 216
large: 172
inode:
valid:
Fixed: 13
invalid:
Fixed: 245
valid: 13
invalid: 245
"#
}
}

View File

@@ -1,9 +1,8 @@
use crate::color::{ColoredString, Colors};
use crate::color::{Colors, Elem};
use crate::flags::{Block, Display, Flags, Layout};
use crate::icon::Icons;
use crate::meta::name::DisplayOption;
use crate::meta::{FileType, Meta};
use ansi_term::{ANSIString, ANSIStrings};
use std::collections::HashMap;
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use terminal_size::terminal_size;
@@ -256,13 +255,11 @@ fn get_output<'a>(
display_option: &DisplayOption,
padding_rules: &HashMap<Block, usize>,
tree: (usize, &'a str),
) -> Vec<ANSIString<'a>> {
let mut strings: Vec<ANSIString> = Vec::new();
) -> Vec<String> {
let mut strings: Vec<String> = Vec::new();
for (i, block) in flags.blocks.0.iter().enumerate() {
let mut block_vec = if Layout::Tree == flags.layout && tree.0 == i {
// TODO: add color after we have theme configuration
// vec![colors.colorize(ANSIString::from(tree.1).to_string(), &Elem::TreeEdge)]
vec![ANSIString::from(tree.1)]
vec![colors.colorize(tree.1.to_string(), &Elem::TreeEdge)]
} else {
Vec::new()
};
@@ -298,7 +295,13 @@ fn get_output<'a>(
}
}
};
strings.push(ColoredString::from(ANSIStrings(&block_vec).to_string()));
strings.push(
block_vec
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join(" "),
);
}
strings
}

View File

@@ -101,17 +101,14 @@ pub enum IconTheme {
}
impl IconTheme {
/// Get a value from a string.
fn from_str(value: &str) -> Option<Self> {
match value {
"fancy" => Some(Self::Fancy),
"unicode" => Some(Self::Unicode),
_ => {
print_error!("Bad icons.theme config, {}", &value);
None
}
}
}
// /// Get a value from a string.
// fn from_str(value: &str) -> Option<Self> {
// match value {
// "fancy" => Some(Self::Fancy),
// "unicode" => Some(Self::Unicode),
// _ => panic!("Bad icons.theme config, {}", &value),
// }
// }
}
impl Configurable<Self> for IconTheme {

View File

@@ -1,7 +1,6 @@
use crate::color::ColoredString;
use crate::color::{ColoredString, Colors};
use crate::flags::Flags;
use crate::meta::FileType;
use ansi_term::ANSIString;
#[derive(Clone, Debug)]
pub struct Indicator(&'static str);
@@ -24,9 +23,9 @@ impl From<FileType> for Indicator {
impl Indicator {
pub fn render(&self, flags: &Flags) -> ColoredString {
if flags.display_indicators.0 {
ANSIString::from(self.0)
ColoredString::new(Colors::default_style(), self.0.to_string())
} else {
ANSIString::from("")
ColoredString::new(Colors::default_style(), "".into())
}
}
}

View File

@@ -1,5 +1,4 @@
use crate::color::{ColoredString, Colors, Elem};
use ansi_term::ANSIStrings;
use std::fs::Metadata;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -94,8 +93,12 @@ impl Permissions {
},
];
let res = ANSIStrings(strings).to_string();
ColoredString::from(res)
let res = strings
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join("");
ColoredString::new(Colors::default_style(), res)
}
pub fn is_executable(&self) -> bool {

View File

@@ -1,6 +1,5 @@
use crate::color::{ColoredString, Colors, Elem};
use crate::flags::{Flags, SizeFlag};
use ansi_term::ANSIStrings;
use std::fs::Metadata;
use std::iter::repeat;
@@ -62,22 +61,30 @@ impl Size {
let val_content = self.render_value(colors, flags);
let unit_content = self.render_unit(colors, flags);
// TODD(zwpaper): make sure the val_content.content().len() works as expected
let left_pad = if let Some(align) = val_alignment {
repeat(" ")
.take(align - val_content.len())
.take(align - val_content.content().len())
.collect::<String>()
} else {
"".to_string()
};
let mut strings: Vec<ColoredString> = vec![ColoredString::from(left_pad), val_content];
let mut strings: Vec<ColoredString> = vec![
ColoredString::new(Colors::default_style(), left_pad),
val_content,
];
if flags.size != SizeFlag::Short {
strings.push(ColoredString::from(" "));
strings.push(ColoredString::new(Colors::default_style(), " ".into()));
}
strings.push(unit_content);
let res = ANSIStrings(&strings).to_string();
ColoredString::from(res)
let res = strings
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join("");
ColoredString::new(Colors::default_style(), res)
}
fn paint(&self, colors: &Colors, flags: &Flags, content: String) -> ColoredString {

View File

@@ -1,6 +1,5 @@
use crate::color::{ColoredString, Colors, Elem};
use crate::flags::Flags;
use ansi_term::{ANSIString, ANSIStrings};
use std::fs::read_link;
use std::path::Path;
@@ -57,14 +56,18 @@ impl SymLink {
};
let strings: &[ColoredString] = &[
ColoredString::from(format!(" {} ", flag.symlink_arrow)), // ⇒ \u{21d2}
ColoredString::new(Colors::default_style(), format!(" {} ", flag.symlink_arrow)), // ⇒ \u{21d2}
colors.colorize(target_string, elem),
];
let res = ANSIStrings(strings).to_string();
ColoredString::from(res)
let res = strings
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join("");
ColoredString::new(Colors::default_style(), res)
} else {
ANSIString::from("")
ColoredString::new(Colors::default_style(), "".into())
}
}
}