feat: icon color and ordered icon rules support (#503)

This commit is contained in:
三咲雅 · Misaki Masa 2024-01-10 23:24:36 +08:00 committed by GitHub
parent cd84386ead
commit 560a1bf3f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 201 additions and 170 deletions

View File

@ -164,145 +164,149 @@ rules = [
{ name = "*/", fg = "blue" } { name = "*/", fg = "blue" }
] ]
[icons] [icon]
"Desktop/" = "" rules = [
"Documents/" = "" # Programming
"Downloads/" = "" { name = "*.c" , text = "" },
"Pictures/" = "" { name = "*.cpp" , text = "" },
"Music/" = "" { name = "*.css" , text = "" },
"Movies/" = "" { name = "*.fish" , text = "" },
"Videos/" = "" { name = "*.go" , text = "" },
"Public/" = "" { name = "*.h" , text = "" },
"Library/" = "" { name = "*.hpp" , text = "" },
"Development/" = "" { name = "*.html" , text = "" },
".config/" = "" { name = "*.java" , text = "" },
{ name = "*.js" , text = "" },
# Git { name = "*.jsx" , text = "" },
".git/" = "" { name = "*.lua" , text = "" },
".gitignore" = "" { name = "*.nix" , text = "" },
".gitmodules" = "" { name = "*.php" , text = "" },
".gitattributes" = "" { name = "*.py" , text = "" },
{ name = "*.rb" , text = "" },
# Dotfiles { name = "*.rs" , text = "" },
".DS_Store" = "" { name = "*.scss" , text = "" },
".bashrc" = "" { name = "*.sh" , text = "" },
".bashprofile" = "" { name = "*.swift", text = "" },
".zshrc" = "" { name = "*.ts" , text = "" },
".zshenv" = "" { name = "*.tsx" , text = "" },
".zprofile" = "" { name = "*.vim" , text = "" },
".vimrc" = "" { name = "*.vue" , text = "󰡄" },
# Text # Text
"*.txt" = "" { name = "*.conf", text = "" },
"*.md" = "" { name = "*.ini" , text = "" },
"*.rst" = "" { name = "*.json", text = "" },
COPYING = "󰿃" { name = "*.md" , text = "" },
LICENSE = "󰿃" { name = "*.toml", text = "" },
{ name = "*.txt", text = "" },
{ name = "*.yaml", text = "" },
{ name = "*.yml" , text = "" },
# Archives # Archives
"*.zip" = "" { name = "*.7z" , text = "" },
"*.tar" = "" { name = "*.bz2", text = "" },
"*.gz" = "" { name = "*.gz" , text = "" },
"*.7z" = "" { name = "*.rar", text = "" },
"*.bz2" = "" { name = "*.tar", text = "" },
"*.xz" = "" { name = "*.xz" , text = "" },
{ name = "*.zip", text = "" },
# Documents
"*.csv" = ""
"*.doc" = ""
"*.doct" = ""
"*.docx" = ""
"*.dot" = ""
"*.ods" = ""
"*.ots" = ""
"*.pdf" = ""
"*.pom" = ""
"*.pot" = ""
"*.ppm" = ""
"*.pps" = ""
"*.ppt" = ""
"*.potx" = ""
"*.ppmx" = ""
"*.ppsx" = ""
"*.pptx" = ""
"*.xlc" = ""
"*.xlm" = ""
"*.xls" = ""
"*.xlt" = ""
"*.xlsm" = ""
"*.xlsx" = ""
# Audio
"*.mp3" = ""
"*.flac" = ""
"*.wav" = ""
"*.aac" = ""
"*.ogg" = ""
"*.m4a" = ""
"*.mp2" = ""
# Movies
"*.mp4" = ""
"*.mkv" = ""
"*.avi" = ""
"*.mov" = ""
"*.webm" = ""
# Images # Images
"*.jpg" = "" { name = "*.HEIC", text = "" },
"*.jpeg" = "" { name = "*.avif", text = "" },
"*.png" = "" { name = "*.bmp" , text = "" },
"*.gif" = "" { name = "*.gif" , text = "" },
"*.webp" = "" { name = "*.ico" , text = "" },
"*.avif" = "" { name = "*.jpeg", text = "" },
"*.bmp" = "" { name = "*.jpg" , text = "" },
"*.ico" = "" { name = "*.png" , text = "" },
"*.svg" = "" { name = "*.svg" , text = "" },
"*.xcf" = "" { name = "*.webp", text = "" },
"*.HEIC" = ""
# Programming # Movies
"*.c" = "" { name = "*.avi" , text = "" },
"*.cpp" = "" { name = "*.mkv" , text = "" },
"*.h" = "" { name = "*.mov" , text = "" },
"*.hpp" = "" { name = "*.mp4" , text = "" },
"*.rs" = "" { name = "*.webm", text = "" },
"*.go" = ""
"*.py" = "" # Audio
"*.js" = "" { name = "*.aac" , text = "" },
"*.ts" = "" { name = "*.flac", text = "" },
"*.tsx" = "" { name = "*.m4a" , text = "" },
"*.jsx" = "" { name = "*.mp3" , text = "" },
"*.rb" = "" { name = "*.ogg" , text = "" },
"*.php" = "" { name = "*.wav" , text = "" },
"*.java" = ""
"*.sh" = "" # Documents
"*.fish" = "" { name = "*.csv" , text = "" },
"*.swift" = "" { name = "*.doc" , text = "" },
"*.vim" = "" { name = "*.doct", text = "" },
"*.lua" = "" { name = "*.docx", text = "" },
"*.html" = "" { name = "*.dot" , text = "" },
"*.css" = "" { name = "*.ods" , text = "" },
"*.scss" = "" { name = "*.ots" , text = "" },
"*.json" = "" { name = "*.pdf" , text = "" },
"*.toml" = "" { name = "*.pom" , text = "" },
"*.yml" = "" { name = "*.pot" , text = "" },
"*.yaml" = "" { name = "*.potx", text = "" },
"*.ini" = "" { name = "*.ppm" , text = "" },
"*.conf" = "" { name = "*.ppmx", text = "" },
"*.lock" = "" { name = "*.pps" , text = "" },
"*.nix" = "" { name = "*.ppsx", text = "" },
Containerfile = "󰡨" { name = "*.ppt" , text = "" },
Dockerfile = "󰡨" { name = "*.pptx", text = "" },
{ name = "*.xlc" , text = "" },
{ name = "*.xlm" , text = "" },
{ name = "*.xls" , text = "" },
{ name = "*.xlsm", text = "" },
{ name = "*.xlsx", text = "" },
{ name = "*.xlt" , text = "" },
# Lockfiles
{ name = "*.lock", text = "" },
# Misc # Misc
"*.bin" = "" { name = "*.bin", text = "" },
"*.exe" = "" { name = "*.exe", text = "" },
"*.pkg" = "" { name = "*.pkg", text = "" },
# Dotfiles
{ name = ".DS_Store" , text = "" },
{ name = ".bashprofile" , text = "" },
{ name = ".bashrc" , text = "" },
{ name = ".gitattributes", text = "" },
{ name = ".gitignore" , text = "" },
{ name = ".gitmodules" , text = "" },
{ name = ".vimrc" , text = "" },
{ name = ".zprofile" , text = "" },
{ name = ".zshenv" , text = "" },
{ name = ".zshrc" , text = "" },
# Named files
{ name = "COPYING" , text = "󰿃" },
{ name = "Containerfile", text = "󰡨" },
{ name = "Dockerfile" , text = "󰡨" },
{ name = "LICENSE" , text = "󰿃" },
# Directories
{ name = ".config/" , text = "" },
{ name = ".git/" , text = "" },
{ name = "Desktop/" , text = "" },
{ name = "Development/", text = "" },
{ name = "Documents/" , text = "" },
{ name = "Downloads/" , text = "" },
{ name = "Library/" , text = "" },
{ name = "Movies/" , text = "" },
{ name = "Music/" , text = "" },
{ name = "Pictures/" , text = "" },
{ name = "Public/" , text = "" },
{ name = "Videos/" , text = "" },
# Default # Default
"*" = "" { name = "*" , text = "" },
"*/" = "" { name = "*/", text = "" },
]
# : }}} # : }}}

View File

@ -1,13 +1,12 @@
use std::fmt; use serde::{Deserialize, Deserializer};
use serde::{de::{self, Visitor}, Deserializer}; use super::Style;
use crate::{theme::{Color, StyleShadow}, Pattern};
use crate::Pattern;
#[derive(Debug)]
pub struct Icon { pub struct Icon {
pub name: Pattern, pub name: Pattern,
pub display: String, pub text: String,
pub style: Style,
} }
impl Icon { impl Icon {
@ -15,31 +14,28 @@ impl Icon {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
struct IconVisitor; #[derive(Deserialize)]
struct IconOuter {
rules: Vec<IconRule>,
}
#[derive(Deserialize)]
struct IconRule {
name: Pattern,
text: String,
impl<'de> Visitor<'de> for IconVisitor { fg: Option<Color>,
type Value = Vec<Icon>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a icon rule, e.g. \"*.md\" = \"\"")
} }
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> Ok(
where IconOuter::deserialize(deserializer)?
A: de::MapAccess<'de>, .rules
{ .into_iter()
let mut icons = vec![]; .map(|r| Icon {
while let Some((key, value)) = &map.next_entry::<String, String>()? { name: r.name,
icons.push(Icon { text: r.text,
name: Pattern::try_from(key.clone()) style: StyleShadow { fg: r.fg, ..Default::default() }.into(),
.map_err(|e| de::Error::custom(e.to_string()))?, })
display: value.clone(), .collect::<Vec<_>>(),
}); )
}
Ok(icons)
}
}
deserializer.deserialize_map(IconVisitor)
} }
} }

View File

@ -36,7 +36,7 @@ impl From<Style> for ratatui::style::Style {
} }
} }
#[derive(Deserialize)] #[derive(Default, Deserialize)]
pub(super) struct StyleShadow { pub(super) struct StyleShadow {
#[serde(default)] #[serde(default)]
pub(super) fg: Option<Color>, pub(super) fg: Option<Color>,

View File

@ -130,7 +130,7 @@ pub struct Theme {
// File-specific styles // File-specific styles
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)] #[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
pub filetypes: Vec<Filetype>, pub filetypes: Vec<Filetype>,
#[serde(deserialize_with = "Icon::deserialize", skip_serializing)] #[serde(rename = "icon", deserialize_with = "Icon::deserialize", skip_serializing)]
pub icons: Vec<Icon>, pub icons: Vec<Icon>,
} }

View File

@ -1,6 +1,6 @@
use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, UserDataFields, UserDataMethods, Value}; use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, UserDataFields, UserDataMethods, Value};
use yazi_config::{LAYOUT, THEME}; use yazi_config::{LAYOUT, THEME};
use yazi_plugin::{bindings::{Cast, File, Range, Url}, elements::Style}; use yazi_plugin::{bindings::{Cast, File, Icon, Range, Url}, elements::Style};
use yazi_shared::MIME_DIR; use yazi_shared::MIME_DIR;
use super::{CtxRef, FolderRef}; use super::{CtxRef, FolderRef};
@ -69,14 +69,13 @@ impl<'a, 'b> Folder<'a, 'b> {
p.next_back(); p.next_back();
Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose() Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose()
}); });
reg.add_method("icon", |_, me, ()| { reg.add_method("icon", |lua, me, ()| {
Ok(
THEME THEME
.icons .icons
.iter() .iter()
.find(|&x| x.name.match_path(&me.url, me.is_dir())) .find(|&x| x.name.match_path(&me.url, me.is_dir()))
.map(|x| x.display.to_string()), .map(|x| Icon::cast(lua, x))
) .transpose()
}); });
reg.add_function("style", |lua, me: AnyUserData| { reg.add_function("style", |lua, me: AnyUserData| {
let cx = lua.named_registry_value::<CtxRef>("cx")?; let cx = lua.named_registry_value::<CtxRef>("cx")?;

View File

@ -12,6 +12,7 @@ pub(crate) struct Lives;
impl Lives { impl Lives {
pub(crate) fn register() -> mlua::Result<()> { pub(crate) fn register() -> mlua::Result<()> {
yazi_plugin::bindings::Cha::register(&LUA)?; yazi_plugin::bindings::Cha::register(&LUA)?;
yazi_plugin::bindings::Icon::register(&LUA)?;
yazi_plugin::bindings::Url::register(&LUA)?; yazi_plugin::bindings::Url::register(&LUA)?;
super::Active::register(&LUA)?; super::Active::register(&LUA)?;

View File

@ -14,7 +14,10 @@ function Folder:by_kind(kind)
end end
end end
function Folder:icon(file) return ui.Span(" " .. file:icon() .. " ") end function Folder:icon(file)
local icon = file:icon()
return icon and ui.Span(" " .. icon.text .. " "):style(icon.style) or ui.Span("")
end
function Folder:highlighted_name(file) function Folder:highlighted_name(file)
-- Complete prefix when searching across directories -- Complete prefix when searching across directories

View File

@ -0,0 +1,26 @@
use mlua::{AnyUserData, Lua, UserDataFields};
use super::Cast;
use crate::elements::Style;
pub struct Icon;
impl Icon {
pub fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<&yazi_config::theme::Icon>(|reg| {
reg.add_field_method_get("text", |lua, me| lua.create_string(&me.text));
reg.add_field_method_get("style", |_, me| Ok(Style::from(me.style)));
})?;
Ok(())
}
}
impl Cast<&'static yazi_config::theme::Icon> for Icon {
fn cast<'lua>(
lua: &'lua Lua,
data: &'static yazi_config::theme::Icon,
) -> mlua::Result<AnyUserData<'lua>> {
lua.create_any_userdata(data)
}
}

View File

@ -3,6 +3,7 @@
mod bindings; mod bindings;
mod cha; mod cha;
mod file; mod file;
mod icon;
mod range; mod range;
mod url; mod url;
mod window; mod window;
@ -10,6 +11,7 @@ mod window;
pub use bindings::*; pub use bindings::*;
pub use cha::*; pub use cha::*;
pub use file::*; pub use file::*;
pub use icon::*;
pub use range::*; pub use range::*;
pub use url::*; pub use url::*;
pub use window::*; pub use window::*;