diff --git a/Cargo.lock b/Cargo.lock index 1c728ce2..101a1a78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5859,7 +5859,6 @@ dependencies = [ "anyhow", "bitflags 2.11.0", "crossterm 0.29.0", - "dirs", "globset", "hashbrown 0.16.1", "indexmap 2.13.0", @@ -6119,7 +6118,6 @@ name = "yazi-proxy" version = "26.2.2" dependencies = [ "tokio", - "yazi-binding", "yazi-config", "yazi-macro", "yazi-parser", @@ -6256,7 +6254,7 @@ dependencies = [ name = "yazi-watcher" version = "26.2.2" dependencies = [ - "anyhow", + "futures", "hashbrown 0.16.1", "notify", "parking_lot", diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 91764e24..7d35ff4c 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -22,7 +22,6 @@ yazi-tty = { path = "../yazi-tty", version = "26.2.2" } anyhow = { workspace = true } bitflags = { workspace = true } crossterm = { workspace = true } -dirs = { workspace = true } globset = { workspace = true } hashbrown = { workspace = true } indexmap = { workspace = true } diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 337f27c4..6d6081da 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -12,7 +12,6 @@ repository = "https://github.com/sxyazi/yazi" workspace = true [dependencies] -yazi-binding = { path = "../yazi-binding", version = "26.2.2" } yazi-config = { path = "../yazi-config", version = "26.2.2" } yazi-macro = { path = "../yazi-macro", version = "26.2.2" } yazi-parser = { path = "../yazi-parser", version = "26.2.2" } diff --git a/yazi-shared/src/last_value.rs b/yazi-shared/src/last_value.rs new file mode 100644 index 00000000..8ebcc26e --- /dev/null +++ b/yazi-shared/src/last_value.rs @@ -0,0 +1,27 @@ +use std::sync::Arc; + +use parking_lot::Mutex; +use tokio::sync::Notify; + +#[derive(Clone, Debug, Default)] +pub struct LastValue { + inner: Arc<(Notify, Mutex>)>, +} + +impl LastValue { + pub fn set(&self, data: T) { + *self.inner.1.lock() = Some(data); + self.inner.0.notify_waiters(); + } + + pub async fn get(&self) -> T { + loop { + let notified = self.inner.0.notified(); + if let Some(data) = self.inner.1.lock().take() { + return data; + } + + notified.await; + } + } +} diff --git a/yazi-shared/src/lib.rs b/yazi-shared/src/lib.rs index 642c9244..eeaed195 100644 --- a/yazi-shared/src/lib.rs +++ b/yazi-shared/src/lib.rs @@ -1,6 +1,6 @@ yazi_macro::mod_pub!(data errors event loc path pool scheme shell strand translit url wtf8); -yazi_macro::mod_flat!(alias bytes chars completion_token condition debounce env id layer localset natsort os predictor ro_cell source sync_cell terminal tests throttle time utf8); +yazi_macro::mod_flat!(alias bytes chars completion_token condition debounce env id last_value layer localset natsort os predictor ro_cell source sync_cell terminal tests throttle time utf8); pub fn init() { LOCAL_SET.with(tokio::task::LocalSet::new); diff --git a/yazi-watcher/Cargo.toml b/yazi-watcher/Cargo.toml index 2d42fcf3..4e651399 100644 --- a/yazi-watcher/Cargo.toml +++ b/yazi-watcher/Cargo.toml @@ -21,7 +21,7 @@ yazi-shared = { path = "../yazi-shared", version = "26.2.2" } yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } # External dependencies -anyhow = { workspace = true } +futures = { workspace = true } hashbrown = { workspace = true } notify = { version = "8.2.0", default-features = false, features = [ "macos_fsevent" ] } parking_lot = { workspace = true } diff --git a/yazi-watcher/src/local/local.rs b/yazi-watcher/src/local/local.rs index c581bc2e..6d534f5a 100644 --- a/yazi-watcher/src/local/local.rs +++ b/yazi-watcher/src/local/local.rs @@ -5,7 +5,7 @@ use notify::{PollWatcher, RecommendedWatcher, RecursiveMode, Result, Watcher}; use tokio::{pin, sync::mpsc::{self, UnboundedReceiver}}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tracing::error; -use yazi_fs::{File, FilesOp, cha::Cha, mounts::PARTITIONS, provider}; +use yazi_fs::{File, FilesOp, mounts::PARTITIONS, provider::{self, Provider}}; use yazi_shared::url::{UrlBuf, UrlLike}; use yazi_vfs::VfsFile; @@ -74,13 +74,15 @@ impl Local { } } - pub(crate) fn soundless(path: &Path) -> bool { + pub(crate) async fn soundless(path: &Path) -> bool { if cfg!(target_os = "netbsd") || yazi_adapter::WSL.get() { return true; } - let Ok(meta) = path.metadata() else { return true }; - PARTITIONS.read().soundless(Cha::new(path.file_name().unwrap_or_default(), meta)) + match provider::local::Local::regular(path).metadata().await { + Ok(cha) => PARTITIONS.read().soundless(cha), + Err(_) => true, + } } async fn changed(rx: UnboundedReceiver) { diff --git a/yazi-watcher/src/watchee.rs b/yazi-watcher/src/watchee.rs index bcb14f9b..47b8c091 100644 --- a/yazi-watcher/src/watchee.rs +++ b/yazi-watcher/src/watchee.rs @@ -34,13 +34,13 @@ impl<'a> Watchee<'a> { }) } - pub(super) fn new(url: U) -> Self + pub(super) async fn new(url: U) -> Self where U: Into>, { let url = url.into(); if let Some(path) = url.as_local() { - let b = Local::soundless(path); + let b = Local::soundless(path).await; Self::Local(url, b) } else { Self::Remote(url) diff --git a/yazi-watcher/src/watcher.rs b/yazi-watcher/src/watcher.rs index 23dffdd6..a48d2044 100644 --- a/yazi-watcher/src/watcher.rs +++ b/yazi-watcher/src/watcher.rs @@ -1,25 +1,25 @@ +use futures::{StreamExt, stream}; use hashbrown::HashSet; -use tokio::sync::watch; use tracing::error; use yazi_fs::FsUrl; -use yazi_shared::url::{UrlBuf, UrlCow, UrlLike}; +use yazi_shared::{LastValue, url::{UrlBuf, UrlCow, UrlLike}}; use crate::{Reporter, WATCHED, Watchee, backend::Backend}; pub struct Watcher { - tx: watch::Sender>, + last: LastValue>, reporter: Reporter, } impl Watcher { pub fn serve() -> Self { - let (tx, rx) = watch::channel(Default::default()); + let last = LastValue::default(); let backend = Backend::serve(); let reporter = backend.reporter.clone(); - tokio::spawn(Self::watched(rx, backend)); - Self { tx, reporter } + tokio::spawn(Self::watched(last.clone(), backend)); + Self { last, reporter } } pub fn watch<'a, I>(&mut self, urls: I) @@ -28,18 +28,18 @@ impl Watcher { I::Item: Into>, { let it = urls.into_iter(); - let mut set = HashSet::with_capacity(it.size_hint().0); + let mut urls = HashSet::with_capacity(it.size_hint().0); for url in it.map(Into::into) { if !url.is_absolute() { continue; } else if let Some(cache) = url.cache() { - set.insert(cache.into()); + urls.insert(cache.into()); } - set.insert(url.into_owned()); + urls.insert(url.into_owned()); } - self.tx.send(set).ok(); + self.last.set(urls); } pub fn report<'a, I>(&self, urls: I) @@ -50,46 +50,31 @@ impl Watcher { self.reporter.report(urls); } - async fn watched(mut rx: watch::Receiver>, mut backend: Backend) { + async fn watched(last: LastValue>, mut backend: Backend) { loop { - let (rx_, to_unwatch, to_watch) = Self::diff(rx).await; - rx = rx_; + let (to_unwatch, to_watch) = Self::diff(last.get().await).await; if !to_unwatch.is_empty() || !to_watch.is_empty() { backend = Self::sync(backend, to_unwatch, to_watch).await; backend = backend.sync().await; } - - if rx.changed().await.is_err() { - break; - } } } - async fn diff( - mut rx: watch::Receiver>, - ) -> (watch::Receiver>, Vec>, Vec>) { - tokio::task::spawn_blocking(move || { - let new = rx.borrow_and_update(); - let new_: HashSet<_> = new.iter().map(Watchee::new).collect(); + async fn diff(urls: HashSet) -> (Vec>, HashSet>) { + let mut new: HashSet<_> = stream::iter(urls).then(Watchee::new).collect().await; + let old = WATCHED.read(); - let old = WATCHED.read(); + let to_unwatch = old.difference(&new).map(Watchee::to_static).collect(); + new.retain(|watchee| !old.contains(watchee)); - let to_unwatch = old.difference(&new_).map(Watchee::to_static).collect(); - let to_watch = new_.difference(&old).map(Watchee::to_static).collect(); - - drop(new_); - drop(new); - (rx, to_unwatch, to_watch) - }) - .await - .unwrap() + (to_unwatch, new) } async fn sync( mut backend: Backend, to_unwatch: Vec>, - to_watch: Vec>, + to_watch: HashSet>, ) -> Backend { tokio::task::spawn_blocking(move || { for watchee in to_unwatch {