From 1270fd15f9dd868769046bf8670a7a8ebfaa62c6 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Thu, 23 Nov 2023 08:47:01 +0800 Subject: [PATCH] .. --- yazi-core/src/manager/commands/peek.rs | 55 ++++++++++--------- yazi-core/src/manager/tabs.rs | 2 +- yazi-core/src/preview/mod.rs | 2 + yazi-core/src/preview/preview.rs | 75 ++++++++++++++++++++------ yazi-core/src/tab/commands/search.rs | 2 +- yazi-core/src/tab/tab.rs | 2 +- yazi-fm/src/app.rs | 16 +++--- yazi-fm/src/widgets/clear.rs | 4 ++ yazi-shared/src/cha.rs | 2 +- 9 files changed, 108 insertions(+), 52 deletions(-) diff --git a/yazi-core/src/manager/commands/peek.rs b/yazi-core/src/manager/commands/peek.rs index 4a95253a..0dce34b0 100644 --- a/yazi-core/src/manager/commands/peek.rs +++ b/yazi-core/src/manager/commands/peek.rs @@ -3,32 +3,33 @@ use yazi_shared::{Url, MIME_DIR}; use crate::{emit, manager::Manager}; +#[derive(Debug)] pub struct Opt { step: isize, - sequent: Option, - upper_bound: Option, + only_if: Option, + upper_bound: bool, } impl From<&Exec> for Opt { fn from(e: &Exec) -> Self { Self { step: e.args.first().and_then(|s| s.parse().ok()).unwrap_or(0), - sequent: e.named.get("sequent").map(Url::from), - upper_bound: e.named.get("upper-bound").and_then(|s| s.parse().ok()), + only_if: e.named.get("only-if").map(Url::from), + upper_bound: e.named.contains_key("upper-bound"), } } } impl From for Opt { - fn from(step: isize) -> Self { Self { step, sequent: None, upper_bound: None } } + fn from(step: isize) -> Self { Self { step, only_if: None, upper_bound: false } } } impl Manager { #[inline] - pub fn _peek_upper_bound(bound: usize, sequent: &Url) { + pub fn _peek_upper_bound(bound: usize, only_if: &Url) { emit!(Call( - Exec::call("peek", vec![]) - .with("sequent", sequent.to_string()) - .with("upper-bound", bound.to_string()) + Exec::call("peek", vec![bound.to_string()]) + .with("only-if", only_if.to_string()) + .with_bool("upper-bound", true) .vec(), KeymapLayer::Manager )); @@ -36,11 +37,11 @@ impl Manager { pub fn peek(&mut self, opt: impl Into) -> bool { let Some(hovered) = self.hovered() else { - return self.active_mut().preview.reset(|_| true); + return self.active_mut().preview.reset(); }; let opt = opt.into() as Opt; - if matches!(opt.sequent, Some(ref u) if *u != hovered.url) { + if matches!(opt.only_if, Some(ref u) if *u != hovered.url) { return false; } @@ -49,35 +50,37 @@ impl Manager { } let Some(mime) = self.mimetype.get(&hovered.url).cloned() else { - return self.active_mut().preview.reset(|_| true); + return self.active_mut().preview.reset(); }; - let url = hovered.url.clone(); - self.active_mut().preview.arrow(opt.step, &mime); - if let Some(bound) = opt.upper_bound { - self.active_mut().preview.apply_bound(bound); + let (url, cha) = (hovered.url.clone(), hovered.cha); + if self.active().preview.same_url(&url) { + self.active_mut().preview.arrow(opt.step, &mime); + } else if opt.upper_bound { + self.active_mut().preview.apply_bound(opt.step as usize); + } else { + self.active_mut().preview.set_skip(0); } - self.active_mut().preview.go(&url, &mime); + self.active_mut().preview.go(&url, cha, &mime); false } fn peek_folder(&mut self, opt: Opt, url: Url) -> bool { - let (skip, bound) = self - .active() - .history - .get(&url) + let folder = self.active().history.get(&url); + let (skip, bound) = folder .map(|f| (f.offset, f.files.len().saturating_sub(MANAGER.layout.folder_height()))) .unwrap_or_default(); - if opt.sequent.is_some() { + if self.active().preview.same_url(&url) { self.active_mut().preview.arrow(opt.step, MIME_DIR); - } else { - self.active_mut().preview.set_skip(skip); + self.active_mut().preview.apply_bound(bound); + return false; } - self.active_mut().preview.apply_bound(bound); - self.active_mut().preview.go_folder(url, opt.sequent.is_none()); + let in_chunks = folder.is_none(); + self.active_mut().preview.set_skip(skip); + self.active_mut().preview.go_folder(url, in_chunks); false } } diff --git a/yazi-core/src/manager/tabs.rs b/yazi-core/src/manager/tabs.rs index 80967e69..a235fcaa 100644 --- a/yazi-core/src/manager/tabs.rs +++ b/yazi-core/src/manager/tabs.rs @@ -31,7 +31,7 @@ impl Tabs { #[inline] pub(super) fn set_idx(&mut self, idx: usize) { self.idx = idx; - self.active_mut().preview.reset(|l| l.is_image()); + self.active_mut().preview.reset_image(); Manager::_refresh(); } } diff --git a/yazi-core/src/preview/mod.rs b/yazi-core/src/preview/mod.rs index 3a54a75d..2d5ba586 100644 --- a/yazi-core/src/preview/mod.rs +++ b/yazi-core/src/preview/mod.rs @@ -3,3 +3,5 @@ mod provider; pub use preview::*; use provider::*; + +pub static COLLISION: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); diff --git a/yazi-core/src/preview/preview.rs b/yazi-core/src/preview/preview.rs index 64f01bc0..4bb996d5 100644 --- a/yazi-core/src/preview/preview.rs +++ b/yazi-core/src/preview/preview.rs @@ -4,7 +4,7 @@ use tokio::{pin, task::JoinHandle}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use yazi_adaptor::ADAPTOR; use yazi_config::MANAGER; -use yazi_shared::{MimeKind, PeekError, Url}; +use yazi_shared::{Cha, MimeKind, PeekError, Url}; use super::Provider; use crate::{emit, files::{Files, FilesOp}, manager::Manager, Highlighter}; @@ -19,6 +19,7 @@ pub struct Preview { pub struct PreviewLock { pub url: Url, + pub cha: Option, pub skip: usize, pub data: PreviewData, } @@ -31,14 +32,18 @@ pub enum PreviewData { } impl Preview { - pub fn go(&mut self, url: &Url, mime: &str) { - self.reset(|_| true); - let (url, skip, kind) = (url.clone(), self.skip, MimeKind::new(mime)); + pub fn go(&mut self, url: &Url, cha: Cha, mime: &str) { + if self.content_unchanged(url, &cha) { + return; + } + + self.reset(); + let (url, kind, skip) = (url.clone(), MimeKind::new(mime), self.skip); self.handle = Some(tokio::spawn(async move { match Provider::auto(kind, &url, skip).await { Ok(data) => { - emit!(Preview(PreviewLock { url, skip, data })); + emit!(Preview(PreviewLock { url, cha: Some(cha), skip, data })); } Err(PeekError::Exceed(max)) => { Manager::_peek_upper_bound(max, &url); @@ -49,8 +54,13 @@ impl Preview { } pub fn go_folder(&mut self, url: Url, in_chunks: bool) { - self.reset(|_| true); - emit!(Preview(PreviewLock { url: url.clone(), skip: self.skip, data: PreviewData::Folder })); + self.reset(); + self.lock = Some(PreviewLock { + url: url.clone(), + cha: None, + skip: self.skip, + data: PreviewData::Folder, + }); self.handle = Some(tokio::spawn(async move { let Ok(rx) = Files::from_dir(&url).await else { @@ -74,7 +84,7 @@ impl Preview { })); } - pub fn reset bool>(&mut self, f: F) -> bool { + pub fn reset(&mut self) -> bool { self.handle.take().map(|h| h.abort()); Highlighter::abort(); ADAPTOR.image_hide(MANAGER.layout.image_rect()).ok(); @@ -83,14 +93,49 @@ impl Preview { return false; }; - if !f(lock) { - return false; - } - let b = !lock.is_image(); self.lock = None; b } + + pub fn reset_image(&mut self) -> bool { + if !matches!(self.lock, Some(ref lock) if lock.is_image()) { + return false; + } + + self.reset(); + true + } + + #[inline] + pub fn same_url(&self, url: &Url) -> bool { + matches!(self.lock, Some(ref lock) if lock.url == *url) + } + + fn content_unchanged(&self, url: &Url, cha: &Cha) -> bool { + let Some(lock) = &self.lock else { + return false; + }; + let Some(cha_) = &lock.cha else { + return false; + }; + + *url == lock.url + && self.skip == lock.skip + && cha.len == cha_.len + && cha.modified == cha_.modified + && cha.meta == cha_.meta + && { + #[cfg(unix)] + { + cha.permissions == cha_.permissions + } + #[cfg(windows)] + { + true + } + } + } } impl Preview { @@ -106,12 +151,12 @@ impl Preview { pub fn set_skip(&mut self, skip: usize) -> bool { mem::replace(&mut self.skip, skip) != skip } #[inline] - pub fn apply_bound(&mut self, max: usize) -> bool { - if self.skip <= max { + pub fn apply_bound(&mut self, upper: usize) -> bool { + if self.skip <= upper { return false; } - self.skip = max; + self.skip = upper; true } } diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index da436995..6d60aafb 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -76,7 +76,7 @@ impl Tab { handle.abort(); } if self.current.cwd.is_search() { - self.preview.reset(|l| l.is_image()); + self.preview.reset_image(); let rep = self.history_new(&self.current.cwd.to_regular()); drop(mem::replace(&mut self.current, rep)); diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index 2c38a6c0..393fb714 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -49,7 +49,7 @@ impl From<&Url> for Tab { impl Tab { pub fn update_preview(&mut self, lock: PreviewLock) -> bool { let Some(hovered) = self.current.hovered().map(|h| &h.url) else { - return self.preview.reset(|_| true); + return self.preview.reset(); }; if lock.url != *hovered { diff --git a/yazi-fm/src/app.rs b/yazi-fm/src/app.rs index c4f5d1ee..42fa51a1 100644 --- a/yazi-fm/src/app.rs +++ b/yazi-fm/src/app.rs @@ -1,11 +1,11 @@ -use std::ffi::OsString; +use std::{ffi::OsString, sync::atomic::Ordering}; use anyhow::{Ok, Result}; use crossterm::event::KeyEvent; use ratatui::{backend::Backend, prelude::Rect}; use tokio::sync::oneshot; use yazi_config::{keymap::{Exec, Key, KeymapLayer}, BOOT}; -use yazi_core::{emit, files::FilesOp, input::InputMode, manager::Manager, Ctx, Event}; +use yazi_core::{emit, files::FilesOp, input::InputMode, manager::Manager, preview::COLLISION, Ctx, Event}; use yazi_shared::Term; use crate::{Executor, Logs, Panic, Root, Signals}; @@ -81,14 +81,17 @@ impl App { return Ok(()); }; + COLLISION.store(false, Ordering::Relaxed); let frame = term.draw(|f| { yazi_plugin::scope(&self.cx, |_| { f.render_widget(Root::new(&self.cx), f.size()); }); })?; + if !COLLISION.load(Ordering::Relaxed) { + return Ok(()); + } let mut patches = Vec::new(); - // TODO: find a more efficient way to do this for x in frame.area.left()..frame.area.right() { for y in frame.area.top()..frame.area.bottom() { let cell = frame.buffer.get(x, y); @@ -114,14 +117,13 @@ impl App { } self.cx.manager.current_mut().set_page(true); - self.cx.manager.active_mut().preview.reset(|_| true); - // TODO: Peek-trigger - // self.cx.manager.peek(true); + self.cx.manager.active_mut().preview.reset(); + self.cx.manager.peek(0); emit!(Render); } fn dispatch_stop(&mut self, state: bool, tx: Option>) { - self.cx.manager.active_mut().preview.reset(|l| l.is_image()); + self.cx.manager.active_mut().preview.reset_image(); if state { self.signals.stop_term(true); self.term = None; diff --git a/yazi-fm/src/widgets/clear.rs b/yazi-fm/src/widgets/clear.rs index 78f13525..a7a8e947 100644 --- a/yazi-fm/src/widgets/clear.rs +++ b/yazi-fm/src/widgets/clear.rs @@ -1,6 +1,9 @@ +use std::sync::atomic::Ordering; + use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; use yazi_adaptor::ADAPTOR; use yazi_config::MANAGER; +use yazi_core::preview::COLLISION; pub(crate) struct Clear; @@ -30,6 +33,7 @@ impl Widget for Clear { }; ADAPTOR.image_hide(r).ok(); + COLLISION.store(true, Ordering::Relaxed); for x in r.left()..r.right() { for y in r.top()..r.bottom() { buf.get_mut(x, y).set_skip(true); diff --git a/yazi-shared/src/cha.rs b/yazi-shared/src/cha.rs index e15e6469..0f5185f0 100644 --- a/yazi-shared/src/cha.rs +++ b/yazi-shared/src/cha.rs @@ -3,7 +3,7 @@ use std::{fs::Metadata, time::SystemTime}; use bitflags::bitflags; bitflags! { - #[derive(Clone, Copy, Debug, Default)] + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct ChaMeta: u8 { const DIR = 0b00000001;