This commit is contained in:
sxyazi 2023-11-03 21:48:17 +08:00
parent 146606e034
commit 774f382e2d
No known key found for this signature in database
13 changed files with 98 additions and 47 deletions

View file

@ -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;
}

View file

@ -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
));
}

View file

@ -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
}
}

View file

@ -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
));
}

View file

@ -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] }
}

View file

@ -39,7 +39,7 @@ impl Input {
snap.value = new;
self.move_(delta);
self.flush_value(true);
self.flush_value();
true
}
}

View file

@ -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();
}

View file

@ -11,6 +11,5 @@ pub use input::*;
pub use mode::*;
use op::*;
pub use option::*;
pub use shell::*;
use snap::*;
use snaps::*;

View file

@ -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
));
}

View file

@ -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 {

View file

@ -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)
};
}
_ => {}

View file

@ -9,6 +9,7 @@ mod tasks;
pub use active::*;
pub use bindings::*;
#[allow(unused_imports)]
pub use files::*;
pub use shared::*;
pub use tabs::*;

View file

@ -3,5 +3,4 @@
mod cursor;
mod term;
pub use cursor::*;
pub use term::*;