This commit is contained in:
sxyazi 2023-11-08 10:07:52 +08:00
parent 0932752234
commit 8100598224
No known key found for this signature in database
11 changed files with 117 additions and 35 deletions

1
Cargo.lock generated
View file

@ -2565,6 +2565,7 @@ dependencies = [
"futures",
"libc",
"parking_lot",
"percent-encoding",
"ratatui",
"regex",
"tokio",

View file

@ -21,7 +21,6 @@ pub enum Event {
Call(Vec<Exec>, KeymapLayer),
// Manager
Cd(Url),
Refresh,
Files(FilesOp),
Pages(usize),
@ -74,9 +73,6 @@ macro_rules! emit {
$crate::Event::Call($exec, $layer).emit();
};
(Cd($url:expr)) => {
$crate::Event::Cd($url).emit();
};
(Files($op:expr)) => {
$crate::Event::Files($op).emit();
};

View file

@ -1,4 +1,4 @@
use std::process::Stdio;
use std::{path::Path, process::Stdio};
use anyhow::{bail, Result};
use tokio::process::Command;
@ -15,8 +15,8 @@ pub async fn fzf(opt: FzfOpt) -> Result<Url> {
let output = child.wait_with_output().await?;
let selected = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !selected.is_empty() {
return Ok(Url::from(selected));
}
if selected.is_empty() {
bail!("No match")
}
return Ok(Url::from(Path::new(&opt.cwd).join(selected)));
}

View file

@ -3,15 +3,14 @@ use std::{mem, time::Duration};
use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::keymap::{Exec, KeymapLayer};
use yazi_shared::{Debounce, InputError, Url};
use yazi_shared::{expand_path, Debounce, InputError, Url};
use crate::{emit, files::{File, FilesOp}, input::InputOpt, tab::Tab};
impl Tab {
// TODO: change to sync, and remove `Event::Cd`
pub fn cd(&mut self, mut target: Url) -> bool {
let mut hovered = None;
if let (false, Some(parent)) = (target.was_dir(), target.parent_url()) {
if let (false, Some(parent)) = (target.pop_dir(), target.parent_url()) {
emit!(Files(FilesOp::Creating(parent.clone(), File::from_dummy(target.clone()).into_map())));
hovered = Some(target);
target = parent;
@ -68,7 +67,10 @@ impl Tab {
while let Some(result) = rx.next().await {
match result {
Ok(s) => {
emit!(Cd(Url::from(s.trim())));
emit!(Call(
Exec::call("cd", vec![expand_path(s).to_string_lossy().to_string()]).vec(),
KeymapLayer::Manager
));
}
Err(InputError::Completed(before, ticket)) => {
emit!(Call(

View file

@ -1,3 +1,4 @@
use yazi_config::keymap::{Exec, KeymapLayer};
use yazi_shared::Defer;
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, tab::Tab, Event, BLOCKER};
@ -17,7 +18,7 @@ impl Tab {
external::zoxide(ZoxideOpt { cwd }).await
}?;
emit!(Cd(url));
emit!(Call(Exec::call("cd", vec![url.to_string()]).vec(), KeymapLayer::Manager));
Ok::<(), anyhow::Error>(())
});
false

View file

@ -3,6 +3,7 @@ use std::{mem, time::Duration};
use anyhow::bail;
use tokio::pin;
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
use yazi_config::keymap::{Exec, KeymapLayer};
use crate::{emit, external, files::FilesOp, input::InputOpt, tab::Tab};
@ -34,7 +35,10 @@ impl Tab {
let mut first = true;
while let Some(chunk) = rx.next().await {
if first {
emit!(Cd(cwd.clone()));
emit!(Call(
Exec::call("cd", vec![cwd.clone().into_dir().to_string()]).vec(),
KeymapLayer::Manager
));
first = false;
}
emit!(Files(FilesOp::Part(cwd.clone(), ticket, chunk)));

View file

@ -5,7 +5,7 @@ use crossterm::event::KeyEvent;
use tokio::sync::oneshot;
use yazi_config::{keymap::{Exec, Key, KeymapLayer}, BOOT};
use yazi_core::{emit, files::FilesOp, input::InputMode, Ctx, Event};
use yazi_shared::{expand_url, Term};
use yazi_shared::Term;
use crate::{Executor, Logs, Root, Signals};
@ -121,9 +121,6 @@ impl App {
let manager = &mut self.cx.manager;
let tasks = &mut self.cx.tasks;
match event {
Event::Cd(url) => {
manager.active_mut().cd(expand_url(url));
}
Event::Refresh => {
manager.refresh();
}

View file

@ -1,6 +1,6 @@
use yazi_config::{keymap::{Control, Exec, Key, KeymapLayer}, KEYMAP};
use yazi_core::{input::InputMode, tab::FinderCase, Ctx};
use yazi_shared::{optional_bool, Url};
use yazi_shared::{expand_url, optional_bool, Url};
pub(super) struct Executor<'a> {
cx: &'a mut Ctx,
@ -96,7 +96,7 @@ impl<'a> Executor<'a> {
if exec.named.contains_key("interactive") {
self.cx.manager.active_mut().cd_interactive(url)
} else {
self.cx.manager.active_mut().cd(url)
self.cx.manager.active_mut().cd(expand_url(url))
}
}

View file

@ -15,6 +15,7 @@ crossterm = "^0"
futures = "^0"
libc = "^0"
parking_lot = "^0"
percent-encoding = "^2"
ratatui = "^0"
regex = "^1"
tokio = { version = "^1", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "time", "fs" ] }

View file

@ -4,7 +4,7 @@ use tokio::fs;
use crate::Url;
pub fn expand_path(p: impl AsRef<Path>) -> PathBuf {
fn _expand_path(p: &Path) -> PathBuf {
// ${HOME} or $HOME
#[cfg(unix)]
let re = regex::Regex::new(r"\$(?:\{([^}]+)\}|([a-zA-Z\d_]+))").unwrap();
@ -13,7 +13,7 @@ pub fn expand_path(p: impl AsRef<Path>) -> PathBuf {
#[cfg(windows)]
let re = regex::Regex::new(r"%([^%]+)%").unwrap();
let s = p.as_ref().to_string_lossy();
let s = p.to_string_lossy();
let s = re.replace_all(&s, |caps: &regex::Captures| {
let name = caps.get(2).or_else(|| caps.get(1)).unwrap();
env::var(name.as_str()).unwrap_or_else(|_| caps.get(0).unwrap().as_str().to_owned())
@ -37,9 +37,12 @@ pub fn expand_path(p: impl AsRef<Path>) -> PathBuf {
env::current_dir().map_or_else(|_| p.to_path_buf(), |c| c.join(p))
}
#[inline]
pub fn expand_path(p: impl AsRef<Path>) -> PathBuf { _expand_path(p.as_ref()) }
#[inline]
pub fn expand_url(mut u: Url) -> Url {
u.set_path(expand_path(&u));
u.set_path(_expand_path(&u));
u
}

View file

@ -1,5 +1,9 @@
use std::{ffi::{OsStr, OsString}, fmt::{Debug, Formatter}, ops::{Deref, DerefMut}, path::{Path, PathBuf, MAIN_SEPARATOR}};
use percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS};
const ENCODE_SET: &AsciiSet = &CONTROLS.add(b'#');
#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Url {
scheme: UrlScheme,
@ -42,15 +46,41 @@ impl From<&Path> for Url {
}
impl From<String> for Url {
fn from(path: String) -> Self { Self::from(PathBuf::from(path)) }
fn from(path: String) -> Self { Self::from(path.as_str()) }
}
impl From<&String> for Url {
fn from(path: &String) -> Self { Self::from(PathBuf::from(path)) }
fn from(path: &String) -> Self { Self::from(path.as_str()) }
}
impl From<&str> for Url {
fn from(path: &str) -> Self { Self::from(PathBuf::from(path)) }
fn from(mut path: &str) -> Self {
let mut url = Url::default();
match path.split_once("://").map(|(a, b)| (UrlScheme::from(a), b)) {
None => {
url.path = PathBuf::from(path);
return url;
}
Some((UrlScheme::Regular, b)) => {
url.path = PathBuf::from(b);
return url;
}
Some((a, b)) => {
url.scheme = a;
path = b;
}
}
match path.split_once('#') {
None => {
url.path = percent_decode_str(path).decode_utf8_lossy().into_owned().into();
}
Some((a, b)) => {
url.path = percent_decode_str(a).decode_utf8_lossy().into_owned().into();
url.frag = Some(b.to_string()).filter(|s| !s.is_empty());
}
}
url
}
}
impl AsRef<Url> for Url {
@ -65,6 +95,32 @@ impl AsRef<OsStr> for Url {
fn as_ref(&self) -> &OsStr { self.path.as_os_str() }
}
impl ToString for Url {
fn to_string(&self) -> String {
if self.scheme == UrlScheme::Regular {
return self.path.to_string_lossy().to_string();
}
let scheme = match self.scheme {
UrlScheme::Regular => unreachable!(),
UrlScheme::Search => "search://",
UrlScheme::Archive => "archive://",
};
#[cfg(unix)]
let path = {
use std::os::unix::ffi::OsStrExt;
percent_encode(self.path.as_os_str().as_bytes(), ENCODE_SET)
};
#[cfg(windows)]
let path = percent_encode(self.path.to_string_lossy().as_bytes(), ENCODE_SET).to_string();
let frag = self.frag.as_ref().map(|s| format!("#{s}")).unwrap_or_default();
format!("{scheme}{path}{frag}")
}
}
impl Url {
#[inline]
pub fn join(&self, path: impl AsRef<Path>) -> Self {
@ -122,6 +178,17 @@ impl Url {
}
}
#[inline]
pub fn pop_dir(&mut self) -> bool {
if !self.was_dir() {
return false;
}
if let Some(n) = self.path.file_name() {
self.path.set_file_name(n.to_owned());
}
true
}
#[inline]
pub fn into_dir(mut self) -> Self {
if self.was_dir() {
@ -180,3 +247,13 @@ impl Url {
#[inline]
pub fn frag(&self) -> Option<&str> { self.frag.as_deref() }
}
impl From<&str> for UrlScheme {
fn from(value: &str) -> Self {
match value {
"search" => UrlScheme::Search,
"archive" => UrlScheme::Archive,
_ => UrlScheme::Regular,
}
}
}