This commit is contained in:
sxyazi 2023-09-09 18:15:59 +08:00
parent dff12bcdbe
commit 1de6a5f13b
No known key found for this signature in database
8 changed files with 63 additions and 38 deletions

View file

@ -1,7 +1,7 @@
use core::files::File; use core::files::File;
use config::{MANAGER, THEME}; 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 shared::short_path;
use crate::Ctx; use crate::Ctx;
@ -44,7 +44,7 @@ impl<'a> Folder<'a> {
THEME THEME
.icons .icons
.iter() .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()) .map(|x| x.display.as_ref())
.unwrap_or("") .unwrap_or("")
} }
@ -72,11 +72,11 @@ impl<'a> Widget for Folder<'a> {
self.folder.window() self.folder.window()
}; };
let items = window let items: Vec<_> = window
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, f)| { .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) if (!self.is_selection && is_selected)
|| (self.is_selection && mode.pending(self.folder.offset() + i, 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); let mut spans = Vec::with_capacity(10);
spans.push(Span::raw(format!(" {} ", Self::icon(f)))); 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 let Some(link_to) = f.link_to() {
if MANAGER.show_symlink { if MANAGER.show_symlink {
@ -113,7 +113,7 @@ impl<'a> Widget for Folder<'a> {
if let Some(idx) = active if let Some(idx) = active
.finder() .finder()
.filter(|_| hovered && self.is_find) .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 len = active.finder().unwrap().matched().len();
let style = Style::new().fg(Color::Rgb(255, 255, 50)).add_modifier(Modifier::ITALIC); 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) ListItem::new(Line::from(spans)).style(style)
}) })
.collect::<Vec<_>>(); .collect();
List::new(items).render(area, buf); List::new(items).render(area, buf);
} }

View file

@ -5,11 +5,12 @@ use config::manager::SortBy;
use shared::Url; use shared::Url;
use tokio::{fs, select, sync::mpsc::{self, UnboundedReceiver}}; use tokio::{fs, select, sync::mpsc::{self, UnboundedReceiver}};
use super::{File, FilesSorter, FILES_VERSION}; use super::{File, FilesSorter, FILES_TICKET};
pub struct Files { pub struct Files {
items: Vec<File>, items: Vec<File>,
hidden: Vec<File>, hidden: Vec<File>,
ticket: u64,
version: u64, version: u64,
sizes: BTreeMap<Url, u64>, sizes: BTreeMap<Url, u64>,
@ -24,6 +25,7 @@ impl Default for Files {
Self { Self {
items: Default::default(), items: Default::default(),
hidden: Default::default(), hidden: Default::default(),
ticket: Default::default(),
version: Default::default(), version: Default::default(),
sizes: Default::default(), sizes: Default::default(),
@ -126,15 +128,16 @@ impl Files {
if !self.show_hidden { if !self.show_hidden {
(self.hidden, items) = items.into_iter().partition(|f| f.is_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.sorter.sort(&mut items, &self.sizes);
self.items = items; self.items = items;
self.version = FILES_VERSION.fetch_add(1, Ordering::Relaxed); self.version += 1;
true true
} }
pub fn update_part(&mut self, version: u64, items: Vec<File>) -> bool { pub fn update_part(&mut self, version: u64, items: Vec<File>) -> bool {
if !items.is_empty() { if !items.is_empty() {
if version != self.version { if version != self.ticket {
return false; return false;
} }
@ -147,22 +150,26 @@ impl Files {
} }
self.sorter.sort(&mut self.items, &self.sizes); self.sorter.sort(&mut self.items, &self.sizes);
self.version += 1;
return true; return true;
} }
self.version = version; self.ticket = version;
if !self.items.is_empty() { if self.items.is_empty() && self.hidden.is_empty() {
self.items.clear(); return false;
self.hidden.clear();
return true;
} }
false
self.items.clear();
self.hidden.clear();
self.version += 1;
true
} }
pub fn update_size(&mut self, items: BTreeMap<Url, u64>) -> bool { pub fn update_size(&mut self, items: BTreeMap<Url, u64>) -> bool {
self.sizes.extend(items); self.sizes.extend(items);
if self.sorter.by == SortBy::Size { if self.sorter.by == SortBy::Size {
self.sorter.sort(&mut self.items, &self.sizes); self.sorter.sort(&mut self.items, &self.sizes);
self.version += 1;
} }
true true
} }
@ -186,7 +193,11 @@ impl Files {
#[inline] #[inline]
pub fn duplicate(&self, idx: usize) -> Option<File> { self.items.get(idx).cloned() } pub fn duplicate(&self, idx: usize) -> Option<File> { self.items.get(idx).cloned() }
// --- Size // --- Version
#[inline]
pub fn version(&self) -> u64 { self.version }
// --- Sizes
#[inline] #[inline]
pub fn size(&self, url: &Url) -> Option<u64> { self.sizes.get(url).copied() } pub fn size(&self, url: &Url) -> Option<u64> { self.sizes.get(url).copied() }
@ -230,6 +241,7 @@ impl Files {
} }
// --- Sorter // --- Sorter
#[inline]
pub fn sorter(&self) -> &FilesSorter { &self.sorter } pub fn sorter(&self) -> &FilesSorter { &self.sorter }
#[inline] #[inline]
@ -238,6 +250,7 @@ impl Files {
return false; return false;
} }
self.sorter = sorter; self.sorter = sorter;
self.version += 1;
self.sorter.sort(&mut self.items, &self.sizes) self.sorter.sort(&mut self.items, &self.sizes)
} }
@ -259,6 +272,7 @@ impl Files {
} }
self.show_hidden = state; self.show_hidden = state;
self.version += 1;
true true
} }
} }

View file

@ -5,7 +5,7 @@ use shared::Url;
use super::File; use super::File;
use crate::emit; use crate::emit;
pub(super) static FILES_VERSION: AtomicU64 = AtomicU64::new(0); pub(super) static FILES_TICKET: AtomicU64 = AtomicU64::new(0);
#[derive(Debug)] #[derive(Debug)]
pub enum FilesOp { pub enum FilesOp {
@ -28,8 +28,8 @@ impl FilesOp {
#[inline] #[inline]
pub fn prepare(url: &Url) -> u64 { pub fn prepare(url: &Url) -> u64 {
let version = FILES_VERSION.fetch_add(1, Ordering::Relaxed); let ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed);
emit!(Files(Self::Part(url.clone(), version, Vec::new()))); emit!(Files(Self::Part(url.clone(), ticket, Vec::new())));
version ticket
} }
} }

View file

@ -1,14 +1,15 @@
use std::{collections::BTreeMap, ffi::OsStr, path::{Path, PathBuf}}; use std::{collections::BTreeMap, ffi::OsStr};
use anyhow::Result; use anyhow::Result;
use regex::bytes::Regex; use regex::bytes::Regex;
use shared::Url;
use crate::files::Files; use crate::files::Files;
pub struct Finder { pub struct Finder {
query: Regex, query: Regex,
matched: BTreeMap<PathBuf, u8>, matched: BTreeMap<Url, u8>,
version: usize, version: u64,
} }
impl Finder { impl Finder {
@ -47,7 +48,7 @@ impl Finder {
continue; continue;
} }
self.matched.insert(file.path_owned(), i); self.matched.insert(file.url_owned(), i);
if self.matched.len() > 99 { if self.matched.len() > 99 {
break; break;
} }
@ -75,14 +76,14 @@ impl Finder {
impl Finder { impl Finder {
#[inline] #[inline]
pub fn matched(&self) -> &BTreeMap<PathBuf, u8> { &self.matched } pub fn matched(&self) -> &BTreeMap<Url, u8> { &self.matched }
#[inline] #[inline]
pub fn matched_idx(&self, path: &Path) -> Option<u8> { pub fn matched_idx(&self, url: &Url) -> Option<u8> {
if let Some((_, &idx)) = self.matched.iter().find(|(p, _)| *p == path) { if let Some((_, &idx)) = self.matched.iter().find(|(u, _)| *u == url) {
return Some(idx); 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); return Some(100);
} }
None None

View file

@ -28,7 +28,7 @@ impl Folder {
pub fn update(&mut self, op: FilesOp) -> bool { pub fn update(&mut self, op: FilesOp) -> bool {
let b = match op { let b = match op {
FilesOp::Full(_, items) => self.files.update_full(items), 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), FilesOp::Size(_, items) => self.files.update_size(items),
_ => unreachable!(), _ => unreachable!(),
}; };

View file

@ -97,9 +97,9 @@ impl Preview {
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(10000, Duration::from_millis(500)); let rx = UnboundedReceiverStream::new(rx).chunks_timeout(10000, Duration::from_millis(500));
pin!(rx); pin!(rx);
let version = FilesOp::prepare(&url); let ticket = FilesOp::prepare(&url);
while let Some(chunk) = rx.next().await { while let Some(chunk) = rx.next().await {
emit!(Files(FilesOp::Part(url.clone(), version, chunk))); emit!(Files(FilesOp::Part(url.clone(), ticket, chunk)));
} }
})); }));
} }

View file

@ -7,7 +7,6 @@ use shared::{MimeKind, PeekError};
use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped}; use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped};
use tokio::fs; use tokio::fs;
use super::PreviewData; use super::PreviewData;
use crate::{external, highlighter}; use crate::{external, highlighter};
@ -85,7 +84,7 @@ impl Provider {
} }
pub(super) async fn highlight(path: &Path, skip: usize) -> Result<String, PeekError> { pub(super) async fn highlight(path: &Path, skip: usize) -> Result<String, PeekError> {
let tick = INCR.load(Ordering::Relaxed); let ticket = INCR.load(Ordering::Relaxed);
let path = path.to_path_buf(); let path = path.to_path_buf();
let spaces = " ".repeat(PREVIEW.tab_size as usize); let spaces = " ".repeat(PREVIEW.tab_size as usize);
@ -98,7 +97,7 @@ impl Provider {
let mut i = 0; let mut i = 0;
let limit = MANAGER.layout.preview_height(); let limit = MANAGER.layout.preview_height();
while h.reader.read_line(&mut line)? > 0 { 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()); return Err("Highlighting cancelled".into());
} }

View file

@ -6,7 +6,7 @@ use shared::{Defer, Url};
use tokio::{pin, task::JoinHandle}; use tokio::{pin, task::JoinHandle};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; 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}; use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, BLOCKER};
pub struct Tab { pub struct Tab {
@ -17,6 +17,7 @@ pub struct Tab {
pub(super) history: BTreeMap<Url, Folder>, pub(super) history: BTreeMap<Url, Folder>,
pub(super) preview: Preview, pub(super) preview: Preview,
finder: Option<Finder>,
search: Option<JoinHandle<Result<()>>>, search: Option<JoinHandle<Result<()>>>,
pub(super) show_hidden: bool, pub(super) show_hidden: bool,
} }
@ -33,6 +34,7 @@ impl From<Url> for Tab {
history: Default::default(), history: Default::default(),
preview: Default::default(), preview: Default::default(),
finder: None,
search: None, search: None,
show_hidden: true, show_hidden: true,
} }
@ -45,6 +47,11 @@ impl From<&Url> for Tab {
impl Tab { impl Tab {
pub fn escape(&mut self) -> bool { pub fn escape(&mut self) -> bool {
if self.finder.is_some() {
self.finder = None;
return true;
}
if let Some((_, indices)) = self.mode.visual() { if let Some((_, indices)) = self.mode.visual() {
self.current.files.select_index(indices, Some(self.mode.is_select())); self.current.files.select_index(indices, Some(self.mode.is_select()));
self.mode = Mode::Normal; self.mode = Mode::Normal;
@ -284,14 +291,14 @@ impl Tab {
let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300)); let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300));
pin!(rx); pin!(rx);
let version = FilesOp::prepare(&cwd); let ticket = FilesOp::prepare(&cwd);
let mut first = true; let mut first = true;
while let Some(chunk) = rx.next().await { while let Some(chunk) = rx.next().await {
if first { if first {
emit!(Cd(cwd.clone())); emit!(Cd(cwd.clone()));
first = false; first = false;
} }
emit!(Files(FilesOp::Part(cwd.clone(), version, chunk))); emit!(Files(FilesOp::Part(cwd.clone(), ticket, chunk)));
} }
Ok(()) Ok(())
})); }));
@ -439,6 +446,10 @@ impl Tab {
#[inline] #[inline]
pub fn preview_arrow(&mut self, step: isize) -> bool { self.preview.arrow(step) } 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 // --- Sorter
pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool { pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool {
if !self.current.files.set_sorter(sorter) { if !self.current.files.set_sorter(sorter) {