icon: icon theme functionally works

This commit is contained in:
Wei Zhang 2022-07-30 00:44:01 +08:00 committed by Abin Simon
parent b209c6f7f1
commit e2723ef650
7 changed files with 92 additions and 76 deletions

View file

@ -3,9 +3,8 @@ use crossterm::style::{Attribute, ContentStyle, StyledContent, Stylize};
use lscolors::{Indicator, LsColors};
use std::path::Path;
use crate::theme::{Theme,color::ColorTheme};
pub use crate::flags::color::ThemeOption;
use crate::theme::{color::ColorTheme, Theme};
#[allow(dead_code)]
#[derive(Hash, Debug, Eq, PartialEq, Clone)]
@ -138,7 +137,9 @@ impl Colors {
let theme = match t {
ThemeOption::NoColor => None,
ThemeOption::Default | ThemeOption::NoLscolors => Some(Theme::default().color),
ThemeOption::Custom(ref file) => Some(Theme::from_path::<ColorTheme>(file).unwrap_or_default()),
ThemeOption::Custom(ref file) => {
Some(Theme::from_path::<ColorTheme>(file).unwrap_or_default())
}
};
let lscolors = match t {
ThemeOption::Default | ThemeOption::Custom(_) => {
@ -299,8 +300,8 @@ fn to_content_style(ls: &lscolors::Style) -> ContentStyle {
#[cfg(test)]
mod tests {
use super::Colors;
use crate::theme::color_theme::Theme;
use crate::color::ThemeOption;
use crate::theme::color_theme::Theme;
#[test]
fn test_color_new_no_color_theme() {
assert!(Colors::new(ThemeOption::NoColor).theme.is_none());

View file

@ -47,11 +47,8 @@ impl Core {
_ => flags.color.theme.clone(),
};
let icon_theme = match (tty_available, flags.icons.when, flags.icons.theme) {
(_, IconOption::Never, _) | (false, IconOption::Auto, _) => icon::Theme::NoIcon,
(_, _, IconTheme::Fancy) => icon::Theme::Fancy,
(_, _, IconTheme::Unicode) => icon::Theme::Unicode,
};
let icon_when = flags.icons.when;
let icon_theme = flags.icons.theme.clone();
// TODO: Rework this so that flags passed downstream does not
// have Auto option for any (icon, color, hyperlink).
@ -80,7 +77,7 @@ impl Core {
Self {
flags,
colors: Colors::new(color_theme),
icons: Icons::new(icon_theme, icon_separator),
icons: Icons::new(tty_available, icon_when, icon_theme, icon_separator),
sorters,
}
}

View file

@ -90,12 +90,13 @@ impl Configurable<Self> for IconOption {
}
/// The flag showing which icon theme to use.
#[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize, Default)]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum IconTheme {
Unicode,
#[default]
Fancy,
Custom(String),
}
impl IconTheme {

View file

@ -1,14 +1,11 @@
use std::collections::HashMap;
use crate::flags::{IconOption, IconTheme as FlagTheme};
use crate::meta::{FileType, Name};
use crate::flags::IconOption;
use crate::theme::{Theme, icon::IconTheme};
use crate::theme::{icon::IconTheme, Theme};
pub struct Icons {
display_icons: bool,
icon_separator: String,
theme: IconTheme,
theme: Option<IconTheme>,
}
// In order to add a new icon, write the unicode value like "\ue5fb" then
@ -16,61 +13,65 @@ pub struct Icons {
//
// s#\\u[0-9a-f]*#\=eval('"'.submatch(0).'"')#
impl Icons {
pub fn new(opt: IconOption, icon_separator: String) -> Self {
let display_icons = !(opt == IconOption::Never); // TODO(zwpaper): Auto
pub fn new(tty: bool, when: IconOption, theme: FlagTheme, icon_separator: String) -> Self {
let icon_theme = match (tty, when, theme) {
(_, IconOption::Never, _) | (false, IconOption::Auto, _) => None,
(_, _, FlagTheme::Fancy) => Some(IconTheme::default()),
(_, _, FlagTheme::Unicode) => Some(IconTheme::unicode()),
(_, _, FlagTheme::Custom(ref file)) => {
Some(Theme::from_path::<IconTheme>(file).unwrap_or_default())
}
};
Self {
display_icons,
icon_separator,
theme: Theme::default().icon,
theme: icon_theme,
}
}
pub fn get(&self, name: &Name) -> String {
if !self.display_icons {
return String::new();
}
// Check file types
let file_type: FileType = name.file_type();
let icon = match file_type {
FileType::SymLink { is_dir: true } => "\u{f482}", // ""
FileType::SymLink { is_dir: false } => "\u{f481}", // ""
FileType::Socket => "\u{f6a7}", // ""
FileType::Pipe => "\u{f731}", // ""
FileType::CharDevice => "\u{e601}", // ""
FileType::BlockDevice => "\u{fc29}", // "ﰩ"
FileType::Special => "\u{f2dc}", // ""
_ => {
// Use the known names
if let Some(icon) = self
.theme
.icons_by_name
.get(name.file_name().to_lowercase().as_str())
{
icon
}
// Use the known extensions
else if let Some(icon) = name.extension().and_then(|extension| {
self.theme
.icons_by_extension
.get(extension.to_lowercase().as_str())
}) {
icon
} else {
match file_type {
FileType::Directory { .. } => &self.theme.default_folder_icon,
// If a file has no extension and is executable, show an icon.
// Except for Windows, it marks everything as an executable.
#[cfg(not(windows))]
FileType::File { exec: true, .. } => "\u{f489}", // ""
_ => &self.theme.default_file_icon,
match &self.theme {
None => String::new(),
Some(t) => {
// Check file types
let file_type: FileType = name.file_type();
let icon = match file_type {
FileType::SymLink { is_dir: true } => "\u{f482}", // ""
FileType::SymLink { is_dir: false } => "\u{f481}", // ""
FileType::Socket => "\u{f6a7}", // ""
FileType::Pipe => "\u{f731}", // ""
FileType::CharDevice => "\u{e601}", // ""
FileType::BlockDevice => "\u{fc29}", // "ﰩ"
FileType::Special => "\u{f2dc}", // ""
_ => {
// Use the known names
if let Some(icon) = t
.icons_by_name
.get(name.file_name().to_lowercase().as_str())
{
icon
}
// Use the known extensions
else if let Some(icon) = name.extension().and_then(|extension| {
t.icons_by_extension.get(extension.to_lowercase().as_str())
}) {
icon
} else {
match file_type {
FileType::Directory { .. } => &t.default_folder_icon,
// If a file has no extension and is executable, show an icon.
// Except for Windows, it marks everything as an executable.
#[cfg(not(windows))]
FileType::File { exec: true, .. } => "\u{f489}", // ""
_ => &t.default_file_icon,
}
}
}
}
}
};
};
format!("{}{}", icon, self.icon_separator)
format!("{}{}", icon, self.icon_separator)
}
}
}
}

View file

@ -1,9 +1,9 @@
pub mod color;
pub mod icon;
use serde::{Deserialize, de::DeserializeOwned};
use std::path::Path;
use serde::{de::DeserializeOwned, Deserialize};
use std::fs;
use std::path::Path;
use crate::config_file;
use crate::print_error;
@ -23,7 +23,7 @@ pub struct Theme {
impl Default for Theme {
fn default() -> Self {
// TODO(zwpaper): check terminal color and return light or dark
Theme{
Theme {
color: ColorTheme::default(),
icon: IconTheme::default(),
}

View file

@ -6,7 +6,7 @@ use std::fmt;
// Custom color deserialize
fn deserialize_color<'de, D>(deserializer: D) -> Result<Color, D::Error>
where
where
D: serde::de::Deserializer<'de>,
{
struct ColorVisitor;
@ -78,7 +78,6 @@ fn deserialize_color<'de, D>(deserializer: D) -> Result<Color, D::Error>
deserializer.deserialize_any(ColorVisitor)
}
/// A struct holding the theme configuration
/// Color table: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.avg
#[derive(Debug, Deserialize, PartialEq, Eq)]

View file

@ -1,5 +1,5 @@
use std::collections::HashMap;
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
@ -16,7 +16,7 @@ pub struct IconTheme {
impl Default for IconTheme {
fn default() -> Self {
// TODO(zwpaper): check terminal color and return light or dark
IconTheme{
IconTheme {
icons_by_name: Self::get_default_icons_by_name(),
icons_by_extension: Self::get_default_icons_by_extension(),
default_folder_icon: "\u{f115}".into(),
@ -26,6 +26,16 @@ impl Default for IconTheme {
}
impl IconTheme {
pub fn unicode() -> Self {
// TODO(zwpaper): check terminal color and return light or dark
IconTheme {
icons_by_name: HashMap::new(),
icons_by_extension: HashMap::new(),
default_folder_icon: "\u{1f5cb}".into(),
default_file_icon: "\u{1f5c1}".into(),
}
}
fn get_default_icons_by_name() -> HashMap<String, String> {
// Note: filenames must be lower-case
[
@ -147,15 +157,19 @@ impl IconTheme {
("xmonad.hs", "\u{e615}"), // ""
("xorg.conf.d", "\u{e5fc}"), // ""
("xbps.d", "\u{e5fc}"), // ""
].iter().map(|&s| (s.0.to_owned(), s.1.to_owned())).collect::<HashMap<_, _>>()
]
.iter()
.map(|&s| (s.0.to_owned(), s.1.to_owned()))
.collect::<HashMap<_, _>>()
}
fn get_default_icons_by_extension() -> HashMap<String, String> {
// Note: extensions must be lower-case
[("1", "\u{f02d}"), // ""
("7z", "\u{f410}"), // ""
("a", "\u{e624}"), // ""
("ai", "\u{e7b4}"), // ""
[
("1", "\u{f02d}"), // ""
("7z", "\u{f410}"), // ""
("a", "\u{e624}"), // ""
("ai", "\u{e7b4}"), // ""
("ape", "\u{f001}"), // ""
("apk", "\u{e70e}"), // ""
("asc", "\u{f023}"), // ""
@ -407,6 +421,9 @@ impl IconTheme {
("zsh-theme", "\u{f489}"), // ""
("zshrc", "\u{f489}"), // ""
("zst", "\u{f410}"), // ""
].iter().map(|&s| (s.0.to_owned(), s.1.to_owned())).collect::<HashMap<_, _>>()
]
.iter()
.map(|&s| (s.0.to_owned(), s.1.to_owned()))
.collect::<HashMap<_, _>>()
}
}