mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
116 lines
2.7 KiB
Rust
116 lines
2.7 KiB
Rust
use std::{borrow::Cow, env::{current_dir, set_current_dir}, ops::Deref, path::{Path, PathBuf}, sync::{Arc, atomic::{AtomicBool, Ordering}}};
|
|
|
|
use arc_swap::ArcSwap;
|
|
use yazi_shared::url::{AsUrl, Url, UrlBuf, UrlLike};
|
|
use yazi_shim::cell::RoCell;
|
|
|
|
use crate::{FsUrl, Xdg, path::clean_url};
|
|
|
|
pub static CWD: RoCell<Cwd> = RoCell::new();
|
|
|
|
pub struct Cwd(ArcSwap<UrlBuf>);
|
|
|
|
impl Deref for Cwd {
|
|
type Target = ArcSwap<UrlBuf>;
|
|
|
|
fn deref(&self) -> &Self::Target { &self.0 }
|
|
}
|
|
|
|
impl Default for Cwd {
|
|
fn default() -> Self {
|
|
let u = std::env::var_os("PWD")
|
|
.map(PathBuf::from)
|
|
.filter(|p| p.is_absolute())
|
|
.map(clean_url)
|
|
.or_else(|| current_dir().ok().map(UrlBuf::from))
|
|
.expect("failed to get current working directory");
|
|
|
|
Self(ArcSwap::new(Arc::new(u)))
|
|
}
|
|
}
|
|
|
|
impl Cwd {
|
|
pub fn path(&self) -> PathBuf { self.0.load().as_url().unified_path().into_owned() }
|
|
|
|
pub fn set(&self, url: &UrlBuf, callback: fn()) -> bool {
|
|
if !url.is_absolute() {
|
|
return false;
|
|
} else if self.0.load().as_ref() == url {
|
|
return false;
|
|
}
|
|
|
|
self.0.store(Arc::new(url.clone()));
|
|
Self::sync_cwd(callback);
|
|
|
|
true
|
|
}
|
|
|
|
pub fn ensure(url: Url) -> Cow<Path> {
|
|
use std::{io::ErrorKind::{AlreadyExists, NotADirectory, NotFound}, path::Component as C};
|
|
|
|
let Some(cache) = url.cache() else {
|
|
return url.unified_path();
|
|
};
|
|
|
|
if !matches!(std::fs::create_dir_all(&cache), Err(e) if e.kind() == NotADirectory || e.kind() == AlreadyExists)
|
|
{
|
|
return cache.into();
|
|
}
|
|
|
|
let latter = cache.strip_prefix(Xdg::temp_dir()).expect("under temp dir");
|
|
let mut it = latter.components().peekable();
|
|
while it.peek() == Some(&C::CurDir) {
|
|
it.next().unwrap();
|
|
}
|
|
|
|
let mut count = 0;
|
|
for c in it {
|
|
match c {
|
|
C::Prefix(_) | C::RootDir | C::ParentDir => return cache.into(),
|
|
C::CurDir | C::Normal(_) => count += 1,
|
|
}
|
|
}
|
|
|
|
for n in (0..count).rev() {
|
|
let mut it = cache.components();
|
|
for _ in 0..n {
|
|
it.next_back().unwrap();
|
|
}
|
|
match std::fs::remove_file(it.as_path()) {
|
|
Ok(_) => break,
|
|
Err(e) if e.kind() == NotFound => break,
|
|
Err(_) => {}
|
|
}
|
|
}
|
|
|
|
std::fs::create_dir_all(&cache).ok();
|
|
cache.into()
|
|
}
|
|
|
|
fn sync_cwd(callback: fn()) {
|
|
static SYNCING: AtomicBool = AtomicBool::new(false);
|
|
if SYNCING.swap(true, Ordering::Relaxed) {
|
|
return;
|
|
}
|
|
|
|
tokio::task::spawn_blocking(move || {
|
|
let cwd = CWD.load();
|
|
let path = Self::ensure(cwd.as_url());
|
|
|
|
set_current_dir(&path).ok();
|
|
let cur = current_dir().unwrap_or_default();
|
|
|
|
unsafe { std::env::set_var("PWD", path.as_ref()) }
|
|
SYNCING.store(false, Ordering::Relaxed);
|
|
|
|
let cwd = CWD.load();
|
|
let path = Self::ensure(cwd.as_url());
|
|
if cur != path {
|
|
set_current_dir(&path).ok();
|
|
unsafe { std::env::set_var("PWD", path.as_ref()) }
|
|
}
|
|
|
|
callback();
|
|
});
|
|
}
|
|
}
|