mirror of
https://github.com/sxyazi/yazi.git
synced 2024-10-27 03:27:42 +03:00
..
This commit is contained in:
parent
b7d184abaf
commit
73d5d34b88
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
yazi-core/src/manager/linked.rs
Normal file
44
yazi-core/src/manager/linked.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -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::*;
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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());
|
||||||
|
@ -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) {
|
||||||
|
@ -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!(),
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user