feat: add async support for init_completion

This commit is contained in:
XOR-op 2023-10-31 00:50:47 -04:00
parent 4093000b21
commit f214d36e5a
6 changed files with 76 additions and 49 deletions

View file

@ -4,6 +4,7 @@ use crate::{completion::CompletionOpt, Position};
pub struct Completion { pub struct Completion {
items: Vec<String>, items: Vec<String>,
cursor: usize, cursor: usize,
pub identifier: String,
pub position: Position, pub position: Position,
pub column_cnt: u8, pub column_cnt: u8,
@ -15,6 +16,13 @@ impl Completion {
pub fn show(&mut self, opt: CompletionOpt) { pub fn show(&mut self, opt: CompletionOpt) {
self.close(); self.close();
self.visible = true; self.visible = true;
self.identifier = format!(
"{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
);
self.items = opt.items; self.items = opt.items;
self.position = opt.position; self.position = opt.position;
@ -24,6 +32,7 @@ impl Completion {
pub fn close(&mut self) -> bool { pub fn close(&mut self) -> bool {
self.cursor = 0; self.cursor = 0;
self.identifier = String::new();
self.visible = false; self.visible = false;
true true
} }

View file

@ -1,30 +1,44 @@
use crossterm::event::KeyCode; use crossterm::event::KeyCode;
use yazi_config::keymap::Key; use yazi_config::keymap::{Exec, Key, KeymapLayer};
use crate::{completion::CompletionOpt, input::Input}; use crate::{completion::CompletionOpt, emit, input::Input};
impl Input { impl Input {
pub fn complete(&mut self) -> bool { pub fn complete(&mut self) -> bool {
if !self.completion.visible { if !self.completion.visible {
let current = self.snaps.current().value.as_str(); if let Some(f) = self.init_completion.as_ref() {
let result = self.init_completion.as_ref().map(|f| f(current)).unwrap_or_default(); let future = f(self.snaps.current().value.clone());
match result.len() { let id = self.completion.identifier.clone();
tokio::spawn(async move {
let result = future.await;
let mut exec = Exec::call("complete", result);
exec.named.insert("identifier".to_string(), id);
emit!(Call(exec.vec(), KeymapLayer::Input));
});
}
}
false
}
pub fn fill_completion(&mut self, exec: &Exec) -> bool {
if !exec.named.get("identifier").is_some_and(|id| *id == self.completion.identifier) {
return false;
};
match exec.args.len() {
0 => false, 0 => false,
1 => { 1 => {
if let Some(f) = self.finish_completion.as_ref() { if let Some(f) = self.finish_completion.as_ref() {
self.replace_str(f(current, result.get(0).unwrap()).as_str()) self
.replace_str(f(self.snaps.current().value.as_str(), exec.args.get(0).unwrap()).as_str())
} else { } else {
false false
} }
} }
_ => { _ => {
self.completion.show(CompletionOpt::top().with_items(result)); self.completion.show(CompletionOpt::top().with_items(exec.args.clone()));
true true
} }
} }
} else {
false
}
} }
pub fn navigate_completion(&mut self, key: &Key) -> bool { pub fn navigate_completion(&mut self, key: &Key) -> bool {

View file

@ -6,7 +6,7 @@ use unicode_width::UnicodeWidthStr;
use yazi_config::keymap::Key; use yazi_config::keymap::Key;
use yazi_shared::{CharKind, InputError}; use yazi_shared::{CharKind, InputError};
use super::{mode::InputMode, op::InputOp, InputOpt, InputSnap, InputSnaps}; use super::{mode::InputMode, op::InputOp, FinishCompletionType, InitCompletionType, InputOpt, InputSnap, InputSnaps};
use crate::{completion::Completion, external, Position}; use crate::{completion::Completion, external, Position};
#[derive(Default)] #[derive(Default)]
@ -25,8 +25,8 @@ pub struct Input {
pub(super) highlight: bool, pub(super) highlight: bool,
pub completion: Completion, pub completion: Completion,
pub(super) init_completion: Option<Box<dyn Fn(&str) -> Vec<String> + Send>>, pub(super) init_completion: Option<InitCompletionType>,
pub(super) finish_completion: Option<Box<dyn Fn(&str, &str) -> String + Send>>, pub(super) finish_completion: Option<FinishCompletionType>,
} }
impl Input { impl Input {

View file

@ -1,15 +1,21 @@
use std::{future::Future, pin::Pin};
use ratatui::prelude::Rect; use ratatui::prelude::Rect;
use crate::Position; use crate::Position;
pub type InitCompletionType =
Box<dyn Fn(String) -> Pin<Box<dyn Future<Output = Vec<String>> + Send>> + Send>;
pub type FinishCompletionType = Box<dyn Fn(&str, &str) -> String + Send>;
pub struct InputOpt { pub struct InputOpt {
pub title: String, pub title: String,
pub value: String, pub value: String,
pub position: Position, pub position: Position,
pub realtime: bool, pub realtime: bool,
pub highlight: bool, pub highlight: bool,
pub init_completion: Option<Box<dyn Fn(&str) -> Vec<String> + Send>>, pub init_completion: Option<InitCompletionType>,
pub finish_completion: Option<Box<dyn Fn(&str, &str) -> String + Send>>, pub finish_completion: Option<FinishCompletionType>,
} }
impl InputOpt { impl InputOpt {
@ -63,7 +69,7 @@ impl InputOpt {
#[inline] #[inline]
pub fn with_completion< pub fn with_completion<
F1: Fn(&str) -> Vec<String> + Send + 'static, F1: Fn(String) -> Pin<Box<dyn Future<Output = Vec<String>> + Send>> + Send + 'static,
F2: Fn(&str, &str) -> String + Send + 'static, F2: Fn(&str, &str) -> String + Send + 'static,
>( >(
mut self, mut self,

View file

@ -62,30 +62,25 @@ impl Tab {
let mut result = emit!(Input( let mut result = emit!(Input(
InputOpt::top("Change directory:").with_value(target.to_string_lossy()).with_completion( InputOpt::top("Change directory:").with_value(target.to_string_lossy()).with_completion(
|prefix| { |prefix| {
let mut cmp_prefix = prefix; Box::pin(async move {
if prefix.contains('/') { let mut cmp_prefix = prefix.as_str();
let mut result = vec![];
if let Ok(mut list) = if prefix.contains('/') {
let (old_prefix, old_file_prefix) = prefix.rsplit_once('/').unwrap(); let (old_prefix, old_file_prefix) = prefix.rsplit_once('/').unwrap();
cmp_prefix = old_file_prefix; cmp_prefix = old_file_prefix;
std::fs::read_dir(old_prefix.to_string() + "/") tokio::fs::read_dir(old_prefix.to_string() + "/").await
} else { } else {
std::fs::read_dir(".") tokio::fs::read_dir(".").await
} } {
.ok() while let Ok(Some(f)) = list.next_entry().await {
.map(|list| {
list
.filter_map(|file| {
file.ok().and_then(|f| {
let name = f.file_name().to_string_lossy().to_string(); let name = f.file_name().to_string_lossy().to_string();
if f.metadata().is_ok_and(|m| m.is_dir()) && name.starts_with(cmp_prefix) { if f.metadata().await.is_ok_and(|m| m.is_dir()) && name.starts_with(cmp_prefix) {
Some(name) result.push(name.clone())
} else {
None
} }
}
}
result
}) })
})
.collect()
})
.unwrap_or_default()
}, },
|current, new| { |current, new| {
if let Some((prefix, _)) = current.rsplit_once('/') { if let Some((prefix, _)) = current.rsplit_once('/') {

View file

@ -233,6 +233,9 @@ impl Executor {
let in_operating = exec.named.contains_key("in-operating"); let in_operating = exec.named.contains_key("in-operating");
return if in_operating { cx.input.move_in_operating(step) } else { cx.input.move_(step) }; return if in_operating { cx.input.move_in_operating(step) } else { cx.input.move_(step) };
} }
// asynchronized completion
"complete" => return cx.input.fill_completion(exec),
_ => {} _ => {}
} }