mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: size calculator for remote file systems
This commit is contained in:
parent
a3fc9a0ec7
commit
0abfc3c5f1
28 changed files with 476 additions and 348 deletions
|
|
@ -1,7 +1,7 @@
|
|||
use std::{ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}};
|
||||
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, UserDataMethods};
|
||||
use yazi_fs::cha::ChaKind;
|
||||
use yazi_fs::cha::{ChaKind, ChaMode};
|
||||
|
||||
#[derive(Clone, Copy, FromLua)]
|
||||
pub struct Cha(pub yazi_fs::cha::Cha);
|
||||
|
|
@ -25,11 +25,15 @@ impl Cha {
|
|||
lua.globals().raw_set(
|
||||
"Cha",
|
||||
lua.create_function(|lua, t: Table| {
|
||||
let kind =
|
||||
ChaKind::from_bits(t.raw_get("kind")?).ok_or_else(|| "Invalid kind".into_lua_err())?;
|
||||
let kind = ChaKind::from_bits(t.raw_get("kind").unwrap_or_default())
|
||||
.ok_or_else(|| "Invalid kind".into_lua_err())?;
|
||||
|
||||
let mode =
|
||||
ChaMode::from_bits(t.raw_get("mode")?).ok_or_else(|| "Invalid mode".into_lua_err())?;
|
||||
|
||||
Self(yazi_fs::cha::Cha {
|
||||
kind,
|
||||
mode,
|
||||
len: t.raw_get("len").unwrap_or_default(),
|
||||
atime: parse_time(t.raw_get("atime").ok())?,
|
||||
btime: parse_time(t.raw_get("btime").ok())?,
|
||||
|
|
@ -37,8 +41,6 @@ impl Cha {
|
|||
ctime: parse_time(t.raw_get("ctime").ok())?,
|
||||
mtime: parse_time(t.raw_get("mtime").ok())?,
|
||||
#[cfg(unix)]
|
||||
mode: t.raw_get("mode").unwrap_or_default(),
|
||||
#[cfg(unix)]
|
||||
dev: t.raw_get("dev").unwrap_or_default(),
|
||||
#[cfg(unix)]
|
||||
uid: t.raw_get("uid").unwrap_or_default(),
|
||||
|
|
@ -55,6 +57,7 @@ impl Cha {
|
|||
|
||||
impl UserData for Cha {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("mode", |_, me| Ok(me.mode.bits()));
|
||||
fields.add_field_method_get("is_dir", |_, me| Ok(me.is_dir()));
|
||||
fields.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden()));
|
||||
fields.add_field_method_get("is_link", |_, me| Ok(me.is_link()));
|
||||
|
|
@ -70,7 +73,6 @@ impl UserData for Cha {
|
|||
#[cfg(unix)]
|
||||
{
|
||||
use std::ops::Not;
|
||||
fields.add_field_method_get("mode", |_, me| Ok(me.is_dummy().not().then_some(me.mode)));
|
||||
fields.add_field_method_get("dev", |_, me| Ok(me.is_dummy().not().then_some(me.dev)));
|
||||
fields.add_field_method_get("uid", |_, me| Ok(me.is_dummy().not().then_some(me.uid)));
|
||||
fields.add_field_method_get("gid", |_, me| Ok(me.is_dummy().not().then_some(me.gid)));
|
||||
|
|
@ -94,12 +96,12 @@ impl UserData for Cha {
|
|||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("perm", |_, _me, ()| {
|
||||
methods.add_method("perm", |lua, _me, ()| {
|
||||
Ok(
|
||||
#[cfg(unix)]
|
||||
Some(yazi_fs::permissions(_me.mode, _me.is_dummy())),
|
||||
lua.create_string(_me.mode.permissions(_me.is_dummy())),
|
||||
#[cfg(windows)]
|
||||
None::<String>,
|
||||
Ok(mlua::Value::Nil),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{mem, ops::Deref, sync::atomic::{AtomicU64, Ordering}, time::UNIX_EPOCH
|
|||
use anyhow::Result;
|
||||
use hashbrown::HashMap;
|
||||
use parking_lot::RwLock;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufWriter};
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_fs::provider::local::{Gate, Local};
|
||||
use yazi_shared::{RoCell, timestamp_us};
|
||||
|
|
@ -79,7 +79,7 @@ impl State {
|
|||
}
|
||||
|
||||
async fn load(&self) -> Result<()> {
|
||||
let mut file = Local::open(BOOT.state_dir.join(".dds")).await?.reader();
|
||||
let mut file = BufReader::new(Local::open(BOOT.state_dir.join(".dds")).await?);
|
||||
let mut buf = String::new();
|
||||
|
||||
let mut inner = HashMap::new();
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
use std::{fs::{FileType, Metadata}, time::SystemTime};
|
||||
use std::{fs::{FileType, Metadata}, ops::Deref, time::SystemTime};
|
||||
|
||||
use yazi_macro::{unix_either, win_either};
|
||||
use yazi_shared::url::{Url, UrlBuf};
|
||||
|
||||
use super::ChaKind;
|
||||
use crate::provider;
|
||||
use crate::{cha::ChaMode, provider};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Cha {
|
||||
pub kind: ChaKind,
|
||||
pub mode: ChaMode,
|
||||
pub len: u64,
|
||||
pub atime: Option<SystemTime>,
|
||||
pub btime: Option<SystemTime>,
|
||||
|
|
@ -16,21 +17,26 @@ pub struct Cha {
|
|||
pub ctime: Option<SystemTime>,
|
||||
pub mtime: Option<SystemTime>,
|
||||
#[cfg(unix)]
|
||||
pub mode: libc::mode_t,
|
||||
#[cfg(unix)]
|
||||
pub dev: libc::dev_t,
|
||||
#[cfg(unix)]
|
||||
pub uid: libc::uid_t,
|
||||
pub uid: u32,
|
||||
#[cfg(unix)]
|
||||
pub gid: libc::gid_t,
|
||||
pub gid: u32,
|
||||
#[cfg(unix)]
|
||||
pub nlink: libc::nlink_t,
|
||||
pub nlink: u64,
|
||||
}
|
||||
|
||||
impl Deref for Cha {
|
||||
type Target = ChaMode;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.mode }
|
||||
}
|
||||
|
||||
impl Default for Cha {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
kind: ChaKind::DUMMY,
|
||||
mode: ChaMode::empty(),
|
||||
len: 0,
|
||||
atime: None,
|
||||
btime: None,
|
||||
|
|
@ -38,8 +44,6 @@ impl Default for Cha {
|
|||
ctime: None,
|
||||
mtime: None,
|
||||
#[cfg(unix)]
|
||||
mode: 0,
|
||||
#[cfg(unix)]
|
||||
dev: 0,
|
||||
#[cfg(unix)]
|
||||
uid: 0,
|
||||
|
|
@ -54,7 +58,7 @@ impl Default for Cha {
|
|||
impl Cha {
|
||||
#[inline]
|
||||
pub fn new<'a>(url: impl Into<Url<'a>>, meta: Metadata) -> Self {
|
||||
Self::from_just_meta(&meta).attach(ChaKind::hidden(url, &meta))
|
||||
Self::from_bare_meta(&meta).attach(ChaKind::hidden(url, &meta))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -75,12 +79,12 @@ impl Cha {
|
|||
attached |= ChaKind::ORPHAN;
|
||||
}
|
||||
|
||||
Self::from_just_meta(&meta).attach(attached)
|
||||
Self::from_bare_meta(&meta).attach(attached)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_dummy(_url: &UrlBuf, ft: Option<FileType>) -> Self {
|
||||
let mut me = ft.map(Self::from_half_ft).unwrap_or_default();
|
||||
let mut me = ft.map(Self::from_bare_ft).unwrap_or_default();
|
||||
#[cfg(unix)]
|
||||
if _url.urn().is_hidden() {
|
||||
me.kind |= ChaKind::HIDDEN;
|
||||
|
|
@ -88,61 +92,74 @@ impl Cha {
|
|||
me
|
||||
}
|
||||
|
||||
fn from_half_ft(ft: FileType) -> Self {
|
||||
let mut kind = ChaKind::DUMMY;
|
||||
|
||||
fn from_bare_ft(ft: FileType) -> Self {
|
||||
#[cfg(unix)]
|
||||
let mode = {
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
if ft.is_dir() {
|
||||
kind |= ChaKind::DIR;
|
||||
libc::S_IFDIR
|
||||
if ft.is_file() {
|
||||
ChaMode::T_FILE
|
||||
} else if ft.is_dir() {
|
||||
ChaMode::T_DIR
|
||||
} else if ft.is_symlink() {
|
||||
kind |= ChaKind::LINK;
|
||||
libc::S_IFLNK
|
||||
ChaMode::T_LINK
|
||||
} else if ft.is_block_device() {
|
||||
libc::S_IFBLK
|
||||
ChaMode::T_BLOCK
|
||||
} else if ft.is_char_device() {
|
||||
libc::S_IFCHR
|
||||
} else if ft.is_fifo() {
|
||||
libc::S_IFIFO
|
||||
ChaMode::T_CHAR
|
||||
} else if ft.is_socket() {
|
||||
libc::S_IFSOCK
|
||||
ChaMode::T_SOCK
|
||||
} else if ft.is_fifo() {
|
||||
ChaMode::T_FIFO
|
||||
} else {
|
||||
0
|
||||
ChaMode::empty()
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if ft.is_dir() {
|
||||
kind |= ChaKind::DIR;
|
||||
let mode = {
|
||||
if ft.is_file() {
|
||||
ChaMode::T_FILE
|
||||
} else if ft.is_dir() {
|
||||
ChaMode::T_DIR
|
||||
} else if ft.is_symlink() {
|
||||
kind |= ChaKind::LINK;
|
||||
ChaMode::T_LINK
|
||||
} else {
|
||||
ChaMode::empty()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
kind,
|
||||
#[cfg(unix)]
|
||||
mode,
|
||||
..Default::default()
|
||||
}
|
||||
let kind = ChaKind::DUMMY
|
||||
| if mode.contains(ChaMode::T_LINK) { ChaKind::LINK } else { ChaKind::empty() };
|
||||
|
||||
Self { kind, mode, ..Default::default() }
|
||||
}
|
||||
|
||||
fn from_just_meta(m: &Metadata) -> Self {
|
||||
fn from_bare_meta(m: &Metadata) -> Self {
|
||||
#[cfg(unix)]
|
||||
use std::{os::unix::{fs::MetadataExt, prelude::PermissionsExt}, time::{Duration, UNIX_EPOCH}};
|
||||
use std::{os::unix::fs::MetadataExt, time::{Duration, UNIX_EPOCH}};
|
||||
|
||||
let mut kind = ChaKind::empty();
|
||||
if m.is_dir() {
|
||||
kind |= ChaKind::DIR;
|
||||
} else if m.is_symlink() {
|
||||
kind |= ChaKind::LINK;
|
||||
}
|
||||
#[cfg(unix)]
|
||||
let mode = {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
ChaMode::from_bits_retain(m.permissions().mode() as u16)
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
let mode = {
|
||||
if m.is_file() {
|
||||
ChaMode::T_FILE
|
||||
} else if m.is_dir() {
|
||||
ChaMode::T_DIR
|
||||
} else if m.is_symlink() {
|
||||
ChaMode::T_LINK
|
||||
} else {
|
||||
ChaMode::empty()
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
kind,
|
||||
kind: ChaKind::empty(),
|
||||
mode,
|
||||
len: m.len(),
|
||||
atime: m.accessed().ok(),
|
||||
btime: m.created().ok(),
|
||||
|
|
@ -150,8 +167,6 @@ impl Cha {
|
|||
ctime: UNIX_EPOCH.checked_add(Duration::new(m.ctime() as u64, m.ctime_nsec() as u32)),
|
||||
mtime: m.modified().ok(),
|
||||
#[cfg(unix)]
|
||||
mode: m.permissions().mode() as _,
|
||||
#[cfg(unix)]
|
||||
dev: m.dev() as _,
|
||||
#[cfg(unix)]
|
||||
uid: m.uid() as _,
|
||||
|
|
@ -180,9 +195,6 @@ impl Cha {
|
|||
}
|
||||
|
||||
impl Cha {
|
||||
#[inline]
|
||||
pub const fn is_dir(&self) -> bool { self.kind.contains(ChaKind::DIR) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_hidden(&self) -> bool {
|
||||
win_either!(
|
||||
|
|
@ -191,38 +203,9 @@ impl Cha {
|
|||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_link(&self) -> bool { self.kind.contains(ChaKind::LINK) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_orphan(&self) -> bool { self.kind.contains(ChaKind::ORPHAN) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_dummy(&self) -> bool { self.kind.contains(ChaKind::DUMMY) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_block(&self) -> bool {
|
||||
unix_either!(self.mode & libc::S_IFMT == libc::S_IFBLK, false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_char(&self) -> bool {
|
||||
unix_either!(self.mode & libc::S_IFMT == libc::S_IFCHR, false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_fifo(&self) -> bool {
|
||||
unix_either!(self.mode & libc::S_IFMT == libc::S_IFIFO, false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_sock(&self) -> bool {
|
||||
unix_either!(self.mode & libc::S_IFMT == libc::S_IFSOCK, false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_exec(&self) -> bool { unix_either!(self.mode & libc::S_IXUSR != 0, false) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_sticky(&self) -> bool { unix_either!(self.mode & libc::S_ISVTX != 0, false) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,13 @@ use yazi_shared::url::Url;
|
|||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ChaKind: u8 {
|
||||
const DIR = 0b00000001;
|
||||
const HIDDEN = 0b0000_0001;
|
||||
const SYSTEM = 0b0000_0010;
|
||||
|
||||
const HIDDEN = 0b00000010;
|
||||
const LINK = 0b00000100;
|
||||
const ORPHAN = 0b00001000;
|
||||
const LINK = 0b0000_0100;
|
||||
const ORPHAN = 0b0000_1000;
|
||||
|
||||
const DUMMY = 0b00010000;
|
||||
#[cfg(windows)]
|
||||
const SYSTEM = 0b00100000;
|
||||
const DUMMY = 0b0001_0000;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(cha kind);
|
||||
yazi_macro::mod_flat!(cha kind mode);
|
||||
|
|
|
|||
120
yazi-fs/src/cha/mode.rs
Normal file
120
yazi-fs/src/cha/mode.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ChaMode: u16 {
|
||||
// File type
|
||||
const T_MASK = 0b1111_0000_0000_0000;
|
||||
const T_SOCK = 0b1100_0000_0000_0000;
|
||||
const T_LINK = 0b1010_0000_0000_0000;
|
||||
const T_FILE = 0b1000_0000_0000_0000;
|
||||
const T_BLOCK = 0b0110_0000_0000_0000;
|
||||
const T_DIR = 0b0100_0000_0000_0000;
|
||||
const T_CHAR = 0b0010_0000_0000_0000;
|
||||
const T_FIFO = 0b0001_0000_0000_0000;
|
||||
// Special
|
||||
const S_SUID = 0b0000_1000_0000_0000;
|
||||
const S_SGID = 0b0000_0100_0000_0000;
|
||||
const S_STICKY = 0b0000_0010_0000_0000;
|
||||
// User
|
||||
const U_MASK = 0b0000_0001_1100_0000;
|
||||
const U_READ = 0b0000_0001_0000_0000;
|
||||
const U_WRITE = 0b0000_0000_1000_0000;
|
||||
const U_EXEC = 0b0000_0000_0100_0000;
|
||||
// Group
|
||||
const G_MASK = 0b0000_0000_0011_1000;
|
||||
const G_READ = 0b0000_0000_0010_0000;
|
||||
const G_WRITE = 0b0000_0000_0001_0000;
|
||||
const G_EXEC = 0b0000_0000_0000_1000;
|
||||
// Others
|
||||
const O_MASK = 0b0000_0000_0000_0111;
|
||||
const O_READ = 0b0000_0000_0000_0100;
|
||||
const O_WRITE = 0b0000_0000_0000_0010;
|
||||
const O_EXEC = 0b0000_0000_0000_0001;
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaMode {
|
||||
#[inline]
|
||||
pub fn r#type(self) -> Self { self & Self::T_MASK }
|
||||
|
||||
// Convert a file mode to a string representation
|
||||
#[cfg(unix)]
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
pub fn permissions(self, dummy: bool) -> [u8; 10] {
|
||||
let mut s = *b"-?????????";
|
||||
|
||||
// File type
|
||||
s[0] = match self.r#type() {
|
||||
Self::T_DIR => b'd',
|
||||
Self::T_LINK => b'l',
|
||||
Self::T_BLOCK => b'b',
|
||||
Self::T_CHAR => b'c',
|
||||
Self::T_SOCK => b's',
|
||||
Self::T_FIFO => b'p',
|
||||
_ => b'-',
|
||||
};
|
||||
if dummy {
|
||||
return s;
|
||||
}
|
||||
|
||||
// User
|
||||
s[1] = if self.contains(Self::U_READ) { b'r' } else { b'-' };
|
||||
s[2] = if self.contains(Self::U_WRITE) { b'w' } else { b'-' };
|
||||
s[3] = if self.contains(Self::U_EXEC) {
|
||||
if self.contains(Self::S_SUID) { b's' } else { b'x' }
|
||||
} else {
|
||||
if self.contains(Self::S_SUID) { b'S' } else { b'-' }
|
||||
};
|
||||
|
||||
// Group
|
||||
s[4] = if self.contains(Self::G_READ) { b'r' } else { b'-' };
|
||||
s[5] = if self.contains(Self::G_WRITE) { b'w' } else { b'-' };
|
||||
s[6] = if self.contains(Self::G_EXEC) {
|
||||
if self.contains(Self::S_SGID) { b's' } else { b'x' }
|
||||
} else {
|
||||
if self.contains(Self::S_SGID) { b'S' } else { b'-' }
|
||||
};
|
||||
|
||||
// Others
|
||||
s[7] = if self.contains(Self::O_READ) { b'r' } else { b'-' };
|
||||
s[8] = if self.contains(Self::O_WRITE) { b'w' } else { b'-' };
|
||||
s[9] = if self.contains(Self::O_EXEC) {
|
||||
if self.contains(Self::S_STICKY) { b't' } else { b'x' }
|
||||
} else {
|
||||
if self.contains(Self::S_STICKY) { b'T' } else { b'-' }
|
||||
};
|
||||
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaMode {
|
||||
#[inline]
|
||||
pub const fn is_file(self) -> bool { self.contains(Self::T_FILE) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_dir(self) -> bool { self.contains(Self::T_DIR) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_link(&self) -> bool { self.contains(Self::T_LINK) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_block(&self) -> bool { self.contains(Self::T_BLOCK) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_char(&self) -> bool { self.contains(Self::T_CHAR) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_sock(&self) -> bool { self.contains(Self::T_SOCK) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_fifo(&self) -> bool { self.contains(Self::T_FIFO) }
|
||||
|
||||
// TODO: deprecate
|
||||
#[inline]
|
||||
pub const fn is_exec(&self) -> bool { self.contains(Self::U_EXEC) }
|
||||
|
||||
#[inline]
|
||||
pub const fn is_sticky(&self) -> bool { self.contains(Self::S_STICKY) }
|
||||
}
|
||||
|
|
@ -94,59 +94,6 @@ pub async fn remove_dir_clean(dir: &UrlBuf) {
|
|||
provider::remove_dir(dir).await.ok();
|
||||
}
|
||||
|
||||
// Convert a file mode to a string representation
|
||||
#[cfg(unix)]
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
pub fn permissions(m: libc::mode_t, dummy: bool) -> 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);
|
||||
|
||||
// Filetype
|
||||
s.push(match m & S_IFMT {
|
||||
S_IFBLK => 'b',
|
||||
S_IFCHR => 'c',
|
||||
S_IFDIR => 'd',
|
||||
S_IFIFO => 'p',
|
||||
S_IFLNK => 'l',
|
||||
S_IFSOCK => 's',
|
||||
_ => '-',
|
||||
});
|
||||
|
||||
if dummy {
|
||||
s.push_str("?????????");
|
||||
return s;
|
||||
}
|
||||
|
||||
// Owner
|
||||
s.push(if m & S_IRUSR != 0 { 'r' } else { '-' });
|
||||
s.push(if m & S_IWUSR != 0 { 'w' } else { '-' });
|
||||
s.push(if m & S_IXUSR != 0 {
|
||||
if m & S_ISUID != 0 { 's' } else { 'x' }
|
||||
} else {
|
||||
if m & S_ISUID != 0 { 'S' } else { '-' }
|
||||
});
|
||||
|
||||
// Group
|
||||
s.push(if m & S_IRGRP != 0 { 'r' } else { '-' });
|
||||
s.push(if m & S_IWGRP != 0 { 'w' } else { '-' });
|
||||
s.push(if m & S_IXGRP != 0 {
|
||||
if m & S_ISGID != 0 { 's' } else { 'x' }
|
||||
} else {
|
||||
if m & S_ISGID != 0 { 'S' } else { '-' }
|
||||
});
|
||||
|
||||
// Other
|
||||
s.push(if m & S_IROTH != 0 { 'r' } else { '-' });
|
||||
s.push(if m & S_IWOTH != 0 { 'w' } else { '-' });
|
||||
s.push(if m & S_IXOTH != 0 {
|
||||
if m & S_ISVTX != 0 { 't' } else { 'x' }
|
||||
} else {
|
||||
if m & S_ISVTX != 0 { 'T' } else { '-' }
|
||||
});
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
// Find the max common root in a list of urls
|
||||
// e.g. /a/b/c, /a/b/d -> /a/b
|
||||
// /aa/bb/cc, /aa/dd/ee -> /aa
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
yazi_macro::mod_pub!(cha mounts provider path);
|
||||
|
||||
yazi_macro::mod_flat!(calculator cwd file files filter fns op sorter sorting stage xdg);
|
||||
yazi_macro::mod_flat!(cwd file files filter fns op sorter sorting stage xdg);
|
||||
|
||||
pub fn init() {
|
||||
CWD.init(<_>::default());
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
// --- BufRead
|
||||
pub trait BufRead: tokio::io::AsyncRead + Send {}
|
||||
|
||||
impl<T: tokio::io::AsyncRead + Send> BufRead for T {}
|
||||
|
||||
// --- BufReadSync
|
||||
pub trait BufReadSync: std::io::BufRead + std::io::Seek + Send {}
|
||||
|
||||
impl<T: std::io::BufRead + std::io::Seek + Send> BufReadSync for T {}
|
||||
81
yazi-fs/src/provider/calculator.rs
Normal file
81
yazi-fs/src/provider/calculator.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
use std::{collections::VecDeque, io, time::{Duration, Instant}};
|
||||
|
||||
use yazi_shared::{Either, url::{Url, UrlBuf}};
|
||||
|
||||
use crate::provider::{self, ReadDir};
|
||||
|
||||
pub enum SizeCalculator {
|
||||
File(Option<u64>),
|
||||
Dir(VecDeque<Either<UrlBuf, ReadDir>>),
|
||||
}
|
||||
|
||||
impl SizeCalculator {
|
||||
pub async fn new<'a, U>(url: U) -> io::Result<Self>
|
||||
where
|
||||
U: Into<Url<'a>>,
|
||||
{
|
||||
let url: Url = url.into();
|
||||
let meta = provider::symlink_metadata(url).await?;
|
||||
Ok(if meta.is_dir() {
|
||||
Self::Dir(VecDeque::from([Either::Left(url.to_owned())]))
|
||||
} else {
|
||||
Self::File(Some(meta.len()))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn total<'a, U>(url: U) -> io::Result<u64>
|
||||
where
|
||||
U: Into<Url<'a>>,
|
||||
{
|
||||
let mut it = Self::new(url).await?;
|
||||
let mut total = 0;
|
||||
while let Some(n) = it.next().await? {
|
||||
total += n;
|
||||
}
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
pub async fn next(&mut self) -> io::Result<Option<u64>> {
|
||||
Ok(match self {
|
||||
Self::File(size) => size.take(),
|
||||
Self::Dir(buf) => Self::next_chunk(buf).await,
|
||||
})
|
||||
}
|
||||
|
||||
async fn next_chunk(buf: &mut VecDeque<Either<UrlBuf, ReadDir>>) -> Option<u64> {
|
||||
let (mut i, mut size, now) = (0, 0, Instant::now());
|
||||
macro_rules! pop_and_continue {
|
||||
() => {{
|
||||
buf.pop_front();
|
||||
if buf.is_empty() {
|
||||
return Some(size);
|
||||
}
|
||||
continue;
|
||||
}};
|
||||
}
|
||||
|
||||
while i < 2000 && now.elapsed() < Duration::from_millis(100) {
|
||||
i += 1;
|
||||
let front = buf.front_mut()?;
|
||||
|
||||
if let Either::Left(p) = front {
|
||||
*front = match provider::read_dir(p).await {
|
||||
Ok(it) => Either::Right(it),
|
||||
Err(_) => pop_and_continue!(),
|
||||
};
|
||||
}
|
||||
|
||||
let Ok(Some(ent)) = front.right_mut()?.next_entry().await else {
|
||||
pop_and_continue!();
|
||||
};
|
||||
|
||||
let Ok(ft) = ent.file_type().await else { continue };
|
||||
if ft.is_dir() {
|
||||
buf.push_back(Either::Left(ent.url()));
|
||||
} else if let Ok(meta) = ent.metadata().await {
|
||||
size += meta.len();
|
||||
}
|
||||
}
|
||||
Some(size)
|
||||
}
|
||||
}
|
||||
|
|
@ -33,36 +33,3 @@ impl DirEntry {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- DirEntrySync
|
||||
pub enum DirEntrySync {
|
||||
Local(super::local::DirEntrySync),
|
||||
}
|
||||
|
||||
impl DirEntrySync {
|
||||
#[must_use]
|
||||
pub fn url(&self) -> UrlBuf {
|
||||
match self {
|
||||
Self::Local(local) => local.url(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn file_name(&self) -> OsString {
|
||||
match self {
|
||||
Self::Local(local) => local.file_name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn metadata(&self) -> io::Result<std::fs::Metadata> {
|
||||
match self {
|
||||
Self::Local(local) => local.metadata(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_type(&self) -> io::Result<std::fs::FileType> {
|
||||
match self {
|
||||
Self::Local(local) => local.file_type(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
use std::{collections::VecDeque, future::poll_fn, io, mem, pin::Pin, task::{Poll, ready}, time::{Duration, Instant}};
|
||||
use std::{collections::VecDeque, future::poll_fn, io, mem, path::{Path, PathBuf}, pin::Pin, task::{Poll, ready}, time::{Duration, Instant}};
|
||||
|
||||
use tokio::task::JoinHandle;
|
||||
use yazi_shared::{Either, url::UrlBuf};
|
||||
use yazi_shared::Either;
|
||||
|
||||
use crate::provider::{self, ReadDirSync};
|
||||
|
||||
type Task = Either<UrlBuf, ReadDirSync>;
|
||||
type Task = Either<PathBuf, std::fs::ReadDir>;
|
||||
|
||||
pub enum SizeCalculator {
|
||||
Idle((VecDeque<Task>, Option<u64>)),
|
||||
|
|
@ -13,23 +11,23 @@ pub enum SizeCalculator {
|
|||
}
|
||||
|
||||
impl SizeCalculator {
|
||||
pub async fn new(url: &UrlBuf) -> io::Result<Self> {
|
||||
let u = url.to_owned();
|
||||
pub async fn new(path: &Path) -> io::Result<Self> {
|
||||
let p = path.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let meta = provider::symlink_metadata_sync(&u)?;
|
||||
let meta = std::fs::symlink_metadata(&p)?;
|
||||
if !meta.is_dir() {
|
||||
return Ok(Self::Idle((VecDeque::new(), Some(meta.len()))));
|
||||
}
|
||||
|
||||
let mut buf = VecDeque::from([Either::Right(provider::read_dir_sync(&u)?)]);
|
||||
let mut buf = VecDeque::from([Either::Right(std::fs::read_dir(&p)?)]);
|
||||
let size = Self::next_chunk(&mut buf);
|
||||
Ok(Self::Idle((buf, size)))
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
pub async fn total(url: &UrlBuf) -> io::Result<u64> {
|
||||
let mut it = Self::new(url).await?;
|
||||
pub async fn total(path: &Path) -> io::Result<u64> {
|
||||
let mut it = Self::new(path).await?;
|
||||
let mut total = 0;
|
||||
while let Some(n) = it.next().await? {
|
||||
total += n;
|
||||
|
|
@ -63,7 +61,7 @@ impl SizeCalculator {
|
|||
.await
|
||||
}
|
||||
|
||||
fn next_chunk(buf: &mut VecDeque<Either<UrlBuf, ReadDirSync>>) -> Option<u64> {
|
||||
fn next_chunk(buf: &mut VecDeque<Either<PathBuf, std::fs::ReadDir>>) -> Option<u64> {
|
||||
let (mut i, mut size, now) = (0, 0, Instant::now());
|
||||
macro_rules! pop_and_continue {
|
||||
() => {{
|
||||
|
|
@ -79,8 +77,8 @@ impl SizeCalculator {
|
|||
i += 1;
|
||||
let front = buf.front_mut()?;
|
||||
|
||||
if let Either::Left(u) = front {
|
||||
*front = match provider::read_dir_sync(u) {
|
||||
if let Either::Left(p) = front {
|
||||
*front = match std::fs::read_dir(p) {
|
||||
Ok(it) => Either::Right(it),
|
||||
Err(_) => pop_and_continue!(),
|
||||
};
|
||||
|
|
@ -93,7 +91,7 @@ impl SizeCalculator {
|
|||
let Ok(ent) = next else { continue };
|
||||
let Ok(ft) = ent.file_type() else { continue };
|
||||
if ft.is_dir() {
|
||||
buf.push_back(Either::Left(ent.url()));
|
||||
buf.push_back(Either::Left(ent.path()));
|
||||
} else if let Ok(meta) = ent.metadata() {
|
||||
size += meta.len();
|
||||
}
|
||||
|
|
@ -22,25 +22,3 @@ impl DirEntry {
|
|||
#[must_use]
|
||||
pub fn url(&self) -> UrlBuf { self.0.path().into() }
|
||||
}
|
||||
|
||||
// --- DirEntrySync
|
||||
pub struct DirEntrySync(std::fs::DirEntry);
|
||||
|
||||
impl Deref for DirEntrySync {
|
||||
type Target = std::fs::DirEntry;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl From<std::fs::DirEntry> for DirEntrySync {
|
||||
fn from(value: std::fs::DirEntry) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
impl From<DirEntrySync> for crate::provider::DirEntrySync {
|
||||
fn from(value: DirEntrySync) -> Self { Self::Local(value) }
|
||||
}
|
||||
|
||||
impl DirEntrySync {
|
||||
#[must_use]
|
||||
pub fn url(&self) -> UrlBuf { self.0.path().into() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{io, path::{Path, PathBuf}};
|
||||
|
||||
use crate::{cha::Cha, provider::local::{Gate, ReadDir, ReadDirSync, RwFile}};
|
||||
use crate::{cha::Cha, provider::local::{Gate, ReadDir, RwFile}};
|
||||
|
||||
pub struct Local;
|
||||
|
||||
|
|
@ -145,14 +145,6 @@ impl Local {
|
|||
tokio::fs::read_dir(path).await.map(Into::into)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_dir_sync<P>(path: P) -> io::Result<ReadDirSync>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
std::fs::read_dir(path).map(Into::into)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn read_link<P>(path: P) -> io::Result<PathBuf>
|
||||
where
|
||||
|
|
@ -261,14 +253,6 @@ impl Local {
|
|||
tokio::fs::symlink_metadata(path).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn symlink_metadata_sync<P>(path: P) -> io::Result<std::fs::Metadata>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
std::fs::symlink_metadata(path)
|
||||
}
|
||||
|
||||
pub async fn trash<P>(path: P) -> io::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(casefold dir_entry gate identical local read_dir rw_file);
|
||||
yazi_macro::mod_flat!(calculator casefold dir_entry gate identical local read_dir rw_file);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::io;
|
||||
|
||||
use super::{DirEntry, DirEntrySync};
|
||||
use super::DirEntry;
|
||||
|
||||
pub struct ReadDir(tokio::fs::ReadDir);
|
||||
|
||||
|
|
@ -17,22 +17,3 @@ impl ReadDir {
|
|||
self.0.next_entry().await.map(|entry| entry.map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
// --- ReadDirSync
|
||||
pub struct ReadDirSync(std::fs::ReadDir);
|
||||
|
||||
impl From<std::fs::ReadDir> for ReadDirSync {
|
||||
fn from(value: std::fs::ReadDir) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
impl From<ReadDirSync> for crate::provider::ReadDirSync {
|
||||
fn from(value: ReadDirSync) -> Self { Self::Local(value) }
|
||||
}
|
||||
|
||||
impl Iterator for ReadDirSync {
|
||||
type Item = io::Result<DirEntrySync>;
|
||||
|
||||
fn next(&mut self) -> Option<io::Result<DirEntrySync>> {
|
||||
self.0.next().map(|result| result.map(Into::into))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
use std::{pin::Pin, task::Poll};
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
|
||||
|
||||
pub struct RwFile(tokio::fs::File);
|
||||
|
||||
impl From<tokio::fs::File> for RwFile {
|
||||
|
|
@ -8,11 +12,67 @@ impl From<RwFile> for crate::provider::RwFile {
|
|||
fn from(value: RwFile) -> Self { Self::Local(value) }
|
||||
}
|
||||
|
||||
impl From<tokio::fs::File> for crate::provider::RwFile {
|
||||
fn from(value: tokio::fs::File) -> Self { RwFile(value).into() }
|
||||
impl AsyncRead for RwFile {
|
||||
#[inline]
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &mut tokio::io::ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
Pin::new(&mut self.0).poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl RwFile {
|
||||
impl AsyncSeek for RwFile {
|
||||
#[inline]
|
||||
pub fn reader(self) -> tokio::io::BufReader<tokio::fs::File> { tokio::io::BufReader::new(self.0) }
|
||||
fn start_seek(mut self: Pin<&mut Self>, position: std::io::SeekFrom) -> std::io::Result<()> {
|
||||
Pin::new(&mut self.0).start_seek(position)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_complete(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<std::io::Result<u64>> {
|
||||
Pin::new(&mut self.0).poll_complete(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for RwFile {
|
||||
#[inline]
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
Pin::new(&mut self.0).poll_write(cx, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Result<(), std::io::Error>> {
|
||||
Pin::new(&mut self.0).poll_flush(cx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Result<(), std::io::Error>> {
|
||||
Pin::new(&mut self.0).poll_shutdown(cx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
Pin::new(&mut self.0).poll_write_vectored(cx, bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
yazi_macro::mod_pub!(local sftp);
|
||||
|
||||
yazi_macro::mod_flat!(buffer dir_entry provider read_dir rw_file);
|
||||
yazi_macro::mod_flat!(calculator dir_entry provider read_dir rw_file);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::{io, path::{Path, PathBuf}};
|
|||
|
||||
use yazi_shared::url::{Url, UrlBuf};
|
||||
|
||||
use crate::{cha::Cha, provider::{ReadDir, ReadDirSync, RwFile, local::{self, Local}}};
|
||||
use crate::{cha::Cha, provider::{ReadDir, RwFile, local::{self, Local}}};
|
||||
|
||||
#[inline]
|
||||
pub fn cache<'a, U>(url: U) -> Option<PathBuf>
|
||||
|
|
@ -12,6 +12,19 @@ where
|
|||
if let Some(path) = url.into().as_path() { Local::cache(path) } else { None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn calculate<'a, U>(url: U) -> io::Result<u64>
|
||||
where
|
||||
U: Into<Url<'a>>,
|
||||
{
|
||||
let url: Url = url.into();
|
||||
if let Some(path) = url.as_path() {
|
||||
local::SizeCalculator::total(path).await
|
||||
} else {
|
||||
super::SizeCalculator::total(url).await
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn canonicalize<'a, U>(url: U) -> io::Result<UrlBuf>
|
||||
where
|
||||
|
|
@ -156,18 +169,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_dir_sync<'a, U>(url: U) -> io::Result<ReadDirSync>
|
||||
where
|
||||
U: Into<Url<'a>>,
|
||||
{
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::read_dir_sync(path).map(Into::into)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn read_link<'a, U>(url: U) -> io::Result<PathBuf>
|
||||
where
|
||||
|
|
@ -278,18 +279,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn symlink_metadata_sync<'a, U>(url: U) -> io::Result<std::fs::Metadata>
|
||||
where
|
||||
U: Into<Url<'a>>,
|
||||
{
|
||||
if let Some(path) = url.into().as_path() {
|
||||
Local::symlink_metadata_sync(path)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn trash<'a, U>(url: U) -> io::Result<()>
|
||||
where
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::io;
|
||||
|
||||
use super::{DirEntry, DirEntrySync};
|
||||
use super::DirEntry;
|
||||
|
||||
pub enum ReadDir {
|
||||
Local(super::local::ReadDir),
|
||||
|
|
@ -13,18 +13,3 @@ impl ReadDir {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- ReadDirSync
|
||||
pub enum ReadDirSync {
|
||||
Local(super::local::ReadDirSync),
|
||||
}
|
||||
|
||||
impl Iterator for ReadDirSync {
|
||||
type Item = io::Result<DirEntrySync>;
|
||||
|
||||
fn next(&mut self) -> Option<io::Result<DirEntrySync>> {
|
||||
match self {
|
||||
Self::Local(local) => local.next().map(|result| result.map(Into::into)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,71 @@
|
|||
use crate::provider::BufRead;
|
||||
use std::pin::Pin;
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
pub enum RwFile {
|
||||
Local(super::local::RwFile),
|
||||
}
|
||||
|
||||
impl RwFile {
|
||||
pub fn reader(self) -> Box<dyn BufRead> {
|
||||
match self {
|
||||
Self::Local(local) => Box::new(local.reader()),
|
||||
impl AsyncRead for RwFile {
|
||||
#[inline]
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &mut tokio::io::ReadBuf<'_>,
|
||||
) -> std::task::Poll<std::io::Result<()>> {
|
||||
match &mut *self {
|
||||
Self::Local(f) => Pin::new(f).poll_read(cx, buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for RwFile {
|
||||
#[inline]
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
match &mut *self {
|
||||
Self::Local(f) => Pin::new(f).poll_write(cx, buf),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
match &mut *self {
|
||||
Self::Local(f) => Pin::new(f).poll_flush(cx),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
match &mut *self {
|
||||
Self::Local(f) => Pin::new(f).poll_shutdown(cx),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
match &mut *self {
|
||||
Self::Local(f) => Pin::new(f).poll_write_vectored(cx, bufs),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
match self {
|
||||
Self::Local(f) => f.is_write_vectored(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,13 +84,6 @@ impl Sftp {
|
|||
todo!()
|
||||
}
|
||||
|
||||
pub fn read_dir_sync<P>(path: P) -> io::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn read_link<P>(path: P) -> io::Result<PathBuf>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
|
|
@ -166,13 +159,6 @@ impl Sftp {
|
|||
todo!()
|
||||
}
|
||||
|
||||
pub fn symlink_metadata_sync<P>(path: P) -> io::Result<std::fs::Metadata>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn trash<P>(path: P) -> io::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ function M:peek(job)
|
|||
for _, f in ipairs(files) do
|
||||
local icon = File({
|
||||
url = Url(f.path),
|
||||
cha = Cha { kind = f.attr:sub(1, 1) == "D" and 1 or 0 },
|
||||
cha = Cha { mode = tonumber(f.attr:sub(1, 1) == "D" and "40700" or "100644", 8) },
|
||||
}):icon()
|
||||
|
||||
if f.size > 0 then
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
use mlua::{IntoLuaMulti, UserData, UserDataMethods, Value};
|
||||
use yazi_binding::Error;
|
||||
|
||||
pub struct SizeCalculator(pub yazi_fs::SizeCalculator);
|
||||
pub enum SizeCalculator {
|
||||
Local(yazi_fs::provider::local::SizeCalculator),
|
||||
Remote(yazi_fs::provider::SizeCalculator),
|
||||
}
|
||||
|
||||
impl UserData for SizeCalculator {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_async_method_mut("recv", |lua, mut me, ()| async move {
|
||||
match me.0.next().await {
|
||||
let next = match &mut *me {
|
||||
Self::Local(it) => it.next().await,
|
||||
Self::Remote(it) => it.next().await,
|
||||
};
|
||||
|
||||
match next {
|
||||
Ok(value) => value.into_lua_multi(&lua),
|
||||
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
|
||||
}
|
||||
|
|
|
|||
15
yazi-plugin/src/external/highlighter.rs
vendored
15
yazi-plugin/src/external/highlighter.rs
vendored
|
|
@ -3,9 +3,9 @@ use std::{borrow::Cow, io::Cursor, mem, path::{Path, PathBuf}, sync::OnceLock};
|
|||
use anyhow::{Result, anyhow};
|
||||
use ratatui::{layout::Size, text::{Line, Span, Text}};
|
||||
use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}};
|
||||
use tokio::io::AsyncBufReadExt;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncSeekExt, BufReader};
|
||||
use yazi_config::{THEME, YAZI, preview::PreviewWrap};
|
||||
use yazi_fs::provider::local::Local;
|
||||
use yazi_fs::provider::local::{self, Local};
|
||||
use yazi_shared::{Ids, errors::PeekError, replace_to_printable};
|
||||
|
||||
static INCR: Ids = Ids::new();
|
||||
|
|
@ -39,9 +39,9 @@ impl Highlighter {
|
|||
pub fn abort() { INCR.next(); }
|
||||
|
||||
pub async fn highlight(&self, skip: usize, size: Size) -> Result<Text<'static>, PeekError> {
|
||||
let mut reader = Local::open(&self.path).await?.reader();
|
||||
let mut reader = BufReader::new(Local::open(&self.path).await?);
|
||||
|
||||
let syntax = Self::find_syntax(&self.path).await;
|
||||
let syntax = Self::find_syntax(&self.path, &mut reader).await;
|
||||
let mut plain = syntax.is_err();
|
||||
|
||||
let mut before = Vec::with_capacity(if plain { 0 } else { skip });
|
||||
|
|
@ -130,7 +130,10 @@ impl Highlighter {
|
|||
.await?
|
||||
}
|
||||
|
||||
async fn find_syntax(path: &Path) -> Result<&'static SyntaxReference> {
|
||||
async fn find_syntax(
|
||||
path: &Path,
|
||||
reader: &mut BufReader<local::RwFile>,
|
||||
) -> Result<&'static SyntaxReference> {
|
||||
let (_, syntaxes) = Self::init();
|
||||
let name = path.file_name().map(|n| n.to_string_lossy()).unwrap_or_default();
|
||||
if let Some(s) = syntaxes.find_syntax_by_extension(&name) {
|
||||
|
|
@ -143,8 +146,8 @@ impl Highlighter {
|
|||
}
|
||||
|
||||
let mut line = String::new();
|
||||
let mut reader = Local::open(&path).await?.reader();
|
||||
reader.read_line(&mut line).await?;
|
||||
reader.rewind().await?;
|
||||
syntaxes.find_syntax_by_first_line(&line).ok_or_else(|| anyhow!("No syntax found"))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -147,8 +147,14 @@ fn read_dir(lua: &Lua) -> mlua::Result<Function> {
|
|||
|
||||
fn calc_size(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_async_function(|lua, url: UrlRef| async move {
|
||||
match yazi_fs::SizeCalculator::new(&url).await {
|
||||
Ok(it) => SizeCalculator(it).into_lua_multi(&lua),
|
||||
let it = if let Some(path) = url.as_path() {
|
||||
provider::local::SizeCalculator::new(path).await.map(SizeCalculator::Local)
|
||||
} else {
|
||||
provider::SizeCalculator::new(&*url).await.map(SizeCalculator::Remote)
|
||||
};
|
||||
|
||||
match it {
|
||||
Ok(it) => it.into_lua_multi(&lua),
|
||||
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use tokio::sync::mpsc;
|
|||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::error;
|
||||
use yazi_config::Priority;
|
||||
use yazi_fs::{FilesOp, SizeCalculator};
|
||||
use yazi_fs::{FilesOp, provider};
|
||||
use yazi_plugin::isolate;
|
||||
use yazi_shared::{event::CmdCow, url::UrlBuf};
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ impl Prework {
|
|||
}
|
||||
|
||||
pub(crate) async fn size_do(&self, task: PreworkInSize) -> Result<(), PreworkOutSize> {
|
||||
let length = SizeCalculator::total(&task.target).await.unwrap_or(0);
|
||||
let length = provider::calculate(&task.target).await.unwrap_or(0);
|
||||
task.throttle.done((task.target, length), |buf| {
|
||||
{
|
||||
let mut loading = self.sizing.write();
|
||||
|
|
|
|||
|
|
@ -10,10 +10,13 @@ pub struct DirEntry<'a> {
|
|||
}
|
||||
|
||||
impl<'a> DirEntry<'a> {
|
||||
#[must_use]
|
||||
pub fn path(&self) -> PathBuf { self.dir.join(&self.name) }
|
||||
|
||||
#[must_use]
|
||||
pub fn name(&self) -> Cow<'_, OsStr> { self.name.to_os_str() }
|
||||
|
||||
#[must_use]
|
||||
pub fn long_name(&self) -> Cow<'_, OsStr> { self.long_name.to_os_str() }
|
||||
|
||||
pub fn attrs(&self) -> &Attrs { &self.attrs }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue