feat: add support for highlighting by file type (#510)

This commit is contained in:
Azad 2024-01-13 16:19:40 +01:00 committed by GitHub
parent 876419a6c4
commit bf9f8f4273
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 123 additions and 54 deletions

14
Cargo.lock generated
View File

@ -2634,7 +2634,7 @@ dependencies = [
[[package]]
name = "yazi-adaptor"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"anyhow",
"arc-swap",
@ -2652,7 +2652,7 @@ dependencies = [
[[package]]
name = "yazi-config"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"anyhow",
"arc-swap",
@ -2677,7 +2677,7 @@ dependencies = [
[[package]]
name = "yazi-core"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"anyhow",
"base64",
@ -2707,7 +2707,7 @@ dependencies = [
[[package]]
name = "yazi-fm"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"ansi-to-tui",
"anyhow",
@ -2734,7 +2734,7 @@ dependencies = [
[[package]]
name = "yazi-plugin"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"ansi-to-tui",
"anyhow",
@ -2767,7 +2767,7 @@ checksum = "f4b6c8e12e39ac0f79fa96f36e5b88e0da8d230691abd729eec709b43c74f632"
[[package]]
name = "yazi-scheduler"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"anyhow",
"async-priority-channel",
@ -2788,7 +2788,7 @@ dependencies = [
[[package]]
name = "yazi-shared"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"anyhow",
"bitflags 2.4.1",

View File

@ -1,4 +1,4 @@
# A TOML linter such as https://taplo.tamasfe.dev/ can use this schema to validate your config.
# A TOML linter such as https://taplo.tamasfe.dev/ can use this schema to validate your config.
# If you encounter any issues, please make an issue at https://github.com/yazi-rs/schemas.
"$schema" = "https://yazi-rs.github.io/schemas/keymap.json"

View File

@ -1,4 +1,4 @@
# A TOML linter such as https://taplo.tamasfe.dev/ can use this schema to validate your config.
# A TOML linter such as https://taplo.tamasfe.dev/ can use this schema to validate your config.
# If you encounter any issues, please make an issue at https://github.com/yazi-rs/schemas.
"$schema" = "https://yazi-rs.github.io/schemas/theme.json"

View File

@ -1,4 +1,4 @@
# A TOML linter such as https://taplo.tamasfe.dev/ can use this schema to validate your config.
# A TOML linter such as https://taplo.tamasfe.dev/ can use this schema to validate your config.
# If you encounter any issues, please make an issue at https://github.com/yazi-rs/schemas.
"$schema" = "https://yazi-rs.github.io/schemas/yazi.json"

View File

@ -1,21 +1,36 @@
use std::path::Path;
use std::str::FromStr;
use anyhow::bail;
use serde::{Deserialize, Deserializer};
use yazi_shared::MIME_DIR;
use yazi_shared::fs::File;
use super::{Color, Style, StyleShadow};
use crate::Pattern;
pub struct Filetype {
pub is: FiletypeIs,
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
pub style: Style,
}
impl Filetype {
pub fn matches(&self, path: &Path, mime: Option<&str>) -> bool {
let is_dir = mime == Some(MIME_DIR);
self.name.as_ref().is_some_and(|n| n.match_path(path, is_dir))
pub fn matches(&self, file: &File, mime: Option<&str>) -> bool {
let b = match self.is {
FiletypeIs::None => true,
FiletypeIs::Block => file.cha.is_block_device(),
FiletypeIs::Exec => file.cha.is_exec(),
FiletypeIs::Fifo => file.cha.is_fifo(),
FiletypeIs::Link => file.cha.is_link(),
FiletypeIs::Orphan => file.cha.is_orphan(),
FiletypeIs::Sock => file.cha.is_socket(),
FiletypeIs::Sticky => file.cha.is_sticky(),
};
if !b {
return false;
}
self.name.as_ref().is_some_and(|n| n.match_path(&file.url, file.is_dir()))
|| self.mime.as_ref().zip(mime).map_or(false, |(m, s)| m.matches(s))
}
}
@ -31,6 +46,8 @@ impl Filetype {
}
#[derive(Deserialize)]
struct FiletypeRule {
#[serde(default)]
is: FiletypeIs,
name: Option<Pattern>,
mime: Option<Pattern>,
@ -61,6 +78,7 @@ impl Filetype {
.rules
.into_iter()
.map(|r| Filetype {
is: r.is,
name: r.name,
mime: r.mime,
style: StyleShadow {
@ -82,3 +100,41 @@ impl Filetype {
)
}
}
// --- FiletypeIs
#[derive(Default, Deserialize)]
#[serde(try_from = "String")]
pub enum FiletypeIs {
#[default]
None,
Block,
Exec,
Fifo,
Link,
Orphan,
Sock,
Sticky,
}
impl FromStr for FiletypeIs {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"block" => Self::Block,
"exec" => Self::Exec,
"fifo" => Self::Fifo,
"link" => Self::Link,
"orphan" => Self::Orphan,
"sock" => Self::Sock,
"sticky" => Self::Sticky,
_ => bail!("invalid filetype: {s}"),
})
}
}
impl TryFrom<String> for FiletypeIs {
type Error = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
}

View File

@ -87,13 +87,7 @@ impl<'a, 'b> Folder<'a, 'b> {
cx.manager.mimetype.get(&file.url).map(|x| &**x)
};
Ok(
THEME
.filetypes
.iter()
.find(|&x| x.matches(&file.url, mime))
.map(|x| Style::from(x.style)),
)
Ok(THEME.filetypes.iter().find(|&x| x.matches(&file, mime)).map(|x| Style::from(x.style)))
});
reg.add_function("is_hovered", |_, me: AnyUserData| {
let folder = me.named_user_value::<FolderRef>("folder")?;

View File

@ -12,17 +12,18 @@ impl Cha {
reg.add_field_method_get("is_dir", |_, me| Ok(me.is_dir()));
reg.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden()));
reg.add_field_method_get("is_link", |_, me| Ok(me.is_link()));
reg.add_field_method_get("is_bad_link", |_, me| Ok(me.is_bad_link()));
reg.add_field_method_get("is_orphan", |_, me| Ok(me.is_orphan()));
reg.add_field_method_get("is_block_device", |_, me| Ok(me.is_block_device()));
reg.add_field_method_get("is_char_device", |_, me| Ok(me.is_char_device()));
reg.add_field_method_get("is_fifo", |_, me| Ok(me.is_fifo()));
reg.add_field_method_get("is_socket", |_, me| Ok(me.is_socket()));
#[cfg(unix)]
{
reg.add_field_method_get("is_block_device", |_, me| Ok(me.is_block_device()));
reg.add_field_method_get("is_char_device", |_, me| Ok(me.is_char_device()));
reg.add_field_method_get("is_fifo", |_, me| Ok(me.is_fifo()));
reg.add_field_method_get("is_socket", |_, me| Ok(me.is_socket()));
reg.add_field_method_get("uid", |_, me| Ok(me.uid));
reg.add_field_method_get("gid", |_, me| Ok(me.gid));
}
reg.add_field_method_get("length", |_, me| Ok(me.len));
reg.add_field_method_get("created", |_, me| {
Ok(me.created.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok()))

View File

@ -5,16 +5,16 @@ use bitflags::bitflags;
bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChaKind: u8 {
const DIR = 0b00000001;
const DIR = 0b00000001;
const HIDDEN = 0b00000010;
const LINK = 0b00000100;
const BAD_LINK = 0b00001000;
const HIDDEN = 0b00000010;
const LINK = 0b00000100;
const ORPHAN = 0b00001000;
const BLOCK_DEVICE = 0b00010000;
const CHAR_DEVICE = 0b00100000;
const FIFO = 0b01000000;
const SOCKET = 0b10000000;
const BLOCK_DEVICE = 0b00010000;
const CHAR_DEVICE = 0b00100000;
const FIFO = 0b01000000;
const SOCKET = 0b10000000;
}
}
@ -26,7 +26,7 @@ pub struct Cha {
pub created: Option<SystemTime>,
pub modified: Option<SystemTime>,
#[cfg(unix)]
pub permissions: u32,
pub permissions: libc::mode_t,
#[cfg(unix)]
pub uid: u32,
#[cfg(unix)]
@ -70,7 +70,7 @@ impl From<Metadata> for Cha {
#[cfg(unix)]
permissions: {
use std::os::unix::prelude::PermissionsExt;
m.permissions().mode()
m.permissions().mode() as libc::mode_t
},
#[cfg(unix)]
uid: {
@ -96,30 +96,50 @@ impl Cha {
impl Cha {
#[inline]
pub fn is_dir(self) -> bool { self.kind.contains(ChaKind::DIR) }
pub fn is_dir(&self) -> bool { self.kind.contains(ChaKind::DIR) }
#[inline]
pub fn is_hidden(self) -> bool { self.kind.contains(ChaKind::HIDDEN) }
pub fn is_hidden(&self) -> bool { self.kind.contains(ChaKind::HIDDEN) }
#[inline]
pub fn is_link(self) -> bool { self.kind.contains(ChaKind::LINK) }
pub fn is_link(&self) -> bool { self.kind.contains(ChaKind::LINK) }
#[inline]
pub fn is_bad_link(self) -> bool { self.kind.contains(ChaKind::BAD_LINK) }
pub fn is_orphan(&self) -> bool { self.kind.contains(ChaKind::ORPHAN) }
#[cfg(unix)]
#[inline]
pub fn is_block_device(self) -> bool { self.kind.contains(ChaKind::BLOCK_DEVICE) }
pub fn is_block_device(&self) -> bool { self.kind.contains(ChaKind::BLOCK_DEVICE) }
#[cfg(unix)]
#[inline]
pub fn is_char_device(self) -> bool { self.kind.contains(ChaKind::CHAR_DEVICE) }
pub fn is_char_device(&self) -> bool { self.kind.contains(ChaKind::CHAR_DEVICE) }
#[cfg(unix)]
#[inline]
pub fn is_fifo(self) -> bool { self.kind.contains(ChaKind::FIFO) }
pub fn is_fifo(&self) -> bool { self.kind.contains(ChaKind::FIFO) }
#[cfg(unix)]
#[inline]
pub fn is_socket(self) -> bool { self.kind.contains(ChaKind::SOCKET) }
pub fn is_socket(&self) -> bool { self.kind.contains(ChaKind::SOCKET) }
#[inline]
pub fn is_exec(&self) -> bool {
#[cfg(unix)]
{
self.permissions & libc::S_IXUSR != 0
}
#[cfg(windows)]
{
false
}
}
#[inline]
pub fn is_sticky(&self) -> bool {
#[cfg(unix)]
{
self.permissions & libc::S_ISVTX != 0
}
#[cfg(windows)]
{
false
}
}
}

View File

@ -37,7 +37,7 @@ impl File {
}
if is_link && meta.is_symlink() {
ck |= ChaKind::BAD_LINK;
ck |= ChaKind::ORPHAN;
}
if url.is_hidden() {

View File

@ -94,11 +94,9 @@ pub fn copy_with_progress(from: &Path, to: &Path) -> mpsc::Receiver<Result<u64,
// Convert a file mode to a string representation
#[cfg(unix)]
#[allow(clippy::collapsible_else_if)]
pub fn permissions(mode: u32) -> String {
use libc::{mode_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR};
pub fn permissions(m: libc::mode_t) -> String {
use libc::{S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR};
let mut s = String::with_capacity(10);
let m = mode as mode_t;
// File type
s.push(match m & S_IFMT {