This commit is contained in:
sxyazi 2023-11-23 08:47:01 +08:00
parent ff369f4d15
commit 1270fd15f9
No known key found for this signature in database
9 changed files with 108 additions and 52 deletions

View file

@ -3,32 +3,33 @@ use yazi_shared::{Url, MIME_DIR};
use crate::{emit, manager::Manager}; use crate::{emit, manager::Manager};
#[derive(Debug)]
pub struct Opt { pub struct Opt {
step: isize, step: isize,
sequent: Option<Url>, only_if: Option<Url>,
upper_bound: Option<usize>, upper_bound: bool,
} }
impl From<&Exec> for Opt { impl From<&Exec> for Opt {
fn from(e: &Exec) -> Self { fn from(e: &Exec) -> Self {
Self { Self {
step: e.args.first().and_then(|s| s.parse().ok()).unwrap_or(0), step: e.args.first().and_then(|s| s.parse().ok()).unwrap_or(0),
sequent: e.named.get("sequent").map(Url::from), only_if: e.named.get("only-if").map(Url::from),
upper_bound: e.named.get("upper-bound").and_then(|s| s.parse().ok()), upper_bound: e.named.contains_key("upper-bound"),
} }
} }
} }
impl From<isize> for Opt { impl From<isize> 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 { impl Manager {
#[inline] #[inline]
pub fn _peek_upper_bound(bound: usize, sequent: &Url) { pub fn _peek_upper_bound(bound: usize, only_if: &Url) {
emit!(Call( emit!(Call(
Exec::call("peek", vec![]) Exec::call("peek", vec![bound.to_string()])
.with("sequent", sequent.to_string()) .with("only-if", only_if.to_string())
.with("upper-bound", bound.to_string()) .with_bool("upper-bound", true)
.vec(), .vec(),
KeymapLayer::Manager KeymapLayer::Manager
)); ));
@ -36,11 +37,11 @@ impl Manager {
pub fn peek(&mut self, opt: impl Into<Opt>) -> bool { pub fn peek(&mut self, opt: impl Into<Opt>) -> bool {
let Some(hovered) = self.hovered() else { 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; 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; return false;
} }
@ -49,35 +50,37 @@ impl Manager {
} }
let Some(mime) = self.mimetype.get(&hovered.url).cloned() else { 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(); let (url, cha) = (hovered.url.clone(), hovered.cha);
self.active_mut().preview.arrow(opt.step, &mime); if self.active().preview.same_url(&url) {
if let Some(bound) = opt.upper_bound { self.active_mut().preview.arrow(opt.step, &mime);
self.active_mut().preview.apply_bound(bound); } 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 false
} }
fn peek_folder(&mut self, opt: Opt, url: Url) -> bool { fn peek_folder(&mut self, opt: Opt, url: Url) -> bool {
let (skip, bound) = self let folder = self.active().history.get(&url);
.active() let (skip, bound) = folder
.history
.get(&url)
.map(|f| (f.offset, f.files.len().saturating_sub(MANAGER.layout.folder_height()))) .map(|f| (f.offset, f.files.len().saturating_sub(MANAGER.layout.folder_height())))
.unwrap_or_default(); .unwrap_or_default();
if opt.sequent.is_some() { if self.active().preview.same_url(&url) {
self.active_mut().preview.arrow(opt.step, MIME_DIR); self.active_mut().preview.arrow(opt.step, MIME_DIR);
} else { self.active_mut().preview.apply_bound(bound);
self.active_mut().preview.set_skip(skip); return false;
} }
self.active_mut().preview.apply_bound(bound); let in_chunks = folder.is_none();
self.active_mut().preview.go_folder(url, opt.sequent.is_none()); self.active_mut().preview.set_skip(skip);
self.active_mut().preview.go_folder(url, in_chunks);
false false
} }
} }

View file

@ -31,7 +31,7 @@ impl Tabs {
#[inline] #[inline]
pub(super) fn set_idx(&mut self, idx: usize) { pub(super) fn set_idx(&mut self, idx: usize) {
self.idx = idx; self.idx = idx;
self.active_mut().preview.reset(|l| l.is_image()); self.active_mut().preview.reset_image();
Manager::_refresh(); Manager::_refresh();
} }
} }

View file

@ -3,3 +3,5 @@ mod provider;
pub use preview::*; pub use preview::*;
use provider::*; use provider::*;
pub static COLLISION: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);

View file

@ -4,7 +4,7 @@ use tokio::{pin, task::JoinHandle};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_adaptor::ADAPTOR; use yazi_adaptor::ADAPTOR;
use yazi_config::MANAGER; use yazi_config::MANAGER;
use yazi_shared::{MimeKind, PeekError, Url}; use yazi_shared::{Cha, MimeKind, PeekError, Url};
use super::Provider; use super::Provider;
use crate::{emit, files::{Files, FilesOp}, manager::Manager, Highlighter}; use crate::{emit, files::{Files, FilesOp}, manager::Manager, Highlighter};
@ -19,6 +19,7 @@ pub struct Preview {
pub struct PreviewLock { pub struct PreviewLock {
pub url: Url, pub url: Url,
pub cha: Option<Cha>,
pub skip: usize, pub skip: usize,
pub data: PreviewData, pub data: PreviewData,
} }
@ -31,14 +32,18 @@ pub enum PreviewData {
} }
impl Preview { impl Preview {
pub fn go(&mut self, url: &Url, mime: &str) { pub fn go(&mut self, url: &Url, cha: Cha, mime: &str) {
self.reset(|_| true); if self.content_unchanged(url, &cha) {
let (url, skip, kind) = (url.clone(), self.skip, MimeKind::new(mime)); return;
}
self.reset();
let (url, kind, skip) = (url.clone(), MimeKind::new(mime), self.skip);
self.handle = Some(tokio::spawn(async move { self.handle = Some(tokio::spawn(async move {
match Provider::auto(kind, &url, skip).await { match Provider::auto(kind, &url, skip).await {
Ok(data) => { Ok(data) => {
emit!(Preview(PreviewLock { url, skip, data })); emit!(Preview(PreviewLock { url, cha: Some(cha), skip, data }));
} }
Err(PeekError::Exceed(max)) => { Err(PeekError::Exceed(max)) => {
Manager::_peek_upper_bound(max, &url); Manager::_peek_upper_bound(max, &url);
@ -49,8 +54,13 @@ impl Preview {
} }
pub fn go_folder(&mut self, url: Url, in_chunks: bool) { pub fn go_folder(&mut self, url: Url, in_chunks: bool) {
self.reset(|_| true); self.reset();
emit!(Preview(PreviewLock { url: url.clone(), skip: self.skip, data: PreviewData::Folder })); self.lock = Some(PreviewLock {
url: url.clone(),
cha: None,
skip: self.skip,
data: PreviewData::Folder,
});
self.handle = Some(tokio::spawn(async move { self.handle = Some(tokio::spawn(async move {
let Ok(rx) = Files::from_dir(&url).await else { let Ok(rx) = Files::from_dir(&url).await else {
@ -74,7 +84,7 @@ impl Preview {
})); }));
} }
pub fn reset<F: FnOnce(&PreviewLock) -> bool>(&mut self, f: F) -> bool { pub fn reset(&mut self) -> bool {
self.handle.take().map(|h| h.abort()); self.handle.take().map(|h| h.abort());
Highlighter::abort(); Highlighter::abort();
ADAPTOR.image_hide(MANAGER.layout.image_rect()).ok(); ADAPTOR.image_hide(MANAGER.layout.image_rect()).ok();
@ -83,14 +93,49 @@ impl Preview {
return false; return false;
}; };
if !f(lock) {
return false;
}
let b = !lock.is_image(); let b = !lock.is_image();
self.lock = None; self.lock = None;
b 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 { impl Preview {
@ -106,12 +151,12 @@ impl Preview {
pub fn set_skip(&mut self, skip: usize) -> bool { mem::replace(&mut self.skip, skip) != skip } pub fn set_skip(&mut self, skip: usize) -> bool { mem::replace(&mut self.skip, skip) != skip }
#[inline] #[inline]
pub fn apply_bound(&mut self, max: usize) -> bool { pub fn apply_bound(&mut self, upper: usize) -> bool {
if self.skip <= max { if self.skip <= upper {
return false; return false;
} }
self.skip = max; self.skip = upper;
true true
} }
} }

View file

@ -76,7 +76,7 @@ impl Tab {
handle.abort(); handle.abort();
} }
if self.current.cwd.is_search() { 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()); let rep = self.history_new(&self.current.cwd.to_regular());
drop(mem::replace(&mut self.current, rep)); drop(mem::replace(&mut self.current, rep));

View file

@ -49,7 +49,7 @@ impl From<&Url> for Tab {
impl Tab { impl Tab {
pub fn update_preview(&mut self, lock: PreviewLock) -> bool { pub fn update_preview(&mut self, lock: PreviewLock) -> bool {
let Some(hovered) = self.current.hovered().map(|h| &h.url) else { let Some(hovered) = self.current.hovered().map(|h| &h.url) else {
return self.preview.reset(|_| true); return self.preview.reset();
}; };
if lock.url != *hovered { if lock.url != *hovered {

View file

@ -1,11 +1,11 @@
use std::ffi::OsString; use std::{ffi::OsString, sync::atomic::Ordering};
use anyhow::{Ok, Result}; use anyhow::{Ok, Result};
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
use ratatui::{backend::Backend, prelude::Rect}; use ratatui::{backend::Backend, prelude::Rect};
use tokio::sync::oneshot; use tokio::sync::oneshot;
use yazi_config::{keymap::{Exec, Key, KeymapLayer}, BOOT}; 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 yazi_shared::Term;
use crate::{Executor, Logs, Panic, Root, Signals}; use crate::{Executor, Logs, Panic, Root, Signals};
@ -81,14 +81,17 @@ impl App {
return Ok(()); return Ok(());
}; };
COLLISION.store(false, Ordering::Relaxed);
let frame = term.draw(|f| { let frame = term.draw(|f| {
yazi_plugin::scope(&self.cx, |_| { yazi_plugin::scope(&self.cx, |_| {
f.render_widget(Root::new(&self.cx), f.size()); f.render_widget(Root::new(&self.cx), f.size());
}); });
})?; })?;
if !COLLISION.load(Ordering::Relaxed) {
return Ok(());
}
let mut patches = Vec::new(); let mut patches = Vec::new();
// TODO: find a more efficient way to do this
for x in frame.area.left()..frame.area.right() { for x in frame.area.left()..frame.area.right() {
for y in frame.area.top()..frame.area.bottom() { for y in frame.area.top()..frame.area.bottom() {
let cell = frame.buffer.get(x, y); let cell = frame.buffer.get(x, y);
@ -114,14 +117,13 @@ impl App {
} }
self.cx.manager.current_mut().set_page(true); self.cx.manager.current_mut().set_page(true);
self.cx.manager.active_mut().preview.reset(|_| true); self.cx.manager.active_mut().preview.reset();
// TODO: Peek-trigger self.cx.manager.peek(0);
// self.cx.manager.peek(true);
emit!(Render); emit!(Render);
} }
fn dispatch_stop(&mut self, state: bool, tx: Option<oneshot::Sender<()>>) { fn dispatch_stop(&mut self, state: bool, tx: Option<oneshot::Sender<()>>) {
self.cx.manager.active_mut().preview.reset(|l| l.is_image()); self.cx.manager.active_mut().preview.reset_image();
if state { if state {
self.signals.stop_term(true); self.signals.stop_term(true);
self.term = None; self.term = None;

View file

@ -1,6 +1,9 @@
use std::sync::atomic::Ordering;
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
use yazi_adaptor::ADAPTOR; use yazi_adaptor::ADAPTOR;
use yazi_config::MANAGER; use yazi_config::MANAGER;
use yazi_core::preview::COLLISION;
pub(crate) struct Clear; pub(crate) struct Clear;
@ -30,6 +33,7 @@ impl Widget for Clear {
}; };
ADAPTOR.image_hide(r).ok(); ADAPTOR.image_hide(r).ok();
COLLISION.store(true, Ordering::Relaxed);
for x in r.left()..r.right() { for x in r.left()..r.right() {
for y in r.top()..r.bottom() { for y in r.top()..r.bottom() {
buf.get_mut(x, y).set_skip(true); buf.get_mut(x, y).set_skip(true);

View file

@ -3,7 +3,7 @@ use std::{fs::Metadata, time::SystemTime};
use bitflags::bitflags; use bitflags::bitflags;
bitflags! { bitflags! {
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChaMeta: u8 { pub struct ChaMeta: u8 {
const DIR = 0b00000001; const DIR = 0b00000001;