mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
cf95280ed8
commit
753e005664
15 changed files with 76 additions and 67 deletions
|
|
@ -110,8 +110,9 @@ impl Manager {
|
|||
};
|
||||
|
||||
let find = |folder: Option<&Folder>| {
|
||||
folder.is_some_and(|folder| {
|
||||
p == *folder.loc && folder.files.iter().any(|f| f.is_dir() && url == f.url())
|
||||
folder.filter(|&f| p == *f.loc).is_some_and(|folder| {
|
||||
let loc = url.to_loc(&folder.loc);
|
||||
folder.files.iter().any(|f| f.is_dir() && f.urn() == loc.urn())
|
||||
})
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ impl Manager {
|
|||
};
|
||||
|
||||
let mut ops = vec![opt.op];
|
||||
for u in LINKED.read().from_dir(ops[0].url()) {
|
||||
for u in LINKED.read().from_dir(ops[0].cwd()) {
|
||||
ops.push(ops[0].chroot(u));
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ impl Manager {
|
|||
}
|
||||
|
||||
fn update_tab(tab: &mut Tab, op: Cow<FilesOp>, tasks: &Tasks) {
|
||||
let url = op.url();
|
||||
let url = op.cwd();
|
||||
tab.selected.apply_op(&op);
|
||||
|
||||
if url == tab.cwd().url() {
|
||||
|
|
@ -97,7 +97,7 @@ impl Manager {
|
|||
}
|
||||
|
||||
fn update_hovered(tab: &mut Tab, op: Cow<FilesOp>) {
|
||||
let url = op.url();
|
||||
let url = op.cwd();
|
||||
let folder = tab.history.entry(url.clone()).or_insert_with(|| Folder::from(url));
|
||||
|
||||
let foreign = matches!(op, Cow::Borrowed(_));
|
||||
|
|
@ -115,7 +115,7 @@ impl Manager {
|
|||
|(p, pp)| matches!(*op, FilesOp::Deleting(ref parent, ref urls) if *parent == pp && urls.contains(p)),
|
||||
);
|
||||
|
||||
let folder = tab.history.entry(op.url().clone()).or_insert_with(|| Folder::from(op.url()));
|
||||
let folder = tab.history.entry(op.cwd().clone()).or_insert_with(|| Folder::from(op.cwd()));
|
||||
let hovered = folder.hovered().filter(|_| folder.tracing).map(|h| h.urn_owned());
|
||||
if folder.update(op.into_owned()) {
|
||||
folder.repos(hovered.as_ref().map(|u| u._deref()));
|
||||
|
|
|
|||
|
|
@ -60,25 +60,22 @@ impl Watcher {
|
|||
}
|
||||
|
||||
pub(super) fn trigger_dirs(&self, folders: &[&Folder]) {
|
||||
let todo: Vec<_> = folders
|
||||
.iter()
|
||||
.filter(|&f| f.loc.is_regular())
|
||||
.map(|&f| (f.loc.url().clone(), f.cha))
|
||||
.collect();
|
||||
let todo: Vec<_> =
|
||||
folders.iter().filter(|&f| f.loc.is_regular()).map(|&f| (f.loc.url_owned(), f.cha)).collect();
|
||||
if todo.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
async fn go(url: Url, cha: Cha) {
|
||||
let Some(cha) = Files::assert_stale(&url, cha).await else { return };
|
||||
async fn go(cwd: Url, cha: Cha) {
|
||||
let Some(cha) = Files::assert_stale(&cwd, cha).await else { return };
|
||||
|
||||
if let Ok(files) = Files::from_dir_bulk(&url).await {
|
||||
FilesOp::Full(url, files, cha).emit();
|
||||
if let Ok(files) = Files::from_dir_bulk(&cwd).await {
|
||||
FilesOp::Full(cwd, files, cha).emit();
|
||||
}
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
futures::future::join_all(todo.into_iter().map(|(url, cha)| go(url, cha))).await;
|
||||
futures::future::join_all(todo.into_iter().map(|(cwd, cha)| go(cwd, cha))).await;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ impl Tab {
|
|||
|
||||
// Take parent to history
|
||||
if let Some(rep) = self.parent.take() {
|
||||
self.history.insert(rep.loc.url().clone(), rep);
|
||||
self.history.insert(rep.loc.url_owned(), rep);
|
||||
}
|
||||
|
||||
// Current
|
||||
let rep = self.history.remove_or(&opt.target);
|
||||
let rep = mem::replace(&mut self.current, rep);
|
||||
if rep.loc.is_regular() {
|
||||
self.history.insert(rep.loc.url().clone(), rep);
|
||||
self.history.insert(rep.loc.url_owned(), rep);
|
||||
}
|
||||
|
||||
// Parent
|
||||
|
|
|
|||
|
|
@ -38,29 +38,29 @@ impl Preview {
|
|||
}
|
||||
|
||||
pub fn go_folder(&mut self, file: File, dir: Option<Cha>, force: bool) {
|
||||
let (cha, url) = (file.cha, file.url_owned());
|
||||
let (cha, cwd) = (file.cha, file.url_owned());
|
||||
self.go(file, MIME_DIR, force);
|
||||
|
||||
if self.content_unchanged(&url, cha) {
|
||||
if self.content_unchanged(&cwd, cha) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.folder_loader.take().map(|h| h.abort());
|
||||
self.folder_loader = Some(tokio::spawn(async move {
|
||||
let Some(new) = Files::assert_stale(&url, dir.unwrap_or(Cha::dummy())).await else {
|
||||
let Some(new) = Files::assert_stale(&cwd, dir.unwrap_or(Cha::dummy())).await else {
|
||||
return;
|
||||
};
|
||||
let Ok(rx) = Files::from_dir(&url).await else { return };
|
||||
let Ok(rx) = Files::from_dir(&cwd).await else { return };
|
||||
|
||||
let stream =
|
||||
UnboundedReceiverStream::new(rx).chunks_timeout(50000, Duration::from_millis(500));
|
||||
pin!(stream);
|
||||
|
||||
let ticket = FilesOp::prepare(&url);
|
||||
let ticket = FilesOp::prepare(&cwd);
|
||||
while let Some(chunk) = stream.next().await {
|
||||
FilesOp::Part(url.clone(), chunk, ticket).emit();
|
||||
FilesOp::Part(cwd.clone(), chunk, ticket).emit();
|
||||
}
|
||||
FilesOp::Done(url, new, ticket).emit();
|
||||
FilesOp::Done(cwd, new, ticket).emit();
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,10 +112,10 @@ impl Selected {
|
|||
};
|
||||
|
||||
if !removal.is_empty() {
|
||||
self.remove_many(&removal, !op.url().is_search());
|
||||
self.remove_many(&removal, !op.cwd().is_search());
|
||||
}
|
||||
if !addition.is_empty() {
|
||||
self.add_many(&addition, !op.url().is_search());
|
||||
self.add_many(&addition, !op.cwd().is_search());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.loc.url().clone()));
|
||||
reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.loc.url_owned()));
|
||||
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()));
|
||||
|
|
|
|||
|
|
@ -96,19 +96,19 @@ impl Files {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn assert_stale(url: &Url, cha: Cha) -> Option<Cha> {
|
||||
match fs::metadata(url).await.map(Cha::from) {
|
||||
pub async fn assert_stale(cwd: &Url, cha: Cha) -> Option<Cha> {
|
||||
match fs::metadata(cwd).await.map(Cha::from) {
|
||||
Ok(c) if !c.is_dir() => {
|
||||
// FIXME: use `ErrorKind::NotADirectory` instead once it gets stabilized
|
||||
FilesOp::IOErr(url.clone(), std::io::ErrorKind::AlreadyExists).emit();
|
||||
FilesOp::IOErr(cwd.clone(), std::io::ErrorKind::AlreadyExists).emit();
|
||||
}
|
||||
Ok(c) if c.hits(cha) => {}
|
||||
Ok(c) => return Some(c),
|
||||
Err(e) => {
|
||||
if maybe_exists(url).await {
|
||||
FilesOp::IOErr(url.clone(), e.kind()).emit();
|
||||
} else if let Some(p) = url.parent_url() {
|
||||
FilesOp::Deleting(p, vec![url.clone()]).emit();
|
||||
if maybe_exists(cwd).await {
|
||||
FilesOp::IOErr(cwd.clone(), e.kind()).emit();
|
||||
} else if let Some(p) = cwd.parent_url() {
|
||||
FilesOp::Deleting(p, vec![cwd.clone()]).emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -280,10 +280,9 @@ impl Files {
|
|||
}
|
||||
|
||||
let (mut hidden, mut items) = if let Some(filter) = &self.filter {
|
||||
files.into_iter().partition(|(_, f)| {
|
||||
(f.is_hidden() && !self.show_hidden)
|
||||
|| !f.url().file_name().is_some_and(|s| filter.matches(s))
|
||||
})
|
||||
files
|
||||
.into_iter()
|
||||
.partition(|(_, f)| (f.is_hidden() && !self.show_hidden) || !filter.matches(f.name()))
|
||||
} else if self.show_hidden {
|
||||
(HashMap::new(), files)
|
||||
} else {
|
||||
|
|
@ -330,10 +329,9 @@ impl Files {
|
|||
|
||||
fn split_files(&self, files: impl IntoIterator<Item = File>) -> (Vec<File>, Vec<File>) {
|
||||
if let Some(filter) = &self.filter {
|
||||
files.into_iter().partition(|f| {
|
||||
(f.is_hidden() && !self.show_hidden)
|
||||
|| !f.url().file_name().is_some_and(|s| filter.matches(s))
|
||||
})
|
||||
files
|
||||
.into_iter()
|
||||
.partition(|f| (f.is_hidden() && !self.show_hidden) || !filter.matches(f.name()))
|
||||
} else if self.show_hidden {
|
||||
(vec![], files.into_iter().collect())
|
||||
} else {
|
||||
|
|
|
|||
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_search(&opt.cwd, opt.cwd.join(line)).await {
|
||||
if let Ok(file) = File::from_search_item(&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_search(&opt.cwd, opt.cwd.join(line)).await {
|
||||
if let Ok(file) = File::from_search_item(&opt.cwd, opt.cwd.join(line)).await {
|
||||
tx.send(file).ok();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ impl File {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn from_search(cwd: &Url, url: Url) -> Result<Self> {
|
||||
let loc = Loc::from_search(cwd, url);
|
||||
pub async fn from_search_item(cwd: &Url, url: Url) -> Result<Self> {
|
||||
let loc = Loc::from_search_item(cwd, url);
|
||||
let meta = fs::symlink_metadata(loc.url()).await?;
|
||||
Ok(Self::from_loc(loc, meta).await)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ 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 };
|
||||
let urn = if url.is_search_item() { self.twin_urn(&url) } else { name };
|
||||
Self { url, urn, name }
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ impl Loc {
|
|||
Self { url, urn, name: urn }
|
||||
}
|
||||
|
||||
pub fn from_search(cwd: &Url, url: Url) -> Self {
|
||||
pub fn from_search_item(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 }
|
||||
|
|
@ -66,7 +66,7 @@ impl Loc {
|
|||
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 };
|
||||
let urn = if url.is_search_item() { self.twin_urn(&url) } else { name };
|
||||
Self { url, urn, name }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,18 +21,18 @@ pub enum FilesOp {
|
|||
|
||||
impl FilesOp {
|
||||
#[inline]
|
||||
pub fn url(&self) -> &Url {
|
||||
pub fn cwd(&self) -> &Url {
|
||||
match self {
|
||||
Self::Full(url, ..) => url,
|
||||
Self::Part(url, ..) => url,
|
||||
Self::Done(url, ..) => url,
|
||||
Self::Size(url, _) => url,
|
||||
Self::IOErr(url, _) => url,
|
||||
Self::Full(u, ..) => u,
|
||||
Self::Part(u, ..) => u,
|
||||
Self::Done(u, ..) => u,
|
||||
Self::Size(u, _) => u,
|
||||
Self::IOErr(u, _) => u,
|
||||
|
||||
Self::Creating(url, _) => url,
|
||||
Self::Deleting(url, _) => url,
|
||||
Self::Updating(url, _) => url,
|
||||
Self::Upserting(url, _) => url,
|
||||
Self::Creating(u, _) => u,
|
||||
Self::Deleting(u, _) => u,
|
||||
Self::Updating(u, _) => u,
|
||||
Self::Upserting(u, _) => u,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,9 +41,9 @@ impl FilesOp {
|
|||
emit!(Call(Cmd::new("update_files").with_any("op", self), Layer::Manager));
|
||||
}
|
||||
|
||||
pub fn prepare(url: &Url) -> u64 {
|
||||
pub fn prepare(cwd: &Url) -> u64 {
|
||||
let ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed);
|
||||
Self::Part(url.clone(), vec![], ticket).emit();
|
||||
Self::Part(cwd.clone(), vec![], ticket).emit();
|
||||
ticket
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ use std::{ffi::{OsStr, OsString}, fmt::{Debug, Display, Formatter}, ops::{Deref,
|
|||
use percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::Loc;
|
||||
|
||||
const ENCODE_SET: &AsciiSet = &CONTROLS.add(b'#');
|
||||
|
||||
#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
|
|
@ -161,6 +163,14 @@ impl Url {
|
|||
pub fn is_hidden(&self) -> bool {
|
||||
self.file_name().map_or(false, |s| s.as_encoded_bytes().starts_with(b"."))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_loc(&self, cwd: &Url) -> Loc { self.clone().into_loc(cwd) }
|
||||
|
||||
#[inline]
|
||||
pub fn into_loc(self, cwd: &Url) -> Loc {
|
||||
if self.is_search_item() { Loc::from_search_item(cwd, self) } else { Loc::from(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Url {
|
||||
|
|
@ -189,6 +199,9 @@ impl Url {
|
|||
Self { scheme: UrlScheme::Search, path: self.path.clone(), frag: frag.to_owned() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_search_item(&self) -> bool { self.scheme == UrlScheme::SearchItem }
|
||||
|
||||
#[inline]
|
||||
pub fn into_search_item(mut self) -> Self {
|
||||
self.scheme = UrlScheme::SearchItem;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue