mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
dff12bcdbe
commit
1de6a5f13b
8 changed files with 63 additions and 38 deletions
|
|
@ -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::<Vec<_>>();
|
||||
.collect();
|
||||
|
||||
List::new(items).render(area, buf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<File>,
|
||||
hidden: Vec<File>,
|
||||
ticket: u64,
|
||||
version: u64,
|
||||
|
||||
sizes: BTreeMap<Url, u64>,
|
||||
|
|
@ -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<File>) -> 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.ticket = version;
|
||||
if self.items.is_empty() && self.hidden.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.items.clear();
|
||||
self.hidden.clear();
|
||||
return true;
|
||||
}
|
||||
false
|
||||
self.version += 1;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn update_size(&mut self, items: BTreeMap<Url, u64>) -> 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<File> { self.items.get(idx).cloned() }
|
||||
|
||||
// --- Size
|
||||
// --- Version
|
||||
#[inline]
|
||||
pub fn version(&self) -> u64 { self.version }
|
||||
|
||||
// --- Sizes
|
||||
#[inline]
|
||||
pub fn size(&self, url: &Url) -> Option<u64> { 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<PathBuf, u8>,
|
||||
version: usize,
|
||||
matched: BTreeMap<Url, u8>,
|
||||
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<PathBuf, u8> { &self.matched }
|
||||
pub fn matched(&self) -> &BTreeMap<Url, u8> { &self.matched }
|
||||
|
||||
#[inline]
|
||||
pub fn matched_idx(&self, path: &Path) -> Option<u8> {
|
||||
if let Some((_, &idx)) = self.matched.iter().find(|(p, _)| *p == path) {
|
||||
pub fn matched_idx(&self, url: &Url) -> Option<u8> {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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!(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, PeekError> {
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ 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<Url, Folder>,
|
||||
pub(super) preview: Preview,
|
||||
|
||||
finder: Option<Finder>,
|
||||
search: Option<JoinHandle<Result<()>>>,
|
||||
pub(super) show_hidden: bool,
|
||||
}
|
||||
|
|
@ -33,6 +34,7 @@ impl From<Url> 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;
|
||||
|
|
@ -284,14 +291,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(())
|
||||
}));
|
||||
|
|
@ -439,6 +446,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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue