mirror of
https://github.com/lsd-rs/lsd
synced 2024-12-13 21:52:37 +00:00
✨ icon: icon theme functionally works
This commit is contained in:
parent
b209c6f7f1
commit
e2723ef650
7 changed files with 92 additions and 76 deletions
|
@ -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());
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
105
src/icon.rs
105
src/icon.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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<_, _>>()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue