mirror of
https://github.com/eza-community/eza
synced 2024-09-15 22:20:24 +00:00
feat(ui): Make file types themeable
This commit is contained in:
parent
f149fe6afb
commit
b5174b1582
|
@ -7,11 +7,9 @@
|
||||||
//! # Contributors
|
//! # Contributors
|
||||||
//! Please keep these lists sorted. If you're using vim, :sort i
|
//! Please keep these lists sorted. If you're using vim, :sort i
|
||||||
|
|
||||||
use ansiterm::Style;
|
|
||||||
use phf::{phf_map, Map};
|
use phf::{phf_map, Map};
|
||||||
|
|
||||||
use crate::fs::File;
|
use crate::fs::File;
|
||||||
use crate::theme::FileColours;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum FileType {
|
pub enum FileType {
|
||||||
|
@ -269,7 +267,7 @@ impl FileType {
|
||||||
/// Lookup the file type based on the file's name, by the file name
|
/// Lookup the file type based on the file's name, by the file name
|
||||||
/// lowercase extension, or if the file could be compiled from related
|
/// lowercase extension, or if the file could be compiled from related
|
||||||
/// source code.
|
/// source code.
|
||||||
fn get_file_type(file: &File<'_>) -> Option<FileType> {
|
pub(crate) fn get_file_type(file: &File<'_>) -> Option<FileType> {
|
||||||
// Case-insensitive readme is checked first for backwards compatibility.
|
// Case-insensitive readme is checked first for backwards compatibility.
|
||||||
if file.name.to_lowercase().starts_with("readme") {
|
if file.name.to_lowercase().starts_with("readme") {
|
||||||
return Some(Self::Immediate)
|
return Some(Self::Immediate)
|
||||||
|
@ -291,27 +289,3 @@ impl FileType {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FileTypeColor;
|
|
||||||
|
|
||||||
impl FileColours for FileTypeColor {
|
|
||||||
/// Map from the file type to the display style/color for the file.
|
|
||||||
fn colour_file(&self, file: &File<'_>) -> Option<Style> {
|
|
||||||
use ansiterm::Colour::*;
|
|
||||||
|
|
||||||
match FileType::get_file_type(file) {
|
|
||||||
Some(FileType::Compiled) => Some(Yellow.normal()),
|
|
||||||
Some(FileType::Compressed) => Some(Red.normal()),
|
|
||||||
Some(FileType::Crypto) => Some(Green.bold()),
|
|
||||||
Some(FileType::Document) => Some(Green.normal()),
|
|
||||||
Some(FileType::Image) => Some(Purple.normal()),
|
|
||||||
Some(FileType::Immediate) => Some(Yellow.bold().underline()),
|
|
||||||
Some(FileType::Lossless) => Some(Cyan.bold()),
|
|
||||||
Some(FileType::Music) => Some(Cyan.normal()),
|
|
||||||
Some(FileType::Temp) => Some(White.normal()),
|
|
||||||
Some(FileType::Video) => Some(Purple.bold()),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -78,6 +78,19 @@ impl UiStyles {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
file_type: FileType {
|
||||||
|
image: Purple.normal(),
|
||||||
|
video: Purple.bold(),
|
||||||
|
music: Cyan.normal(),
|
||||||
|
lossless: Cyan.bold(),
|
||||||
|
crypto: Green.bold(),
|
||||||
|
document: Green.normal(),
|
||||||
|
compressed: Red.normal(),
|
||||||
|
temp: White.normal(),
|
||||||
|
compiled: Yellow.normal(),
|
||||||
|
immediate: Yellow.bold().underline()
|
||||||
|
},
|
||||||
|
|
||||||
punctuation: DarkGray.bold(),
|
punctuation: DarkGray.bold(),
|
||||||
date: Blue.normal(),
|
date: Blue.normal(),
|
||||||
inode: Purple.normal(),
|
inode: Purple.normal(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use ansiterm::Style;
|
use ansiterm::Style;
|
||||||
|
|
||||||
use crate::fs::File;
|
use crate::fs::File;
|
||||||
|
use crate::info::filetype::FileType;
|
||||||
use crate::output::file_name::Colours as FileNameColours;
|
use crate::output::file_name::Colours as FileNameColours;
|
||||||
use crate::output::render;
|
use crate::output::render;
|
||||||
|
|
||||||
|
@ -65,8 +66,6 @@ impl Options {
|
||||||
|
|
||||||
#[allow(trivial_casts)] // the `as Box<_>` stuff below warns about this for some reason
|
#[allow(trivial_casts)] // the `as Box<_>` stuff below warns about this for some reason
|
||||||
pub fn to_theme(&self, isatty: bool) -> Theme {
|
pub fn to_theme(&self, isatty: bool) -> Theme {
|
||||||
use crate::info::filetype::FileTypeColor;
|
|
||||||
|
|
||||||
if self.use_colours == UseColours::Never || (self.use_colours == UseColours::Automatic && ! isatty) {
|
if self.use_colours == UseColours::Never || (self.use_colours == UseColours::Automatic && ! isatty) {
|
||||||
let ui = UiStyles::plain();
|
let ui = UiStyles::plain();
|
||||||
let exts = Box::new(NoFileColours);
|
let exts = Box::new(NoFileColours);
|
||||||
|
@ -79,10 +78,10 @@ impl Options {
|
||||||
|
|
||||||
// Use between 0 and 2 file name highlighters
|
// Use between 0 and 2 file name highlighters
|
||||||
let exts = match (exts.is_non_empty(), use_default_filetypes) {
|
let exts = match (exts.is_non_empty(), use_default_filetypes) {
|
||||||
(false, false) => Box::new(NoFileColours) as Box<_>,
|
(false, false) => Box::new(NoFileColours) as Box<_>,
|
||||||
(false, true) => Box::new(FileTypeColor) as Box<_>,
|
(false, true) => Box::new(FileTypes) as Box<_>,
|
||||||
( true, false) => Box::new(exts) as Box<_>,
|
( true, false) => Box::new(exts) as Box<_>,
|
||||||
( true, true) => Box::new((exts, FileTypeColor)) as Box<_>,
|
( true, true) => Box::new((exts, FileTypes)) as Box<_>,
|
||||||
};
|
};
|
||||||
|
|
||||||
Theme { ui, exts }
|
Theme { ui, exts }
|
||||||
|
@ -145,13 +144,13 @@ impl Definitions {
|
||||||
|
|
||||||
|
|
||||||
pub trait FileColours: std::marker::Sync {
|
pub trait FileColours: std::marker::Sync {
|
||||||
fn colour_file(&self, file: &File<'_>) -> Option<Style>;
|
fn colour_file(&self, file: &File<'_>, theme: &Theme) -> Option<Style>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
struct NoFileColours;
|
struct NoFileColours;
|
||||||
impl FileColours for NoFileColours {
|
impl FileColours for NoFileColours {
|
||||||
fn colour_file(&self, _file: &File<'_>) -> Option<Style> {
|
fn colour_file(&self, _file: &File<'_>, _theme: &Theme) -> Option<Style> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,9 +163,9 @@ impl<A, B> FileColours for (A, B)
|
||||||
where A: FileColours,
|
where A: FileColours,
|
||||||
B: FileColours,
|
B: FileColours,
|
||||||
{
|
{
|
||||||
fn colour_file(&self, file: &File<'_>) -> Option<Style> {
|
fn colour_file(&self, file: &File<'_>, theme: &Theme) -> Option<Style> {
|
||||||
self.0.colour_file(file)
|
self.0.colour_file(file, theme)
|
||||||
.or_else(|| self.1.colour_file(file))
|
.or_else(|| self.1.colour_file(file, theme))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,17 +175,6 @@ struct ExtensionMappings {
|
||||||
mappings: Vec<(glob::Pattern, Style)>,
|
mappings: Vec<(glob::Pattern, Style)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through backwards so that colours specified later in the list override
|
|
||||||
// colours specified earlier, like we do with options and strict mode
|
|
||||||
|
|
||||||
impl FileColours for ExtensionMappings {
|
|
||||||
fn colour_file(&self, file: &File<'_>) -> Option<Style> {
|
|
||||||
self.mappings.iter().rev()
|
|
||||||
.find(|t| t.0.matches(&file.name))
|
|
||||||
.map (|t| t.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtensionMappings {
|
impl ExtensionMappings {
|
||||||
fn is_non_empty(&self) -> bool {
|
fn is_non_empty(&self) -> bool {
|
||||||
! self.mappings.is_empty()
|
! self.mappings.is_empty()
|
||||||
|
@ -197,8 +185,37 @@ impl ExtensionMappings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loop through backwards so that colours specified later in the list override
|
||||||
|
// colours specified earlier, like we do with options and strict mode
|
||||||
|
|
||||||
|
impl FileColours for ExtensionMappings {
|
||||||
|
fn colour_file(&self, file: &File<'_>, _theme: &Theme) -> Option<Style> {
|
||||||
|
self.mappings.iter().rev()
|
||||||
|
.find(|t| t.0.matches(&file.name))
|
||||||
|
.map (|t| t.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FileTypes;
|
||||||
|
|
||||||
|
impl FileColours for FileTypes {
|
||||||
|
fn colour_file(&self, file: &File<'_>, theme: &Theme) -> Option<Style> {
|
||||||
|
match FileType::get_file_type(file) {
|
||||||
|
Some(FileType::Image) => Some(theme.ui.file_type.image),
|
||||||
|
Some(FileType::Video) => Some(theme.ui.file_type.video),
|
||||||
|
Some(FileType::Music) => Some(theme.ui.file_type.music),
|
||||||
|
Some(FileType::Lossless) => Some(theme.ui.file_type.lossless),
|
||||||
|
Some(FileType::Crypto) => Some(theme.ui.file_type.crypto),
|
||||||
|
Some(FileType::Document) => Some(theme.ui.file_type.document),
|
||||||
|
Some(FileType::Compressed) => Some(theme.ui.file_type.compressed),
|
||||||
|
Some(FileType::Temp) => Some(theme.ui.file_type.temp),
|
||||||
|
Some(FileType::Compiled) => Some(theme.ui.file_type.compiled),
|
||||||
|
Some(FileType::Immediate) => Some(theme.ui.file_type.immediate),
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
impl render::BlocksColours for Theme {
|
impl render::BlocksColours for Theme {
|
||||||
|
@ -330,7 +347,7 @@ impl FileNameColours for Theme {
|
||||||
fn mount_point(&self) -> Style { self.ui.filekinds.mount_point }
|
fn mount_point(&self) -> Style { self.ui.filekinds.mount_point }
|
||||||
|
|
||||||
fn colour_file(&self, file: &File<'_>) -> Style {
|
fn colour_file(&self, file: &File<'_>) -> Style {
|
||||||
self.exts.colour_file(file).unwrap_or(self.ui.filekinds.normal)
|
self.exts.colour_file(file, &self).unwrap_or(self.ui.filekinds.normal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,6 +554,17 @@ mod customs_test {
|
||||||
|
|
||||||
test!(exa_mp: ls "", exa "mp=1;34;4" => colours c -> { c.filekinds.mount_point = Blue.bold().underline(); });
|
test!(exa_mp: ls "", exa "mp=1;34;4" => colours c -> { c.filekinds.mount_point = Blue.bold().underline(); });
|
||||||
|
|
||||||
|
test!(exa_im: ls "", exa "im=38;5;128" => colours c -> { c.file_type.image = Fixed(128).normal(); });
|
||||||
|
test!(exa_vi: ls "", exa "vi=38;5;129" => colours c -> { c.file_type.video = Fixed(129).normal(); });
|
||||||
|
test!(exa_mu: ls "", exa "mu=38;5;130" => colours c -> { c.file_type.music = Fixed(130).normal(); });
|
||||||
|
test!(exa_lo: ls "", exa "lo=38;5;131" => colours c -> { c.file_type.lossless = Fixed(131).normal(); });
|
||||||
|
test!(exa_cr: ls "", exa "cr=38;5;132" => colours c -> { c.file_type.crypto = Fixed(132).normal(); });
|
||||||
|
test!(exa_do: ls "", exa "do=38;5;133" => colours c -> { c.file_type.document = Fixed(133).normal(); });
|
||||||
|
test!(exa_co: ls "", exa "co=38;5;134" => colours c -> { c.file_type.compressed = Fixed(134).normal(); });
|
||||||
|
test!(exa_tm: ls "", exa "tm=38;5;135" => colours c -> { c.file_type.temp = Fixed(135).normal(); });
|
||||||
|
test!(exa_cm: ls "", exa "cm=38;5;136" => colours c -> { c.file_type.compiled = Fixed(136).normal(); });
|
||||||
|
test!(exa_ie: ls "", exa "ie=38;5;137" => colours c -> { c.file_type.immediate = Fixed(137).normal(); });
|
||||||
|
|
||||||
// All the while, LS_COLORS treats them as filenames:
|
// All the while, LS_COLORS treats them as filenames:
|
||||||
test!(ls_uu: ls "uu=38;5;117", exa "" => exts [ ("uu", Fixed(117).normal()) ]);
|
test!(ls_uu: ls "uu=38;5;117", exa "" => exts [ ("uu", Fixed(117).normal()) ]);
|
||||||
test!(ls_un: ls "un=38;5;118", exa "" => exts [ ("un", Fixed(118).normal()) ]);
|
test!(ls_un: ls "un=38;5;118", exa "" => exts [ ("un", Fixed(118).normal()) ]);
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub struct UiStyles {
|
||||||
pub links: Links,
|
pub links: Links,
|
||||||
pub git: Git,
|
pub git: Git,
|
||||||
pub security_context: SecurityContext,
|
pub security_context: SecurityContext,
|
||||||
|
pub file_type: FileType,
|
||||||
|
|
||||||
pub punctuation: Style,
|
pub punctuation: Style,
|
||||||
pub date: Style,
|
pub date: Style,
|
||||||
|
@ -121,6 +122,20 @@ pub struct SecurityContext {
|
||||||
pub selinux: SELinuxContext,
|
pub selinux: SELinuxContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct FileType {
|
||||||
|
pub image: Style,
|
||||||
|
pub video: Style,
|
||||||
|
pub music: Style,
|
||||||
|
pub lossless: Style,
|
||||||
|
pub crypto: Style,
|
||||||
|
pub document: Style,
|
||||||
|
pub compressed: Style,
|
||||||
|
pub temp: Style,
|
||||||
|
pub compiled: Style,
|
||||||
|
pub immediate: Style,
|
||||||
|
}
|
||||||
|
|
||||||
impl UiStyles {
|
impl UiStyles {
|
||||||
pub fn plain() -> Self {
|
pub fn plain() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
|
@ -213,6 +228,17 @@ impl UiStyles {
|
||||||
|
|
||||||
"mp" => self.filekinds.mount_point = pair.to_style(),
|
"mp" => self.filekinds.mount_point = pair.to_style(),
|
||||||
|
|
||||||
|
"im" => self.file_type.image = pair.to_style(),
|
||||||
|
"vi" => self.file_type.video = pair.to_style(),
|
||||||
|
"mu" => self.file_type.music = pair.to_style(),
|
||||||
|
"lo" => self.file_type.lossless = pair.to_style(),
|
||||||
|
"cr" => self.file_type.crypto = pair.to_style(),
|
||||||
|
"do" => self.file_type.document = pair.to_style(),
|
||||||
|
"co" => self.file_type.compressed = pair.to_style(),
|
||||||
|
"tm" => self.file_type.temp = pair.to_style(),
|
||||||
|
"cm" => self.file_type.compiled = pair.to_style(),
|
||||||
|
"ie" => self.file_type.immediate = pair.to_style(),
|
||||||
|
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue