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)]
|
#[derive(Default)]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,43 @@
|
||||||
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();
|
||||||
0 => false,
|
tokio::spawn(async move {
|
||||||
1 => {
|
let result = future.await;
|
||||||
if let Some(f) = self.finish_completion.as_ref() {
|
let mut exec = Exec::call("complete", result);
|
||||||
self.replace_str(f(current, result.get(0).unwrap()).as_str())
|
exec.named.insert("identifier".to_string(), id);
|
||||||
} else {
|
emit!(Call(exec.vec(), KeymapLayer::Input));
|
||||||
false
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
false
|
||||||
self.completion.show(CompletionOpt::top().with_items(result));
|
}
|
||||||
true
|
|
||||||
|
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_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 {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 (old_prefix, old_file_prefix) = prefix.rsplit_once('/').unwrap();
|
let mut result = vec![];
|
||||||
cmp_prefix = old_file_prefix;
|
if let Ok(mut list) = if prefix.contains('/') {
|
||||||
std::fs::read_dir(old_prefix.to_string() + "/")
|
let (old_prefix, old_file_prefix) = prefix.rsplit_once('/').unwrap();
|
||||||
} else {
|
cmp_prefix = old_file_prefix;
|
||||||
std::fs::read_dir(".")
|
tokio::fs::read_dir(old_prefix.to_string() + "/").await
|
||||||
}
|
} else {
|
||||||
.ok()
|
tokio::fs::read_dir(".").await
|
||||||
.map(|list| {
|
} {
|
||||||
list
|
while let Ok(Some(f)) = list.next_entry().await {
|
||||||
.filter_map(|file| {
|
let name = f.file_name().to_string_lossy().to_string();
|
||||||
file.ok().and_then(|f| {
|
if f.metadata().await.is_ok_and(|m| m.is_dir()) && name.starts_with(cmp_prefix) {
|
||||||
let name = f.file_name().to_string_lossy().to_string();
|
result.push(name.clone())
|
||||||
if f.metadata().is_ok_and(|m| m.is_dir()) && name.starts_with(cmp_prefix) {
|
}
|
||||||
Some(name)
|
}
|
||||||
} 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('/') {
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue