mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
perf: introduce URN to speed up large directory file sorting (#1622)
This commit is contained in:
parent
d20b3d8408
commit
3ee1209d3c
34 changed files with 283 additions and 195 deletions
|
|
@ -82,7 +82,6 @@ impl Ueberzug {
|
|||
}
|
||||
|
||||
fn create_demon(adapter: Adapter) -> Result<Child> {
|
||||
// TODO: demon
|
||||
let result = Command::new("ueberzugpp")
|
||||
.args(["layer", "-so", &adapter.to_string()])
|
||||
.env("SPDLOG_LEVEL", if cfg!(debug_assertions) { "debug" } else { "" })
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ impl Manager {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: consider old and new in the different directories
|
||||
if !succeeded.is_empty() {
|
||||
Pubsub::pub_from_bulk(succeeded.iter().map(|(u, f)| (u, f.url())).collect());
|
||||
FilesOp::Upserting(cwd, succeeded).emit();
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ impl Manager {
|
|||
// Refresh watcher
|
||||
let mut to_watch = HashSet::with_capacity(3 * self.tabs.len());
|
||||
for tab in self.tabs.iter() {
|
||||
to_watch.insert(&tab.current.cwd);
|
||||
to_watch.insert(tab.cwd());
|
||||
if let Some(ref p) = tab.parent {
|
||||
to_watch.insert(&p.cwd);
|
||||
to_watch.insert(&p.loc);
|
||||
}
|
||||
if let Some(h) = tab.current.hovered().filter(|&h| h.is_dir()) {
|
||||
to_watch.insert(h.url());
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ impl Manager {
|
|||
|
||||
let find = |folder: Option<&Folder>| {
|
||||
folder.is_some_and(|folder| {
|
||||
folder.cwd == p && folder.files.iter().any(|f| f.is_dir() && url == f.url())
|
||||
p == *folder.loc && folder.files.iter().any(|f| f.is_dir() && url == f.url())
|
||||
})
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ impl Tabs {
|
|||
} else {
|
||||
tab.conf = self.active().conf.clone();
|
||||
tab.apply_files_attrs();
|
||||
tab.cd(self.active().current.cwd.clone());
|
||||
tab.cd(self.active().cwd().clone());
|
||||
}
|
||||
|
||||
self.items.insert(self.cursor + 1, tab);
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ impl Manager {
|
|||
let url = op.url();
|
||||
tab.selected.apply_op(&op);
|
||||
|
||||
if tab.current.cwd == *url {
|
||||
if url == tab.cwd() {
|
||||
Self::update_current(tab, op, tasks);
|
||||
} else if matches!(&tab.parent, Some(p) if p.cwd == *url) {
|
||||
} else if matches!(&tab.parent, Some(p) if url == &*p.loc) {
|
||||
Self::update_parent(tab, op);
|
||||
} else if matches!(tab.current.hovered(), Some(h) if url == h.url()) {
|
||||
Self::update_hovered(tab, op);
|
||||
|
|
@ -59,7 +59,7 @@ impl Manager {
|
|||
}
|
||||
|
||||
fn update_parent(tab: &mut Tab, op: Cow<FilesOp>) {
|
||||
let cwd = tab.current.cwd.clone();
|
||||
let cwd = tab.cwd().clone();
|
||||
let leave = matches!(*op, FilesOp::Deleting(_, ref urls) if urls.contains(&cwd));
|
||||
|
||||
if let Some(f) = tab.parent.as_mut() {
|
||||
|
|
@ -108,7 +108,7 @@ impl Manager {
|
|||
}
|
||||
|
||||
fn update_history(tab: &mut Tab, op: Cow<FilesOp>) {
|
||||
let leave = tab.parent.as_ref().and_then(|f| f.cwd.parent_url().map(|p| (&f.cwd, p))).is_some_and(
|
||||
let leave = tab.parent.as_ref().and_then(|f| f.loc.parent_url().map(|p| (&f.loc, p))).is_some_and(
|
||||
|(p, pp)| matches!(*op, FilesOp::Deleting(ref parent, ref urls) if *parent == pp && urls.contains(p)),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl Manager {
|
|||
return;
|
||||
};
|
||||
|
||||
if opt.only_if.is_some_and(|u| u != self.current().cwd) {
|
||||
if opt.only_if.is_some_and(|u| u != *self.active().cwd()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ impl Manager {
|
|||
|
||||
impl Manager {
|
||||
#[inline]
|
||||
pub fn cwd(&self) -> &Url { &self.current().cwd }
|
||||
pub fn cwd(&self) -> &Url { &self.current().loc }
|
||||
|
||||
#[inline]
|
||||
pub fn active(&self) -> &Tab { self.tabs.active() }
|
||||
|
|
|
|||
|
|
@ -60,8 +60,11 @@ impl Watcher {
|
|||
}
|
||||
|
||||
pub(super) fn trigger_dirs(&self, folders: &[&Folder]) {
|
||||
let todo: Vec<_> =
|
||||
folders.iter().filter(|&f| f.cwd.is_regular()).map(|&f| (f.cwd.clone(), f.cha)).collect();
|
||||
let todo: Vec<_> = folders
|
||||
.iter()
|
||||
.filter(|&f| f.loc.is_regular())
|
||||
.map(|&f| (f.loc.url().clone(), f.cha))
|
||||
.collect();
|
||||
if todo.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,25 +39,25 @@ impl Tab {
|
|||
return self.cd_interactive();
|
||||
}
|
||||
|
||||
if self.current.cwd == opt.target {
|
||||
if opt.target == *self.cwd() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Take parent to history
|
||||
if let Some(rep) = self.parent.take() {
|
||||
self.history.insert(rep.cwd.clone(), rep);
|
||||
self.history.insert(rep.loc.url().clone(), rep);
|
||||
}
|
||||
|
||||
// Current
|
||||
let rep = self.history_new(&opt.target);
|
||||
let rep = self.history.remove_or(&opt.target);
|
||||
let rep = mem::replace(&mut self.current, rep);
|
||||
if rep.cwd.is_regular() {
|
||||
self.history.insert(rep.cwd.clone(), rep);
|
||||
if rep.loc.is_regular() {
|
||||
self.history.insert(rep.loc.url().clone(), rep);
|
||||
}
|
||||
|
||||
// Parent
|
||||
if let Some(parent) = opt.target.parent_url() {
|
||||
self.parent = Some(self.history_new(&parent));
|
||||
self.parent = Some(self.history.remove_or(&parent));
|
||||
}
|
||||
|
||||
// Backstack
|
||||
|
|
@ -65,7 +65,7 @@ impl Tab {
|
|||
self.backstack.push(opt.target.clone());
|
||||
}
|
||||
|
||||
Pubsub::pub_from_cd(self.idx, &self.current.cwd);
|
||||
Pubsub::pub_from_cd(self.idx, self.cwd());
|
||||
ManagerProxy::refresh();
|
||||
render!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ impl Tab {
|
|||
}
|
||||
|
||||
pub fn escape_search(&mut self) -> bool {
|
||||
let b = self.current.cwd.is_search();
|
||||
let b = self.cwd().is_search();
|
||||
self.search_stop();
|
||||
|
||||
render_and!(b)
|
||||
|
|
@ -108,7 +108,7 @@ impl Tab {
|
|||
let urls: Vec<_> =
|
||||
indices.into_iter().filter_map(|i| self.current.files.get(i)).map(|f| f.url()).collect();
|
||||
|
||||
let same = !self.current.cwd.is_search();
|
||||
let same = !self.cwd().is_search();
|
||||
if !select {
|
||||
self.selected.remove_many(&urls, same);
|
||||
} else if self.selected.add_many(&urls, same) != urls.len() {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ impl Tab {
|
|||
.current
|
||||
.hovered()
|
||||
.and_then(|h| h.parent())
|
||||
.filter(|p| *p != self.current.cwd)
|
||||
.or_else(|| self.current.cwd.parent_url())
|
||||
.filter(|p| p != self.cwd())
|
||||
.or_else(|| self.cwd().parent_url())
|
||||
.map(|u| self.cd(u));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,11 +44,10 @@ impl Tab {
|
|||
handle.abort();
|
||||
}
|
||||
|
||||
let mut cwd = self.current.cwd.clone();
|
||||
let cwd = self.cwd().to_search(&opt.subject);
|
||||
let hidden = self.conf.show_hidden;
|
||||
|
||||
self.search = Some(tokio::spawn(async move {
|
||||
cwd = cwd.into_search(opt.subject.clone());
|
||||
let rx = if opt.via == SearchOptVia::Rg {
|
||||
external::rg(external::RgOpt {
|
||||
cwd: cwd.clone(),
|
||||
|
|
@ -82,8 +81,8 @@ impl Tab {
|
|||
if let Some(handle) = self.search.take() {
|
||||
handle.abort();
|
||||
}
|
||||
if self.current.cwd.is_search() {
|
||||
let rep = self.history_new(&self.current.cwd.to_regular());
|
||||
if self.cwd().is_search() {
|
||||
let rep = self.history.remove_or(&self.cwd().to_regular());
|
||||
drop(mem::replace(&mut self.current, rep));
|
||||
ManagerProxy::refresh();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl Tab {
|
|||
None => iter.partition(|&u| self.selected.contains_key(u)),
|
||||
};
|
||||
|
||||
let same = !self.current.cwd.is_search();
|
||||
let same = !self.cwd().is_search();
|
||||
render!(self.selected.remove_many(&removal, same) > 0);
|
||||
|
||||
let added = self.selected.add_many(&addition, same);
|
||||
|
|
|
|||
26
yazi-core/src/tab/history.rs
Normal file
26
yazi-core/src/tab/history.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use std::{collections::HashMap, ops::{Deref, DerefMut}};
|
||||
|
||||
use yazi_fs::Folder;
|
||||
use yazi_shared::fs::Url;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct History(HashMap<Url, Folder>);
|
||||
|
||||
impl Deref for History {
|
||||
type Target = HashMap<Url, Folder>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl DerefMut for History {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
|
||||
}
|
||||
|
||||
impl History {
|
||||
#[inline]
|
||||
pub fn remove_or(&mut self, url: &Url) -> Folder {
|
||||
self.0.remove(url).unwrap_or_else(|| Folder::from(url))
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ mod backstack;
|
|||
mod commands;
|
||||
mod config;
|
||||
mod finder;
|
||||
mod history;
|
||||
mod mode;
|
||||
mod preview;
|
||||
mod selected;
|
||||
|
|
@ -10,6 +11,7 @@ mod tab;
|
|||
pub use backstack::*;
|
||||
pub use config::*;
|
||||
pub use finder::*;
|
||||
pub use history::*;
|
||||
pub use mode::*;
|
||||
pub use preview::*;
|
||||
pub use selected::*;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, iter};
|
||||
use std::iter;
|
||||
|
||||
use anyhow::Result;
|
||||
use ratatui::layout::Rect;
|
||||
|
|
@ -8,7 +8,7 @@ use yazi_config::{popup::{Origin, Position}, LAYOUT};
|
|||
use yazi_fs::{Folder, FolderStage};
|
||||
use yazi_shared::{fs::Url, render};
|
||||
|
||||
use super::{Backstack, Config, Finder, Mode, Preview};
|
||||
use super::{Backstack, Config, Finder, History, Mode, Preview};
|
||||
use crate::tab::Selected;
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -20,7 +20,7 @@ pub struct Tab {
|
|||
pub parent: Option<Folder>,
|
||||
|
||||
pub backstack: Backstack<Url>,
|
||||
pub history: HashMap<Url, Folder>,
|
||||
pub history: History,
|
||||
pub selected: Selected,
|
||||
|
||||
pub preview: Preview,
|
||||
|
|
@ -38,6 +38,9 @@ impl Tab {
|
|||
|
||||
impl Tab {
|
||||
// --- Current
|
||||
#[inline]
|
||||
pub fn cwd(&self) -> &Url { &self.current.loc }
|
||||
|
||||
pub fn hovered_rect(&self) -> Option<Rect> {
|
||||
let y = self.current.files.position(self.current.hovered()?.url())? - self.current.offset;
|
||||
|
||||
|
|
@ -83,10 +86,6 @@ impl Tab {
|
|||
}
|
||||
|
||||
// --- History
|
||||
#[inline]
|
||||
pub fn history_new(&mut self, url: &Url) -> Folder {
|
||||
self.history.remove(url).unwrap_or_else(|| Folder::from(url))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hovered_folder(&self) -> Option<&Folder> {
|
||||
|
|
@ -113,8 +112,8 @@ impl Tab {
|
|||
apply(parent);
|
||||
|
||||
// The parent should always track the CWD
|
||||
parent.hover(&self.current.cwd);
|
||||
parent.tracing = parent.hovered().map(|h| h.url()) == Some(&self.current.cwd);
|
||||
parent.hover(&self.current.loc);
|
||||
parent.tracing = parent.hovered().map(|h| h.url()) == Some(&self.current.loc);
|
||||
}
|
||||
|
||||
self
|
||||
|
|
|
|||
|
|
@ -46,11 +46,11 @@ impl File {
|
|||
Ok(cx.manager.mimetype.get(me.url()).cloned())
|
||||
});
|
||||
reg.add_method("prefix", |lua, me, ()| {
|
||||
if !me.folder().cwd.is_search() {
|
||||
if !me.folder().loc.is_search() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut p = me.url().strip_prefix(&me.folder().cwd).unwrap_or(me.url()).components();
|
||||
let mut p = me.url().strip_prefix(&me.folder().loc).unwrap_or(me.url()).components();
|
||||
p.next_back();
|
||||
Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose()
|
||||
});
|
||||
|
|
@ -77,7 +77,7 @@ impl File {
|
|||
});
|
||||
reg.add_method("is_marked", |_, me, ()| {
|
||||
use yazi_core::tab::Mode::*;
|
||||
if !me.tab().mode.is_visual() || me.folder().cwd != me.tab().current.cwd {
|
||||
if !me.tab().mode.is_visual() || me.folder().loc != me.tab().current.loc {
|
||||
return Ok(0u8);
|
||||
}
|
||||
|
||||
|
|
@ -89,11 +89,11 @@ impl File {
|
|||
});
|
||||
reg.add_method("is_selected", |_, me, ()| Ok(me.tab().selected.contains_key(me.url())));
|
||||
reg.add_method("in_parent", |_, me, ()| {
|
||||
Ok(me.tab().parent.as_ref().is_some_and(|f| me.folder().cwd == f.cwd))
|
||||
Ok(me.tab().parent.as_ref().is_some_and(|f| me.folder().loc == f.loc))
|
||||
});
|
||||
reg.add_method("in_current", |_, me, ()| Ok(me.folder().cwd == me.tab().current.cwd));
|
||||
reg.add_method("in_current", |_, me, ()| Ok(me.folder().loc == me.tab().current.loc));
|
||||
reg.add_method("in_preview", |_, me, ()| {
|
||||
Ok(me.tab().current.hovered().is_some_and(|f| me.folder().cwd == *f.url()))
|
||||
Ok(me.tab().current.hovered().is_some_and(|f| f.url() == &*me.folder().loc))
|
||||
});
|
||||
reg.add_method("found", |lua, me, ()| {
|
||||
let cx = lua.named_registry_value::<CtxRef>("cx")?;
|
||||
|
|
@ -113,7 +113,7 @@ impl File {
|
|||
let Some(finder) = &cx.manager.active().finder else {
|
||||
return Ok(None);
|
||||
};
|
||||
if me.folder().cwd != me.tab().current.cwd {
|
||||
if me.folder().loc != me.tab().current.loc {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(h) = finder.filter.highlighted(me.name()) else {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ impl Folder {
|
|||
|
||||
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.register_userdata_type::<Self>(|reg| {
|
||||
reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.cwd.clone()));
|
||||
reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.loc.url().clone()));
|
||||
reg.add_field_method_get("files", |_, me| Files::make(0..me.files.len(), me, me.tab()));
|
||||
reg.add_field_method_get("stage", |lua, me| lua.create_any_userdata(me.stage));
|
||||
reg.add_field_method_get("window", |_, me| Files::make(me.window.clone(), me, me.tab()));
|
||||
|
|
|
|||
|
|
@ -24,15 +24,7 @@ impl Tab {
|
|||
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.register_userdata_type::<Self>(|reg| {
|
||||
reg.add_method("name", |lua, me, ()| {
|
||||
Some(
|
||||
lua.create_string(
|
||||
me.current
|
||||
.cwd
|
||||
.file_name()
|
||||
.map_or(me.current.cwd.as_os_str().as_encoded_bytes(), |n| n.as_encoded_bytes()),
|
||||
),
|
||||
)
|
||||
.transpose()
|
||||
Some(lua.create_string(me.current.loc.name().as_encoded_bytes())).transpose()
|
||||
});
|
||||
|
||||
reg.add_field_method_get("mode", |_, me| Mode::make(&me.mode));
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ impl Deref for Files {
|
|||
}
|
||||
|
||||
impl Files {
|
||||
pub async fn from_dir(url: &Url) -> std::io::Result<UnboundedReceiver<File>> {
|
||||
let mut it = fs::read_dir(url).await?;
|
||||
pub async fn from_dir(dir: &Url) -> std::io::Result<UnboundedReceiver<File>> {
|
||||
let mut it = fs::read_dir(dir).await?;
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
|
||||
tokio::spawn(async move {
|
||||
|
|
@ -66,18 +66,18 @@ impl Files {
|
|||
Ok(rx)
|
||||
}
|
||||
|
||||
pub async fn from_dir_bulk(url: &Url) -> std::io::Result<Vec<File>> {
|
||||
let mut it = fs::read_dir(url).await?;
|
||||
let mut items = Vec::with_capacity(5000);
|
||||
while let Ok(Some(item)) = it.next_entry().await {
|
||||
items.push(item);
|
||||
pub async fn from_dir_bulk(dir: &Url) -> std::io::Result<Vec<File>> {
|
||||
let mut it = fs::read_dir(dir).await?;
|
||||
let mut entries = Vec::with_capacity(5000);
|
||||
while let Ok(Some(entry)) = it.next_entry().await {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
let (first, rest) = items.split_at(items.len() / 3);
|
||||
let (second, third) = rest.split_at(items.len() / 3);
|
||||
async fn go(entities: &[DirEntry]) -> Vec<File> {
|
||||
let mut files = Vec::with_capacity(entities.len() / 3 + 1);
|
||||
for entry in entities {
|
||||
let (first, rest) = entries.split_at(entries.len() / 3);
|
||||
let (second, third) = rest.split_at(entries.len() / 3);
|
||||
async fn go(entries: &[DirEntry]) -> Vec<File> {
|
||||
let mut files = Vec::with_capacity(entries.len() / 3 + 1);
|
||||
for entry in entries {
|
||||
let url = Url::from(entry.path());
|
||||
files.push(match entry.metadata().await {
|
||||
Ok(meta) => File::from_meta(url, meta).await,
|
||||
|
|
@ -238,7 +238,7 @@ impl Files {
|
|||
($dist:expr, $src:expr, $inc:literal) => {
|
||||
let len = $dist.len();
|
||||
|
||||
$dist.retain(|f| !$src.remove(&f.url));
|
||||
$dist.retain(|f| !$src.remove(f.url()));
|
||||
if $dist.len() != len {
|
||||
self.revision += $inc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ use std::mem;
|
|||
|
||||
use yazi_config::{LAYOUT, MANAGER};
|
||||
use yazi_proxy::ManagerProxy;
|
||||
use yazi_shared::fs::{Cha, File, FilesOp, Url};
|
||||
use yazi_shared::fs::{Cha, File, FilesOp, Loc, Url};
|
||||
|
||||
use super::FolderStage;
|
||||
use crate::{Files, Step};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Folder {
|
||||
pub cwd: Url,
|
||||
pub loc: Loc,
|
||||
pub cha: Cha,
|
||||
pub files: Files,
|
||||
pub stage: FolderStage,
|
||||
|
|
@ -22,7 +22,7 @@ pub struct Folder {
|
|||
}
|
||||
|
||||
impl From<&Url> for Folder {
|
||||
fn from(cwd: &Url) -> Self { Self { cwd: cwd.clone(), ..Default::default() } }
|
||||
fn from(cwd: &Url) -> Self { Self { loc: Loc::from(cwd.clone()), ..Default::default() } }
|
||||
}
|
||||
|
||||
impl Folder {
|
||||
|
|
@ -101,7 +101,7 @@ impl Folder {
|
|||
|
||||
let new = self.cursor / limit;
|
||||
if mem::replace(&mut self.page, new) != new || force {
|
||||
ManagerProxy::update_paged_by(new, &self.cwd);
|
||||
ManagerProxy::update_paged_by(new, &self.loc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ impl FilesSorter {
|
|||
|
||||
let by_alphabetical = |a: &File, b: &File| {
|
||||
if self.sensitive {
|
||||
return self.cmp(a.name(), b.name(), self.promote(a, b));
|
||||
self.cmp(a.name(), b.name(), self.promote(a, b))
|
||||
} else {
|
||||
self.cmp(a.name().to_ascii_uppercase(), b.name().to_ascii_uppercase(), self.promote(a, b))
|
||||
}
|
||||
|
||||
self.cmp(a.name().to_ascii_uppercase(), b.name().to_ascii_uppercase(), self.promote(a, b))
|
||||
};
|
||||
|
||||
match self.by {
|
||||
|
|
|
|||
|
|
@ -1,46 +1 @@
|
|||
-- TODO: remove this after 0.3.0 release
|
||||
|
||||
Manager = {}
|
||||
Folder = {}
|
||||
File = {}
|
||||
|
||||
local function warn(name)
|
||||
ya.notify {
|
||||
title = "Deprecated API",
|
||||
content = string.format(
|
||||
[[The `%s` global variable has been removed in Yazi v0.3, please remove it from your `init.lua`.
|
||||
|
||||
See https://github.com/sxyazi/yazi/pull/1257 for details.]],
|
||||
name
|
||||
),
|
||||
timeout = 20,
|
||||
level = "warn",
|
||||
}
|
||||
end
|
||||
|
||||
local b1, b2, b3 = false, false, false
|
||||
function __yazi_check_and_warn_deprecated_api()
|
||||
if not b1 then
|
||||
for _ in pairs(Manager) do
|
||||
b1 = true
|
||||
warn("Manager")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not b2 then
|
||||
for _ in pairs(Folder) do
|
||||
b2 = true
|
||||
warn("Folder")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not b3 then
|
||||
for _ in pairs(File) do
|
||||
b3 = true
|
||||
warn("File")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
--
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ function Root:render()
|
|||
for _, child in ipairs(self._children) do
|
||||
children = ya.list_merge(children, ya.render_with(child))
|
||||
end
|
||||
__yazi_check_and_warn_deprecated_api() -- TODO: remove this after 0.3.0 release
|
||||
return children
|
||||
end
|
||||
|
||||
|
|
|
|||
2
yazi-plugin/src/external/fd.rs
vendored
2
yazi-plugin/src/external/fd.rs
vendored
|
|
@ -29,7 +29,7 @@ pub fn fd(opt: FdOpt) -> Result<UnboundedReceiver<File>> {
|
|||
|
||||
tokio::spawn(async move {
|
||||
while let Ok(Some(line)) = it.next_line().await {
|
||||
if let Ok(file) = File::from(opt.cwd.join(line)).await {
|
||||
if let Ok(file) = File::from_search(&opt.cwd, opt.cwd.join(line)).await {
|
||||
tx.send(file).ok();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
yazi-plugin/src/external/rg.rs
vendored
2
yazi-plugin/src/external/rg.rs
vendored
|
|
@ -28,7 +28,7 @@ pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
|
|||
|
||||
tokio::spawn(async move {
|
||||
while let Ok(Some(line)) = it.next_line().await {
|
||||
if let Ok(file) = File::from(opt.cwd.join(line)).await {
|
||||
if let Ok(file) = File::from_search(&opt.cwd, opt.cwd.join(line)).await {
|
||||
tx.send(file).ok();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use mlua::{AnyUserData, Lua, Table, UserDataFields, UserDataMethods, UserDataRef, UserDataRegistry};
|
||||
use yazi_config::THEME;
|
||||
use yazi_shared::fs::Loc;
|
||||
|
||||
use crate::{bindings::{Cast, Icon}, cha::Cha, url::Url};
|
||||
|
||||
|
|
@ -48,14 +49,11 @@ impl File {
|
|||
lua.globals().raw_set(
|
||||
"File",
|
||||
lua.create_function(|lua, t: Table| {
|
||||
// FIXME
|
||||
todo!();
|
||||
Ok(())
|
||||
// Self::cast(lua, yazi_shared::fs::File {
|
||||
// cha: t.raw_get::<_, AnyUserData>("cha")?.take()?,
|
||||
// url: t.raw_get::<_, AnyUserData>("url")?.take()?,
|
||||
// ..Default::default()
|
||||
// })
|
||||
Self::cast(lua, yazi_shared::fs::File {
|
||||
loc: Loc::from(t.raw_get::<_, AnyUserData>("url")?.take()?),
|
||||
cha: t.raw_get::<_, AnyUserData>("cha")?.take()?,
|
||||
..Default::default()
|
||||
})
|
||||
})?,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ pub fn install(lua: &Lua) -> mlua::Result<()> {
|
|||
),
|
||||
(
|
||||
"read_dir",
|
||||
lua.create_async_function(|lua, (url, options): (UrlRef, Table)| async move {
|
||||
lua.create_async_function(|lua, (dir, options): (UrlRef, Table)| async move {
|
||||
let glob = if let Ok(s) = options.raw_get::<_, mlua::String>("glob") {
|
||||
Some(
|
||||
GlobBuilder::new(s.to_str()?)
|
||||
|
|
@ -73,7 +73,7 @@ pub fn install(lua: &Lua) -> mlua::Result<()> {
|
|||
let limit = options.raw_get("limit").unwrap_or(usize::MAX);
|
||||
let resolve = options.raw_get("resolve").unwrap_or(false);
|
||||
|
||||
let mut it = match fs::read_dir(&*url).await {
|
||||
let mut it = match fs::read_dir(&*dir).await {
|
||||
Ok(it) => it,
|
||||
Err(e) => return (Value::Nil, e.raw_os_error()).into_lua_multi(lua),
|
||||
};
|
||||
|
|
@ -97,7 +97,6 @@ pub fn install(lua: &Lua) -> mlua::Result<()> {
|
|||
} else {
|
||||
yazi_shared::fs::File::from_dummy(url, next.file_type().await.ok())
|
||||
};
|
||||
|
||||
files.push(File::cast(lua, file)?);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
use std::{cell::Cell, ffi::OsStr, fs::{FileType, Metadata}, ops::Deref};
|
||||
use std::{cell::Cell, ffi::OsStr, fs::{FileType, Metadata}, ops::Deref, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::fs;
|
||||
|
||||
use super::Loc;
|
||||
use crate::{fs::{Cha, ChaKind, Url}, theme::IconCache};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct File {
|
||||
pub loc: Loc,
|
||||
pub cha: Cha,
|
||||
pub url: Url,
|
||||
pub link_to: Option<Url>,
|
||||
pub icon: Cell<IconCache>,
|
||||
}
|
||||
|
|
@ -32,13 +33,45 @@ impl File {
|
|||
Ok(Self::from_meta(url, meta).await)
|
||||
}
|
||||
|
||||
pub async fn from_meta(url: Url, mut meta: Metadata) -> Self {
|
||||
#[inline]
|
||||
pub async fn from_search(cwd: &Url, url: Url) -> Result<Self> {
|
||||
let loc = Loc::from_search(cwd, url);
|
||||
let meta = fs::symlink_metadata(loc.url()).await?;
|
||||
Ok(Self::from_loc(loc, meta).await)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn from_meta(url: Url, meta: Metadata) -> Self {
|
||||
Self::from_loc(Loc::from(url), meta).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_dummy(url: Url, ft: Option<FileType>) -> Self {
|
||||
Self {
|
||||
loc: Loc::from(url),
|
||||
cha: ft.map_or_else(Cha::dummy, Cha::from),
|
||||
link_to: None,
|
||||
icon: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rebase(&self, parent: &Url) -> Self {
|
||||
Self {
|
||||
loc: self.loc.rebase(parent),
|
||||
cha: self.cha,
|
||||
link_to: self.link_to.clone(),
|
||||
icon: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_loc(loc: Loc, mut meta: Metadata) -> Self {
|
||||
let mut ck = ChaKind::empty();
|
||||
let (is_link, mut link_to) = (meta.is_symlink(), None);
|
||||
|
||||
if is_link {
|
||||
meta = fs::metadata(&url).await.unwrap_or(meta);
|
||||
link_to = fs::read_link(&url).await.map(Url::from).ok();
|
||||
meta = fs::metadata(loc.url()).await.unwrap_or(meta);
|
||||
link_to = fs::read_link(loc.url()).await.map(Url::from).ok();
|
||||
}
|
||||
|
||||
if is_link && meta.is_symlink() {
|
||||
|
|
@ -48,7 +81,7 @@ impl File {
|
|||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if url.is_hidden() {
|
||||
if loc.url().is_hidden() {
|
||||
ck |= ChaKind::HIDDEN;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
|
|
@ -59,26 +92,27 @@ impl File {
|
|||
}
|
||||
}
|
||||
|
||||
Self { cha: Cha::from(meta).with_kind(ck), url, link_to, icon: Default::default() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_dummy(url: Url, ft: Option<FileType>) -> Self {
|
||||
Self { cha: ft.map_or_else(Cha::dummy, Cha::from), url: url.to_owned(), ..Default::default() }
|
||||
Self { loc, cha: Cha::from(meta).with_kind(ck), link_to, icon: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
// --- Url
|
||||
// --- Loc
|
||||
#[inline]
|
||||
pub fn url_owned(&self) -> Url { self.url.clone() }
|
||||
pub fn url(&self) -> &Url { self.loc.url() }
|
||||
|
||||
#[inline]
|
||||
pub fn name(&self) -> Option<&OsStr> { self.url.file_name() }
|
||||
pub fn url_owned(&self) -> Url { self.url().clone() }
|
||||
|
||||
#[inline]
|
||||
pub fn stem(&self) -> Option<&OsStr> { self.url.file_stem() }
|
||||
pub fn urn(&self) -> &Path { self.loc.urn() }
|
||||
|
||||
#[inline]
|
||||
pub fn parent(&self) -> Option<Url> { self.url.parent_url() }
|
||||
pub fn name(&self) -> &OsStr { self.loc.name() }
|
||||
|
||||
#[inline]
|
||||
pub fn stem(&self) -> Option<&OsStr> { self.url().file_stem() }
|
||||
|
||||
#[inline]
|
||||
pub fn parent(&self) -> Option<Url> { self.url().parent_url() }
|
||||
}
|
||||
|
|
|
|||
96
yazi-shared/src/fs/loc.rs
Normal file
96
yazi-shared/src/fs/loc.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
use std::{ffi::OsStr, fmt::{self, Debug, Formatter}, ops::Deref, path::Path};
|
||||
|
||||
use super::Url;
|
||||
|
||||
pub struct Loc {
|
||||
url: Url,
|
||||
urn: *const OsStr,
|
||||
name: *const OsStr,
|
||||
}
|
||||
|
||||
unsafe impl Send for Loc {}
|
||||
|
||||
impl Default for Loc {
|
||||
fn default() -> Self { Self { url: Url::default(), urn: OsStr::new(""), name: OsStr::new("") } }
|
||||
}
|
||||
|
||||
impl Deref for Loc {
|
||||
type Target = Url;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.url }
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Loc {
|
||||
fn as_ref(&self) -> &Path { self.url() }
|
||||
}
|
||||
|
||||
impl Eq for Loc {}
|
||||
|
||||
impl PartialEq for Loc {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.url == other.url && self.urn() == other.urn() && self.name() == other.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Loc {
|
||||
fn clone(&self) -> Self {
|
||||
let url = self.url.clone();
|
||||
let name = url.file_name().unwrap_or(OsStr::new("")) as *const OsStr;
|
||||
let urn = if url.is_search() { self.twin_urn(&url) } else { name };
|
||||
Self { url, urn, name }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Loc {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.debug_struct("Loc")
|
||||
.field("url", &self.url)
|
||||
.field("urn", &self.urn())
|
||||
.field("name", &self.name())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Loc {
|
||||
pub fn from(url: Url) -> Self {
|
||||
let urn = url.file_name().unwrap_or(OsStr::new("")) as *const OsStr;
|
||||
Self { url, urn, name: urn }
|
||||
}
|
||||
|
||||
pub fn from_search(cwd: &Url, url: Url) -> Self {
|
||||
let urn = url.strip_prefix(cwd).unwrap_or(&url).as_os_str() as *const OsStr;
|
||||
let name = url.file_name().unwrap_or(OsStr::new("")) as *const OsStr;
|
||||
Self { url, urn, name }
|
||||
}
|
||||
|
||||
pub fn rebase(&self, parent: &Url) -> Self {
|
||||
let url = parent.join(self.name());
|
||||
let name = url.file_name().unwrap_or(OsStr::new("")) as *const OsStr;
|
||||
let urn = if url.is_search() { self.twin_urn(&url) } else { name };
|
||||
Self { url, urn, name }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn twin_urn<'a>(&self, new: &'a Url) -> &'a OsStr {
|
||||
let total = new.components().count();
|
||||
let take = self.urn().components().count();
|
||||
|
||||
let mut it = new.components();
|
||||
for _ in 0..total - take {
|
||||
it.next().unwrap();
|
||||
}
|
||||
|
||||
it.as_path().as_os_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Loc {
|
||||
#[inline]
|
||||
pub fn url(&self) -> &Url { &self.url }
|
||||
|
||||
#[inline]
|
||||
pub fn urn(&self) -> &Path { Path::new(unsafe { &*self.urn }) }
|
||||
|
||||
#[inline]
|
||||
pub fn name(&self) -> &OsStr { unsafe { &*self.name } }
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
mod cha;
|
||||
mod file;
|
||||
mod fns;
|
||||
mod loc;
|
||||
mod op;
|
||||
mod path;
|
||||
mod url;
|
||||
|
|
@ -8,6 +9,7 @@ mod url;
|
|||
pub use cha::*;
|
||||
pub use file::*;
|
||||
pub use fns::*;
|
||||
pub use loc::*;
|
||||
pub use op::*;
|
||||
pub use path::*;
|
||||
pub use url::*;
|
||||
|
|
|
|||
|
|
@ -48,51 +48,28 @@ impl FilesOp {
|
|||
}
|
||||
|
||||
pub fn chroot(&self, new: &Url) -> Self {
|
||||
let old = self.url();
|
||||
macro_rules! new {
|
||||
($url:expr) => {{ new.join($url.strip_prefix(old).unwrap()) }};
|
||||
($url:expr) => {{ new.join($url.file_name().unwrap()) }};
|
||||
}
|
||||
macro_rules! files {
|
||||
($files:expr) => {{
|
||||
$files
|
||||
.iter()
|
||||
.map(|file| {
|
||||
let mut f = file.clone();
|
||||
// FIXME
|
||||
todo!();
|
||||
// f.url = new!(f.url);
|
||||
f
|
||||
})
|
||||
.collect()
|
||||
}};
|
||||
($files:expr) => {{ $files.iter().map(|f| f.rebase(new)).collect() }};
|
||||
}
|
||||
macro_rules! map {
|
||||
($map:expr) => {{
|
||||
$map
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let mut f = v.clone();
|
||||
// FIXME
|
||||
todo!();
|
||||
// f.url = new!(f.url);
|
||||
(new!(k), f)
|
||||
})
|
||||
.collect()
|
||||
}};
|
||||
($map:expr) => {{ $map.iter().map(|(u, f)| (new!(u), f.rebase(new))).collect() }};
|
||||
}
|
||||
|
||||
let u = new.clone();
|
||||
let n = new.clone();
|
||||
match self {
|
||||
Self::Full(_, files, mtime) => Self::Full(u, files!(files), *mtime),
|
||||
Self::Part(_, files, ticket) => Self::Part(u, files!(files), *ticket),
|
||||
Self::Done(_, mtime, ticket) => Self::Done(u, *mtime, *ticket),
|
||||
Self::Size(_, map) => Self::Size(u, map.iter().map(|(k, v)| (new!(k), *v)).collect()),
|
||||
Self::IOErr(_, err) => Self::IOErr(u, *err),
|
||||
Self::Full(_, files, mtime) => Self::Full(n, files!(files), *mtime),
|
||||
Self::Part(_, files, ticket) => Self::Part(n, files!(files), *ticket),
|
||||
Self::Done(_, mtime, ticket) => Self::Done(n, *mtime, *ticket),
|
||||
Self::Size(_, map) => Self::Size(n, map.iter().map(|(u, &s)| (new!(u), s)).collect()),
|
||||
Self::IOErr(_, err) => Self::IOErr(n, *err),
|
||||
|
||||
Self::Creating(_, files) => Self::Creating(u, files!(files)),
|
||||
Self::Deleting(_, urls) => Self::Deleting(u, urls.iter().map(|u| new!(u)).collect()),
|
||||
Self::Updating(_, map) => Self::Updating(u, map!(map)),
|
||||
Self::Upserting(_, map) => Self::Upserting(u, map!(map)),
|
||||
Self::Creating(_, files) => Self::Creating(n, files!(files)),
|
||||
Self::Deleting(_, urls) => Self::Deleting(n, urls.iter().map(|u| new!(u)).collect()),
|
||||
Self::Updating(_, map) => Self::Updating(n, map!(map)),
|
||||
Self::Upserting(_, map) => Self::Upserting(n, map!(map)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ impl Url {
|
|||
let url = Self::from(self.path.join(path));
|
||||
match self.scheme {
|
||||
UrlScheme::Regular => url,
|
||||
UrlScheme::Search => url,
|
||||
UrlScheme::Search => url.into_search(),
|
||||
UrlScheme::Archive => url.into_archive(),
|
||||
}
|
||||
}
|
||||
|
|
@ -161,12 +161,14 @@ impl Url {
|
|||
}
|
||||
|
||||
impl Url {
|
||||
// --- Scheme
|
||||
// --- Regular
|
||||
#[inline]
|
||||
pub fn is_regular(&self) -> bool { self.scheme == UrlScheme::Regular }
|
||||
|
||||
#[inline]
|
||||
pub fn to_regular(&self) -> Self { self.clone().into_regular() }
|
||||
pub fn to_regular(&self) -> Self {
|
||||
Self { scheme: UrlScheme::Regular, path: self.path.clone(), frag: String::new() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_regular(mut self) -> Self {
|
||||
|
|
@ -175,16 +177,19 @@ impl Url {
|
|||
self
|
||||
}
|
||||
|
||||
// --- Search
|
||||
#[inline]
|
||||
pub fn is_search(&self) -> bool { self.scheme == UrlScheme::Search }
|
||||
|
||||
#[inline]
|
||||
pub fn to_search(&self, frag: String) -> Self { self.clone().into_search(frag) }
|
||||
pub fn to_search(&self, frag: &str) -> Self {
|
||||
Self { scheme: UrlScheme::Search, path: self.path.clone(), frag: frag.to_owned() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_search(mut self, frag: String) -> Self {
|
||||
pub fn into_search(mut self) -> Self {
|
||||
self.scheme = UrlScheme::Search;
|
||||
self.frag = frag;
|
||||
self.frag = String::new();
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +197,9 @@ impl Url {
|
|||
pub fn is_archive(&self) -> bool { self.scheme == UrlScheme::Archive }
|
||||
|
||||
#[inline]
|
||||
pub fn to_archive(&self) -> Self { self.clone().into_archive() }
|
||||
pub fn to_archive(&self) -> Self {
|
||||
Self { scheme: UrlScheme::Archive, path: self.path.clone(), frag: String::new() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_archive(mut self) -> Self {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue