fix: interactive cd autocomplete doesn't follow the latest CWD changes (#2025)

This commit is contained in:
三咲雅 · Misaki Masa 2024-12-11 14:25:29 +08:00 committed by GitHub
parent 22757199f9
commit 61cab0f30a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 32 additions and 34 deletions

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, mem, ops::ControlFlow}; use std::{borrow::Cow, mem, ops::ControlFlow, path::PathBuf};
use yazi_macro::render; use yazi_macro::render;
use yazi_shared::event::{Cmd, CmdCow, Data}; use yazi_shared::event::{Cmd, CmdCow, Data};
@ -9,7 +9,7 @@ const LIMIT: usize = 30;
struct Opt { struct Opt {
cache: Vec<String>, cache: Vec<String>,
cache_name: Cow<'static, str>, cache_name: PathBuf,
word: Cow<'static, str>, word: Cow<'static, str>,
ticket: usize, ticket: usize,
} }
@ -18,7 +18,7 @@ impl From<CmdCow> for Opt {
fn from(mut c: CmdCow) -> Self { fn from(mut c: CmdCow) -> Self {
Self { Self {
cache: c.take_any("cache").unwrap_or_default(), cache: c.take_any("cache").unwrap_or_default(),
cache_name: c.take_str("cache-name").unwrap_or_default(), cache_name: c.take_any("cache-name").unwrap_or_default(),
word: c.take_str("word").unwrap_or_default(), word: c.take_str("word").unwrap_or_default(),
ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0), ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0),
} }
@ -37,9 +37,9 @@ impl Completion {
} }
if !opt.cache.is_empty() { if !opt.cache.is_empty() {
self.caches.insert(opt.cache_name.as_ref().to_owned(), opt.cache); self.caches.insert(opt.cache_name.clone(), opt.cache);
} }
let Some(cache) = self.caches.get(opt.cache_name.as_ref()) else { let Some(cache) = self.caches.get(&opt.cache_name) else {
return; return;
}; };

View file

@ -1,17 +1,12 @@
use std::{borrow::Cow, mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}}; use std::{borrow::Cow, mem, path::{MAIN_SEPARATOR_STR, PathBuf}};
use tokio::fs; use tokio::fs;
use yazi_fs::{CWD, expand_path};
use yazi_macro::{emit, render}; use yazi_macro::{emit, render};
use yazi_shared::{Layer, event::{Cmd, CmdCow, Data}}; use yazi_shared::{Layer, event::{Cmd, CmdCow, Data}};
use crate::completion::Completion; use crate::completion::Completion;
#[cfg(windows)]
const SEPARATOR: [char; 2] = ['/', '\\'];
#[cfg(not(windows))]
const SEPARATOR: char = std::path::MAIN_SEPARATOR;
struct Opt { struct Opt {
word: Cow<'static, str>, word: Cow<'static, str>,
ticket: usize, ticket: usize,
@ -34,13 +29,13 @@ impl Completion {
} }
self.ticket = opt.ticket; self.ticket = opt.ticket;
let Some((parent, child)) = Self::split_path(&opt.word) else { let Some((parent, word)) = Self::split_path(&opt.word) else {
return self.close(false); return self.close(false);
}; };
if self.caches.contains_key(&parent) { if self.caches.contains_key(&parent) {
return self.show( return self.show(
Cmd::default().with("cache-name", parent).with("word", child).with("ticket", opt.ticket), Cmd::default().with_any("cache-name", parent).with("word", word).with("ticket", opt.ticket),
); );
} }
@ -62,8 +57,8 @@ impl Completion {
emit!(Call( emit!(Call(
Cmd::new("show") Cmd::new("show")
.with_any("cache", cache) .with_any("cache", cache)
.with("cache-name", parent) .with_any("cache-name", parent)
.with("word", child) .with("word", word)
.with("ticket", ticket), .with("ticket", ticket),
Layer::Completion Layer::Completion
)); ));
@ -75,40 +70,42 @@ impl Completion {
render!(mem::replace(&mut self.visible, false)); render!(mem::replace(&mut self.visible, false));
} }
fn split_path(s: &str) -> Option<(String, String)> { fn split_path(s: &str) -> Option<(PathBuf, String)> {
if s == "~" { if s == "~" {
return None; // We don't autocomplete a `~`, but `~/` return None; // We don't autocomplete a `~`, but `~/`
} }
let s = if let Some(rest) = s.strip_prefix("~") { #[cfg(windows)]
Cow::Owned(format!( const SEP: [char; 2] = ['/', '\\'];
"{}{rest}", #[cfg(not(windows))]
dirs::home_dir().unwrap_or_default().to_string_lossy().trim_end_matches(SEPARATOR), const SEP: char = std::path::MAIN_SEPARATOR;
))
} else {
Cow::Borrowed(s)
};
Some(match s.rsplit_once(SEPARATOR) { Some(match s.rsplit_once(SEP) {
Some((p, c)) => (format!("{p}{}", MAIN_SEPARATOR), c.to_owned()), Some(("", c)) => (PathBuf::from(MAIN_SEPARATOR_STR), c.to_owned()),
None => (".".to_owned(), s.into_owned()), Some((p, c)) => (expand_path(p), c.to_owned()),
None => (CWD.load().to_path_buf(), s.to_owned()),
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::Path;
use super::*; use super::*;
fn compare(s: &str, parent: &str, child: &str) -> bool { fn compare(s: &str, parent: &str, child: &str) -> bool {
matches!(Completion::split_path(s), Some((p, c)) if p == parent && c == child) let (p, c) = Completion::split_path(s).unwrap();
let p = p.strip_prefix(yazi_fs::CWD.load().as_ref()).unwrap_or(&p);
p == Path::new(parent) && c == child
} }
#[cfg(unix)] #[cfg(unix)]
#[test] #[test]
fn test_split() { fn test_split() {
assert!(compare("", ".", "")); yazi_fs::init();
assert!(compare(" ", ".", " ")); assert!(compare("", "", ""));
assert!(compare(" ", "", " "));
assert!(compare("/", "/", "")); assert!(compare("/", "/", ""));
assert!(compare("//", "//", "")); assert!(compare("//", "//", ""));
assert!(compare("/foo", "/", "foo")); assert!(compare("/foo", "/", "foo"));
@ -119,7 +116,8 @@ mod tests {
#[cfg(windows)] #[cfg(windows)]
#[test] #[test]
fn test_split() { fn test_split() {
assert!(compare("foo", ".", "foo")); yazi_fs::init();
assert!(compare("foo", "", "foo"));
assert!(compare("foo\\", "foo\\", "")); assert!(compare("foo\\", "foo\\", ""));
assert!(compare("foo\\bar", "foo\\", "bar")); assert!(compare("foo\\bar", "foo\\", "bar"));
assert!(compare("foo\\bar\\", "foo\\bar\\", "")); assert!(compare("foo\\bar\\", "foo\\bar\\", ""));

View file

@ -1,8 +1,8 @@
use std::collections::HashMap; use std::{collections::HashMap, path::PathBuf};
#[derive(Default)] #[derive(Default)]
pub struct Completion { pub struct Completion {
pub(super) caches: HashMap<String, Vec<String>>, pub(super) caches: HashMap<PathBuf, Vec<String>>,
pub(super) cands: Vec<String>, pub(super) cands: Vec<String>,
pub(super) offset: usize, pub(super) offset: usize,
pub cursor: usize, pub cursor: usize,