mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: cd path auto-completion supports ~ expansion (#1081)
This commit is contained in:
parent
58e5d2280a
commit
5f21998665
6 changed files with 50 additions and 36 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -2769,6 +2769,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"bitflags 2.5.0",
|
||||
"crossterm",
|
||||
"dirs",
|
||||
"futures",
|
||||
"libc",
|
||||
"notify",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
|
|||
anyhow = "1.0.86"
|
||||
bitflags = "2.5.0"
|
||||
crossterm = "0.27.0"
|
||||
dirs = "5.0.1"
|
||||
futures = "0.3.30"
|
||||
notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] }
|
||||
parking_lot = "0.12.2"
|
||||
|
|
@ -29,11 +30,11 @@ ratatui = "0.26.3"
|
|||
regex = "1.10.4"
|
||||
scopeguard = "1.2.0"
|
||||
serde = "1.0.202"
|
||||
shell-words = "1.1.0"
|
||||
tokio = { version = "1.37.0", features = [ "full" ] }
|
||||
tokio-stream = "0.1.15"
|
||||
tokio-util = "0.7.11"
|
||||
unicode-width = "0.1.12"
|
||||
shell-words = "1.1.0"
|
||||
|
||||
# Logging
|
||||
tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] }
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ pub struct Opt {
|
|||
impl From<Cmd> for Opt {
|
||||
fn from(c: Cmd) -> Self { Self { submit: c.bool("submit") } }
|
||||
}
|
||||
impl From<bool> for Opt {
|
||||
fn from(submit: bool) -> Self { Self { submit } }
|
||||
}
|
||||
|
||||
impl Completion {
|
||||
pub fn close(&mut self, opt: impl Into<Opt>) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}};
|
||||
use std::{borrow::Cow, mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}};
|
||||
|
||||
use tokio::fs;
|
||||
use yazi_shared::{emit, event::{Cmd, Data}, render, Layer};
|
||||
|
|
@ -33,7 +33,9 @@ impl Completion {
|
|||
}
|
||||
|
||||
self.ticket = opt.ticket;
|
||||
let (parent, child) = Self::split_path(&opt.word);
|
||||
let Some((parent, child)) = Self::split_path(&opt.word) else {
|
||||
return self.close(false);
|
||||
};
|
||||
|
||||
if self.caches.contains_key(&parent) {
|
||||
return self.show(
|
||||
|
|
@ -72,12 +74,24 @@ impl Completion {
|
|||
render!(mem::replace(&mut self.visible, false));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn split_path(s: &str) -> (String, String) {
|
||||
match s.rsplit_once(SEPARATOR) {
|
||||
Some((p, c)) => (format!("{p}{}", MAIN_SEPARATOR), c.to_owned()),
|
||||
None => (".".to_owned(), s.to_owned()),
|
||||
fn split_path(s: &str) -> Option<(String, String)> {
|
||||
if s == "~" {
|
||||
return None; // We don't autocomplete a `~`, but `~/`
|
||||
}
|
||||
|
||||
let s = if let Some(rest) = s.strip_prefix("~") {
|
||||
Cow::Owned(format!(
|
||||
"{}{rest}",
|
||||
dirs::home_dir().unwrap_or_default().to_string_lossy().trim_end_matches(SEPARATOR),
|
||||
))
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
};
|
||||
|
||||
Some(match s.rsplit_once(SEPARATOR) {
|
||||
Some((p, c)) => (format!("{p}{}", MAIN_SEPARATOR), c.to_owned()),
|
||||
None => (".".to_owned(), s.into_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,28 +99,32 @@ impl Completion {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn compare(s: &str, parent: &str, child: &str) -> bool {
|
||||
matches!(Completion::split_path(s), Some((p, c)) if p == parent && c == child)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
assert_eq!(Completion::split_path(""), (".".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path(" "), (".".to_owned(), " ".to_owned()));
|
||||
assert_eq!(Completion::split_path("/"), ("/".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("//"), ("//".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("/foo"), ("/".to_owned(), "foo".to_owned()));
|
||||
assert_eq!(Completion::split_path("/foo/"), ("/foo/".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("/foo/bar"), ("/foo/".to_owned(), "bar".to_owned()));
|
||||
assert!(compare("", ".", ""));
|
||||
assert!(compare(" ", ".", " "));
|
||||
assert!(compare("/", "/", ""));
|
||||
assert!(compare("//", "//", ""));
|
||||
assert!(compare("/foo", "/", "foo"));
|
||||
assert!(compare("/foo/", "/foo/", ""));
|
||||
assert!(compare("/foo/bar", "/foo/", "bar"));
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
assert_eq!(Completion::split_path("foo"), (".".to_owned(), "foo".to_owned()));
|
||||
assert_eq!(Completion::split_path("foo\\"), ("foo\\".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("foo\\bar"), ("foo\\".to_owned(), "bar".to_owned()));
|
||||
assert_eq!(Completion::split_path("foo\\bar\\"), ("foo\\bar\\".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("C:\\"), ("C:\\".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("C:\\foo"), ("C:\\".to_owned(), "foo".to_owned()));
|
||||
assert_eq!(Completion::split_path("C:\\foo\\"), ("C:\\foo\\".to_owned(), "".to_owned()));
|
||||
assert_eq!(Completion::split_path("C:\\foo\\bar"), ("C:\\foo\\".to_owned(), "bar".to_owned()));
|
||||
assert!(compare("foo", ".", "foo"));
|
||||
assert!(compare("foo\\", "foo\\", ""));
|
||||
assert!(compare("foo\\bar", "foo\\", "bar"));
|
||||
assert!(compare("foo\\bar\\", "foo\\bar\\", ""));
|
||||
assert!(compare("C:\\", "C:\\", ""));
|
||||
assert!(compare("C:\\foo", "C:\\", "foo"));
|
||||
assert!(compare("C:\\foo\\", "C:\\foo\\", ""));
|
||||
assert!(compare("C:\\foo\\bar", "C:\\foo\\", "bar"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::env;
|
||||
use std::{env, path::MAIN_SEPARATOR};
|
||||
|
||||
use crossterm::{execute, terminal::SetTitle};
|
||||
use yazi_shared::event::Cmd;
|
||||
|
|
@ -7,13 +7,9 @@ use crate::{manager::Manager, tasks::Tasks};
|
|||
|
||||
impl Manager {
|
||||
fn title(&self) -> String {
|
||||
#[cfg(unix)]
|
||||
let home = env::var_os("HOME").unwrap_or_default();
|
||||
#[cfg(windows)]
|
||||
let home = env::var_os("USERPROFILE").unwrap_or_default();
|
||||
|
||||
let home = dirs::home_dir().unwrap_or_default();
|
||||
if let Some(p) = self.cwd().strip_prefix(home) {
|
||||
format!("Yazi: ~/{}", p.display())
|
||||
format!("Yazi: ~{}{}", MAIN_SEPARATOR, p.display())
|
||||
} else {
|
||||
format!("Yazi: {}", self.cwd().display())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,12 +37,7 @@ fn _expand_path(p: &Path) -> PathBuf {
|
|||
|
||||
let p = Path::new(s.as_ref());
|
||||
if let Ok(rest) = p.strip_prefix("~") {
|
||||
#[cfg(unix)]
|
||||
let home = env::var_os("HOME");
|
||||
#[cfg(windows)]
|
||||
let home = env::var_os("USERPROFILE");
|
||||
|
||||
return if let Some(p) = home { PathBuf::from(p).join(rest) } else { rest.to_path_buf() };
|
||||
return dirs::home_dir().unwrap_or_default().join(rest);
|
||||
}
|
||||
|
||||
if p.is_absolute() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue