From 0fa8df100d9906318c1d38d59c9b48e1c334e889 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sat, 26 Aug 2023 16:58:09 +0800 Subject: [PATCH] .. --- Cargo.lock | 12 ++-- app/src/app.rs | 26 +++---- app/src/executor.rs | 3 +- app/src/manager/preview.rs | 3 +- core/src/event.rs | 18 +++-- core/src/manager/manager.rs | 59 ++++++--------- core/src/manager/preview/preview.rs | 103 ++++++++++++++++++--------- core/src/manager/preview/provider.rs | 2 +- core/src/manager/tab.rs | 69 ++++++++++-------- core/src/tasks/tasks.rs | 2 +- core/src/which/which.rs | 2 +- shared/src/mime.rs | 34 +++++---- 12 files changed, 184 insertions(+), 149 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83bef86f..a8a371af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1294,9 +1294,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1527,18 +1527,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.186" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.186" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", diff --git a/app/src/app.rs b/app/src/app.rs index 6610bed7..4b653c12 100644 --- a/app/src/app.rs +++ b/app/src/app.rs @@ -87,9 +87,8 @@ impl App { fn dispatch_resize(&mut self) { self.cx.manager.current_mut().set_page(true); - self.cx.manager.active_mut().preview_reset_image(); - // TODO: use peek - self.cx.manager.preview(self.cx.image_layer()); + self.cx.manager.active_mut().preview_reset(); + self.cx.manager.peek(true, self.cx.image_layer()); emit!(Render); } @@ -101,8 +100,6 @@ impl App { } else { self.term = Some(Term::start().unwrap()); self.signals.stop_term(false); - // TODO: use peek - self.cx.manager.preview(self.cx.image_layer()); emit!(Render); emit!(Hover); } @@ -154,21 +151,24 @@ impl App { Event::Mimetype(mimes) => { if manager.update_mimetype(mimes, tasks) { emit!(Render); - self.cx.manager.preview(self.cx.image_layer()); + emit!(Peek); } } Event::Hover(file) => { - if file.map(|f| manager.current_mut().hover_force(f)).unwrap_or(false) { + if manager.update_hover(file) { emit!(Render); } - self.cx.manager.preview(self.cx.image_layer()); + emit!(Peek); } - Event::Preview(path, mime, data) => { - manager.update_preview(path, mime, data); - emit!(Render); + Event::Peek(skip, sequent) => { + let b = sequent.is_some(); + manager.active_mut().update_peek(skip as isize, sequent); + self.cx.manager.peek(b, self.cx.image_layer()); } - Event::Peek(path, skip) => { - manager.update_peek(path, skip); + Event::Preview(lock) => { + if manager.active_mut().update_preview(lock) { + emit!(Render); + } } Event::Select(opt, tx) => { diff --git a/app/src/executor.rs b/app/src/executor.rs index 7074c5af..68702479 100644 --- a/app/src/executor.rs +++ b/app/src/executor.rs @@ -63,7 +63,8 @@ impl Executor { } "peek" => { let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); - cx.manager.active_mut().peek(step) + cx.manager.active_mut().update_peek(step, None); + cx.manager.peek(true, cx.image_layer()) } "leave" => cx.manager.active_mut().leave(), "enter" => cx.manager.active_mut().enter(), diff --git a/app/src/manager/preview.rs b/app/src/manager/preview.rs index e7dbbb73..3986cae9 100644 --- a/app/src/manager/preview.rs +++ b/app/src/manager/preview.rs @@ -26,8 +26,7 @@ impl<'a> Widget for Preview<'a> { return; } - match &preview.data { - PreviewData::None => {} + match &preview.lock.as_ref().unwrap().data { PreviewData::Folder => { if let Some(folder) = manager.active().history(hovered) { Folder::new(self.cx, folder).with_preview(true).render(area, buf); diff --git a/core/src/event.rs b/core/src/event.rs index 0bcfe7a8..fd53cd0f 100644 --- a/core/src/event.rs +++ b/core/src/event.rs @@ -6,7 +6,8 @@ use crossterm::event::KeyEvent; use shared::RoCell; use tokio::sync::{mpsc::UnboundedSender, oneshot}; -use super::{files::{File, FilesOp}, input::InputOpt, manager::PreviewData, select::SelectOpt}; +use super::{files::{File, FilesOp}, input::InputOpt, select::SelectOpt}; +use crate::manager::PreviewLock; static TX: RoCell> = RoCell::new(); @@ -26,8 +27,8 @@ pub enum Event { Pages(usize), Mimetype(BTreeMap), Hover(Option), - Preview(PathBuf, String, PreviewData), - Peek(PathBuf, usize), + Peek(usize, Option), + Preview(PreviewLock), // Input Select(SelectOpt, oneshot::Sender>), @@ -88,11 +89,14 @@ macro_rules! emit { (Hover($file:expr)) => { $crate::Event::Hover(Some($file)).emit(); }; - (Preview($path:expr, $mime:expr, $data:expr)) => { - $crate::Event::Preview($path, $mime, $data).emit(); + (Peek) => { + $crate::Event::Peek(0, None).emit(); }; - (Peek($path:expr, $skip:expr)) => { - $crate::Event::Peek($path, $skip).emit(); + (Peek($skip:expr, $path:expr)) => { + $crate::Event::Peek($skip, Some($path)).emit(); + }; + (Preview($lock:expr)) => { + $crate::Event::Preview($lock).emit(); }; (Select($opt:expr)) => {{ diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index 7a448fd6..678fe5ca 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -5,7 +5,7 @@ use config::{BOOT, OPEN}; use shared::{max_common_root, Defer, Term, MIME_DIR}; use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; -use super::{PreviewData, Tab, Tabs, Watcher}; +use super::{Tab, Tabs, Watcher}; use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::InputOpt, manager::Folder, select::SelectOpt, tasks::Tasks, Event, BLOCKER}; pub struct Manager { @@ -52,7 +52,7 @@ impl Manager { self.watcher.watch(to_watch); } - pub fn preview(&mut self, show_image: bool) -> bool { + pub fn peek(&mut self, sequent: bool, show_image: bool) -> bool { let Some(hovered) = self.hovered().cloned() else { return self.active_mut().preview.reset(); }; @@ -61,19 +61,23 @@ impl Manager { self.active_mut().preview_reset_image(); } - if hovered.meta.is_dir() { - self.active_mut().preview.go(&hovered.path, MIME_DIR, show_image); - if let Some(offset) = self.active().history(&hovered.path).map(|f| f.offset()) { - emit!(Peek(hovered.path, offset)); - } - } else if let Some(mime) = self.mimetype.get(&hovered.path).cloned() { - self.active_mut().preview.go(&hovered.path, &mime, show_image); + let mime = if hovered.meta.is_dir() { + MIME_DIR.to_owned() + } else if let Some(m) = self.mimetype.get(&hovered.path).cloned() { + m } else { tokio::spawn(async move { if let Ok(mimes) = external::file(&[hovered.path]).await { emit!(Mimetype(mimes)); } }); + return false; + }; + + if sequent { + self.active_mut().preview.sequent(&hovered.path, &mime, show_image); + } else { + self.active_mut().preview.go(&hovered.path, &mime, show_image); } false } @@ -393,39 +397,16 @@ impl Manager { true } - pub fn update_preview(&mut self, path: PathBuf, mime: String, data: PreviewData) -> bool { - let Some(hovered) = self.hovered().map(|h| &h.path) else { - return self.active_mut().preview.reset(); + pub fn update_hover(&mut self, file: Option) -> bool { + let b = file.map(|f| self.current_mut().hover_force(f)).unwrap_or(false); + let Some(hovered) = self.hovered() else { + return b; }; - if path != *hovered { - return false; + if hovered.meta.is_dir() { + self.watcher.trigger_dirs(&[&hovered.path]); } - - let preview = &mut self.active_mut().preview; - preview.lock = Some((path, mime)); - preview.data = data; - true - } - - pub fn update_peek(&mut self, path: PathBuf, skip: usize) -> bool { - if !matches!(self.hovered(), Some(f) if f.path == path) { - return false; - } - - if self.active().preview.lock.is_some() { - self.active_mut().preview.peek(skip); - return false; - } - - if self.active().history(&path).is_some() { - let preview = &mut self.active_mut().preview; - preview.lock = Some((path, MIME_DIR.to_string())); - preview.data = PreviewData::Folder; - } - - self.active_mut().preview.peek(skip); - false + b } } diff --git a/core/src/manager/preview/preview.rs b/core/src/manager/preview/preview.rs index 0a6cc51c..21227a2d 100644 --- a/core/src/manager/preview/preview.rs +++ b/core/src/manager/preview/preview.rs @@ -1,26 +1,30 @@ -use std::{mem, path::{Path, PathBuf}, sync::atomic::Ordering}; +use std::{path::{Path, PathBuf}, sync::atomic::Ordering}; use adaptor::Adaptor; use config::MANAGER; use shared::{MimeKind, PeekError}; use tokio::task::JoinHandle; -use super::{provider::INCR, Provider}; +use super::{Provider, INCR}; use crate::emit; #[derive(Default)] pub struct Preview { - pub lock: Option<(PathBuf, String)>, - pub data: PreviewData, + pub lock: Option, skip: usize, handle: Option>, } -#[derive(Debug, Default, PartialEq, Eq)] +pub struct PreviewLock { + pub path: PathBuf, + pub mime: String, + pub skip: usize, + pub data: PreviewData, +} + +#[derive(Debug)] pub enum PreviewData { - #[default] - None, Folder, Text(String), Image, @@ -29,50 +33,67 @@ pub enum PreviewData { impl Preview { pub fn go(&mut self, path: &Path, mime: &str, show_image: bool) { let kind = MimeKind::new(mime); - if !show_image && matches!(kind, MimeKind::Image | MimeKind::Video) { + if !show_image && kind.show_as_image() { return; } else if self.same(path, mime) { return; - } else { - self.reset(); + } + + self.reset(); + if !self.same_path(path) { + self.skip = 0; } let (path, mime, skip) = (path.to_path_buf(), mime.to_owned(), self.skip); self.handle = Some(tokio::spawn(async move { - let result = Provider::auto(kind, &path, skip).await; - emit!(Preview(path, mime, result.unwrap_or_default())); + match Provider::auto(kind, &path, skip).await { + Ok(data) => { + emit!(Preview(PreviewLock { path, mime, skip, data })); + } + Err(PeekError::Exceed(max)) => { + emit!(Peek(max, path)); + } + _ => {} + } })); } - pub fn peek(&mut self, skip: usize) { - let Some((path, kind, mime)) = self.lock.clone().map(|(p, m)| (p, MimeKind::new(&m), m)) else { + pub fn sequent(&mut self, path: &Path, mime: &str, show_image: bool) { + let kind = MimeKind::new(mime); + if !show_image && kind.show_as_image() { return; - }; + } else if self.same(path, mime) { + return; + } self.handle.take().map(|h| h.abort()); INCR.fetch_add(1, Ordering::Relaxed); - self.skip = skip; + let (path, mime, skip) = (path.to_path_buf(), mime.to_owned(), self.skip); self.handle = Some(tokio::spawn(async move { match Provider::auto(kind, &path, skip).await { - Ok(result) => { - emit!(Preview(path, mime, result)); + Ok(data) => { + emit!(Preview(PreviewLock { path, mime, skip, data })); } Err(PeekError::Exceed(max)) => { - emit!(Peek(path, max)); + emit!(Peek(max, path)); } _ => {} - }; + } })); } #[inline] - pub fn peek_step(&mut self, step: isize) -> usize { - let Some(kind) = self.lock.clone().map(|(_, m)| MimeKind::new(&m)) else { - return 0; - }; + pub fn arrow(&mut self, step: isize, absolute: bool) -> bool { + let old = self.skip; + if absolute { + self.skip = step.unsigned_abs(); + } else if let Some(kind) = self.lock.as_ref().map(|l| MimeKind::new(&l.mime)) { + let size = Provider::step_size(kind, step.unsigned_abs()); + self.skip = if step < 0 { old.saturating_sub(size) } else { old + size }; + } - Provider::step_size(kind, step.unsigned_abs()) + self.skip != old } pub fn reset(&mut self) -> bool { @@ -80,12 +101,9 @@ impl Preview { INCR.fetch_add(1, Ordering::Relaxed); Adaptor::image_hide(MANAGER.layout.preview_rect()).ok(); + let b = matches!(&self.lock, Some(l) if !l.is_image()); self.lock = None; - self.skip = 0; - !matches!( - mem::replace(&mut self.data, PreviewData::None), - PreviewData::None | PreviewData::Image - ) + b } pub fn reset_image(&mut self) -> bool { @@ -93,26 +111,41 @@ impl Preview { INCR.fetch_add(1, Ordering::Relaxed); Adaptor::image_hide(MANAGER.layout.preview_rect()).ok(); - if self.data == PreviewData::Image { + if matches!(&self.lock, Some(l) if l.is_image()) { self.lock = None; - self.data = PreviewData::None; - self.skip = 0; } false } } impl Preview { + #[inline] + pub fn lock(&self) -> &Option { &self.lock } + #[inline] pub fn skip(&self) -> usize { self.skip } #[inline] pub fn same(&self, path: &Path, mime: &str) -> bool { - self.lock.as_ref().map(|(p, m)| p == path && m == mime).unwrap_or(false) + if let Some(ref lock) = self.lock { + return lock.path == path && lock.mime == mime && lock.skip == self.skip; + } + false } #[inline] pub fn same_path(&self, path: &Path) -> bool { - self.lock.as_ref().map(|(p, _)| p == path).unwrap_or(false) + if let Some(ref lock) = self.lock { + return lock.path == path; + } + 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) } +} diff --git a/core/src/manager/preview/provider.rs b/core/src/manager/preview/provider.rs index e7d15f96..82e3e535 100644 --- a/core/src/manager/preview/provider.rs +++ b/core/src/manager/preview/provider.rs @@ -21,7 +21,7 @@ impl Provider { skip: usize, ) -> Result { match kind { - MimeKind::Empty => Ok(PreviewData::None), + MimeKind::Empty => Err("Empty file".into()), MimeKind::Archive => Provider::archive(path, skip).await.map(PreviewData::Text), MimeKind::Dir => Provider::folder(path).await, MimeKind::Image => Provider::image(path).await, diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index 3a3b13ed..42c87d1b 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -3,10 +3,10 @@ use std::{collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}, mem, path:: use anyhow::{Error, Result}; use config::{open::Opener, MANAGER}; use futures::StreamExt; -use shared::{Defer, MIME_DIR}; +use shared::Defer; use tokio::task::JoinHandle; -use super::{Folder, Mode, Preview}; +use super::{Folder, Mode, Preview, PreviewLock}; use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::InputOpt, Event, BLOCKER}; pub struct Tab { @@ -76,32 +76,6 @@ impl Tab { true } - pub fn peek(&mut self, step: isize) -> bool { - let size = self.preview.peek_step(step); - let old = self.preview.skip(); - let new = if step < 0 { old.saturating_sub(size) } else { old + size }; - if new == old { - return false; - } - - if !matches!(&self.preview.lock, Some((_, m)) if m == MIME_DIR) { - self.preview.peek(new); - return false; - }; - - let path = self.preview.lock.as_ref().unwrap().0.clone(); - if let Some(folder) = self.history(&path) { - let max = folder.files.len().saturating_sub(MANAGER.layout.preview_height()); - if new > max { - emit!(Peek(path, max)); - return false; - } - } - - self.preview.peek(new); - false - } - pub async fn cd(&mut self, mut target: PathBuf) -> bool { let Ok(file) = File::from(&target).await else { return false; @@ -335,6 +309,42 @@ impl Tab { false } + + pub fn update_peek(&mut self, step: isize, path: Option) { + let Some(ref hovered) = self.current.hovered else { + return; + }; + + if path.as_ref().map(|p| *p != hovered.path).unwrap_or(false) { + return; + } else if !self.preview.lock.as_ref().map(|l| l.path == hovered.path).unwrap_or(false) { + return; + } else if !self.preview.arrow(step, path.is_some()) { + return; + } else if !hovered.meta.is_dir() { + return; + } + + if let Some(folder) = self.history(&hovered.path) { + let max = folder.files.len().saturating_sub(MANAGER.layout.preview_height()); + if self.preview.skip() > max { + self.preview.arrow(max as isize, true); + } + } + } + + pub fn update_preview(&mut self, lock: PreviewLock) -> bool { + let Some(hovered) = self.current.hovered.as_ref().map(|h| &h.path) else { + return self.preview.reset(); + }; + + if lock.path != *hovered { + return false; + } + + self.preview.lock = Some(lock); + true + } } impl Tab { @@ -388,6 +398,9 @@ impl Tab { #[inline] pub fn preview(&self) -> &Preview { &self.preview } + #[inline] + pub fn preview_reset(&mut self) -> bool { self.preview.reset() } + #[inline] pub fn preview_reset_image(&mut self) -> bool { self.preview.reset_image() } } diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index cb8267f6..2ef1ca95 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -34,7 +34,7 @@ impl Tasks { pub fn toggle(&mut self) -> bool { self.visible = !self.visible; - emit!(Hover); // Show/hide preview for images + emit!(Peek); // Show/hide preview for images true } diff --git a/core/src/which/which.rs b/core/src/which/which.rs index 758bb101..a18f7e6d 100644 --- a/core/src/which/which.rs +++ b/core/src/which/which.rs @@ -51,6 +51,6 @@ impl Which { #[inline] fn switch(&mut self, state: bool) { self.visible = state; - emit!(Hover); // Show/hide preview for images + emit!(Peek); // Show/hide preview for images } } diff --git a/shared/src/mime.rs b/shared/src/mime.rs index 04831d6e..cb42d532 100644 --- a/shared/src/mime.rs +++ b/shared/src/mime.rs @@ -18,21 +18,6 @@ pub enum MimeKind { } impl MimeKind { - pub fn valid(s: &str) -> bool { - if s == "inode/x-empty" { - return true; - } - - let parts = s.split('/').collect::>(); - if parts.len() != 2 { - return false; - } - - #[rustfmt::skip] - let b = matches!(parts[0], "application" | "audio" | "example" | "font" | "image" | "message" | "model" | "multipart" | "text" | "video"); - b && !parts[1].is_empty() - } - pub fn new(s: &str) -> Self { if s == MIME_DIR { Self::Dir @@ -61,4 +46,23 @@ impl MimeKind { Self::Others } } + + pub fn valid(s: &str) -> bool { + if s == "inode/x-empty" { + return true; + } + + let parts = s.split('/').collect::>(); + if parts.len() != 2 { + return false; + } + + #[rustfmt::skip] + let b = matches!(parts[0], "application" | "audio" | "example" | "font" | "image" | "message" | "model" | "multipart" | "text" | "video"); + b && !parts[1].is_empty() + } + + pub fn show_as_image(&self) -> bool { + matches!(self, MimeKind::Image | MimeKind::Video | MimeKind::PDF) + } }