From c1e1f26c4c388ed4a39de117062a2db364535a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sat, 11 May 2024 17:42:35 +0800 Subject: [PATCH] feat: add new `debounce` option to `ya.input()` API (#1025) --- Cargo.lock | 1 + yazi-plugin/Cargo.toml | 1 + yazi-plugin/src/bindings/input.rs | 29 +++++++++++++++++------------ yazi-plugin/src/utils/layer.rs | 23 +++++++++++++++-------- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b95c82cb..f2aa41cc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2857,6 +2857,7 @@ dependencies = [ "shell-words", "syntect", "tokio", + "tokio-stream", "tokio-util", "tracing", "unicode-width", diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 24778d07a..0ea6f64ab 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -36,6 +36,7 @@ shell-escape = "0.1.5" shell-words = "1.1.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.37.0", features = [ "full" ] } +tokio-stream = "0.1.15" tokio-util = "0.7.11" unicode-width = "0.1.12" yazi-prebuild = "0.1.2" diff --git a/yazi-plugin/src/bindings/input.rs b/yazi-plugin/src/bindings/input.rs index fd6b671c8..90fccd294 100644 --- a/yazi-plugin/src/bindings/input.rs +++ b/yazi-plugin/src/bindings/input.rs @@ -1,15 +1,23 @@ +use std::pin::Pin; + use mlua::{prelude::LuaUserDataMethods, UserData}; -use tokio::sync::mpsc::UnboundedReceiver; +use tokio::pin; +use tokio_stream::StreamExt; use yazi_shared::InputError; -pub struct InputRx { - inner: UnboundedReceiver>, +pub struct InputRx>> { + inner: T, } -impl InputRx { - pub fn new(inner: UnboundedReceiver>) -> Self { Self { inner } } +impl>> InputRx { + pub fn new(inner: T) -> Self { Self { inner } } + + pub async fn consume(inner: T) -> (Option, u8) { + pin!(inner); + inner.next().await.map(Self::parse).unwrap_or((None, 0)) + } - pub fn parse(res: Result) -> (Option, u8) { + fn parse(res: Result) -> (Option, u8) { match res { Ok(s) => (Some(s), 1), Err(InputError::Canceled(s)) => (Some(s), 2), @@ -19,14 +27,11 @@ impl InputRx { } } -impl UserData for InputRx { +impl> + 'static> UserData for InputRx { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_async_method_mut("recv", |_, me, ()| async move { - let Some(res) = me.inner.recv().await else { - return Ok((None, 0)); - }; - - Ok(Self::parse(res)) + let mut inner = unsafe { Pin::new_unchecked(&mut me.inner) }; + Ok(inner.next().await.map(Self::parse).unwrap_or((None, 0))) }); } } diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 0ed0335da..40c49d8aa 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -1,10 +1,11 @@ -use std::str::FromStr; +use std::{str::FromStr, time::Duration}; use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value}; use tokio::sync::mpsc; +use tokio_stream::wrappers::UnboundedReceiverStream; use yazi_config::{keymap::{Control, Key}, popup::InputCfg}; use yazi_proxy::{AppProxy, InputProxy}; -use yazi_shared::{emit, event::Cmd, Layer}; +use yazi_shared::{emit, event::Cmd, Debounce, Layer}; use super::Utils; use crate::bindings::{InputRx, Position}; @@ -59,7 +60,7 @@ impl Utils { "input", lua.create_async_function(|lua, t: Table| async move { let realtime = t.raw_get("realtime").unwrap_or_default(); - let mut rx = InputProxy::show(InputCfg { + let rx = UnboundedReceiverStream::new(InputProxy::show(InputCfg { title: t.raw_get("title")?, value: t.raw_get("value").unwrap_or_default(), cursor: None, // TODO @@ -67,14 +68,20 @@ impl Utils { realtime, completion: false, highlight: false, - }); + })); - if realtime { + if !realtime { + return InputRx::consume(rx).await.into_lua_multi(lua); + } + + let debounce = t.raw_get::<_, f64>("debounce").unwrap_or_default(); + if debounce < 0.0 { + Err("negative debounce duration".into_lua_err()) + } else if debounce == 0.0 { (InputRx::new(rx), Value::Nil).into_lua_multi(lua) - } else if let Some(res) = rx.recv().await { - InputRx::parse(res).into_lua_multi(lua) } else { - (Value::Nil, 0).into_lua_multi(lua) + (InputRx::new(Debounce::new(rx, Duration::from_secs_f64(debounce))), Value::Nil) + .into_lua_multi(lua) } })?, )?;