yazi/core/src/manager/preview/preview.rs
2023-10-12 00:09:10 +08:00

207 lines
4.6 KiB
Rust

use std::{sync::atomic::Ordering, time::Duration};
use adaptor::ADAPTOR;
use config::MANAGER;
use shared::{MimeKind, PeekError, Url, MIME_DIR};
use tokio::{pin, task::JoinHandle};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use super::{Provider, INCR};
use crate::{emit, files::{Files, FilesOp}};
#[derive(Default)]
pub struct Preview {
pub lock: Option<PreviewLock>,
skip: usize,
handle: Option<JoinHandle<()>>,
}
pub struct PreviewLock {
pub url: Url,
pub mime: String,
pub skip: usize,
pub data: PreviewData,
}
#[derive(Debug)]
pub enum PreviewData {
Folder,
Text(String),
Image,
}
impl Preview {
pub fn go(&mut self, url: &Url, mime: &str, show_image: bool) {
let kind = MimeKind::new(mime);
if !show_image && kind.show_as_image() {
return;
} else if self.same(url, mime) {
return;
}
self.reset(|_| true);
if !self.same_mime(url, mime) {
self.skip = 0;
}
let (url, mime, skip) = (url.clone(), mime.to_owned(), self.skip);
self.handle = Some(tokio::spawn(async move {
match Provider::auto(kind, &url, skip).await {
Ok(data) => {
emit!(Preview(PreviewLock { url, mime, skip, data }));
}
Err(PeekError::Exceed(max)) => {
emit!(Peek(max, url));
}
_ => {}
}
}));
}
pub fn folder(&mut self, url: &Url, position: Option<(usize, usize)>, sequent: bool) {
if let Some((_, len)) = position {
self.skip = self.skip.min(len.saturating_sub(MANAGER.layout.preview_height()));
}
if self.same(url, MIME_DIR) {
return;
} else if !self.same_mime(url, MIME_DIR) {
self.skip = position.map(|(offset, _)| offset).unwrap_or(0);
}
self.reset(|_| true);
emit!(Preview(PreviewLock {
url: url.clone(),
mime: MIME_DIR.to_owned(),
skip: self.skip,
data: PreviewData::Folder,
}));
if sequent {
return;
}
let url = url.clone();
self.handle = Some(tokio::spawn(async move {
let Ok(rx) = Files::from_dir(&url).await else {
emit!(Files(FilesOp::IOErr(url)));
return;
};
if position.is_some() {
emit!(Files(FilesOp::Full(url, UnboundedReceiverStream::new(rx).collect().await)));
return;
}
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(10000, Duration::from_millis(500));
pin!(rx);
let ticket = FilesOp::prepare(&url);
while let Some(chunk) = rx.next().await {
emit!(Files(FilesOp::Part(url.clone(), ticket, chunk)));
}
}));
}
pub fn sequent(&mut self, url: &Url, mime: &str, show_image: bool) {
let kind = MimeKind::new(mime);
if !show_image && kind.show_as_image() {
return;
} else if self.same(url, mime) {
return;
}
self.handle.take().map(|h| h.abort());
INCR.fetch_add(1, Ordering::Relaxed);
let (url, mime, skip) = (url.clone(), mime.to_owned(), self.skip);
self.handle = Some(tokio::spawn(async move {
match Provider::auto(kind, &url, skip).await {
Ok(data) => {
emit!(Preview(PreviewLock { url, mime, skip, data }));
}
Err(PeekError::Exceed(max)) => {
emit!(Peek(max, url));
}
_ => {}
}
}));
}
pub fn arrow(&mut self, step: isize) -> bool {
let Some(kind) = self.lock.as_ref().map(|l| MimeKind::new(&l.mime)) else {
return false;
};
let old = self.skip;
let size = Provider::step_size(kind, step.unsigned_abs());
self.skip = if step < 0 { old.saturating_sub(size) } else { old + size };
self.skip != old
}
pub fn arrow_max(&mut self, max: usize) -> bool {
if self.skip > max {
self.skip = max;
return true;
}
false
}
pub fn reset<F: FnOnce(&PreviewLock) -> bool>(&mut self, f: F) -> bool {
self.handle.take().map(|h| h.abort());
INCR.fetch_add(1, Ordering::Relaxed);
ADAPTOR.image_hide(MANAGER.layout.preview_rect()).ok();
let Some(ref lock) = self.lock else {
return false;
};
if !f(lock) {
return false;
}
let b = !lock.is_image();
self.lock = None;
b
}
}
impl Preview {
#[inline]
pub fn skip(&self) -> usize { self.skip }
#[inline]
pub fn same(&self, url: &Url, mime: &str) -> bool {
if let Some(ref lock) = self.lock {
return &lock.url == url && lock.mime == mime && lock.skip == self.skip;
}
false
}
#[inline]
pub fn same_mime(&self, url: &Url, mime: &str) -> bool {
if let Some(ref lock) = self.lock {
return &lock.url == url && lock.mime == mime;
}
false
}
#[inline]
pub fn same_path(&self, url: &Url) -> bool {
if let Some(ref lock) = self.lock {
return &lock.url == url;
}
false
}
}
impl PreviewLock {
#[inline]
pub fn is_image(&self) -> bool { matches!(self.data, PreviewData::Image) }
#[inline]
pub fn is_folder(&self) -> bool { matches!(self.data, PreviewData::Folder) }
}