mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
fix: interactive cd autocomplete doesn't follow the latest CWD changes (#2025)
This commit is contained in:
parent
22757199f9
commit
61cab0f30a
3 changed files with 32 additions and 34 deletions
|
|
@ -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_shared::event::{Cmd, CmdCow, Data};
|
||||
|
|
@ -9,7 +9,7 @@ const LIMIT: usize = 30;
|
|||
|
||||
struct Opt {
|
||||
cache: Vec<String>,
|
||||
cache_name: Cow<'static, str>,
|
||||
cache_name: PathBuf,
|
||||
word: Cow<'static, str>,
|
||||
ticket: usize,
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ impl From<CmdCow> for Opt {
|
|||
fn from(mut c: CmdCow) -> Self {
|
||||
Self {
|
||||
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(),
|
||||
ticket: c.get("ticket").and_then(Data::as_usize).unwrap_or(0),
|
||||
}
|
||||
|
|
@ -37,9 +37,9 @@ impl Completion {
|
|||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 yazi_fs::{CWD, expand_path};
|
||||
use yazi_macro::{emit, render};
|
||||
use yazi_shared::{Layer, event::{Cmd, CmdCow, Data}};
|
||||
|
||||
use crate::completion::Completion;
|
||||
|
||||
#[cfg(windows)]
|
||||
const SEPARATOR: [char; 2] = ['/', '\\'];
|
||||
|
||||
#[cfg(not(windows))]
|
||||
const SEPARATOR: char = std::path::MAIN_SEPARATOR;
|
||||
|
||||
struct Opt {
|
||||
word: Cow<'static, str>,
|
||||
ticket: usize,
|
||||
|
|
@ -34,13 +29,13 @@ impl Completion {
|
|||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
if self.caches.contains_key(&parent) {
|
||||
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(
|
||||
Cmd::new("show")
|
||||
.with_any("cache", cache)
|
||||
.with("cache-name", parent)
|
||||
.with("word", child)
|
||||
.with_any("cache-name", parent)
|
||||
.with("word", word)
|
||||
.with("ticket", ticket),
|
||||
Layer::Completion
|
||||
));
|
||||
|
|
@ -75,40 +70,42 @@ impl Completion {
|
|||
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 == "~" {
|
||||
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)
|
||||
};
|
||||
#[cfg(windows)]
|
||||
const SEP: [char; 2] = ['/', '\\'];
|
||||
#[cfg(not(windows))]
|
||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
||||
|
||||
Some(match s.rsplit_once(SEPARATOR) {
|
||||
Some((p, c)) => (format!("{p}{}", MAIN_SEPARATOR), c.to_owned()),
|
||||
None => (".".to_owned(), s.into_owned()),
|
||||
Some(match s.rsplit_once(SEP) {
|
||||
Some(("", c)) => (PathBuf::from(MAIN_SEPARATOR_STR), c.to_owned()),
|
||||
Some((p, c)) => (expand_path(p), c.to_owned()),
|
||||
None => (CWD.load().to_path_buf(), s.to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
|
||||
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)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
assert!(compare("", ".", ""));
|
||||
assert!(compare(" ", ".", " "));
|
||||
yazi_fs::init();
|
||||
assert!(compare("", "", ""));
|
||||
assert!(compare(" ", "", " "));
|
||||
assert!(compare("/", "/", ""));
|
||||
assert!(compare("//", "//", ""));
|
||||
assert!(compare("/foo", "/", "foo"));
|
||||
|
|
@ -119,7 +116,8 @@ mod tests {
|
|||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_split() {
|
||||
assert!(compare("foo", ".", "foo"));
|
||||
yazi_fs::init();
|
||||
assert!(compare("foo", "", "foo"));
|
||||
assert!(compare("foo\\", "foo\\", ""));
|
||||
assert!(compare("foo\\bar", "foo\\", "bar"));
|
||||
assert!(compare("foo\\bar\\", "foo\\bar\\", ""));
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Completion {
|
||||
pub(super) caches: HashMap<String, Vec<String>>,
|
||||
pub(super) caches: HashMap<PathBuf, Vec<String>>,
|
||||
pub(super) cands: Vec<String>,
|
||||
pub(super) offset: usize,
|
||||
pub cursor: usize,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue