mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 14:51:03 +00:00
feat: new input DDS event watching changes in input content (#4043)
This commit is contained in:
parent
b37be0529a
commit
f1e93d7f52
26 changed files with 220 additions and 50 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -5565,6 +5565,7 @@ dependencies = [
|
||||||
"indexmap 2.14.0",
|
"indexmap 2.14.0",
|
||||||
"libc",
|
"libc",
|
||||||
"mlua",
|
"mlua",
|
||||||
|
"parking_lot",
|
||||||
"paste",
|
"paste",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
|
|
@ -6001,8 +6002,10 @@ version = "26.5.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
"yazi-config",
|
"yazi-config",
|
||||||
"yazi-core",
|
"yazi-core",
|
||||||
|
"yazi-dds",
|
||||||
"yazi-macro",
|
"yazi-macro",
|
||||||
"yazi-scheduler",
|
"yazi-scheduler",
|
||||||
"yazi-shared",
|
"yazi-shared",
|
||||||
|
|
@ -6227,11 +6230,13 @@ version = "26.5.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clipboard-win",
|
"clipboard-win",
|
||||||
|
"dyn-clone",
|
||||||
"futures",
|
"futures",
|
||||||
"mlua",
|
"mlua",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"serde",
|
"serde",
|
||||||
|
"strum",
|
||||||
"tokio",
|
"tokio",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"yazi-adapter",
|
"yazi-adapter",
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ futures = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
mlua = { workspace = true }
|
mlua = { workspace = true }
|
||||||
|
parking_lot = { workspace = true }
|
||||||
paste = { workspace = true }
|
paste = { workspace = true }
|
||||||
ratatui = { workspace = true }
|
ratatui = { workspace = true }
|
||||||
scopeguard = { workspace = true }
|
scopeguard = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ impl Actor for Close {
|
||||||
};
|
};
|
||||||
|
|
||||||
guard.ticket.next();
|
guard.ticket.next();
|
||||||
if let Some(tx) = guard.tx.take() {
|
if let Some(cb) = guard.cb.take() {
|
||||||
let value = guard.snap().value.clone();
|
let value = guard.snap().value.clone();
|
||||||
_ = tx.send(if form.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) });
|
cb(if form.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) });
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@ pub(super) struct Core {
|
||||||
c_tabs: Option<Value>,
|
c_tabs: Option<Value>,
|
||||||
c_tasks: Option<Value>,
|
c_tasks: Option<Value>,
|
||||||
c_yanked: Option<Value>,
|
c_yanked: Option<Value>,
|
||||||
c_layer: Option<Value>,
|
c_input: Option<Value>,
|
||||||
c_which: Option<Value>,
|
c_which: Option<Value>,
|
||||||
|
c_layer: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Core {
|
impl Deref for Core {
|
||||||
|
|
@ -33,8 +34,9 @@ impl Core {
|
||||||
c_tabs: None,
|
c_tabs: None,
|
||||||
c_tasks: None,
|
c_tasks: None,
|
||||||
c_yanked: None,
|
c_yanked: None,
|
||||||
c_layer: None,
|
c_input: None,
|
||||||
c_which: None,
|
c_which: None,
|
||||||
|
c_layer: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,10 +61,11 @@ impl UserData for Core {
|
||||||
b"tabs" => reuse!(tabs, super::Tabs::make(&me.mgr.tabs)),
|
b"tabs" => reuse!(tabs, super::Tabs::make(&me.mgr.tabs)),
|
||||||
b"tasks" => reuse!(tasks, super::Tasks::make(&me.tasks)),
|
b"tasks" => reuse!(tasks, super::Tasks::make(&me.tasks)),
|
||||||
b"yanked" => reuse!(yanked, super::Yanked::make(&me.mgr.yanked)),
|
b"yanked" => reuse!(yanked, super::Yanked::make(&me.mgr.yanked)),
|
||||||
|
b"input" => reuse!(input, super::Input::make(&me.input)),
|
||||||
|
b"which" => reuse!(which, super::Which::make(&me.which)),
|
||||||
b"layer" => {
|
b"layer" => {
|
||||||
reuse!(layer, Ok::<_, mlua::Error>(yazi_binding::Layer::from(me.layer())))
|
reuse!(layer, Ok::<_, mlua::Error>(yazi_binding::Layer::from(me.layer())))
|
||||||
}
|
}
|
||||||
b"which" => reuse!(which, super::Which::make(&me.which)),
|
|
||||||
_ => Value::Nil,
|
_ => Value::Nil,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
||||||
29
yazi-actor/src/lives/input.rs
Normal file
29
yazi-actor/src/lives/input.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use mlua::{AnyUserData, UserData, UserDataFields};
|
||||||
|
use yazi_shim::mlua::UserDataFieldsExt;
|
||||||
|
|
||||||
|
use super::{Lives, PtrCell};
|
||||||
|
use crate::lives::InputAlt;
|
||||||
|
|
||||||
|
pub(super) struct Input {
|
||||||
|
inner: PtrCell<yazi_core::input::Input>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Input {
|
||||||
|
type Target = yazi_core::input::Input;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
pub(super) fn make(inner: &yazi_core::input::Input) -> mlua::Result<AnyUserData> {
|
||||||
|
Lives::scoped_userdata(Self { inner: inner.into() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for Input {
|
||||||
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
|
fields.add_cached_field("alt", |_, me| me.alt.as_ref().map(InputAlt::make).transpose());
|
||||||
|
}
|
||||||
|
}
|
||||||
27
yazi-actor/src/lives/input_alt.rs
Normal file
27
yazi-actor/src/lives/input_alt.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use mlua::{AnyUserData, UserData, UserDataFields};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use yazi_shim::mlua::UserDataFieldsExt;
|
||||||
|
|
||||||
|
use super::{Lives, PtrCell};
|
||||||
|
|
||||||
|
pub(super) struct InputAlt(PtrCell<Arc<Mutex<yazi_widgets::input::Input>>>);
|
||||||
|
|
||||||
|
impl Deref for InputAlt {
|
||||||
|
type Target = Arc<Mutex<yazi_widgets::input::Input>>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputAlt {
|
||||||
|
pub(super) fn make(inner: &Arc<Mutex<yazi_widgets::input::Input>>) -> mlua::Result<AnyUserData> {
|
||||||
|
Lives::scoped_userdata(Self(inner.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for InputAlt {
|
||||||
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
|
fields.add_cached_field("value", |lua, me| lua.create_string(me.lock().value()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
yazi_macro::mod_flat!(behavior core file files filter finder folder lives mode mut_cell preference preview ptr selected tab tabs task tasks which yanked);
|
yazi_macro::mod_flat!(behavior core file files filter finder folder input input_alt lives mode mut_cell preference preview ptr selected tab tabs task tasks which yanked);
|
||||||
|
|
||||||
pub(super) fn init() {
|
pub(super) fn init() {
|
||||||
unsafe { FILE_CACHE.get().write(std::mem::MaybeUninit::new(<_>::default())) };
|
unsafe { FILE_CACHE.get().write(std::mem::MaybeUninit::new(<_>::default())) };
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ pub struct Border {
|
||||||
pub edge: Edge,
|
pub edge: Edge,
|
||||||
pub r#type: ratatui::widgets::BorderType,
|
pub r#type: ratatui::widgets::BorderType,
|
||||||
pub style: ratatui::style::Style,
|
pub style: ratatui::style::Style,
|
||||||
|
pub merge: ratatui::symbols::merge::MergeStrategy,
|
||||||
|
|
||||||
pub titles: Vec<(ratatui::widgets::TitlePosition, ratatui::text::Line<'static>)>,
|
pub titles: Vec<(ratatui::widgets::TitlePosition, ratatui::text::Line<'static>)>,
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +65,8 @@ impl Widget for Border {
|
||||||
let mut block = ratatui::widgets::Block::default()
|
let mut block = ratatui::widgets::Block::default()
|
||||||
.borders(self.edge.0)
|
.borders(self.edge.0)
|
||||||
.border_type(self.r#type)
|
.border_type(self.r#type)
|
||||||
.border_style(self.style);
|
.border_style(self.style)
|
||||||
|
.merge_borders(self.merge);
|
||||||
|
|
||||||
for title in self.titles {
|
for title in self.titles {
|
||||||
block = match title {
|
block = match title {
|
||||||
|
|
@ -116,5 +118,13 @@ impl UserData for Border {
|
||||||
ud.borrow_mut::<Self>()?.edge = edge;
|
ud.borrow_mut::<Self>()?.edge = edge;
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
});
|
});
|
||||||
|
methods.add_function("merge", |_, (ud, exact): (AnyUserData, bool)| {
|
||||||
|
ud.borrow_mut::<Self>()?.merge = if exact {
|
||||||
|
ratatui::symbols::merge::MergeStrategy::Exact
|
||||||
|
} else {
|
||||||
|
ratatui::symbols::merge::MergeStrategy::Fuzzy
|
||||||
|
};
|
||||||
|
Ok(ud)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ impl TryFrom<Table> for Input {
|
||||||
realtime: false,
|
realtime: false,
|
||||||
completion: false,
|
completion: false,
|
||||||
},
|
},
|
||||||
tx: None,
|
cb: None,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Self { inner: Arc::new(Mutex::new(input)), ..Default::default() })
|
Ok(Self { inner: Arc::new(Mutex::new(input)), ..Default::default() })
|
||||||
|
|
|
||||||
|
|
@ -26,18 +26,18 @@ yazi-shim = { path = "../yazi-shim", version = "26.5.6" }
|
||||||
yazi-version = { path = "../yazi-version", version = "26.5.6" }
|
yazi-version = { path = "../yazi-version", version = "26.5.6" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
hashbrown = { workspace = true }
|
hashbrown = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
inventory = { workspace = true }
|
inventory = { workspace = true }
|
||||||
mlua = { workspace = true }
|
mlua = { workspace = true }
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
paste = { workspace = true }
|
paste = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tokio-stream = { workspace = true }
|
tokio-stream = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
uds_windows = "1.2.1"
|
uds_windows = "1.2.1"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::{Result, bail};
|
||||||
use mlua::{ExternalResult, IntoLua, Lua, Value};
|
use mlua::{ExternalResult, IntoLua, Lua, Value};
|
||||||
use yazi_shared::Id;
|
use yazi_shared::Id;
|
||||||
|
|
||||||
use super::{EmberBulkRename, EmberBye, EmberCd, EmberCustom, EmberDelete, EmberDownload, EmberDuplicate, EmberHey, EmberHi, EmberHover, EmberLoad, EmberMount, EmberMove, EmberRename, EmberTab, EmberTrash, EmberYank};
|
use super::{EmberBulkRename, EmberBye, EmberCd, EmberCustom, EmberDelete, EmberDownload, EmberDuplicate, EmberHey, EmberHi, EmberHover, EmberInput, EmberLoad, EmberMount, EmberMove, EmberRename, EmberTab, EmberTrash, EmberYank};
|
||||||
use crate::Payload;
|
use crate::Payload;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -22,6 +22,7 @@ pub enum Ember<'a> {
|
||||||
Trash(EmberTrash<'a>),
|
Trash(EmberTrash<'a>),
|
||||||
Delete(EmberDelete<'a>),
|
Delete(EmberDelete<'a>),
|
||||||
Download(EmberDownload<'a>),
|
Download(EmberDownload<'a>),
|
||||||
|
Input(EmberInput<'a>),
|
||||||
Mount(EmberMount),
|
Mount(EmberMount),
|
||||||
Custom(EmberCustom),
|
Custom(EmberCustom),
|
||||||
}
|
}
|
||||||
|
|
@ -44,6 +45,7 @@ impl Ember<'static> {
|
||||||
"trash" => Self::Trash(serde_json::from_str(body)?),
|
"trash" => Self::Trash(serde_json::from_str(body)?),
|
||||||
"delete" => Self::Delete(serde_json::from_str(body)?),
|
"delete" => Self::Delete(serde_json::from_str(body)?),
|
||||||
"download" => Self::Download(serde_json::from_str(body)?),
|
"download" => Self::Download(serde_json::from_str(body)?),
|
||||||
|
"input" => Self::Input(serde_json::from_str(body)?),
|
||||||
"mount" => Self::Mount(serde_json::from_str(body)?),
|
"mount" => Self::Mount(serde_json::from_str(body)?),
|
||||||
_ => EmberCustom::from_str(kind, body)?,
|
_ => EmberCustom::from_str(kind, body)?,
|
||||||
})
|
})
|
||||||
|
|
@ -72,6 +74,7 @@ impl Ember<'static> {
|
||||||
| "trash"
|
| "trash"
|
||||||
| "delete"
|
| "delete"
|
||||||
| "download"
|
| "download"
|
||||||
|
| "input"
|
||||||
| "mount"
|
| "mount"
|
||||||
) || kind.starts_with("key-")
|
) || kind.starts_with("key-")
|
||||||
|| kind.starts_with("ind-")
|
|| kind.starts_with("ind-")
|
||||||
|
|
@ -111,6 +114,7 @@ impl<'a> Ember<'a> {
|
||||||
Self::Trash(_) => "trash",
|
Self::Trash(_) => "trash",
|
||||||
Self::Delete(_) => "delete",
|
Self::Delete(_) => "delete",
|
||||||
Self::Download(_) => "download",
|
Self::Download(_) => "download",
|
||||||
|
Self::Input(_) => "input",
|
||||||
Self::Mount(_) => "mount",
|
Self::Mount(_) => "mount",
|
||||||
Self::Custom(b) => b.kind.as_str(),
|
Self::Custom(b) => b.kind.as_str(),
|
||||||
}
|
}
|
||||||
|
|
@ -139,6 +143,7 @@ impl<'a> IntoLua for Ember<'a> {
|
||||||
Self::Trash(b) => b.into_lua(lua),
|
Self::Trash(b) => b.into_lua(lua),
|
||||||
Self::Delete(b) => b.into_lua(lua),
|
Self::Delete(b) => b.into_lua(lua),
|
||||||
Self::Download(b) => b.into_lua(lua),
|
Self::Download(b) => b.into_lua(lua),
|
||||||
|
Self::Input(b) => b.into_lua(lua),
|
||||||
Self::Mount(b) => b.into_lua(lua),
|
Self::Mount(b) => b.into_lua(lua),
|
||||||
Self::Custom(b) => b.into_lua(lua),
|
Self::Custom(b) => b.into_lua(lua),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
34
yazi-dds/src/ember/input.rs
Normal file
34
yazi-dds/src/ember/input.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use mlua::{IntoLua, Lua, Value};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::Ember;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct EmberInput<'a> {
|
||||||
|
pub r#type: Cow<'a, str>,
|
||||||
|
pub value: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> EmberInput<'a> {
|
||||||
|
pub fn borrowed(r#type: &'a str, value: &'a str) -> Ember<'a> {
|
||||||
|
Self { r#type: r#type.into(), value: value.into() }.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmberInput<'static> {
|
||||||
|
pub fn owned(r#type: &'static str, value: &str) -> Ember<'static> {
|
||||||
|
Self { r#type: r#type.into(), value: value.to_owned().into() }.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<EmberInput<'a>> for Ember<'a> {
|
||||||
|
fn from(value: EmberInput<'a>) -> Self { Self::Input(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoLua for EmberInput<'_> {
|
||||||
|
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||||
|
lua.create_table_from([("type", self.r#type), ("value", self.value)])?.into_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
yazi_macro::mod_flat!(
|
yazi_macro::mod_flat!(
|
||||||
bulk_rename bye cd custom delete download duplicate ember hey hi hover load mount r#move rename tab trash yank
|
bulk_rename bye cd custom delete download duplicate ember hey hi hover input load mount r#move rename tab trash yank
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ impl Display for Payload<'_> {
|
||||||
Ember::Trash(b) => serde_json::to_string(b),
|
Ember::Trash(b) => serde_json::to_string(b),
|
||||||
Ember::Delete(b) => serde_json::to_string(b),
|
Ember::Delete(b) => serde_json::to_string(b),
|
||||||
Ember::Download(b) => serde_json::to_string(b),
|
Ember::Download(b) => serde_json::to_string(b),
|
||||||
|
Ember::Input(b) => serde_json::to_string(b),
|
||||||
Ember::Mount(b) => serde_json::to_string(b),
|
Ember::Mount(b) => serde_json::to_string(b),
|
||||||
Ember::Custom(b) => serde_json::to_string(b),
|
Ember::Custom(b) => serde_json::to_string(b),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -174,5 +174,7 @@ impl Pubsub {
|
||||||
|
|
||||||
pub_after!(download(urls: Vec<UrlBuf>), (&urls), (urls));
|
pub_after!(download(urls: Vec<UrlBuf>), (&urls), (urls));
|
||||||
|
|
||||||
|
pub_after!(input(r#type: &'static str, value: &str), (r#type, value));
|
||||||
|
|
||||||
pub_after!(mount(), ());
|
pub_after!(mount(), ());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -249,12 +249,12 @@ impl<'a> Executor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(&mut self, action: ActionCow) -> Result<Data> {
|
fn input(&mut self, action: ActionCow) -> Result<Data> {
|
||||||
let Some(mut guard) = self.app.core.input.lock_mut() else { succ!() };
|
let mut guard;
|
||||||
|
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
if action.name == stringify!($name) {
|
if action.name == stringify!($name) {
|
||||||
on!(input:$name, action);
|
let cx = &mut Ctx::new(&action, &mut self.app.core, &mut self.app.term)?;
|
||||||
|
return act!(input:$name, cx, action);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($layer:ident : $name:ident, $opt:expr) => {{
|
($layer:ident : $name:ident, $opt:expr) => {{
|
||||||
|
|
@ -268,6 +268,11 @@ impl<'a> Executor<'a> {
|
||||||
on!(show);
|
on!(show);
|
||||||
on!(close);
|
on!(close);
|
||||||
|
|
||||||
|
guard = match self.app.core.input.lock_mut() {
|
||||||
|
Some(g) => g,
|
||||||
|
None => succ!(),
|
||||||
|
};
|
||||||
|
|
||||||
match guard.mode() {
|
match guard.mode() {
|
||||||
InputMode::Normal => match action.name.as_ref() {
|
InputMode::Normal => match action.name.as_ref() {
|
||||||
"help" => on!(help:toggle, Layer::Input),
|
"help" => on!(help:toggle, Layer::Input),
|
||||||
|
|
@ -275,9 +280,10 @@ impl<'a> Executor<'a> {
|
||||||
"lua" => on!(app:lua, action),
|
"lua" => on!(app:lua, action),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
InputMode::Insert => {
|
InputMode::Insert => match action.name.as_ref() {
|
||||||
on!(complete);
|
"complete" => on!(input:complete, action),
|
||||||
}
|
_ => {}
|
||||||
|
},
|
||||||
InputMode::Replace => {}
|
InputMode::Replace => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! input {
|
macro_rules! input {
|
||||||
($cx:ident, $cfg:expr) => {{
|
($cx:ident, $cfg:expr) => {{
|
||||||
|
use yazi_dds::Pubsub;
|
||||||
|
use yazi_shim::strum::IntoStr;
|
||||||
|
use yazi_widgets::input::{InputCallback, InputEvent, InputOpt};
|
||||||
|
|
||||||
let (tx, rx) = ::tokio::sync::mpsc::unbounded_channel();
|
let (tx, rx) = ::tokio::sync::mpsc::unbounded_channel();
|
||||||
match $crate::act!(input:show, $cx, yazi_widgets::input::InputOpt { cfg: $cfg, tx: Some(tx) }) {
|
let cb: Box<dyn InputCallback> = Box::new(move |event| {
|
||||||
|
$crate::err!(Pubsub::pub_after_input((&event).into_str(), event.value()));
|
||||||
|
tx.send(event).ok();
|
||||||
|
});
|
||||||
|
|
||||||
|
match $crate::act!(input:show, $cx, InputOpt { cfg: $cfg, cb: Some(cb) }) {
|
||||||
Ok(_) => Ok(rx),
|
Ok(_) => Ok(rx),
|
||||||
Err(e) => Err(e)
|
Err(e) => Err(e)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ impl Utils {
|
||||||
edge: Edge(ratatui::widgets::Borders::ALL),
|
edge: Edge(ratatui::widgets::Borders::ALL),
|
||||||
r#type: ratatui::widgets::BorderType::Rounded,
|
r#type: ratatui::widgets::BorderType::Rounded,
|
||||||
style: THEME.spot.border.get().into(),
|
style: THEME.spot.border.get().into(),
|
||||||
|
merge: Default::default(),
|
||||||
titles: vec![(
|
titles: vec![(
|
||||||
ratatui::widgets::TitlePosition::Top,
|
ratatui::widgets::TitlePosition::Top,
|
||||||
ratatui::text::Line::raw("Spot").centered().style(THEME.spot.title.get()),
|
ratatui::text::Line::raw("Spot").centered().style(THEME.spot.title.get()),
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,12 @@ yazi-config = { path = "../yazi-config", version = "26.5.6" }
|
||||||
yazi-core = { path = "../yazi-core", version = "26.5.6" }
|
yazi-core = { path = "../yazi-core", version = "26.5.6" }
|
||||||
yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
|
yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
|
||||||
yazi-scheduler = { path = "../yazi-scheduler", version = "26.5.6" }
|
yazi-scheduler = { path = "../yazi-scheduler", version = "26.5.6" }
|
||||||
|
yazi-dds = { path = "../yazi-dds", version = "26.5.6" }
|
||||||
yazi-shared = { path = "../yazi-shared", version = "26.5.6" }
|
yazi-shared = { path = "../yazi-shared", version = "26.5.6" }
|
||||||
yazi-shim = { path = "../yazi-shim", version = "26.5.6" }
|
yazi-shim = { path = "../yazi-shim", version = "26.5.6" }
|
||||||
yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" }
|
yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_macro::{emit, relay};
|
use yazi_dds::Pubsub;
|
||||||
use yazi_widgets::input::InputEvent;
|
use yazi_macro::{emit, err, relay};
|
||||||
|
use yazi_shim::strum::IntoStr;
|
||||||
|
use yazi_widgets::input::{InputCallback, InputEvent};
|
||||||
|
|
||||||
pub struct InputProxy;
|
pub struct InputProxy;
|
||||||
|
|
||||||
impl InputProxy {
|
impl InputProxy {
|
||||||
pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver<InputEvent> {
|
pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver<InputEvent> {
|
||||||
let (tx, rx) = mpsc::unbounded_channel();
|
let (tx, rx) = mpsc::unbounded_channel();
|
||||||
emit!(Call(relay!(input:show).with_any("tx", tx).with_any("cfg", cfg)));
|
let cb: Box<dyn InputCallback> = Box::new(move |event| {
|
||||||
|
err!(Pubsub::pub_after_input((&event).into_str(), event.value()));
|
||||||
|
tx.send(event).ok();
|
||||||
|
});
|
||||||
|
|
||||||
|
emit!(Call(relay!(input:show).with_any("cb", cb).with_any("cfg", cfg)));
|
||||||
rx
|
rx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,13 @@ yazi-tty = { path = "../yazi-tty", version = "26.5.9" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
dyn-clone = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
mlua = { workspace = true }
|
mlua = { workspace = true }
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
ratatui = { workspace = true }
|
ratatui = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
strum = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
unicode-width = { workspace = true }
|
unicode-width = { workspace = true }
|
||||||
|
|
||||||
|
|
|
||||||
22
yazi-widgets/src/input/callback.rs
Normal file
22
yazi-widgets/src/input/callback.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use dyn_clone::DynClone;
|
||||||
|
use yazi_macro::impl_data_any;
|
||||||
|
|
||||||
|
use crate::input::InputEvent;
|
||||||
|
|
||||||
|
pub trait InputCallback: Fn(InputEvent) + Send + Sync + DynClone + 'static {}
|
||||||
|
|
||||||
|
impl<T: Fn(InputEvent) + Send + Sync + Clone + 'static> InputCallback for T {}
|
||||||
|
|
||||||
|
impl Clone for Box<dyn InputCallback> {
|
||||||
|
fn clone(&self) -> Self { dyn_clone::clone_box(&**self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for dyn InputCallback {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("InputCallback").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_data_any!(Box<dyn InputCallback>);
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
use strum::IntoStaticStr;
|
||||||
use yazi_shared::Id;
|
use yazi_shared::Id;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, IntoStaticStr)]
|
||||||
pub enum InputEvent {
|
pub enum InputEvent {
|
||||||
Submit(String),
|
Submit(String),
|
||||||
Cancel(String),
|
Cancel(String),
|
||||||
|
|
@ -10,5 +11,11 @@ pub enum InputEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputEvent {
|
impl InputEvent {
|
||||||
|
pub fn value(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Submit(v) | Self::Cancel(v) | Self::Type(v) | Self::Trigger(v, _) => v.as_str(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_submit(&self) -> bool { matches!(self, Self::Submit(_)) }
|
pub fn is_submit(&self) -> bool { matches!(self, Self::Submit(_)) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{borrow::Cow, ops::Range};
|
use std::{borrow::Cow, ops::Range};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use yazi_config::{YAZI, popup::Position};
|
use yazi_config::{YAZI, popup::Position};
|
||||||
use yazi_macro::act;
|
use yazi_macro::act;
|
||||||
use yazi_shared::Ids;
|
use yazi_shared::Ids;
|
||||||
|
|
@ -9,7 +8,7 @@ use yazi_shim::path::CROSS_SEPARATOR;
|
||||||
use yazi_term::CursorStyle;
|
use yazi_term::CursorStyle;
|
||||||
|
|
||||||
use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp};
|
use super::{InputSnap, InputSnaps, mode::InputMode, op::InputOp};
|
||||||
use crate::{CLIPBOARD, input::{InputEvent, InputOpt}};
|
use crate::{CLIPBOARD, input::{InputCallback, InputEvent, InputOpt}};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
|
|
@ -19,7 +18,7 @@ pub struct Input {
|
||||||
pub realtime: bool,
|
pub realtime: bool,
|
||||||
pub completion: bool,
|
pub completion: bool,
|
||||||
|
|
||||||
pub tx: Option<mpsc::UnboundedSender<InputEvent>>,
|
pub cb: Option<Box<dyn InputCallback>>,
|
||||||
pub ticket: Ids,
|
pub ticket: Ids,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,7 +32,7 @@ impl Input {
|
||||||
realtime: opt.cfg.realtime,
|
realtime: opt.cfg.realtime,
|
||||||
completion: opt.cfg.completion,
|
completion: opt.cfg.completion,
|
||||||
|
|
||||||
tx: opt.tx,
|
cb: opt.cb,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -93,20 +92,19 @@ impl Input {
|
||||||
|
|
||||||
pub(super) fn flush_type(&mut self) {
|
pub(super) fn flush_type(&mut self) {
|
||||||
self.ticket.next();
|
self.ticket.next();
|
||||||
if let Some(tx) = self.tx.as_ref().filter(|_| self.realtime) {
|
if let Some(cb) = self.cb.as_ref().filter(|_| self.realtime) {
|
||||||
tx.send(InputEvent::Type(self.value().to_owned())).ok();
|
cb(InputEvent::Type(self.value().to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flush_trigger(true);
|
self.flush_trigger(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn flush_trigger(&self, force: bool) {
|
pub(super) fn flush_trigger(&self, force: bool) {
|
||||||
if let Some(tx) = self.tx.as_ref().filter(|_| self.completion) {
|
if let Some(cb) = self.cb.as_ref().filter(|_| self.completion) {
|
||||||
tx.send(InputEvent::Trigger(
|
cb(InputEvent::Trigger(
|
||||||
self.partition().0.to_owned(),
|
self.partition().0.to_owned(),
|
||||||
(!force).then_some(self.ticket.current()),
|
(!force).then_some(self.ticket.current()),
|
||||||
))
|
));
|
||||||
.ok();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
yazi_macro::mod_pub!(actor parser);
|
yazi_macro::mod_pub!(actor parser);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(chars event gait input mode op option snap snaps widget);
|
yazi_macro::mod_flat!(callback chars event gait input mode op option snap snaps widget);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use yazi_config::popup::InputCfg;
|
use yazi_config::popup::InputCfg;
|
||||||
use yazi_shared::event::ActionCow;
|
use yazi_shared::event::ActionCow;
|
||||||
|
|
||||||
use crate::input::InputEvent;
|
use crate::input::InputCallback;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InputOpt {
|
pub struct InputOpt {
|
||||||
pub cfg: InputCfg,
|
pub cfg: InputCfg,
|
||||||
pub tx: Option<mpsc::UnboundedSender<InputEvent>>,
|
pub cb: Option<Box<dyn InputCallback>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<ActionCow> for InputOpt {
|
impl TryFrom<ActionCow> for InputOpt {
|
||||||
|
|
@ -20,7 +19,7 @@ impl TryFrom<ActionCow> for InputOpt {
|
||||||
bail!("invalid 'cfg' in InputOpt");
|
bail!("invalid 'cfg' in InputOpt");
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self { cfg, tx: a.take_any("tx") })
|
Ok(Self { cfg, cb: a.take_any("cb") })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue