This commit is contained in:
sxyazi 2023-12-26 18:07:27 +08:00
parent b7d184abaf
commit 73d5d34b88
No known key found for this signature in database
9 changed files with 182 additions and 70 deletions

View File

@ -40,14 +40,6 @@ impl Manager {
return false; return false;
} }
let mime = if hovered.is_dir() {
MIME_DIR.to_owned()
} else if let Some(s) = self.mimetype.get(&hovered.url) {
s.to_owned()
} else {
return self.active_mut().preview.reset();
};
let hovered = hovered.clone(); let hovered = hovered.clone();
if !self.active().preview.same_url(&hovered.url) { if !self.active().preview.same_url(&hovered.url) {
self.active_mut().preview.skip = 0; self.active_mut().preview.skip = 0;
@ -63,10 +55,19 @@ impl Manager {
} }
} }
if opt.force { if hovered.is_dir() {
self.active_mut().preview.force(hovered, mime); if self.active().history.contains_key(&hovered.url) {
self.active_mut().preview.go(hovered, MIME_DIR, opt.force);
} else {
self.active_mut().preview.go_folder(hovered, opt.force);
}
return false;
}
if let Some(s) = self.mimetype.get(&hovered.url).cloned() {
self.active_mut().preview.go(hovered, &s, opt.force);
} else { } else {
self.active_mut().preview.go(hovered, mime); return self.active_mut().preview.reset();
} }
false false
} }

View File

@ -25,7 +25,7 @@ impl Manager {
} else if matches!(self.hovered(), Some(h) if h.url == url) { } else if matches!(self.hovered(), Some(h) if h.url == url) {
self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(&url)); self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(&url));
self.active_mut().apply_files_attrs(true); self.active_mut().apply_files_attrs(true);
self.active_mut().history.get_mut(&url).unwrap().update(op) self.active_mut().history.get_mut(&url).unwrap().update(op) | self.peek(true)
} else { } else {
self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(&url)).update(op); self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(&url)).update(op);
false false
@ -59,17 +59,24 @@ impl Manager {
let Ok(opt) = opt.try_into() else { let Ok(opt) = opt.try_into() else {
return false; return false;
}; };
let calc = !matches!(opt.op, FilesOp::Size(..) | FilesOp::IOErr(_) | FilesOp::Deleting(..)); let calc = !matches!(opt.op, FilesOp::Size(..) | FilesOp::IOErr(_) | FilesOp::Deleting(..));
let b = match opt.op {
FilesOp::IOErr(..) => self.handle_ioerr(opt.op), let mut ops = vec![opt.op];
_ => self.handle_read(opt.op), for u in self.watcher.linked.read().from_dir(ops[0].url()) {
}; ops.push(ops[0].chroot(u));
}
let mut b = false;
for op in ops {
b |= match op {
FilesOp::IOErr(..) => self.handle_ioerr(op),
_ => self.handle_read(op),
};
}
if calc { if calc {
tasks.preload_sorted(&self.current().files); tasks.preload_sorted(&self.current().files);
} }
b b
} }
} }

View File

@ -21,23 +21,38 @@ impl Manager {
return false; return false;
}; };
let updates: HashMap<_, _> = opt let linked = self.watcher.linked.read();
let updates = opt
.data .data
.into_table_string() .into_table_string()
.into_iter() .into_iter()
.map(|(url, mime)| (Url::from(url), mime)) .map(|(url, mime)| (Url::from(url), mime))
.filter(|(url, mime)| self.mimetype.get(url) != Some(mime)) .filter(|(url, mime)| self.mimetype.get(url) != Some(mime))
.collect(); .fold(HashMap::new(), |mut map, (u, m)| {
for u in linked.from_file(&u) {
map.insert(u, m.clone());
}
map.insert(u, m);
map
});
drop(linked);
if updates.is_empty() { if updates.is_empty() {
return false; return false;
} }
let paged = self.current().paginate(self.current().page); let affected: Vec<_> = self
tasks.preload_affected(paged, &updates); .current()
.paginate(self.current().page)
.iter()
.filter(|&f| updates.contains_key(&f.url))
.cloned()
.collect();
self.mimetype.extend(updates); self.mimetype.extend(updates);
self.peek(false); self.peek(false);
tasks.preload_affected(&affected, &self.mimetype);
true true
} }
} }

View File

@ -0,0 +1,44 @@
use std::{collections::BTreeMap, ops::{Deref, DerefMut}};
use yazi_shared::fs::Url;
#[derive(Default)]
pub struct Linked(BTreeMap<Url, Url> /* from ==> to */);
impl Deref for Linked {
type Target = BTreeMap<Url, Url>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for Linked {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl Linked {
pub fn from_dir(&self, url: &Url) -> Vec<&Url> {
if let Some(to) = self.get(url) {
self.iter().filter(|(k, v)| *v == to && *k != url).map(|(k, _)| k).collect()
} else {
self.iter().filter(|(_, v)| *v == url).map(|(k, _)| k).collect()
}
}
pub fn from_file(&self, url: &Url) -> Vec<Url> {
if self.is_empty() {
return Default::default();
}
let Some(p) = url.parent_url() else {
return Default::default();
};
let relatives = self.from_dir(&p);
if relatives.is_empty() {
return Default::default();
}
let name = url.file_name().unwrap();
relatives.into_iter().map(|u| u.join(name)).collect()
}
}

View File

@ -1,8 +1,10 @@
mod commands; mod commands;
mod linked;
mod manager; mod manager;
mod tabs; mod tabs;
mod watcher; mod watcher;
pub use linked::*;
pub use manager::*; pub use manager::*;
pub use tabs::*; pub use tabs::*;
pub use watcher::*; pub use watcher::*;

View File

@ -8,12 +8,13 @@ use tracing::error;
use yazi_plugin::isolate; use yazi_plugin::isolate;
use yazi_shared::fs::{File, FilesOp, Url}; use yazi_shared::fs::{File, FilesOp, Url};
use super::Linked;
use crate::folder::Files; use crate::folder::Files;
pub struct Watcher { pub struct Watcher {
watcher: RecommendedWatcher, watcher: RecommendedWatcher,
watched: Arc<RwLock<BTreeSet<Url>>>, watched: Arc<RwLock<BTreeSet<Url>>>,
pub linked: Arc<RwLock<BTreeMap<Url, Url>>>, // from ==> to pub linked: Arc<RwLock<Linked>>,
} }
impl Watcher { impl Watcher {
@ -133,7 +134,7 @@ impl Watcher {
pin!(rx); pin!(rx);
while let Some(urls) = rx.next().await { while let Some(urls) = rx.next().await {
let urls = urls.into_iter().collect::<BTreeSet<_>>(); let urls: BTreeSet<_> = urls.into_iter().collect();
let mut reload = Vec::with_capacity(urls.len()); let mut reload = Vec::with_capacity(urls.len());
for u in urls { for u in urls {
@ -161,16 +162,3 @@ impl Watcher {
} }
} }
} }
impl Watcher {
pub fn relatives(&self, url: &Url) -> BTreeSet<Url> {
let mut map = BTreeSet::from_iter([url.clone()]);
let linked = self.linked.read();
if let Some(to) = linked.get(url) {
map.extend(linked.iter().filter(|(_, v)| *v == to).map(|(k, _)| k).cloned());
}
map
}
}

View File

@ -1,26 +1,31 @@
use std::time::Duration;
use tokio::{pin, task::JoinHandle};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use yazi_adaptor::ADAPTOR; use yazi_adaptor::ADAPTOR;
use yazi_config::PLUGIN; use yazi_config::PLUGIN;
use yazi_plugin::{external::Highlighter, utils::PreviewLock}; use yazi_plugin::{external::Highlighter, utils::PreviewLock};
use yazi_shared::fs::{Cha, File, Url}; use yazi_shared::{fs::{Cha, File, FilesOp, Url}, MIME_DIR};
use crate::folder::Files;
#[derive(Default)] #[derive(Default)]
pub struct Preview { pub struct Preview {
pub lock: Option<PreviewLock>, pub lock: Option<PreviewLock>,
pub skip: usize, pub skip: usize,
previewer_ct: Option<CancellationToken>, previewer_ct: Option<CancellationToken>,
folder_handle: Option<JoinHandle<()>>,
} }
impl Preview { impl Preview {
pub fn go(&mut self, file: File, mime: String) { pub fn go(&mut self, file: File, mime: &str, force: bool) {
if !self.content_unchanged(&file.url, &file.cha) { if !force && self.content_unchanged(&file.url, &file.cha) {
self.force(file, mime); return;
} }
}
pub fn force(&mut self, file: File, mime: String) { let Some(previewer) = PLUGIN.previewer(&file.url, mime) else {
let Some(previewer) = PLUGIN.previewer(&file.url, &mime) else {
self.reset(); self.reset();
return; return;
}; };
@ -33,6 +38,31 @@ impl Preview {
} }
} }
pub fn go_folder(&mut self, file: File, force: bool) {
if !force && self.content_unchanged(&file.url, &file.cha) {
return;
}
self.go(file.clone(), MIME_DIR, force);
self.folder_handle.take().map(|h| h.abort());
self.folder_handle = Some(tokio::spawn(async move {
let Ok(rx) = Files::from_dir(&file.url).await else {
FilesOp::IOErr(file.url).emit();
return;
};
let stream =
UnboundedReceiverStream::new(rx).chunks_timeout(10000, Duration::from_millis(350));
pin!(stream);
let ticket = FilesOp::prepare(&file.url);
while let Some(chunk) = stream.next().await {
FilesOp::Part(file.url.clone(), chunk, ticket).emit();
}
}));
}
#[inline] #[inline]
pub fn abort(&mut self) { pub fn abort(&mut self) {
self.previewer_ct.take().map(|ct| ct.cancel()); self.previewer_ct.take().map(|ct| ct.cancel());

View File

@ -172,7 +172,7 @@ impl Tasks {
}; };
for rule in PLUGIN.preloaders(&f.url, mime, factors) { for rule in PLUGIN.preloaders(&f.url, mime, factors) {
if loaded.get(&f.url).is_some_and(|x| x & 1 << rule.id != 0) { if loaded.get(&f.url).is_some_and(|x| x & (1 << rule.id) != 0) {
continue; continue;
} }
if rule.multi { if rule.multi {
@ -188,7 +188,11 @@ impl Tasks {
let mut go = |rule: &PluginRule, targets: Vec<&File>| { let mut go = |rule: &PluginRule, targets: Vec<&File>| {
for &f in &targets { for &f in &targets {
*loaded.entry(f.url.clone()).or_default() |= 1 << rule.id; if let Some(n) = loaded.get_mut(&f.url) {
*n |= 1 << rule.id;
} else {
loaded.insert(f.url.clone(), 1 << rule.id);
}
} }
self.scheduler.preload_paged(rule, targets); self.scheduler.preload_paged(rule, targets);
}; };
@ -203,15 +207,15 @@ impl Tasks {
} }
} }
pub fn preload_affected(&self, paged: &[File], mimetype: &HashMap<Url, String>) { pub fn preload_affected(&self, affected: &[File], mimetype: &HashMap<Url, String>) {
{ {
let mut loaded = self.scheduler.preload.rule_loaded.write(); let mut loaded = self.scheduler.preload.rule_loaded.write();
for u in mimetype.keys() { for f in affected {
loaded.remove(u); loaded.remove(&f.url);
} }
} }
self.preload_paged(paged, mimetype); self.preload_paged(affected, mimetype);
} }
pub fn preload_sorted(&self, targets: &Files) { pub fn preload_sorted(&self, targets: &Files) {

View File

@ -45,25 +45,46 @@ impl FilesOp {
ticket ticket
} }
pub fn chroot(&self, new: Url) -> Self { pub fn chroot(&self, new: &Url) -> Self {
todo!() let old = self.url();
// Full(Url, Vec<File>), macro_rules! new {
// Part(Url, u64, Vec<File>), ($url:expr) => {{ new.join($url.strip_prefix(old).unwrap()) }};
// Size(Url, BTreeMap<Url, u64>), }
// IOErr(Url), macro_rules! files {
($files:expr) => {{
$files
.iter()
.map(|file| {
let mut f = file.clone();
f.url = new!(f.url);
f
})
.collect()
}};
}
macro_rules! map {
($map:expr) => {{
$map
.iter()
.map(|(k, v)| {
let mut f = v.clone();
f.url = new!(f.url);
(new!(k), f)
})
.collect()
}};
}
// Creating(Url, BTreeMap<Url, File>), let u = new.clone();
// Deleting(Url, BTreeSet<Url>), match self {
// Replacing(Url, BTreeMap<Url, File>), Self::Full(_, files) => Self::Full(u, files!(files)),
Self::Part(_, files, ticket) => Self::Part(u, files!(files), *ticket),
// match self { Self::Size(_, map) => Self::Size(u, map.iter().map(|(k, v)| (new!(k), *v)).collect()),
// FilesOp::Full(_, vec) => {} Self::IOErr(_) => Self::IOErr(u),
// FilesOp::Part(_, _, vec) => todo!(), Self::Creating(_, files) => Self::Creating(u, files!(files)),
// FilesOp::Size(_, map) => todo!(), Self::Deleting(_, urls) => Self::Deleting(u, urls.iter().map(|u| new!(u)).collect()),
// FilesOp::IOErr(_) => todo!(), Self::Updating(_, map) => Self::Updating(u, map!(map)),
// FilesOp::Creating(_, map) => todo!(), Self::Upserting(_, map) => Self::Upserting(u, map!(map)),
// FilesOp::Deleting(_, set) => todo!(), }
// FilesOp::Replacing(_, map) => todo!(),
// }
} }
} }