diff --git a/app/src/app.rs b/app/src/app.rs index baf8c08b..90f2c77c 100644 --- a/app/src/app.rs +++ b/app/src/app.rs @@ -128,17 +128,16 @@ impl App { manager.refresh(); } Event::Files(op) => { - let calc = matches!(op, FilesOp::Read(..) | FilesOp::Search(..)); + let read = matches!(op, FilesOp::Read(..)); let b = match op { FilesOp::Read(..) => manager.update_read(op), FilesOp::Size(..) => manager.update_read(op), - FilesOp::Search(..) => manager.update_search(op), FilesOp::IOErr(..) => manager.update_ioerr(op), }; if b { emit!(Render); } - if calc { + if read { tasks.precache_size(&manager.current().files); } } diff --git a/core/src/files/files.rs b/core/src/files/files.rs index 69d0ce1d..517df5eb 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -127,6 +127,7 @@ impl Files { true } + // TODO: remove this pub fn update_search(&mut self, items: Vec) -> bool { if !items.is_empty() { if self.show_hidden { diff --git a/core/src/files/op.rs b/core/src/files/op.rs index c9208162..8c45339e 100644 --- a/core/src/files/op.rs +++ b/core/src/files/op.rs @@ -6,7 +6,6 @@ use super::File; pub enum FilesOp { Read(PathBuf, Vec), Size(PathBuf, BTreeMap), - Search(PathBuf, Vec), IOErr(PathBuf), } @@ -16,15 +15,11 @@ impl FilesOp { match self { Self::Read(path, _) => path, Self::Size(path, _) => path, - Self::Search(path, _) => path, Self::IOErr(path) => path, } .clone() } #[inline] - pub fn read_empty(path: &Path) -> Self { Self::Read(path.to_path_buf(), Vec::new()) } - - #[inline] - pub fn search_empty(path: &Path) -> Self { Self::Search(path.to_path_buf(), Vec::new()) } + pub fn clear(path: &Path) -> Self { Self::Read(path.to_path_buf(), Vec::new()) } } diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index d94d6b38..d8b627e1 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -9,26 +9,22 @@ use crate::{emit, files::{File, Files, FilesOp}}; pub struct Folder { pub cwd: PathBuf, pub files: Files, - offset: usize, - cursor: usize, - pub page: usize, - pub hovered: Option, - pub in_search: bool, + offset: usize, + cursor: usize, + + pub page: usize, + pub hovered: Option, } impl Folder { + #[inline] pub fn new(cwd: &Path) -> Self { Self { cwd: cwd.to_path_buf(), ..Default::default() } } - pub fn new_search(cwd: &Path) -> Self { - Self { cwd: cwd.to_path_buf(), in_search: true, ..Default::default() } - } - pub fn update(&mut self, op: FilesOp) -> bool { let b = match op { FilesOp::Read(_, items) => self.files.update_read(items), FilesOp::Size(_, items) => self.files.update_size(items), - FilesOp::Search(_, items) => self.files.update_search(items), _ => unreachable!(), }; if !b { diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index 35808d6a..6b9518e9 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -56,18 +56,21 @@ impl Manager { pub fn peek(&mut self, sequent: bool, show_image: bool) -> bool { let Some(hovered) = self.hovered().cloned() else { - return self.active_mut().preview.reset(); + return self.active_mut().preview_reset(); }; + let path = hovered.path(); if !show_image { self.active_mut().preview_reset_image(); } - let mime = if hovered.is_dir() { - MIME_DIR.to_owned() - } else if let Some(m) = self.mimetype.get(hovered.path()).cloned() { - m - } else { + if hovered.is_dir() { + let len = self.active().history(path).map(|f| f.files.len()); + self.active_mut().preview.folder(path, len, sequent); + return false; + } + + let Some(mime) = self.mimetype.get(path).cloned() else { tokio::spawn(async move { if let Ok(mimes) = external::file(&[hovered.path()]).await { emit!(Mimetype(mimes)); @@ -77,9 +80,9 @@ impl Manager { }; if sequent { - self.active_mut().preview.sequent(hovered.path(), &mime, show_image); + self.active_mut().preview.sequent(path, &mime, show_image); } else { - self.active_mut().preview.go(hovered.path(), &mime, show_image); + self.active_mut().preview.go(path, &mime, show_image); } false } @@ -393,16 +396,9 @@ impl Manager { true } + #[inline] 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 hovered.is_dir() { - self.watcher.trigger_dirs(&[hovered.path()]); - } - b + file.map(|f| self.current_mut().hover_force(f)) == Some(true) } } diff --git a/core/src/manager/preview/preview.rs b/core/src/manager/preview/preview.rs index 21227a2d..8c9afd19 100644 --- a/core/src/manager/preview/preview.rs +++ b/core/src/manager/preview/preview.rs @@ -2,11 +2,12 @@ use std::{path::{Path, PathBuf}, sync::atomic::Ordering}; use adaptor::Adaptor; use config::MANAGER; -use shared::{MimeKind, PeekError}; +use shared::{MimeKind, PeekError, MIME_DIR}; use tokio::task::JoinHandle; + use super::{Provider, INCR}; -use crate::emit; +use crate::{emit, files::{Files, FilesOp}}; #[derive(Default)] pub struct Preview { @@ -39,8 +40,8 @@ impl Preview { return; } - self.reset(); - if !self.same_path(path) { + self.reset(|_| true); + if !self.same_mime(path, mime) { self.skip = 0; } @@ -58,6 +59,47 @@ impl Preview { })); } + pub fn folder(&mut self, path: &Path, files: Option, sequent: bool) { + if let Some(files) = files { + self.skip = self.skip.min(files.saturating_sub(MANAGER.layout.preview_height())); + } + + if self.same(path, MIME_DIR) { + return; + } else if !self.same_mime(path, MIME_DIR) { + self.skip = 0; + } + + self.reset(|_| true); + if files.is_some() || sequent { + emit!(Preview(PreviewLock { + path: path.to_path_buf(), + mime: MIME_DIR.to_owned(), + skip: self.skip, + data: PreviewData::Folder, + })); + } + + if sequent { + return; + } + + let (path, skip) = (path.to_path_buf(), self.skip); + self.handle = Some(tokio::spawn(async move { + emit!(Files(match Files::read_dir(&path).await { + Ok(items) => FilesOp::Read(path.clone(), items), + Err(_) => FilesOp::IOErr(path.clone()), + })); + + emit!(Preview(PreviewLock { + path, + mime: MIME_DIR.to_owned(), + skip, + data: PreviewData::Folder, + })); + })); + } + pub fn sequent(&mut self, path: &Path, mime: &str, show_image: bool) { let kind = MimeKind::new(mime); if !show_image && kind.show_as_image() { @@ -84,37 +126,32 @@ impl Preview { } #[inline] - 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 }; - } + 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 reset(&mut self) -> bool { + pub fn reset 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 b = matches!(&self.lock, Some(l) if !l.is_image()); - self.lock = None; - b - } + let Some(ref lock) = self.lock else { + return false; + }; - pub fn reset_image(&mut self) -> bool { - self.handle.take().map(|h| h.abort()); - INCR.fetch_add(1, Ordering::Relaxed); - Adaptor::image_hide(MANAGER.layout.preview_rect()).ok(); - - if matches!(&self.lock, Some(l) if l.is_image()) { + let b = !lock.is_image(); + if f(lock) { self.lock = None; } - false + b } } @@ -133,6 +170,14 @@ impl Preview { false } + #[inline] + pub fn same_mime(&self, path: &Path, mime: &str) -> bool { + if let Some(ref lock) = self.lock { + return lock.path == path && lock.mime == mime; + } + false + } + #[inline] pub fn same_path(&self, path: &Path) -> bool { if let Some(ref lock) = self.lock { diff --git a/core/src/manager/preview/provider.rs b/core/src/manager/preview/provider.rs index 66bfbaab..45680eae 100644 --- a/core/src/manager/preview/provider.rs +++ b/core/src/manager/preview/provider.rs @@ -7,8 +7,9 @@ use shared::{MimeKind, PeekError}; use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped}; use tokio::fs; + use super::PreviewData; -use crate::{emit, external, files::{Files, FilesOp}, highlighter}; +use crate::{external, highlighter}; pub(super) struct Provider; @@ -23,7 +24,6 @@ impl Provider { match kind { 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, MimeKind::Video => Provider::video(path, skip).await, MimeKind::JSON => Provider::json(path, skip).await.map(PreviewData::Text), @@ -37,25 +37,15 @@ impl Provider { match kind { MimeKind::Empty => 0, MimeKind::Archive => step * MANAGER.layout.preview_height() / 10, - MimeKind::Dir => step * MANAGER.layout.preview_height() / 10, MimeKind::Image => 0, MimeKind::Video => step, MimeKind::JSON => step * MANAGER.layout.preview_height() / 10, MimeKind::PDF => 1, MimeKind::Text => step * MANAGER.layout.preview_height() / 10, - MimeKind::Others => 0, + MimeKind::Others => step * MANAGER.layout.preview_height() / 10, } } - pub(super) async fn folder(path: &Path) -> Result { - emit!(Files(match Files::read_dir(path).await { - Ok(items) => FilesOp::Read(path.to_path_buf(), items), - Err(_) => FilesOp::IOErr(path.to_path_buf()), - })); - - Ok(PreviewData::Folder) - } - pub(super) async fn image(path: &Path) -> Result { Adaptor::image_show(path, MANAGER.layout.preview_rect()).await?; Ok(PreviewData::Image) diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index de6f6b98..5c30a5ff 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -1,9 +1,9 @@ use std::{borrow::Cow, collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}, mem, path::{Path, PathBuf}}; use anyhow::{Error, Result}; -use config::{open::Opener, MANAGER}; +use config::open::Opener; use futures::StreamExt; -use shared::{Defer, MIME_DIR}; +use shared::Defer; use tokio::task::JoinHandle; use super::{Folder, Mode, Preview, PreviewLock}; @@ -86,7 +86,7 @@ impl Tab { } if self.current.cwd == target { - if hovered.map(|h| self.current.hover_force(h)).unwrap_or(false) { + if hovered.map(|h| self.current.hover_force(h)) == Some(true) { emit!(Hover); } return false; @@ -241,9 +241,9 @@ impl Tab { external::fd(external::FdOpt { cwd: cwd.clone(), hidden, glob: false, subject }) }?; - emit!(Files(FilesOp::search_empty(&cwd))); + emit!(Files(FilesOp::clear(&cwd))); while let Some(chunk) = rx.next().await { - emit!(Files(FilesOp::Search(cwd.clone(), Files::read(&chunk).await))); + emit!(Files(FilesOp::Read(cwd.clone(), Files::read(&chunk).await))); } Ok(()) })); @@ -310,31 +310,21 @@ impl Tab { false } - pub fn update_peek(&mut self, step: isize, path: Option) { + pub fn update_peek(&mut self, step: isize, path: Option) -> bool { let Some(ref hovered) = self.current.hovered else { - return; + return false; }; - if path.as_ref().map(|p| p != hovered.path()).unwrap_or(false) { - return; - } else if !self.preview.arrow(step, path.is_some()) { - return; - } else if !matches!(&self.preview.lock, Some(l) if l.mime == MIME_DIR) { - return; + if path.as_ref().map(|p| p != hovered.path()) == Some(true) { + return false; } - let path = &self.preview.lock.as_ref().unwrap().path; - if let Some(folder) = self.history(path) { - let max = folder.files.len().saturating_sub(MANAGER.layout.preview_height()); - if self.preview.skip() > max { - self.preview.arrow(max as isize, true); - } - } + self.preview.arrow(step) } 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(); + return self.preview_reset(); }; if lock.path != *hovered { @@ -394,10 +384,10 @@ impl Tab { pub fn preview(&self) -> &Preview { &self.preview } #[inline] - pub fn preview_reset(&mut self) -> bool { self.preview.reset() } + pub fn preview_reset(&mut self) -> bool { self.preview.reset(|_| true) } #[inline] - pub fn preview_reset_image(&mut self) -> bool { self.preview.reset_image() } + pub fn preview_reset_image(&mut self) -> bool { self.preview.reset(|l| l.is_image()) } // --- Sorter pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool { @@ -433,7 +423,7 @@ impl Tab { applied |= match self.current.hovered { Some(ref h) if h.is_dir() => { - self.history.get_mut(h.path()).map(|f| f.files.set_show_hidden(state)).unwrap_or(false) + self.history.get_mut(h.path()).map(|f| f.files.set_show_hidden(state)) == Some(true) } _ => false, }; diff --git a/shared/src/mime.rs b/shared/src/mime.rs index cb42d532..534949d7 100644 --- a/shared/src/mime.rs +++ b/shared/src/mime.rs @@ -5,7 +5,6 @@ pub enum MimeKind { Empty, Archive, - Dir, Image, Video, @@ -19,9 +18,7 @@ pub enum MimeKind { impl MimeKind { pub fn new(s: &str) -> Self { - if s == MIME_DIR { - Self::Dir - } else if s.starts_with("text/") || s.ends_with("/xml") { + if s.starts_with("text/") || s.ends_with("/xml") { Self::Text } else if s.starts_with("image/") { Self::Image