diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index 9be38b690..c335fca4c 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -1,7 +1,7 @@ use core::files::File; use config::{MANAGER, THEME}; -use ratatui::{buffer::Buffer, layout::Rect, style::Style, widgets::{List, ListItem, Widget}}; +use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{List, ListItem, Widget}}; use shared::short_path; use crate::Ctx; @@ -44,7 +44,7 @@ impl<'a> Folder<'a> { THEME .icons .iter() - .find(|x| x.name.match_path(file.path(), Some(file.is_dir()))) + .find(|x| x.name.match_path(file.url(), Some(file.is_dir()))) .map(|x| x.display.as_ref()) .unwrap_or("") } @@ -72,11 +72,11 @@ impl<'a> Widget for Folder<'a> { self.folder.window() }; - let items = window + let items: Vec<_> = window .iter() .enumerate() .map(|(i, f)| { - let is_selected = self.folder.files.is_selected(f.path()); + let is_selected = self.folder.files.is_selected(f.url()); if (!self.is_selection && is_selected) || (self.is_selection && mode.pending(self.folder.offset() + i, is_selected)) { @@ -102,7 +102,7 @@ impl<'a> Widget for Folder<'a> { let mut spans = Vec::with_capacity(10); spans.push(Span::raw(format!(" {} ", Self::icon(f)))); - spans.push(Span::raw(readable_path(f.path(), &self.folder.cwd))); + spans.push(Span::raw(short_path(f.url(), &self.folder.cwd))); if let Some(link_to) = f.link_to() { if MANAGER.show_symlink { @@ -113,7 +113,7 @@ impl<'a> Widget for Folder<'a> { if let Some(idx) = active .finder() .filter(|_| hovered && self.is_find) - .and_then(|finder| finder.matched_idx(f.path())) + .and_then(|finder| finder.matched_idx(f.url())) { let len = active.finder().unwrap().matched().len(); let style = Style::new().fg(Color::Rgb(255, 255, 50)).add_modifier(Modifier::ITALIC); @@ -129,7 +129,7 @@ impl<'a> Widget for Folder<'a> { ListItem::new(Line::from(spans)).style(style) }) - .collect::>(); + .collect(); List::new(items).render(area, buf); } diff --git a/core/src/files/files.rs b/core/src/files/files.rs index f17df4b41..03b5bd902 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -5,11 +5,12 @@ use config::manager::SortBy; use shared::Url; use tokio::{fs, select, sync::mpsc::{self, UnboundedReceiver}}; -use super::{File, FilesSorter, FILES_VERSION}; +use super::{File, FilesSorter, FILES_TICKET}; pub struct Files { items: Vec, hidden: Vec, + ticket: u64, version: u64, sizes: BTreeMap, @@ -24,6 +25,7 @@ impl Default for Files { Self { items: Default::default(), hidden: Default::default(), + ticket: Default::default(), version: Default::default(), sizes: Default::default(), @@ -126,15 +128,16 @@ impl Files { if !self.show_hidden { (self.hidden, items) = items.into_iter().partition(|f| f.is_hidden); } + self.ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed); self.sorter.sort(&mut items, &self.sizes); self.items = items; - self.version = FILES_VERSION.fetch_add(1, Ordering::Relaxed); + self.version += 1; true } pub fn update_part(&mut self, version: u64, items: Vec) -> bool { if !items.is_empty() { - if version != self.version { + if version != self.ticket { return false; } @@ -147,22 +150,26 @@ impl Files { } self.sorter.sort(&mut self.items, &self.sizes); + self.version += 1; return true; } - self.version = version; - if !self.items.is_empty() { - self.items.clear(); - self.hidden.clear(); - return true; + self.ticket = version; + if self.items.is_empty() && self.hidden.is_empty() { + return false; } - false + + self.items.clear(); + self.hidden.clear(); + self.version += 1; + true } pub fn update_size(&mut self, items: BTreeMap) -> bool { self.sizes.extend(items); if self.sorter.by == SortBy::Size { self.sorter.sort(&mut self.items, &self.sizes); + self.version += 1; } true } @@ -186,7 +193,11 @@ impl Files { #[inline] pub fn duplicate(&self, idx: usize) -> Option { self.items.get(idx).cloned() } - // --- Size + // --- Version + #[inline] + pub fn version(&self) -> u64 { self.version } + + // --- Sizes #[inline] pub fn size(&self, url: &Url) -> Option { self.sizes.get(url).copied() } @@ -230,6 +241,7 @@ impl Files { } // --- Sorter + #[inline] pub fn sorter(&self) -> &FilesSorter { &self.sorter } #[inline] @@ -238,6 +250,7 @@ impl Files { return false; } self.sorter = sorter; + self.version += 1; self.sorter.sort(&mut self.items, &self.sizes) } @@ -259,6 +272,7 @@ impl Files { } self.show_hidden = state; + self.version += 1; true } } diff --git a/core/src/files/op.rs b/core/src/files/op.rs index 346d880a6..92523434c 100644 --- a/core/src/files/op.rs +++ b/core/src/files/op.rs @@ -5,7 +5,7 @@ use shared::Url; use super::File; use crate::emit; -pub(super) static FILES_VERSION: AtomicU64 = AtomicU64::new(0); +pub(super) static FILES_TICKET: AtomicU64 = AtomicU64::new(0); #[derive(Debug)] pub enum FilesOp { @@ -28,8 +28,8 @@ impl FilesOp { #[inline] pub fn prepare(url: &Url) -> u64 { - let version = FILES_VERSION.fetch_add(1, Ordering::Relaxed); - emit!(Files(Self::Part(url.clone(), version, Vec::new()))); - version + let ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed); + emit!(Files(Self::Part(url.clone(), ticket, Vec::new()))); + ticket } } diff --git a/core/src/manager/finder.rs b/core/src/manager/finder.rs index 0ac90aea4..8a08b48bb 100644 --- a/core/src/manager/finder.rs +++ b/core/src/manager/finder.rs @@ -1,14 +1,15 @@ -use std::{collections::BTreeMap, ffi::OsStr, path::{Path, PathBuf}}; +use std::{collections::BTreeMap, ffi::OsStr}; use anyhow::Result; use regex::bytes::Regex; +use shared::Url; use crate::files::Files; pub struct Finder { query: Regex, - matched: BTreeMap, - version: usize, + matched: BTreeMap, + version: u64, } impl Finder { @@ -47,7 +48,7 @@ impl Finder { continue; } - self.matched.insert(file.path_owned(), i); + self.matched.insert(file.url_owned(), i); if self.matched.len() > 99 { break; } @@ -75,14 +76,14 @@ impl Finder { impl Finder { #[inline] - pub fn matched(&self) -> &BTreeMap { &self.matched } + pub fn matched(&self) -> &BTreeMap { &self.matched } #[inline] - pub fn matched_idx(&self, path: &Path) -> Option { - if let Some((_, &idx)) = self.matched.iter().find(|(p, _)| *p == path) { + pub fn matched_idx(&self, url: &Url) -> Option { + if let Some((_, &idx)) = self.matched.iter().find(|(u, _)| *u == url) { return Some(idx); } - if path.file_name().map(|n| self.is_match(n)) == Some(true) { + if url.file_name().map(|n| self.is_match(n)) == Some(true) { return Some(100); } None diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index 3f7654777..9d1a832ef 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -28,7 +28,7 @@ impl Folder { pub fn update(&mut self, op: FilesOp) -> bool { let b = match op { FilesOp::Full(_, items) => self.files.update_full(items), - FilesOp::Part(_, version, items) => self.files.update_part(version, items), + FilesOp::Part(_, ticket, items) => self.files.update_part(ticket, items), FilesOp::Size(_, items) => self.files.update_size(items), _ => unreachable!(), }; diff --git a/core/src/manager/preview/preview.rs b/core/src/manager/preview/preview.rs index 7f0bc4bc7..1ff069468 100644 --- a/core/src/manager/preview/preview.rs +++ b/core/src/manager/preview/preview.rs @@ -97,9 +97,9 @@ impl Preview { let rx = UnboundedReceiverStream::new(rx).chunks_timeout(10000, Duration::from_millis(500)); pin!(rx); - let version = FilesOp::prepare(&url); + let ticket = FilesOp::prepare(&url); while let Some(chunk) = rx.next().await { - emit!(Files(FilesOp::Part(url.clone(), version, chunk))); + emit!(Files(FilesOp::Part(url.clone(), ticket, chunk))); } })); } diff --git a/core/src/manager/preview/provider.rs b/core/src/manager/preview/provider.rs index 45680eae5..cf54a127f 100644 --- a/core/src/manager/preview/provider.rs +++ b/core/src/manager/preview/provider.rs @@ -7,7 +7,6 @@ use shared::{MimeKind, PeekError}; use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped}; use tokio::fs; - use super::PreviewData; use crate::{external, highlighter}; @@ -85,7 +84,7 @@ impl Provider { } pub(super) async fn highlight(path: &Path, skip: usize) -> Result { - let tick = INCR.load(Ordering::Relaxed); + let ticket = INCR.load(Ordering::Relaxed); let path = path.to_path_buf(); let spaces = " ".repeat(PREVIEW.tab_size as usize); @@ -98,7 +97,7 @@ impl Provider { let mut i = 0; let limit = MANAGER.layout.preview_height(); while h.reader.read_line(&mut line)? > 0 { - if tick != INCR.load(Ordering::Relaxed) { + if ticket != INCR.load(Ordering::Relaxed) { return Err("Highlighting cancelled".into()); } diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index a21070c2f..89847318c 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -1,12 +1,12 @@ use std::{borrow::Cow, collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}, mem, time::Duration}; use anyhow::{Error, Result}; -use config::open::Opener; +use config::{keymap::{Exec, KeymapLayer}, open::Opener}; use shared::{Defer, Url}; use tokio::{pin, task::JoinHandle}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; -use super::{Folder, Mode, Preview, PreviewLock}; +use super::{Finder, Folder, Mode, Preview, PreviewLock}; use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, BLOCKER}; pub struct Tab { @@ -17,6 +17,7 @@ pub struct Tab { pub(super) history: BTreeMap, pub(super) preview: Preview, + finder: Option, search: Option>>, pub(super) show_hidden: bool, } @@ -33,6 +34,7 @@ impl From for Tab { history: Default::default(), preview: Default::default(), + finder: None, search: None, show_hidden: true, } @@ -45,6 +47,11 @@ impl From<&Url> for Tab { impl Tab { pub fn escape(&mut self) -> bool { + if self.finder.is_some() { + self.finder = None; + return true; + } + if let Some((_, indices)) = self.mode.visual() { self.current.files.select_index(indices, Some(self.mode.is_select())); self.mode = Mode::Normal; @@ -282,14 +289,14 @@ impl Tab { let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300)); pin!(rx); - let version = FilesOp::prepare(&cwd); + let ticket = FilesOp::prepare(&cwd); let mut first = true; while let Some(chunk) = rx.next().await { if first { emit!(Cd(cwd.clone())); first = false; } - emit!(Files(FilesOp::Part(cwd.clone(), version, chunk))); + emit!(Files(FilesOp::Part(cwd.clone(), ticket, chunk))); } Ok(()) })); @@ -437,6 +444,10 @@ impl Tab { #[inline] pub fn preview_arrow(&mut self, step: isize) -> bool { self.preview.arrow(step) } + // --- Finder + #[inline] + pub fn finder(&self) -> Option<&Finder> { self.finder.as_ref() } + // --- Sorter pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool { if !self.current.files.set_sorter(sorter) {