mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-01 10:17:47 +03:00
feat: add support for highlighting by file type (#510)
This commit is contained in:
parent
876419a6c4
commit
bf9f8f4273
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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) }
|
||||
}
|
||||
|
@ -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")?;
|
||||
|
@ -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()))
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ impl File {
|
||||
}
|
||||
|
||||
if is_link && meta.is_symlink() {
|
||||
ck |= ChaKind::BAD_LINK;
|
||||
ck |= ChaKind::ORPHAN;
|
||||
}
|
||||
|
||||
if url.is_hidden() {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user