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

@ -2,8 +2,9 @@ use crate::{completion::CompletionOpt, Position};
#[derive(Default)]
pub struct Completion {
items: Vec<String>,
cursor: usize,
items: Vec<String>,
cursor: usize,
pub identifier: String,
pub position: Position,
pub column_cnt: u8,
@ -15,6 +16,13 @@ impl Completion {
pub fn show(&mut self, opt: CompletionOpt) {
self.close();
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.position = opt.position;
@ -24,6 +32,7 @@ impl Completion {
pub fn close(&mut self) -> bool {
self.cursor = 0;
self.identifier = String::new();
self.visible = false;
true
}

View file

@ -1,29 +1,43 @@
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 {
pub fn complete(&mut self) -> bool {
if !self.completion.visible {
let current = self.snaps.current().value.as_str();
let result = self.init_completion.as_ref().map(|f| f(current)).unwrap_or_default();
match result.len() {
0 => false,
1 => {
if let Some(f) = self.finish_completion.as_ref() {
self.replace_str(f(current, result.get(0).unwrap()).as_str())
} else {
false
}
}
_ => {
self.completion.show(CompletionOpt::top().with_items(result));
true
if let Some(f) = self.init_completion.as_ref() {
let future = f(self.snaps.current().value.clone());
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,
1 => {
if let Some(f) = self.finish_completion.as_ref() {
self
.replace_str(f(self.snaps.current().value.as_str(), exec.args.get(0).unwrap()).as_str())
} else {
false
}
}
} else {
false
_ => {
self.completion.show(CompletionOpt::top().with_items(exec.args.clone()));
true
}
}
}

View file

@ -6,7 +6,7 @@ use unicode_width::UnicodeWidthStr;
use yazi_config::keymap::Key;
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};
#[derive(Default)]
@ -25,8 +25,8 @@ pub struct Input {
pub(super) highlight: bool,
pub completion: Completion,
pub(super) init_completion: Option<Box<dyn Fn(&str) -> Vec<String> + Send>>,
pub(super) finish_completion: Option<Box<dyn Fn(&str, &str) -> String + Send>>,
pub(super) init_completion: Option<InitCompletionType>,
pub(super) finish_completion: Option<FinishCompletionType>,
}
impl Input {

View file

@ -1,15 +1,21 @@
use std::{future::Future, pin::Pin};
use ratatui::prelude::Rect;
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 title: String,
pub value: String,
pub position: Position,
pub realtime: bool,
pub highlight: bool,
pub init_completion: Option<Box<dyn Fn(&str) -> Vec<String> + Send>>,
pub finish_completion: Option<Box<dyn Fn(&str, &str) -> String + Send>>,
pub init_completion: Option<InitCompletionType>,
pub finish_completion: Option<FinishCompletionType>,
}
impl InputOpt {
@ -63,7 +69,7 @@ impl InputOpt {
#[inline]
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,
>(
mut self,

View file

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

View file

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