mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: add async support for init_completion
This commit is contained in:
parent
4093000b21
commit
f214d36e5a
6 changed files with 76 additions and 49 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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('/') {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue