mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
bbe157bd9f
commit
67fcb484fb
11 changed files with 58 additions and 76 deletions
2
yazi-core/src/external/zoxide.rs
vendored
2
yazi-core/src/external/zoxide.rs
vendored
|
|
@ -20,7 +20,7 @@ pub async fn zoxide(opt: ZoxideOpt) -> Result<Url> {
|
||||||
let selected = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
let selected = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
|
|
||||||
if !selected.is_empty() {
|
if !selected.is_empty() {
|
||||||
return Ok(Url::from(selected).push_slash());
|
return Ok(Url::from(selected));
|
||||||
}
|
}
|
||||||
bail!("No match")
|
bail!("No match")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ impl File {
|
||||||
cm |= ChaMeta::BAD_LINK;
|
cm |= ChaMeta::BAD_LINK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.was_hidden() {
|
if url.is_hidden() {
|
||||||
cm |= ChaMeta::HIDDEN;
|
cm |= ChaMeta::HIDDEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,7 +48,7 @@ impl File {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_dummy(url: Url) -> Self {
|
pub fn from_dummy(url: Url) -> Self {
|
||||||
let cm = if url.was_hidden() { ChaMeta::HIDDEN } else { ChaMeta::empty() };
|
let cm = if url.is_hidden() { ChaMeta::HIDDEN } else { ChaMeta::empty() };
|
||||||
Self { url, cha: Cha::default().with_meta(cm), link_to: None }
|
Self { url, cha: Cha::default().with_meta(cm), link_to: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ use crate::tab::Tab;
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn back(&mut self) -> bool {
|
pub fn back(&mut self) -> bool {
|
||||||
if let Some(url) = self.backstack.shift_backward().cloned() {
|
if let Some(url) = self.backstack.shift_backward().cloned() {
|
||||||
self.cd(url.push_slash());
|
self.cd(url);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn forward(&mut self) -> bool {
|
pub fn forward(&mut self) -> bool {
|
||||||
if let Some(url) = self.backstack.shift_forward().cloned() {
|
if let Some(url) = self.backstack.shift_forward().cloned() {
|
||||||
self.cd(url.push_slash());
|
self.cd(url);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,15 @@
|
||||||
use std::{mem, time::Duration};
|
use std::{mem, time::Duration};
|
||||||
|
|
||||||
use tokio::pin;
|
use tokio::{fs, pin};
|
||||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||||
use yazi_config::keymap::{Exec, KeymapLayer};
|
use yazi_config::keymap::{Exec, KeymapLayer};
|
||||||
use yazi_shared::{expand_path, Debounce, InputError, Url};
|
use yazi_shared::{expand_path, Debounce, InputError, Url};
|
||||||
|
|
||||||
use crate::{emit, files::{File, FilesOp}, input::InputOpt, tab::Tab};
|
use crate::{emit, input::InputOpt, tab::Tab};
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
pub fn cd(&mut self, mut target: Url) -> bool {
|
pub fn cd(&mut self, target: Url) -> bool {
|
||||||
let mut hovered = None;
|
|
||||||
if let (false, Some(parent)) = (target.pop_slash(), target.parent_url()) {
|
|
||||||
emit!(Files(FilesOp::Creating(parent.clone(), File::from_dummy(target.clone()).into_map())));
|
|
||||||
hovered = Some(target);
|
|
||||||
target = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Already in target
|
|
||||||
if self.current.cwd == target {
|
if self.current.cwd == target {
|
||||||
if let Some(h) = hovered {
|
|
||||||
emit!(Hover(h));
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,11 +30,6 @@ impl Tab {
|
||||||
self.parent = Some(self.history_new(&parent));
|
self.parent = Some(self.history_new(&parent));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hover the file
|
|
||||||
if let Some(h) = hovered {
|
|
||||||
emit!(Hover(h));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backstack
|
// Backstack
|
||||||
if target.is_regular() {
|
if target.is_regular() {
|
||||||
self.backstack.push(target.clone());
|
self.backstack.push(target.clone());
|
||||||
|
|
@ -67,8 +51,16 @@ impl Tab {
|
||||||
while let Some(result) = rx.next().await {
|
while let Some(result) = rx.next().await {
|
||||||
match result {
|
match result {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
|
let p = expand_path(s);
|
||||||
|
let Ok(meta) = fs::metadata(&p).await else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
emit!(Call(
|
emit!(Call(
|
||||||
Exec::call("cd", vec![expand_path(s).to_string_lossy().to_string()]).vec(),
|
Exec::call(if meta.is_dir() { "cd" } else { "reveal" }, vec![
|
||||||
|
p.to_string_lossy().to_string()
|
||||||
|
])
|
||||||
|
.vec(),
|
||||||
KeymapLayer::Manager
|
KeymapLayer::Manager
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use yazi_config::keymap::{Exec, KeymapLayer};
|
use yazi_config::keymap::{Exec, KeymapLayer};
|
||||||
use yazi_shared::Defer;
|
use yazi_shared::{ends_with_slash, Defer};
|
||||||
|
|
||||||
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, tab::Tab, Event, BLOCKER};
|
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, tab::Tab, Event, BLOCKER};
|
||||||
|
|
||||||
|
|
@ -18,7 +18,8 @@ impl Tab {
|
||||||
external::zoxide(ZoxideOpt { cwd }).await
|
external::zoxide(ZoxideOpt { cwd }).await
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
emit!(Call(Exec::call("cd", vec![url.to_string()]).vec(), KeymapLayer::Manager));
|
let op = if global && !ends_with_slash(&url) { "reveal" } else { "cd" };
|
||||||
|
emit!(Call(Exec::call(op, vec![url.to_string()]).vec(), KeymapLayer::Manager));
|
||||||
Ok::<(), anyhow::Error>(())
|
Ok::<(), anyhow::Error>(())
|
||||||
});
|
});
|
||||||
false
|
false
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ mod hidden;
|
||||||
mod jump;
|
mod jump;
|
||||||
mod leave;
|
mod leave;
|
||||||
mod linemode;
|
mod linemode;
|
||||||
|
mod reveal;
|
||||||
mod search;
|
mod search;
|
||||||
mod select;
|
mod select;
|
||||||
mod shell;
|
mod shell;
|
||||||
|
|
|
||||||
28
yazi-core/src/tab/commands/reveal.rs
Normal file
28
yazi-core/src/tab/commands/reveal.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
use yazi_config::keymap::Exec;
|
||||||
|
use yazi_shared::Url;
|
||||||
|
|
||||||
|
use crate::{emit, files::{File, FilesOp}, tab::Tab};
|
||||||
|
|
||||||
|
pub struct Opt<'a> {
|
||||||
|
target: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Exec> for Opt<'a> {
|
||||||
|
fn from(e: &'a Exec) -> Self { Self { target: e.args.first().map(|s| s.as_str()).unwrap_or("") } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tab {
|
||||||
|
pub fn reveal<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
||||||
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
|
let target = Url::from(opt.target);
|
||||||
|
let Some(parent) = target.parent_url() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let b = self.cd(parent.clone());
|
||||||
|
emit!(Files(FilesOp::Creating(parent.clone(), File::from_dummy(target.clone()).into_map())));
|
||||||
|
emit!(Hover(target));
|
||||||
|
b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,10 +35,7 @@ impl Tab {
|
||||||
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!(Call(
|
emit!(Call(Exec::call("cd", vec![cwd.clone().to_string()]).vec(), KeymapLayer::Manager));
|
||||||
Exec::call("cd", vec![cwd.clone().push_slash().to_string()]).vec(),
|
|
||||||
KeymapLayer::Manager
|
|
||||||
));
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
emit!(Files(FilesOp::Part(cwd.clone(), ticket, chunk)));
|
emit!(Files(FilesOp::Part(cwd.clone(), ticket, chunk)));
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ impl<'a> Executor<'a> {
|
||||||
self.cx.manager.active_mut().cd(expand_url(url))
|
self.cx.manager.active_mut().cd(expand_url(url))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"reveal" => self.cx.manager.active_mut().reveal(exec),
|
||||||
|
|
||||||
// Selection
|
// Selection
|
||||||
"select" => {
|
"select" => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{borrow::Cow, env, ffi::OsString, path::{Component, Path, PathBuf, MAIN_SEPARATOR, MAIN_SEPARATOR_STR}};
|
use std::{borrow::Cow, env, ffi::OsString, path::{Component, Path, PathBuf, MAIN_SEPARATOR}};
|
||||||
|
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
|
|
@ -20,24 +20,13 @@ fn _expand_path(p: &Path) -> PathBuf {
|
||||||
});
|
});
|
||||||
|
|
||||||
let p = Path::new(s.as_ref());
|
let p = Path::new(s.as_ref());
|
||||||
if let (slash, Ok(rest)) = (ends_with_slash(p), p.strip_prefix("~")) {
|
if let Ok(rest) = p.strip_prefix("~") {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let Some(home) = env::var_os("HOME") else {
|
let home = env::var_os("HOME");
|
||||||
return rest.to_path_buf();
|
|
||||||
};
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let Some(home) = env::var_os("USERPROFILE") else {
|
let home = env::var_os("USERPROFILE");
|
||||||
return rest.to_path_buf();
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut home = PathBuf::from(home);
|
return if let Some(p) = home { PathBuf::from(p).join(rest) } else { rest.to_path_buf() };
|
||||||
pop_end_slash(&mut home);
|
|
||||||
|
|
||||||
let mut p = if rest == Path::new("") { home } else { home.join(rest) };
|
|
||||||
if slash {
|
|
||||||
p.as_mut_os_string().push(MAIN_SEPARATOR_STR);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.is_absolute() {
|
if p.is_absolute() {
|
||||||
|
|
@ -76,18 +65,6 @@ pub fn ends_with_slash(p: &Path) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[allow(clippy::unnecessary_to_owned)]
|
|
||||||
pub fn pop_end_slash(p: &mut PathBuf) -> bool {
|
|
||||||
if !ends_with_slash(p) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if let Some(n) = p.file_name() {
|
|
||||||
p.set_file_name(n.to_owned());
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn unique_path(mut p: Url) -> Url {
|
pub async fn unique_path(mut p: Url) -> Url {
|
||||||
let Some(stem) = p.file_stem().map(|s| s.to_owned()) else {
|
let Some(stem) = p.file_stem().map(|s| s.to_owned()) else {
|
||||||
return p;
|
return p;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
use std::{ffi::{OsStr, OsString}, fmt::{Debug, Formatter}, ops::{Deref, DerefMut}, path::{Path, PathBuf, MAIN_SEPARATOR_STR}};
|
use std::{ffi::{OsStr, OsString}, fmt::{Debug, Formatter}, ops::{Deref, DerefMut}, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS};
|
use percent_encoding::{percent_decode_str, percent_encode, AsciiSet, CONTROLS};
|
||||||
|
|
||||||
use crate::{ends_with_slash, pop_end_slash};
|
|
||||||
|
|
||||||
const ENCODE_SET: &AsciiSet = &CONTROLS.add(b'#');
|
const ENCODE_SET: &AsciiSet = &CONTROLS.add(b'#');
|
||||||
|
|
||||||
#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
|
@ -155,22 +153,9 @@ impl Url {
|
||||||
pub fn into_os_string(self) -> OsString { self.path.into_os_string() }
|
pub fn into_os_string(self) -> OsString { self.path.into_os_string() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn was_hidden(&self) -> bool {
|
pub fn is_hidden(&self) -> bool {
|
||||||
self.file_name().map_or(false, |s| s.to_string_lossy().starts_with('.'))
|
self.file_name().map_or(false, |s| s.to_string_lossy().starts_with('.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn pop_slash(&mut self) -> bool { pop_end_slash(self) }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn push_slash(mut self) -> Self {
|
|
||||||
if !ends_with_slash(&self) {
|
|
||||||
self
|
|
||||||
} else {
|
|
||||||
self.path.as_mut_os_string().push(MAIN_SEPARATOR_STR);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Url {
|
impl Url {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue