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 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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
self.items.clear();
|
self.items.clear();
|
||||||
self.hidden.clear();
|
self.hidden.clear();
|
||||||
return true;
|
self.version += 1;
|
||||||
}
|
true
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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!(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue