use nu_ansi_term::{Color, Style};
use crate::theme::lsc::Pair;
use crate::theme::smooth::{self, SmoothLuts};
fn is_rgb_foreground(style: Style) -> bool {
matches!(style.foreground, Some(Color::Rgb(_, _, _)))
}
#[derive(Debug, Default, PartialEq)]
pub struct UiStyles {
pub colourful: bool,
pub filekinds: FileKinds,
pub perms: Permissions,
pub size: Size,
pub users: Users,
pub links: Links,
pub vcs: Git,
pub punctuation: Style,
pub date_modified: DateAge,
pub date_accessed: DateAge,
pub date_changed: DateAge,
pub date_created: DateAge,
pub inode: Style,
pub blocks: Style,
pub header: Style,
pub octal: Style,
pub flags: Style,
pub symlink_path: Style,
pub control_char: Style,
pub broken_symlink: Style,
pub broken_path_overlay: Style,
pub smooth_luts: SmoothLuts,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct FileKinds {
pub normal: Style,
pub directory: Style,
pub symlink: Style,
pub pipe: Style,
pub block_device: Style,
pub char_device: Style,
pub socket: Style,
pub special: Style,
pub executable: Style,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Permissions {
pub user_read: Style,
pub user_write: Style,
pub user_execute_file: Style,
pub user_execute_other: Style,
pub group_read: Style,
pub group_write: Style,
pub group_execute: Style,
pub other_read: Style,
pub other_write: Style,
pub other_execute: Style,
pub special_user_file: Style,
pub special_other: Style,
pub attribute: Style,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Size {
pub major: Style,
pub minor: Style,
pub number_byte: Style,
pub number_kilo: Style,
pub number_mega: Style,
pub number_giga: Style,
pub number_huge: Style,
pub unit_byte: Style,
pub unit_kilo: Style,
pub unit_mega: Style,
pub unit_giga: Style,
pub unit_huge: Style,
}
impl Size {
pub(crate) fn is_smoothable(&self) -> bool {
is_rgb_foreground(self.number_byte)
&& is_rgb_foreground(self.number_kilo)
&& is_rgb_foreground(self.number_mega)
&& is_rgb_foreground(self.number_giga)
&& is_rgb_foreground(self.number_huge)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct DateAge {
pub now: Style, pub today: Style, pub week: Style, pub month: Style, pub year: Style, pub old: Style,
pub flat: Style,
}
impl DateAge {
pub fn set_all(&mut self, style: Style) {
self.now = style;
self.today = style;
self.week = style;
self.month = style;
self.year = style;
self.old = style;
self.flat = style;
}
pub(crate) fn is_smoothable(&self) -> bool {
is_rgb_foreground(self.now)
&& is_rgb_foreground(self.today)
&& is_rgb_foreground(self.week)
&& is_rgb_foreground(self.month)
&& is_rgb_foreground(self.year)
&& is_rgb_foreground(self.old)
}
pub fn for_age(&self, age_secs: u64) -> Style {
const HOUR: u64 = 3600;
const DAY: u64 = 86400;
const WEEK: u64 = 7 * DAY;
const MONTH: u64 = 30 * DAY;
const YEAR: u64 = 365 * DAY;
if age_secs < HOUR { self.now }
else if age_secs < DAY { self.today }
else if age_secs < WEEK { self.week }
else if age_secs < MONTH { self.month }
else if age_secs < YEAR { self.year }
else { self.old }
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Users {
pub user_you: Style,
pub user_someone_else: Style,
pub group_yours: Style,
pub group_member: Style,
pub group_not_yours: Style,
pub uid_you: Style,
pub uid_someone_else: Style,
pub gid_yours: Style,
pub gid_member: Style,
pub gid_not_yours: Style,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Links {
pub normal: Style,
pub multi_link_file: Style,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Git {
pub new: Style,
pub modified: Style,
pub deleted: Style,
pub renamed: Style,
pub typechange: Style,
pub ignored: Style,
pub conflicted: Style,
}
impl UiStyles {
pub fn plain() -> Self {
Self::default()
}
pub(crate) fn date_for_each<F: FnMut(&mut DateAge)>(&mut self, mut f: F) {
f(&mut self.date_modified);
f(&mut self.date_accessed);
f(&mut self.date_changed);
f(&mut self.date_created);
}
pub fn apply_gradient_flags(&mut self, gradient: super::GradientFlags) {
if gradient.smooth {
if gradient.size && self.size.is_smoothable() {
self.smooth_luts.size =
Some(smooth::build_smooth_lut(&smooth::size_anchors(&self.size)));
}
if gradient.modified && self.date_modified.is_smoothable() {
self.smooth_luts.modified =
Some(smooth::build_smooth_lut(&smooth::date_anchors(&self.date_modified)));
}
if gradient.accessed && self.date_accessed.is_smoothable() {
self.smooth_luts.accessed =
Some(smooth::build_smooth_lut(&smooth::date_anchors(&self.date_accessed)));
}
if gradient.changed && self.date_changed.is_smoothable() {
self.smooth_luts.changed =
Some(smooth::build_smooth_lut(&smooth::date_anchors(&self.date_changed)));
}
if gradient.created && self.date_created.is_smoothable() {
self.smooth_luts.created =
Some(smooth::build_smooth_lut(&smooth::date_anchors(&self.date_created)));
}
}
if !gradient.size {
self.size.number_byte = self.size.major;
self.size.number_kilo = self.size.major;
self.size.number_mega = self.size.major;
self.size.number_giga = self.size.major;
self.size.number_huge = self.size.major;
self.size.unit_byte = self.size.minor;
self.size.unit_kilo = self.size.minor;
self.size.unit_mega = self.size.minor;
self.size.unit_giga = self.size.minor;
self.size.unit_huge = self.size.minor;
}
if !gradient.modified { flatten_date_age(&mut self.date_modified); }
if !gradient.accessed { flatten_date_age(&mut self.date_accessed); }
if !gradient.changed { flatten_date_age(&mut self.date_changed); }
if !gradient.created { flatten_date_age(&mut self.date_created); }
}
}
fn flatten_date_age(d: &mut DateAge) {
d.now = d.flat;
d.today = d.flat;
d.week = d.flat;
d.month = d.flat;
d.year = d.flat;
d.old = d.flat;
}
impl UiStyles {
pub fn set_ls(&mut self, pair: &Pair<'_>) -> bool {
match pair.key {
"di" => self.filekinds.directory = pair.to_style(), "ex" => self.filekinds.executable = pair.to_style(), "fi" => self.filekinds.normal = pair.to_style(), "pi" => self.filekinds.pipe = pair.to_style(), "so" => self.filekinds.socket = pair.to_style(), "bd" => self.filekinds.block_device = pair.to_style(), "cd" => self.filekinds.char_device = pair.to_style(), "ln" => self.filekinds.symlink = pair.to_style(), "or" => self.broken_symlink = pair.to_style(), _ => return false,
}
true
}
pub fn set_number_style(&mut self, style: Style) {
self.size.number_byte = style;
self.size.number_kilo = style;
self.size.number_mega = style;
self.size.number_giga = style;
self.size.number_huge = style;
self.size.major = style;
}
pub fn set_unit_style(&mut self, style: Style) {
self.size.unit_byte = style;
self.size.unit_kilo = style;
self.size.unit_mega = style;
self.size.unit_giga = style;
self.size.unit_huge = style;
self.size.minor = style;
}
pub fn set_config(&mut self, key: &str, value: &str) -> bool {
use super::lsc::parse_style;
let style = parse_style(value);
match key {
"normal" => self.filekinds.normal = style,
"directory" => self.filekinds.directory = style,
"symlink" => self.filekinds.symlink = style,
"pipe" => self.filekinds.pipe = style,
"block-device" => self.filekinds.block_device = style,
"char-device" => self.filekinds.char_device = style,
"socket" => self.filekinds.socket = style,
"special" => self.filekinds.special = style,
"executable" => self.filekinds.executable = style,
"permissions-user-read" | "perm-user-read" | "mode-user-read" => self.perms.user_read = style,
"permissions-user-write" | "perm-user-write" | "mode-user-write" => self.perms.user_write = style,
"permissions-user-execute" | "perm-user-exec" | "mode-user-exec" => self.perms.user_execute_file = style,
"permissions-user-execute-other" | "perm-user-exec-other" | "mode-user-exec-other" => self.perms.user_execute_other = style,
"permissions-group-read" | "perm-group-read" | "mode-group-read" => self.perms.group_read = style,
"permissions-group-write" | "perm-group-write" | "mode-group-write" => self.perms.group_write = style,
"permissions-group-execute" | "perm-group-exec" | "mode-group-exec" => self.perms.group_execute = style,
"permissions-other-read" | "perm-other-read" | "mode-other-read" => self.perms.other_read = style,
"permissions-other-write" | "perm-other-write" | "mode-other-write" => self.perms.other_write = style,
"permissions-other-execute" | "perm-other-exec" | "mode-other-exec" => self.perms.other_execute = style,
"permissions-special-user" | "perm-special-user" | "mode-special-user" => self.perms.special_user_file = style,
"permissions-special-other" | "perm-special-other" | "mode-special-other" => self.perms.special_other = style,
"permissions-attribute" | "perm-attribute" | "mode-attribute" => self.perms.attribute = style,
"size-number-byte" => self.size.number_byte = style,
"size-number-kilo" => self.size.number_kilo = style,
"size-number-mega" => self.size.number_mega = style,
"size-number-giga" => self.size.number_giga = style,
"size-number-huge" => self.size.number_huge = style,
"size-unit-byte" => self.size.unit_byte = style,
"size-unit-kilo" => self.size.unit_kilo = style,
"size-unit-mega" => self.size.unit_mega = style,
"size-unit-giga" => self.size.unit_giga = style,
"size-unit-huge" => self.size.unit_huge = style,
"size-number" => self.set_number_style(style),
"size-unit" => self.set_unit_style(style),
"size-major" => self.size.major = style,
"size-minor" => self.size.minor = style,
"user-you" => self.users.user_you = style,
"user-other" => self.users.user_someone_else = style,
"group-yours" => self.users.group_yours = style,
"group-member" => self.users.group_member = style,
"group-other" => self.users.group_not_yours = style,
"uid-you" => self.users.uid_you = style,
"uid-other" => self.users.uid_someone_else = style,
"gid-yours" => self.users.gid_yours = style,
"gid-member" => self.users.gid_member = style,
"gid-other" => self.users.gid_not_yours = style,
"links" => self.links.normal = style,
"links-multi" => self.links.multi_link_file = style,
"vcs-new" => self.vcs.new = style,
"vcs-modified" => self.vcs.modified = style,
"vcs-deleted" => self.vcs.deleted = style,
"vcs-renamed" => self.vcs.renamed = style,
"vcs-typechange" => self.vcs.typechange = style,
"vcs-ignored" => self.vcs.ignored = style,
"vcs-conflicted" => self.vcs.conflicted = style,
"punctuation" => self.punctuation = style,
"date" => self.date_for_each(|d| d.set_all(style)),
"date-now" => self.date_for_each(|d| d.now = style),
"date-today" => self.date_for_each(|d| d.today = style),
"date-week" => self.date_for_each(|d| d.week = style),
"date-month" => self.date_for_each(|d| d.month = style),
"date-year" => self.date_for_each(|d| d.year = style),
"date-old" => self.date_for_each(|d| d.old = style),
"date-flat" => self.date_for_each(|d| d.flat = style),
"date-modified" => self.date_modified.set_all(style),
"date-modified-now" => self.date_modified.now = style,
"date-modified-today" => self.date_modified.today = style,
"date-modified-week" => self.date_modified.week = style,
"date-modified-month" => self.date_modified.month = style,
"date-modified-year" => self.date_modified.year = style,
"date-modified-old" => self.date_modified.old = style,
"date-modified-flat" => self.date_modified.flat = style,
"date-accessed" => self.date_accessed.set_all(style),
"date-accessed-now" => self.date_accessed.now = style,
"date-accessed-today" => self.date_accessed.today = style,
"date-accessed-week" => self.date_accessed.week = style,
"date-accessed-month" => self.date_accessed.month = style,
"date-accessed-year" => self.date_accessed.year = style,
"date-accessed-old" => self.date_accessed.old = style,
"date-accessed-flat" => self.date_accessed.flat = style,
"date-changed" => self.date_changed.set_all(style),
"date-changed-now" => self.date_changed.now = style,
"date-changed-today" => self.date_changed.today = style,
"date-changed-week" => self.date_changed.week = style,
"date-changed-month" => self.date_changed.month = style,
"date-changed-year" => self.date_changed.year = style,
"date-changed-old" => self.date_changed.old = style,
"date-changed-flat" => self.date_changed.flat = style,
"date-created" => self.date_created.set_all(style),
"date-created-now" => self.date_created.now = style,
"date-created-today" => self.date_created.today = style,
"date-created-week" => self.date_created.week = style,
"date-created-month" => self.date_created.month = style,
"date-created-year" => self.date_created.year = style,
"date-created-old" => self.date_created.old = style,
"date-created-flat" => self.date_created.flat = style,
"inode" => self.inode = style,
"blocks" => self.blocks = style,
"header" => self.header = style,
"octal" => self.octal = style,
"flags" => self.flags = style,
"symlink-path" => self.symlink_path = style,
"control-char" => self.control_char = style,
"broken-symlink" => self.broken_symlink = style,
"broken-overlay" => self.broken_path_overlay = style,
_ => return false,
}
true
}
}
#[cfg(test)]
mod is_smoothable_test {
use super::*;
fn rgb(r: u8, g: u8, b: u8) -> Style {
Style::from(Color::Rgb(r, g, b))
}
fn fixed(n: u8) -> Style {
Style::from(Color::Fixed(n))
}
#[test]
fn size_all_rgb_is_smoothable() {
let size = Size {
number_byte: rgb(0x10, 0x10, 0x10),
number_kilo: rgb(0x20, 0x20, 0x20),
number_mega: rgb(0x40, 0x40, 0x40),
number_giga: rgb(0x80, 0x80, 0x80),
number_huge: rgb(0xC0, 0xC0, 0xC0),
..Size::default()
};
assert!(size.is_smoothable());
}
#[test]
fn size_default_is_not_smoothable() {
assert!(!Size::default().is_smoothable());
}
#[test]
fn size_one_palette_anchor_disqualifies_the_column() {
let size = Size {
number_byte: rgb(0x10, 0x10, 0x10),
number_kilo: rgb(0x20, 0x20, 0x20),
number_mega: fixed(196), number_giga: rgb(0x80, 0x80, 0x80),
number_huge: rgb(0xC0, 0xC0, 0xC0),
..Size::default()
};
assert!(!size.is_smoothable());
}
#[test]
fn size_one_unset_anchor_disqualifies_the_column() {
let size = Size {
number_byte: rgb(0x10, 0x10, 0x10),
number_kilo: rgb(0x20, 0x20, 0x20),
number_mega: Style::default(), number_giga: rgb(0x80, 0x80, 0x80),
number_huge: rgb(0xC0, 0xC0, 0xC0),
..Size::default()
};
assert!(!size.is_smoothable());
}
#[test]
fn size_ignores_unit_slots() {
let size = Size {
number_byte: rgb(0x10, 0x10, 0x10),
number_kilo: rgb(0x20, 0x20, 0x20),
number_mega: rgb(0x40, 0x40, 0x40),
number_giga: rgb(0x80, 0x80, 0x80),
number_huge: rgb(0xC0, 0xC0, 0xC0),
unit_byte: fixed(244),
unit_kilo: fixed(244),
unit_mega: fixed(244),
unit_giga: fixed(244),
unit_huge: fixed(244),
..Size::default()
};
assert!(size.is_smoothable());
}
#[test]
fn date_all_rgb_is_smoothable() {
let date = DateAge {
now: rgb(0x3D, 0xD7, 0xD7),
today: rgb(0x3D, 0xD7, 0xD7),
week: rgb(0x3A, 0xAB, 0xAE),
month: rgb(0x3B, 0x8E, 0xD8),
year: rgb(0x88, 0x88, 0x88),
old: rgb(0x5C, 0x5C, 0x5C),
flat: Style::default(),
};
assert!(date.is_smoothable());
}
#[test]
fn date_default_is_not_smoothable() {
assert!(!DateAge::default().is_smoothable());
}
#[test]
fn date_one_palette_tier_disqualifies_the_column() {
let date = DateAge {
now: rgb(0x3D, 0xD7, 0xD7),
today: rgb(0x3D, 0xD7, 0xD7),
week: fixed(30), month: rgb(0x3B, 0x8E, 0xD8),
year: rgb(0x88, 0x88, 0x88),
old: rgb(0x5C, 0x5C, 0x5C),
flat: Style::default(),
};
assert!(!date.is_smoothable());
}
#[test]
fn date_one_unset_tier_disqualifies_the_column() {
let date = DateAge {
now: rgb(0x3D, 0xD7, 0xD7),
today: Style::default(),
week: rgb(0x3A, 0xAB, 0xAE),
month: rgb(0x3B, 0x8E, 0xD8),
year: rgb(0x88, 0x88, 0x88),
old: rgb(0x5C, 0x5C, 0x5C),
flat: Style::default(),
};
assert!(!date.is_smoothable());
}
#[test]
fn date_ignores_flat_slot() {
let date = DateAge {
now: rgb(0x3D, 0xD7, 0xD7),
today: rgb(0x3D, 0xD7, 0xD7),
week: rgb(0x3A, 0xAB, 0xAE),
month: rgb(0x3B, 0x8E, 0xD8),
year: rgb(0x88, 0x88, 0x88),
old: rgb(0x5C, 0x5C, 0x5C),
flat: fixed(244), };
assert!(date.is_smoothable());
}
}