mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Done!
This commit is contained in:
parent
146606e034
commit
774f382e2d
13 changed files with 98 additions and 47 deletions
|
|
@ -10,7 +10,7 @@ impl From<&Exec> for Opt {
|
|||
|
||||
impl Completion {
|
||||
fn next(&mut self, step: usize) -> bool {
|
||||
let len = self.items.len();
|
||||
let len = self.cands.len();
|
||||
if len == 0 {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@ impl Completion {
|
|||
let submit = opt.into().0;
|
||||
if submit {
|
||||
emit!(Call(
|
||||
Exec::call("complete", vec![self.items[self.cursor].to_owned()])
|
||||
.with_bool("apply", true)
|
||||
.with("ticket", self.ticket)
|
||||
.vec(),
|
||||
Exec::call("complete", vec![self.selected().into()]).with("ticket", self.ticket).vec(),
|
||||
KeymapLayer::Input
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use yazi_config::keymap::Exec;
|
||||
|
||||
use crate::completion::Completion;
|
||||
|
||||
pub struct Opt<'a> {
|
||||
cands: &'a Vec<String>,
|
||||
ticket: usize,
|
||||
cache: &'a Vec<String>,
|
||||
cache_name: &'a str,
|
||||
word: &'a str,
|
||||
ticket: usize,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Exec> for Opt<'a> {
|
||||
fn from(e: &'a Exec) -> Self {
|
||||
Self {
|
||||
cands: &e.args,
|
||||
ticket: e.named.get("ticket").and_then(|v| v.parse().ok()).unwrap_or(0),
|
||||
cache: &e.args,
|
||||
cache_name: e.named.get("cache-name").map(|n| n.as_str()).unwrap_or_default(),
|
||||
word: e.named.get("word").map(|w| w.as_str()).unwrap_or_default(),
|
||||
ticket: e.named.get("ticket").and_then(|v| v.parse().ok()).unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,10 +29,33 @@ impl Completion {
|
|||
return false;
|
||||
}
|
||||
|
||||
if !opt.cache.is_empty() {
|
||||
self.caches.insert(opt.cache_name.to_owned(), opt.cache.clone());
|
||||
}
|
||||
let Some(cache) = self.caches.get(opt.cache_name) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let flow = cache.iter().try_fold(Vec::with_capacity(30), |mut v, s| {
|
||||
if s.contains(opt.word) && s != opt.word {
|
||||
v.push(s.to_owned());
|
||||
if v.len() >= 30 {
|
||||
return ControlFlow::Break(v);
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(v)
|
||||
});
|
||||
|
||||
self.close(false);
|
||||
self.items = opt.cands.clone();
|
||||
self.cands = match flow {
|
||||
ControlFlow::Continue(v) => v,
|
||||
ControlFlow::Break(v) => v,
|
||||
};
|
||||
self.ticket = opt.ticket;
|
||||
self.visible = true;
|
||||
|
||||
if !self.cands.is_empty() {
|
||||
self.visible = true;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@ use yazi_config::keymap::{Exec, KeymapLayer};
|
|||
use crate::{completion::Completion, emit};
|
||||
|
||||
pub struct Opt<'a> {
|
||||
word: &'a str,
|
||||
before: &'a str,
|
||||
ticket: usize,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Exec> for Opt<'a> {
|
||||
fn from(e: &'a Exec) -> Self {
|
||||
Self {
|
||||
word: e.args.first().map(|w| w.as_str()).unwrap_or_default(),
|
||||
ticket: e.named.get("ticket").and_then(|v| v.parse().ok()).unwrap_or(0),
|
||||
before: e.named.get("before").map(|s| s.as_str()).unwrap_or_default(),
|
||||
ticket: e.named.get("ticket").and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,28 +27,43 @@ impl Completion {
|
|||
self.close(false);
|
||||
self.ticket = opt.ticket;
|
||||
|
||||
let word = opt.word.to_owned();
|
||||
let (parent, child) = opt.before.rsplit_once('/').unwrap_or((".", opt.before));
|
||||
if self.caches.contains_key(parent) {
|
||||
return self.show(
|
||||
&Exec::call("show", vec![])
|
||||
.with("cache-name", parent)
|
||||
.with("word", child)
|
||||
.with("ticket", opt.ticket),
|
||||
);
|
||||
}
|
||||
|
||||
let ticket = self.ticket;
|
||||
let (parent, child) = (parent.to_owned(), child.to_owned());
|
||||
tokio::spawn(async move {
|
||||
let (parent, child) = word.rsplit_once('/').unwrap_or((".", word.as_str()));
|
||||
|
||||
let mut dir = fs::read_dir(parent).await?;
|
||||
let mut cands = Vec::new();
|
||||
let mut dir = fs::read_dir(&parent).await?;
|
||||
let mut cache = Vec::new();
|
||||
while let Ok(Some(f)) = dir.next_entry().await {
|
||||
let name = f.file_name().to_string_lossy().into_owned();
|
||||
if !name.starts_with(child) {
|
||||
let Ok(meta) = f.metadata().await else {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
cands.push(name);
|
||||
if cands.len() >= 20 {
|
||||
break;
|
||||
}
|
||||
let sep = if !meta.is_dir() {
|
||||
""
|
||||
} else if cfg!(windows) {
|
||||
"\\"
|
||||
} else {
|
||||
"/"
|
||||
};
|
||||
cache.push(format!("{}{sep}", f.file_name().to_string_lossy()));
|
||||
}
|
||||
|
||||
if !cands.is_empty() {
|
||||
if !cache.is_empty() {
|
||||
emit!(Call(
|
||||
Exec::call("show", cands).with("ticket", ticket).vec(),
|
||||
Exec::call("show", cache)
|
||||
.with("cache-name", parent)
|
||||
.with("word", child)
|
||||
.with("ticket", ticket)
|
||||
.vec(),
|
||||
KeymapLayer::Completion
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,26 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Completion {
|
||||
pub(super) items: Vec<String>,
|
||||
pub(super) caches: BTreeMap<String, Vec<String>>,
|
||||
pub(super) cands: Vec<String>,
|
||||
pub(super) offset: usize,
|
||||
pub cursor: usize,
|
||||
|
||||
pub ticket: usize,
|
||||
pub visible: bool,
|
||||
pub(super) ticket: usize,
|
||||
pub visible: bool,
|
||||
}
|
||||
|
||||
impl Completion {
|
||||
#[inline]
|
||||
pub fn window(&self) -> &[String] {
|
||||
let end = (self.offset + self.limit()).min(self.items.len());
|
||||
&self.items[self.offset..end]
|
||||
let end = (self.offset + self.limit()).min(self.cands.len());
|
||||
&self.cands[self.offset..end]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn limit(&self) -> usize { self.items.len().min(5) }
|
||||
pub fn limit(&self) -> usize { self.cands.len().min(5) }
|
||||
|
||||
#[inline]
|
||||
pub fn selected(&self) -> &String { &self.cands[self.cursor] }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ impl Input {
|
|||
snap.value = new;
|
||||
|
||||
self.move_(delta);
|
||||
self.flush_value(true);
|
||||
self.flush_value();
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ impl Input {
|
|||
}
|
||||
|
||||
self.move_(s.chars().count() as isize);
|
||||
self.flush_value(false);
|
||||
self.flush_value();
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ impl Input {
|
|||
}
|
||||
|
||||
self.move_(-1);
|
||||
self.flush_value(false);
|
||||
self.flush_value();
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -320,13 +320,13 @@ impl Input {
|
|||
return false;
|
||||
}
|
||||
if !matches!(old.op, InputOp::None | InputOp::Select(_)) {
|
||||
self.snaps.tag().then(|| self.flush_value(false));
|
||||
self.snaps.tag().then(|| self.flush_value());
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn flush_value(&mut self, no_complete: bool) {
|
||||
pub(super) fn flush_value(&mut self) {
|
||||
self.ticket = self.ticket.wrapping_add(1);
|
||||
|
||||
if self.realtime {
|
||||
|
|
@ -334,7 +334,7 @@ impl Input {
|
|||
self.callback.as_ref().unwrap().send(Err(InputError::Typed(value))).ok();
|
||||
}
|
||||
|
||||
if self.completion && !no_complete {
|
||||
if self.completion {
|
||||
let before = self.partition()[0].to_owned();
|
||||
self.callback.as_ref().unwrap().send(Err(InputError::Completed(before, self.ticket))).ok();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,5 @@ pub use input::*;
|
|||
pub use mode::*;
|
||||
use op::*;
|
||||
pub use option::*;
|
||||
pub use shell::*;
|
||||
use snap::*;
|
||||
use snaps::*;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ impl Tab {
|
|||
}
|
||||
Err(InputError::Completed(before, ticket)) => {
|
||||
emit!(Call(
|
||||
Exec::call("complete", vec![before]).with("ticket", ticket).vec(),
|
||||
Exec::call("complete", vec![]).with("before", before).with("ticket", ticket).vec(),
|
||||
KeymapLayer::Input
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,13 @@ impl<'a> Widget for Completion<'a> {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| {
|
||||
let mut item = ListItem::new(format!(" {} {}", THEME.completion.icon_file, x));
|
||||
let icon = if x.ends_with('/') || x.ends_with('\\') {
|
||||
&THEME.completion.icon_folder
|
||||
} else {
|
||||
&THEME.completion.icon_file
|
||||
};
|
||||
|
||||
let mut item = ListItem::new(format!(" {} {}", icon, x));
|
||||
if i == self.cx.completion.cursor {
|
||||
item = item.style(THEME.completion.active.into());
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -268,10 +268,10 @@ impl<'a> Executor<'a> {
|
|||
}
|
||||
|
||||
"complete" => {
|
||||
return if exec.named.contains_key("apply") {
|
||||
self.cx.input.complete(exec)
|
||||
} else {
|
||||
return if exec.args.is_empty() {
|
||||
self.cx.completion.trigger(exec)
|
||||
} else {
|
||||
self.cx.input.complete(exec)
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ mod tasks;
|
|||
|
||||
pub use active::*;
|
||||
pub use bindings::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use files::*;
|
||||
pub use shared::*;
|
||||
pub use tabs::*;
|
||||
|
|
|
|||
|
|
@ -3,5 +3,4 @@
|
|||
mod cursor;
|
||||
mod term;
|
||||
|
||||
pub use cursor::*;
|
||||
pub use term::*;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue