mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
fix: make every effort to carry hidden states for dummy files (#2814)
This commit is contained in:
parent
4076e35a2f
commit
4fdc0f120f
22 changed files with 175 additions and 165 deletions
|
|
@ -94,7 +94,7 @@ impl Mgr {
|
||||||
failed.push((o, n, anyhow!("Destination already exists")));
|
failed.push((o, n, anyhow!("Destination already exists")));
|
||||||
} else if let Err(e) = fs::rename(&old, &new).await {
|
} else if let Err(e) = fs::rename(&old, &new).await {
|
||||||
failed.push((o, n, e.into()));
|
failed.push((o, n, e.into()));
|
||||||
} else if let Ok(f) = File::from(new.into()).await {
|
} else if let Ok(f) = File::new(new.into()).await {
|
||||||
succeeded.insert(Url::from(old), f);
|
succeeded.insert(Url::from(old), f);
|
||||||
} else {
|
} else {
|
||||||
failed.push((o, n, anyhow!("Failed to retrieve file info")));
|
failed.push((o, n, anyhow!("Failed to retrieve file info")));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use yazi_config::popup::{ConfirmCfg, InputCfg};
|
use yazi_config::popup::{ConfirmCfg, InputCfg};
|
||||||
|
|
@ -50,7 +48,7 @@ impl Mgr {
|
||||||
fs::create_dir_all(&new).await?;
|
fs::create_dir_all(&new).await?;
|
||||||
} else if let Some(real) = realname(&new).await {
|
} else if let Some(real) = realname(&new).await {
|
||||||
ok_or_not_found(fs::remove_file(&new).await)?;
|
ok_or_not_found(fs::remove_file(&new).await)?;
|
||||||
FilesOp::Deleting(parent.clone(), HashSet::from_iter([UrnBuf::from(real)])).emit();
|
FilesOp::Deleting(parent.clone(), [UrnBuf::from(real)].into()).emit();
|
||||||
fs::File::create(&new).await?;
|
fs::File::create(&new).await?;
|
||||||
} else {
|
} else {
|
||||||
fs::create_dir_all(&parent).await.ok();
|
fs::create_dir_all(&parent).await.ok();
|
||||||
|
|
@ -58,8 +56,8 @@ impl Mgr {
|
||||||
fs::File::create(&new).await?;
|
fs::File::create(&new).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(f) = File::from(new.clone()).await {
|
if let Ok(f) = File::new(new.clone()).await {
|
||||||
FilesOp::Upserting(parent, HashMap::from_iter([(f.urn_owned(), f)])).emit();
|
FilesOp::Upserting(parent, [(f.urn_owned(), f)].into()).emit();
|
||||||
TabProxy::reveal(&new)
|
TabProxy::reveal(&new)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ impl Mgr {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut files = Vec::with_capacity(todo.len());
|
let mut files = Vec::with_capacity(todo.len());
|
||||||
for u in todo {
|
for u in todo {
|
||||||
if let Ok(f) = File::from(u).await {
|
if let Ok(f) = File::new(u).await {
|
||||||
files.push(f);
|
files.push(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{borrow::Cow, collections::{HashMap, HashSet}};
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
@ -80,15 +80,15 @@ impl Mgr {
|
||||||
|
|
||||||
if let Some(o) = overwritten {
|
if let Some(o) = overwritten {
|
||||||
ok_or_not_found(fs::rename(p_new.join(&o), &new).await)?;
|
ok_or_not_found(fs::rename(p_new.join(&o), &new).await)?;
|
||||||
FilesOp::Deleting(p_new.clone(), HashSet::from_iter([UrnBuf::from(o)])).emit();
|
FilesOp::Deleting(p_new.clone(), [UrnBuf::from(o)].into()).emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = File::from(new.clone()).await?;
|
let file = File::new(new.clone()).await?;
|
||||||
if p_new == p_old {
|
if p_new == p_old {
|
||||||
FilesOp::Upserting(p_old, HashMap::from_iter([(n_old, file)])).emit();
|
FilesOp::Upserting(p_old, [(n_old, file)].into()).emit();
|
||||||
} else {
|
} else {
|
||||||
FilesOp::Deleting(p_old, HashSet::from_iter([n_old])).emit();
|
FilesOp::Deleting(p_old, [n_old].into()).emit();
|
||||||
FilesOp::Upserting(p_new, HashMap::from_iter([(n_new, file)])).emit();
|
FilesOp::Upserting(p_new, [(n_new, file)].into()).emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
TabProxy::reveal(&new);
|
TabProxy::reveal(&new);
|
||||||
|
|
|
||||||
|
|
@ -134,8 +134,8 @@ impl Watcher {
|
||||||
|
|
||||||
for u in urls {
|
for u in urls {
|
||||||
let Some((parent, urn)) = u.pair() else { continue };
|
let Some((parent, urn)) = u.pair() else { continue };
|
||||||
let Ok(file) = File::from(u).await else {
|
let Ok(file) = File::new(u).await else {
|
||||||
ops.push(FilesOp::Deleting(parent, HashSet::from_iter([urn])));
|
ops.push(FilesOp::Deleting(parent, [urn].into()));
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -144,11 +144,11 @@ impl Watcher {
|
||||||
|| realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s);
|
|| realname_unchecked(u, &mut cached).await.is_ok_and(|s| urn.as_urn() == s);
|
||||||
|
|
||||||
if !eq {
|
if !eq {
|
||||||
ops.push(FilesOp::Deleting(parent, HashSet::from_iter([urn])));
|
ops.push(FilesOp::Deleting(parent, [urn].into()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops.push(FilesOp::Upserting(parent, HashMap::from_iter([(urn, file)])));
|
ops.push(FilesOp::Upserting(parent, [(urn, file)].into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
FilesOp::mutate(ops);
|
FilesOp::mutate(ops);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use std::{mem, time::Duration};
|
use std::{mem, time::Duration};
|
||||||
|
|
||||||
use tokio::{fs, pin};
|
use tokio::pin;
|
||||||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_dds::Pubsub;
|
use yazi_dds::Pubsub;
|
||||||
use yazi_fs::expand_path;
|
use yazi_fs::{File, FilesOp, expand_path};
|
||||||
use yazi_macro::render;
|
use yazi_macro::render;
|
||||||
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy, TabProxy};
|
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy, TabProxy};
|
||||||
use yazi_shared::{Debounce, errors::InputError, event::CmdCow, url::Url};
|
use yazi_shared::{Debounce, errors::InputError, event::CmdCow, url::Url};
|
||||||
|
|
@ -94,16 +94,17 @@ impl Tab {
|
||||||
while let Some(result) = rx.next().await {
|
while let Some(result) = rx.next().await {
|
||||||
match result {
|
match result {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
let u = Url::from(expand_path(s));
|
let url = Url::from(expand_path(s));
|
||||||
let Ok(meta) = fs::metadata(&u).await else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if meta.is_dir() {
|
let Ok(file) = File::new(url.clone()).await else { return };
|
||||||
TabProxy::cd(&u);
|
if file.is_dir() {
|
||||||
} else {
|
return TabProxy::cd(&url);
|
||||||
TabProxy::reveal(&u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(p) = url.parent_url() {
|
||||||
|
FilesOp::Upserting(p, [(url.urn_owned(), file)].into()).emit();
|
||||||
|
}
|
||||||
|
TabProxy::reveal(&url);
|
||||||
}
|
}
|
||||||
Err(InputError::Completed(before, ticket)) => {
|
Err(InputError::Completed(before, ticket)) => {
|
||||||
CmpProxy::trigger(&before, ticket);
|
CmpProxy::trigger(&before, ticket);
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ impl Tab {
|
||||||
while let Some(chunk) = rx.next().await {
|
while let Some(chunk) = rx.next().await {
|
||||||
FilesOp::Part(cwd.clone(), chunk, ticket).emit();
|
FilesOp::Part(cwd.clone(), chunk, ticket).emit();
|
||||||
}
|
}
|
||||||
FilesOp::Done(cwd, Cha::dummy(), ticket).emit();
|
FilesOp::Done(cwd, Cha::default(), ticket).emit();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ impl Folder {
|
||||||
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
||||||
}
|
}
|
||||||
FilesOp::Part(_, ref files, _) if files.is_empty() => {
|
FilesOp::Part(_, ref files, _) if files.is_empty() => {
|
||||||
(self.cha, self.stage) = (Cha::dummy(), FolderStage::Loading);
|
(self.cha, self.stage) = (Cha::default(), FolderStage::Loading);
|
||||||
}
|
}
|
||||||
FilesOp::Part(_, _, ticket) if ticket == self.files.ticket() => {
|
FilesOp::Part(_, _, ticket) if ticket == self.files.ticket() => {
|
||||||
self.stage = FolderStage::Loading;
|
self.stage = FolderStage::Loading;
|
||||||
|
|
@ -55,7 +55,7 @@ impl Folder {
|
||||||
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
||||||
}
|
}
|
||||||
FilesOp::IOErr(_, kind) => {
|
FilesOp::IOErr(_, kind) => {
|
||||||
(self.cha, self.stage) = (Cha::dummy(), FolderStage::Failed(kind));
|
(self.cha, self.stage) = (Cha::default(), FolderStage::Failed(kind));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ impl Preview {
|
||||||
Some(PreviewLock { url: wd.clone(), cha, mime: MIME_DIR.to_owned(), ..Default::default() });
|
Some(PreviewLock { url: wd.clone(), cha, mime: MIME_DIR.to_owned(), ..Default::default() });
|
||||||
self.folder_loader.take().map(|h| h.abort());
|
self.folder_loader.take().map(|h| h.abort());
|
||||||
self.folder_loader = Some(tokio::spawn(async move {
|
self.folder_loader = Some(tokio::spawn(async move {
|
||||||
let Some(new) = Files::assert_stale(&wd, dir.unwrap_or(Cha::dummy())).await else { return };
|
let Some(new) = Files::assert_stale(&wd, dir.unwrap_or_default()).await else { return };
|
||||||
|
|
||||||
let rx = match Files::from_dir(&wd).await {
|
let rx = match Files::from_dir(&wd).await {
|
||||||
Ok(rx) => rx,
|
Ok(rx) => rx,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use std::{fs::{FileType, Metadata}, path::Path, time::SystemTime};
|
use std::{fs::{FileType, Metadata}, path::Path, time::SystemTime};
|
||||||
|
|
||||||
|
use tokio::fs;
|
||||||
use yazi_macro::{unix_either, win_either};
|
use yazi_macro::{unix_either, win_either};
|
||||||
|
use yazi_shared::url::Url;
|
||||||
|
|
||||||
use super::ChaKind;
|
use super::ChaKind;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct Cha {
|
pub struct Cha {
|
||||||
pub kind: ChaKind,
|
pub kind: ChaKind,
|
||||||
pub len: u64,
|
pub len: u64,
|
||||||
|
|
@ -25,8 +27,110 @@ pub struct Cha {
|
||||||
pub nlink: libc::nlink_t,
|
pub nlink: libc::nlink_t,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Metadata> for Cha {
|
impl Default for Cha {
|
||||||
fn from(m: &Metadata) -> Self {
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
kind: ChaKind::DUMMY,
|
||||||
|
len: 0,
|
||||||
|
atime: None,
|
||||||
|
btime: None,
|
||||||
|
#[cfg(unix)]
|
||||||
|
ctime: None,
|
||||||
|
mtime: None,
|
||||||
|
#[cfg(unix)]
|
||||||
|
mode: 0,
|
||||||
|
#[cfg(unix)]
|
||||||
|
dev: 0,
|
||||||
|
#[cfg(unix)]
|
||||||
|
uid: 0,
|
||||||
|
#[cfg(unix)]
|
||||||
|
gid: 0,
|
||||||
|
#[cfg(unix)]
|
||||||
|
nlink: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cha {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(path: &Path, meta: Metadata) -> Self {
|
||||||
|
Self::from_just_meta(&meta).attach(ChaKind::hidden(path, &meta))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub async fn from_url(url: &Url) -> std::io::Result<Self> {
|
||||||
|
Ok(Self::from_follow(url, fs::symlink_metadata(url).await?).await)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn from_follow(path: &Path, mut meta: Metadata) -> Self {
|
||||||
|
let mut attached = ChaKind::hidden(path, &meta);
|
||||||
|
if meta.is_symlink() {
|
||||||
|
attached |= ChaKind::LINK;
|
||||||
|
meta = fs::metadata(path).await.unwrap_or(meta);
|
||||||
|
}
|
||||||
|
if meta.is_symlink() {
|
||||||
|
attached |= ChaKind::ORPHAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::from_just_meta(&meta).attach(attached)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_dummy(url: &Url, ft: Option<FileType>) -> Self {
|
||||||
|
let mut me = ft.map(Self::from_half_ft).unwrap_or_default();
|
||||||
|
#[cfg(unix)]
|
||||||
|
if yazi_shared::url::Urn::new(url).is_hidden() {
|
||||||
|
me.kind |= ChaKind::HIDDEN;
|
||||||
|
}
|
||||||
|
me
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_half_ft(ft: FileType) -> Self {
|
||||||
|
let mut kind = ChaKind::DUMMY;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let mode = {
|
||||||
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
if ft.is_dir() {
|
||||||
|
kind |= ChaKind::DIR;
|
||||||
|
libc::S_IFDIR
|
||||||
|
} else if ft.is_symlink() {
|
||||||
|
kind |= ChaKind::LINK;
|
||||||
|
libc::S_IFLNK
|
||||||
|
} else if ft.is_block_device() {
|
||||||
|
libc::S_IFBLK
|
||||||
|
} else if ft.is_char_device() {
|
||||||
|
libc::S_IFCHR
|
||||||
|
} else if ft.is_fifo() {
|
||||||
|
libc::S_IFIFO
|
||||||
|
} else if ft.is_socket() {
|
||||||
|
libc::S_IFSOCK
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
if ft.is_dir() {
|
||||||
|
kind |= ChaKind::DIR;
|
||||||
|
} else if ft.is_symlink() {
|
||||||
|
kind |= ChaKind::LINK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
kind,
|
||||||
|
#[cfg(unix)]
|
||||||
|
mode,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_just_meta(m: &Metadata) -> Self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::{os::unix::{fs::MetadataExt, prelude::PermissionsExt}, time::{Duration, UNIX_EPOCH}};
|
||||||
|
|
||||||
let mut kind = ChaKind::empty();
|
let mut kind = ChaKind::empty();
|
||||||
if m.is_dir() {
|
if m.is_dir() {
|
||||||
kind |= ChaKind::DIR;
|
kind |= ChaKind::DIR;
|
||||||
|
|
@ -40,115 +144,20 @@ impl From<&Metadata> for Cha {
|
||||||
atime: m.accessed().ok(),
|
atime: m.accessed().ok(),
|
||||||
btime: m.created().ok(),
|
btime: m.created().ok(),
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
ctime: {
|
ctime: UNIX_EPOCH.checked_add(Duration::new(m.ctime() as u64, m.ctime_nsec() as u32)),
|
||||||
use std::{os::unix::fs::MetadataExt, time::{Duration, UNIX_EPOCH}};
|
|
||||||
UNIX_EPOCH.checked_add(Duration::new(m.ctime() as u64, m.ctime_nsec() as u32))
|
|
||||||
},
|
|
||||||
mtime: m.modified().ok(),
|
mtime: m.modified().ok(),
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mode: {
|
mode: m.permissions().mode() as _,
|
||||||
use std::os::unix::prelude::PermissionsExt;
|
|
||||||
m.permissions().mode() as _
|
|
||||||
},
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
dev: {
|
dev: m.dev() as _,
|
||||||
use std::os::unix::fs::MetadataExt;
|
|
||||||
m.dev() as _
|
|
||||||
},
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
uid: {
|
uid: m.uid() as _,
|
||||||
use std::os::unix::fs::MetadataExt;
|
|
||||||
m.uid() as _
|
|
||||||
},
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
gid: {
|
gid: m.gid() as _,
|
||||||
use std::os::unix::fs::MetadataExt;
|
|
||||||
m.gid() as _
|
|
||||||
},
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
nlink: {
|
nlink: m.nlink() as _,
|
||||||
use std::os::unix::fs::MetadataExt;
|
|
||||||
m.nlink() as _
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Metadata> for Cha {
|
|
||||||
fn from(m: Metadata) -> Self { Self::from(&m) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FileType> for Cha {
|
|
||||||
fn from(t: FileType) -> Self {
|
|
||||||
let mut kind = ChaKind::DUMMY;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
let mode = {
|
|
||||||
use std::os::unix::fs::FileTypeExt;
|
|
||||||
if t.is_dir() {
|
|
||||||
kind |= ChaKind::DIR;
|
|
||||||
libc::S_IFDIR
|
|
||||||
} else if t.is_symlink() {
|
|
||||||
kind |= ChaKind::LINK;
|
|
||||||
libc::S_IFLNK
|
|
||||||
} else if t.is_block_device() {
|
|
||||||
libc::S_IFBLK
|
|
||||||
} else if t.is_char_device() {
|
|
||||||
libc::S_IFCHR
|
|
||||||
} else if t.is_fifo() {
|
|
||||||
libc::S_IFIFO
|
|
||||||
} else if t.is_socket() {
|
|
||||||
libc::S_IFSOCK
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
if t.is_dir() {
|
|
||||||
kind |= ChaKind::DIR;
|
|
||||||
} else if t.is_symlink() {
|
|
||||||
kind |= ChaKind::LINK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
kind,
|
|
||||||
#[cfg(unix)]
|
|
||||||
mode,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cha {
|
|
||||||
pub async fn new(path: &Path, mut meta: Metadata) -> Self {
|
|
||||||
let mut attached = ChaKind::hidden(path, &meta);
|
|
||||||
|
|
||||||
if meta.is_symlink() {
|
|
||||||
attached |= ChaKind::LINK;
|
|
||||||
meta = tokio::fs::metadata(path).await.unwrap_or(meta);
|
|
||||||
}
|
|
||||||
if meta.is_symlink() {
|
|
||||||
attached |= ChaKind::ORPHAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut me = Self::from(meta);
|
|
||||||
me.kind |= attached;
|
|
||||||
me
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn new_nofollow(_path: &Path, meta: Metadata) -> Self {
|
|
||||||
let mut me = Self::from(&meta);
|
|
||||||
me.kind |= ChaKind::hidden(_path, &meta);
|
|
||||||
me
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn dummy() -> Self { Self { kind: ChaKind::DUMMY, ..Default::default() } }
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn hits(self, c: Self) -> bool {
|
pub fn hits(self, c: Self) -> bool {
|
||||||
|
|
@ -159,6 +168,12 @@ impl Cha {
|
||||||
&& self.kind == c.kind
|
&& self.kind == c.kind
|
||||||
&& unix_either!(self.mode == c.mode, true)
|
&& unix_either!(self.mode == c.mode, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn attach(mut self, kind: ChaKind) -> Self {
|
||||||
|
self.kind |= kind;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cha {
|
impl Cha {
|
||||||
|
|
|
||||||
|
|
@ -23,29 +23,25 @@ impl Deref for File {
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn from(url: Url) -> Result<Self> {
|
pub async fn new(url: Url) -> Result<Self> {
|
||||||
let meta = fs::symlink_metadata(&url).await?;
|
let meta = fs::symlink_metadata(&url).await?;
|
||||||
Ok(Self::from_meta(url, meta).await)
|
Ok(Self::from_follow(url, meta).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn from_meta(url: Url, meta: Metadata) -> Self {
|
pub async fn from_follow(url: Url, meta: Metadata) -> Self {
|
||||||
let link_to =
|
let link_to =
|
||||||
if meta.is_symlink() { fs::read_link(&url).await.map(Url::from).ok() } else { None };
|
if meta.is_symlink() { fs::read_link(&url).await.map(Url::from).ok() } else { None };
|
||||||
|
|
||||||
let cha = Cha::new(&url, meta).await;
|
let cha = Cha::from_follow(&url, meta).await;
|
||||||
|
|
||||||
Self { url, cha, link_to, icon: Default::default() }
|
Self { url, cha, link_to, icon: Default::default() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_dummy(url: Url, ft: Option<FileType>) -> Self {
|
pub fn from_dummy(url: Url, ft: Option<FileType>) -> Self {
|
||||||
Self {
|
let cha = Cha::from_dummy(&url, ft);
|
||||||
url,
|
Self { url, cha, link_to: None, icon: Default::default() }
|
||||||
cha: ft.map_or_else(Cha::dummy, Cha::from),
|
|
||||||
link_to: None,
|
|
||||||
icon: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ impl Files {
|
||||||
result = item.metadata() => {
|
result = item.metadata() => {
|
||||||
let url = Url::from(item.path());
|
let url = Url::from(item.path());
|
||||||
_ = tx.send(match result {
|
_ = tx.send(match result {
|
||||||
Ok(meta) => File::from_meta(url, meta).await,
|
Ok(meta) => File::from_follow(url, meta).await,
|
||||||
Err(_) => File::from_dummy(url, item.file_type().await.ok())
|
Err(_) => File::from_dummy(url, item.file_type().await.ok())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +65,7 @@ impl Files {
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
let url = Url::from(entry.path());
|
let url = Url::from(entry.path());
|
||||||
files.push(match entry.metadata().await {
|
files.push(match entry.metadata().await {
|
||||||
Ok(meta) => File::from_meta(url, meta).await,
|
Ok(meta) => File::from_follow(url, meta).await,
|
||||||
Err(_) => File::from_dummy(url, entry.file_type().await.ok()),
|
Err(_) => File::from_dummy(url, entry.file_type().await.ok()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +83,7 @@ impl Files {
|
||||||
|
|
||||||
pub async fn assert_stale(dir: &Url, cha: Cha) -> Option<Cha> {
|
pub async fn assert_stale(dir: &Url, cha: Cha) -> Option<Cha> {
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
match fs::metadata(dir).await.map(Cha::from) {
|
match Cha::from_url(dir).await {
|
||||||
Ok(c) if !c.is_dir() => FilesOp::issue_error(dir, ErrorKind::NotADirectory).await,
|
Ok(c) if !c.is_dir() => FilesOp::issue_error(dir, ErrorKind::NotADirectory).await,
|
||||||
Ok(c) if c.hits(cha) && PARTITIONS.read().heuristic(cha) => {}
|
Ok(c) if c.hits(cha) && PARTITIONS.read().heuristic(cha) => {}
|
||||||
Ok(c) => return Some(c),
|
Ok(c) => return Some(c),
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ impl FilesOp {
|
||||||
} else if maybe_exists(cwd).await {
|
} else if maybe_exists(cwd).await {
|
||||||
Self::IOErr(cwd.clone(), kind).emit();
|
Self::IOErr(cwd.clone(), kind).emit();
|
||||||
} else if let Some((p, n)) = cwd.pair() {
|
} else if let Some((p, n)) = cwd.pair() {
|
||||||
Self::Deleting(p, HashSet::from_iter([n])).emit();
|
Self::Deleting(p, [n].into()).emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields
|
||||||
use yazi_fs::cha::ChaKind;
|
use yazi_fs::cha::ChaKind;
|
||||||
|
|
||||||
#[derive(Clone, Copy, FromLua)]
|
#[derive(Clone, Copy, FromLua)]
|
||||||
pub struct Cha(yazi_fs::cha::Cha);
|
pub struct Cha(pub yazi_fs::cha::Cha);
|
||||||
|
|
||||||
impl Deref for Cha {
|
impl Deref for Cha {
|
||||||
type Target = yazi_fs::cha::Cha;
|
type Target = yazi_fs::cha::Cha;
|
||||||
|
|
@ -12,10 +12,6 @@ impl Deref for Cha {
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Into<yazi_fs::cha::Cha>> From<T> for Cha {
|
|
||||||
fn from(cha: T) -> Self { Self(cha.into()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cha {
|
impl Cha {
|
||||||
pub fn install(lua: &Lua) -> mlua::Result<()> {
|
pub fn install(lua: &Lua) -> mlua::Result<()> {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -33,7 +29,7 @@ impl Cha {
|
||||||
let kind =
|
let kind =
|
||||||
ChaKind::from_bits(t.raw_get("kind")?).ok_or_else(|| "Invalid kind".into_lua_err())?;
|
ChaKind::from_bits(t.raw_get("kind")?).ok_or_else(|| "Invalid kind".into_lua_err())?;
|
||||||
|
|
||||||
Self::from(yazi_fs::cha::Cha {
|
Self(yazi_fs::cha::Cha {
|
||||||
kind,
|
kind,
|
||||||
len: t.raw_get("len").unwrap_or_default(),
|
len: t.raw_get("len").unwrap_or_default(),
|
||||||
atime: parse_time(t.raw_get("atime").ok())?,
|
atime: parse_time(t.raw_get("atime").ok())?,
|
||||||
|
|
|
||||||
2
yazi-plugin/src/external/fd.rs
vendored
2
yazi-plugin/src/external/fd.rs
vendored
|
|
@ -20,7 +20,7 @@ pub fn fd(opt: FdOpt) -> Result<UnboundedReceiver<File>> {
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Ok(Some(line)) = it.next_line().await {
|
while let Ok(Some(line)) = it.next_line().await {
|
||||||
if let Ok(file) = File::from(opt.cwd.join(line)).await {
|
if let Ok(file) = File::new(opt.cwd.join(line)).await {
|
||||||
tx.send(file).ok();
|
tx.send(file).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
yazi-plugin/src/external/rg.rs
vendored
2
yazi-plugin/src/external/rg.rs
vendored
|
|
@ -29,7 +29,7 @@ pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Ok(Some(line)) = it.next_line().await {
|
while let Ok(Some(line)) = it.next_line().await {
|
||||||
if let Ok(file) = File::from(opt.cwd.join(line)).await {
|
if let Ok(file) = File::new(opt.cwd.join(line)).await {
|
||||||
tx.send(file).ok();
|
tx.send(file).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
yazi-plugin/src/external/rga.rs
vendored
2
yazi-plugin/src/external/rga.rs
vendored
|
|
@ -29,7 +29,7 @@ pub fn rga(opt: RgaOpt) -> Result<UnboundedReceiver<File>> {
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Ok(Some(line)) = it.next_line().await {
|
while let Ok(Some(line)) = it.next_line().await {
|
||||||
if let Ok(file) = File::from(opt.cwd.join(line)).await {
|
if let Ok(file) = File::new(opt.cwd.join(line)).await {
|
||||||
tx.send(file).ok();
|
tx.send(file).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ fn cha(lua: &Lua) -> mlua::Result<Function> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match meta {
|
match meta {
|
||||||
Ok(m) => Cha::from(m).into_lua_multi(&lua),
|
Ok(m) => Cha(yazi_fs::cha::Cha::new(&url, m)).into_lua_multi(&lua),
|
||||||
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
|
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -138,7 +138,7 @@ fn read_dir(lua: &Lua) -> mlua::Result<Function> {
|
||||||
let file = if !resolve {
|
let file = if !resolve {
|
||||||
yazi_fs::File::from_dummy(url, next.file_type().await.ok())
|
yazi_fs::File::from_dummy(url, next.file_type().await.ok())
|
||||||
} else if let Ok(meta) = next.metadata().await {
|
} else if let Ok(meta) = next.metadata().await {
|
||||||
yazi_fs::File::from_meta(url, meta).await
|
yazi_fs::File::from_follow(url, meta).await
|
||||||
} else {
|
} else {
|
||||||
yazi_fs::File::from_dummy(url, next.file_type().await.ok())
|
yazi_fs::File::from_dummy(url, next.file_type().await.ok())
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ macro_rules! impl_style_shorthands {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_file_fields {
|
macro_rules! impl_file_fields {
|
||||||
($fields:ident) => {
|
($fields:ident) => {
|
||||||
yazi_binding::cached_field!($fields, cha, |_, me| Ok($crate::bindings::Cha::from(me.cha)));
|
yazi_binding::cached_field!($fields, cha, |_, me| Ok($crate::bindings::Cha(me.cha)));
|
||||||
yazi_binding::cached_field!($fields, url, |_, me| Ok(yazi_binding::Url::new(me.url_owned())));
|
yazi_binding::cached_field!($fields, url, |_, me| Ok(yazi_binding::Url::new(me.url_owned())));
|
||||||
yazi_binding::cached_field!($fields, link_to, |_, me| Ok(
|
yazi_binding::cached_field!($fields, link_to, |_, me| Ok(
|
||||||
me.link_to.clone().map(yazi_binding::Url::new)
|
me.link_to.clone().map(yazi_binding::Url::new)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ impl TabProxy {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn reveal(target: &Url) {
|
pub fn reveal(target: &Url) {
|
||||||
emit!(Call(Cmd::args("mgr:reveal", &[target])));
|
emit!(Call(Cmd::args("mgr:reveal", &[target]).with("no-dummy", true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -328,15 +328,15 @@ impl File {
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn cha(path: &Path, follow: bool) -> io::Result<Cha> {
|
async fn cha(path: &Path, follow: bool) -> io::Result<Cha> {
|
||||||
let meta = fs::symlink_metadata(path).await?;
|
let meta = fs::symlink_metadata(path).await?;
|
||||||
Ok(if follow { Cha::new(path, meta).await } else { Cha::new_nofollow(path, meta) })
|
Ok(if follow { Cha::from_follow(path, meta).await } else { Cha::new(path, meta) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn cha_from(entry: DirEntry, path: &Path, follow: bool) -> io::Result<Cha> {
|
async fn cha_from(entry: DirEntry, path: &Path, follow: bool) -> io::Result<Cha> {
|
||||||
Ok(if follow {
|
Ok(if follow {
|
||||||
Cha::new(path, entry.metadata().await?).await
|
Cha::from_follow(path, entry.metadata().await?).await
|
||||||
} else {
|
} else {
|
||||||
Cha::new_nofollow(path, entry.metadata().await?)
|
Cha::new(path, entry.metadata().await?)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,10 @@ impl Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<bool> for Data {
|
||||||
|
fn from(value: bool) -> Self { Self::Boolean(value) }
|
||||||
|
}
|
||||||
|
|
||||||
impl From<usize> for Data {
|
impl From<usize> for Data {
|
||||||
fn from(value: usize) -> Self { Self::Id(value.into()) }
|
fn from(value: usize) -> Self { Self::Id(value.into()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue