mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 00:31:04 +00:00
140 lines
3.2 KiB
Rust
140 lines
3.2 KiB
Rust
use std::{mem, time::Duration};
|
|
|
|
use tokio::{pin, task::JoinHandle};
|
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
|
use yazi_adaptor::ADAPTOR;
|
|
use yazi_config::MANAGER;
|
|
use yazi_shared::{emit, event::{PreviewData, PreviewLock}, fs::{Cha, FilesOp, Url}, MimeKind, PeekError};
|
|
|
|
use super::Provider;
|
|
use crate::{files::Files, manager::Manager, Highlighter};
|
|
|
|
#[derive(Default)]
|
|
pub struct Preview {
|
|
pub lock: Option<PreviewLock>,
|
|
skip: usize,
|
|
|
|
handle: Option<JoinHandle<()>>,
|
|
}
|
|
|
|
impl Preview {
|
|
pub fn go(&mut self, url: &Url, cha: Cha, mime: &str) {
|
|
if self.content_unchanged(url, &cha) {
|
|
return;
|
|
}
|
|
|
|
self.abort();
|
|
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, cha: Some(cha), skip, data }));
|
|
}
|
|
Err(PeekError::Exceed(max)) => {
|
|
Manager::_peek_upper_bound(max, &url);
|
|
}
|
|
_ => {}
|
|
}
|
|
}));
|
|
}
|
|
|
|
pub fn go_folder(&mut self, url: Url, in_chunks: bool) {
|
|
self.abort();
|
|
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 {
|
|
emit!(Files(FilesOp::IOErr(url.clone())));
|
|
return;
|
|
};
|
|
|
|
if !in_chunks {
|
|
emit!(Files(FilesOp::Full(url.clone(), UnboundedReceiverStream::new(rx).collect().await)));
|
|
return;
|
|
}
|
|
|
|
let stream =
|
|
UnboundedReceiverStream::new(rx).chunks_timeout(10000, Duration::from_millis(500));
|
|
pin!(stream);
|
|
|
|
let ticket = FilesOp::prepare(&url);
|
|
while let Some(chunk) = stream.next().await {
|
|
emit!(Files(FilesOp::Part(url.clone(), ticket, chunk)));
|
|
}
|
|
}));
|
|
}
|
|
|
|
pub fn arrow(&mut self, step: isize, mime: &str, upper: Option<usize>) {
|
|
let size = Provider::step_size(MimeKind::new(mime), step.unsigned_abs());
|
|
self.skip = if step < 0 { self.skip.saturating_sub(size) } else { self.skip + size };
|
|
|
|
if let Some(upper) = upper {
|
|
self.skip = self.skip.min(upper);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn abort(&mut self) {
|
|
self.handle.take().map(|h| h.abort());
|
|
Highlighter::abort();
|
|
ADAPTOR.image_hide(MANAGER.layout.image_rect()).ok();
|
|
}
|
|
|
|
#[inline]
|
|
pub fn reset(&mut self) -> bool {
|
|
self.abort();
|
|
self.lock.take().map(|l| l.is_image()) == Some(false)
|
|
}
|
|
|
|
pub fn reset_image(&mut self) -> bool {
|
|
if matches!(self.lock, Some(ref l) if l.is_image()) {
|
|
self.reset();
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn same_url(&self, url: &Url) -> bool {
|
|
matches!(self.lock, Some(ref lock) if lock.url == *url)
|
|
}
|
|
|
|
pub fn sync_skip(&mut self) -> bool {
|
|
if let Some(lock) = &mut self.lock {
|
|
mem::replace(&mut lock.skip, self.skip) != self.skip
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
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.kind == cha_.kind
|
|
&& {
|
|
#[cfg(unix)]
|
|
{
|
|
cha.permissions == cha_.permissions
|
|
}
|
|
#[cfg(windows)]
|
|
{
|
|
true
|
|
}
|
|
}
|
|
}
|
|
}
|