yazi/yazi-core/src/completion/commands/show.rs
2024-01-22 18:43:50 +08:00

81 lines
2 KiB
Rust

use std::{mem, ops::ControlFlow};
use yazi_shared::{event::Exec, render};
use crate::completion::Completion;
const LIMIT: usize = 30;
pub struct Opt {
cache: Vec<String>,
cache_name: String,
word: String,
ticket: usize,
}
impl From<Exec> for Opt {
fn from(mut e: Exec) -> Self {
Self {
cache: mem::take(&mut e.args),
cache_name: e.take_name("cache-name").unwrap_or_default(),
word: e.take_name("word").unwrap_or_default(),
ticket: e.take_name("ticket").and_then(|v| v.parse().ok()).unwrap_or(0),
}
}
}
impl Completion {
fn match_candidates(word: &str, cache: &[String]) -> Vec<String> {
let flow = cache.iter().try_fold(
(Vec::with_capacity(LIMIT), Vec::with_capacity(LIMIT)),
|(mut prefixed, mut fuzzy), s| {
if s.starts_with(word) {
if s != word {
prefixed.push(s);
if prefixed.len() >= LIMIT {
return ControlFlow::Break((prefixed, fuzzy));
}
}
} else if fuzzy.len() < LIMIT - prefixed.len() && s.contains(word) {
// here we don't break the control flow, since we want more exact matching.
fuzzy.push(s)
}
ControlFlow::Continue((prefixed, fuzzy))
},
);
let (mut prefixed, fuzzy) = match flow {
ControlFlow::Continue(v) => v,
ControlFlow::Break(v) => v,
};
if prefixed.len() < LIMIT {
prefixed.extend(fuzzy.into_iter().take(LIMIT - prefixed.len()))
}
prefixed.into_iter().map(ToOwned::to_owned).collect()
}
pub fn show(&mut self, opt: impl Into<Opt>) {
let opt = opt.into() as Opt;
if self.ticket != opt.ticket {
return;
}
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;
};
self.ticket = opt.ticket;
self.cands = Self::match_candidates(&opt.word, cache);
if self.cands.is_empty() {
return render!(mem::replace(&mut self.visible, false));
}
self.offset = 0;
self.cursor = 0;
self.visible = true;
render!();
}
}