fix: make the input component sync and reactive (#3949)
Some checks are pending
Cachix / Publish Flake (push) Waiting to run
Cachix / Publish Flake-1 (push) Waiting to run
Check / clippy (push) Waiting to run
Check / rustfmt (push) Waiting to run
Check / stylua (push) Waiting to run
Draft / build-unix (gcc-aarch64-linux-gnu, ubuntu-latest, aarch64-unknown-linux-gnu) (push) Waiting to run
Draft / build-unix (gcc-i686-linux-gnu, ubuntu-latest, i686-unknown-linux-gnu) (push) Waiting to run
Draft / build-unix (gcc-riscv64-linux-gnu, ubuntu-latest, riscv64gc-unknown-linux-gnu) (push) Waiting to run
Draft / build-unix (gcc-sparc64-linux-gnu, ubuntu-latest, sparc64-unknown-linux-gnu) (push) Waiting to run
Draft / build-unix (macos-latest, aarch64-apple-darwin) (push) Waiting to run
Draft / build-unix (macos-latest, x86_64-apple-darwin) (push) Waiting to run
Draft / build-unix (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Waiting to run
Draft / build-windows (windows-latest, aarch64-pc-windows-msvc) (push) Waiting to run
Draft / build-windows (windows-latest, x86_64-pc-windows-msvc) (push) Waiting to run
Draft / build-musl (aarch64-unknown-linux-musl) (push) Waiting to run
Draft / build-musl (x86_64-unknown-linux-musl) (push) Waiting to run
Draft / build-snap (amd64, ubuntu-latest) (push) Waiting to run
Draft / build-snap (arm64, ubuntu-24.04-arm) (push) Waiting to run
Draft / snap (push) Blocked by required conditions
Draft / draft (push) Blocked by required conditions
Draft / nightly (push) Blocked by required conditions
Test / test (macos-latest) (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
Test / test (windows-latest) (push) Waiting to run

This commit is contained in:
三咲雅 misaki masa 2026-05-09 09:26:07 +08:00 committed by GitHub
parent 3968c4799c
commit 92b9ea3794
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 192 additions and 146 deletions

48
Cargo.lock generated
View file

@ -312,9 +312,9 @@ dependencies = [
[[package]]
name = "avif-serialize"
version = "0.8.8"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d"
checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38"
dependencies = [
"arrayvec",
]
@ -589,9 +589,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.61"
version = "1.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
dependencies = [
"find-msvc-tools",
"jobserver",
@ -2156,9 +2156,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.97"
version = "0.3.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf"
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
dependencies = [
"cfg-if",
"futures-util",
@ -2564,9 +2564,9 @@ dependencies = [
[[package]]
name = "no_std_io2"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550"
checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003"
dependencies = [
"memchr",
]
@ -3379,9 +3379,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.39.3"
version = "0.39.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "721da970c312655cde9b4ffe0547f20a8494866a4af5ff51f18b7c633d0c870b"
checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
dependencies = [
"memchr",
]
@ -3732,9 +3732,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rfc6979"
version = "0.5.0-rc.5"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23a3127ee32baec36af75b4107082d9bd823501ec14a4e016be4b6b37faa74ae"
checksum = "5236ce872cac07e0fb3969b0cbf468c7d2f37d432f1b627dcb7b8d34563fb0c3"
dependencies = [
"hmac 0.13.0",
"subtle",
@ -4696,9 +4696,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.52.2"
version = "1.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386"
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
dependencies = [
"bytes",
"libc",
@ -5113,9 +5113,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.120"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1"
checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
dependencies = [
"cfg-if",
"once_cell",
@ -5126,9 +5126,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.70"
version = "0.4.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084"
checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8"
dependencies = [
"js-sys",
"wasm-bindgen",
@ -5136,9 +5136,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.120"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103"
checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -5146,9 +5146,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.120"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41"
checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
dependencies = [
"bumpalo",
"proc-macro2",
@ -5159,9 +5159,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.120"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea"
checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
dependencies = [
"unicode-ident",
]

View file

@ -71,7 +71,7 @@ serde_with = "3.19.0"
strum = { version = "0.28.0", features = [ "derive" ] }
syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
thiserror = "2.0.18"
tokio = { version = "1.52.2", features = [ "full" ] }
tokio = { version = "1.52.3", features = [ "full" ] }
tokio-stream = "0.1.18"
tokio-util = "0.7.18"
toml = { version = "1.1.2" }

View file

@ -65,7 +65,7 @@ impl Actor for BulkRename {
batcher.prime(&tmp);
TasksProxy::process_exec(
cwd.into(),
cwd,
Splatter::new(&[UrlCow::default(), tmp.as_url().into()]).splat(&opener.run),
vec![UrlCow::default(), UrlBuf::from(&tmp).into()],
opener.block,

View file

@ -7,9 +7,9 @@ use yazi_config::popup::InputCfg;
use yazi_core::mgr::CdSource;
use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp, path::{clean_url, expand_url}};
use yazi_macro::{act, err, render, succ};
use yazi_macro::{act, err, input, render, succ};
use yazi_parser::mgr::CdForm;
use yazi_proxy::{CmpProxy, InputProxy, MgrProxy};
use yazi_proxy::{CmpProxy, MgrProxy};
use yazi_shared::{Debounce, data::Data, url::{AsUrl, UrlBuf, UrlLike}};
use yazi_vfs::{VfsFile, provider};
use yazi_widgets::input::InputEvent;
@ -62,8 +62,8 @@ impl Actor for Cd {
}
impl Cd {
fn cd_interactive(cx: &Ctx) -> Result<Data> {
let input = InputProxy::show(InputCfg::cd(cx.cwd().as_url()));
fn cd_interactive(cx: &mut Ctx) -> Result<Data> {
let input = input!(cx, InputCfg::cd(cx.cwd().as_url()))?;
tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));

View file

@ -1,9 +1,9 @@
use anyhow::{Result, bail};
use yazi_config::popup::{ConfirmCfg, InputCfg};
use yazi_fs::{File, FilesOp};
use yazi_macro::{ok_or_not_found, succ};
use yazi_macro::{input, ok_or_not_found, succ};
use yazi_parser::mgr::CreateForm;
use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy};
use yazi_proxy::{ConfirmProxy, MgrProxy};
use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@ -20,7 +20,7 @@ impl Actor for Create {
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let cwd = cx.cwd().to_owned();
let mut input = InputProxy::show(InputCfg::create(form.dir));
let mut input = input!(cx, InputCfg::create(form.dir))?;
tokio::spawn(async move {
let Some(InputEvent::Submit(name)) = input.recv().await else { return };

View file

@ -5,9 +5,9 @@ use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_core::mgr::FilterOpt;
use yazi_macro::succ;
use yazi_macro::{input, succ};
use yazi_parser::mgr::FilterForm;
use yazi_proxy::{InputProxy, MgrProxy};
use yazi_proxy::MgrProxy;
use yazi_shared::{Debounce, data::Data};
use yazi_widgets::input::InputEvent;
@ -20,8 +20,8 @@ impl Actor for Filter {
const NAME: &str = "filter";
fn act(_: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
let input = InputProxy::show(InputCfg::filter());
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
let input = input!(cx, InputCfg::filter())?;
tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));

View file

@ -5,9 +5,9 @@ use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_core::mgr::FindDoOpt;
use yazi_macro::succ;
use yazi_macro::{input, succ};
use yazi_parser::mgr::FindForm;
use yazi_proxy::{InputProxy, MgrProxy};
use yazi_proxy::MgrProxy;
use yazi_shared::{Debounce, data::Data};
use yazi_widgets::input::InputEvent;
@ -20,8 +20,8 @@ impl Actor for Find {
const NAME: &str = "find";
fn act(_: &mut Ctx, form: Self::Form) -> Result<Data> {
let input = InputProxy::show(InputCfg::find(form.prev));
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let input = input!(cx, InputCfg::find(form.prev))?;
tokio::spawn(async move {
let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50));

View file

@ -1,8 +1,8 @@
use anyhow::Result;
use yazi_config::popup::ConfirmCfg;
use yazi_macro::{act, succ};
use yazi_macro::{act, confirm, succ};
use yazi_parser::mgr::RemoveForm;
use yazi_proxy::{ConfirmProxy, MgrProxy};
use yazi_proxy::MgrProxy;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
@ -29,14 +29,17 @@ impl Actor for Remove {
return act!(mgr:remove_do, cx, form);
}
let confirm = ConfirmProxy::show(if form.permanently {
ConfirmCfg::delete(&form.targets)
} else {
ConfirmCfg::trash(&form.targets)
});
let confirm = confirm!(
cx,
if form.permanently {
ConfirmCfg::delete(&form.targets)
} else {
ConfirmCfg::trash(&form.targets)
}
)?;
tokio::spawn(async move {
if confirm.await {
if confirm.future().await {
MgrProxy::remove_do(form.targets, form.permanently);
}
});

View file

@ -2,9 +2,9 @@ use anyhow::Result;
use yazi_config::popup::{ConfirmCfg, InputCfg};
use yazi_dds::Pubsub;
use yazi_fs::{File, FilesOp};
use yazi_macro::{act, err, ok_or_not_found, succ};
use yazi_macro::{act, err, input, ok_or_not_found, succ};
use yazi_parser::mgr::RenameForm;
use yazi_proxy::{ConfirmProxy, InputProxy, MgrProxy};
use yazi_proxy::{ConfirmProxy, MgrProxy};
use yazi_shared::{Id, data::Data, url::{UrlBuf, UrlLike}};
use yazi_vfs::{VfsFile, maybe_exists, provider};
use yazi_watcher::WATCHER;
@ -42,7 +42,7 @@ impl Actor for Rename {
};
let (tab, old) = (cx.tab().id, hovered.url_owned());
let mut input = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor));
let mut input = input!(cx, InputCfg::rename().with_value(name).with_cursor(cursor))?;
tokio::spawn(async move {
let Some(InputEvent::Submit(name)) = input.recv().await else { return };

View file

@ -6,10 +6,10 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use yazi_config::popup::InputCfg;
use yazi_core::mgr::{CdSource, SearchVia};
use yazi_fs::{FilesOp, cha::Cha};
use yazi_macro::{act, succ};
use yazi_macro::{act, input, succ};
use yazi_parser::{VoidForm, mgr::SearchForm};
use yazi_plugin::external;
use yazi_proxy::{InputProxy, MgrProxy};
use yazi_proxy::MgrProxy;
use yazi_scheduler::NotifyProxy;
use yazi_shared::{data::Data, url::{AsUrl, UrlLike}};
use yazi_widgets::input::InputEvent;
@ -28,7 +28,7 @@ impl Actor for Search {
handle.abort();
}
let mut input = InputProxy::show(InputCfg::search(opt.via.into()).with_value(&*opt.subject));
let mut input = input!(cx, InputCfg::search(opt.via.into()).with_value(&*opt.subject))?;
tokio::spawn(async move {
if let Some(InputEvent::Submit(subject)) = input.recv().await {

View file

@ -2,9 +2,9 @@ use std::borrow::Cow;
use anyhow::Result;
use yazi_config::popup::InputCfg;
use yazi_macro::{act, succ};
use yazi_macro::{act, input, succ};
use yazi_parser::mgr::ShellForm;
use yazi_proxy::{InputProxy, TasksProxy};
use yazi_proxy::TasksProxy;
use yazi_scheduler::process::ProcessOpt;
use yazi_shared::data::Data;
use yazi_widgets::input::InputEvent;
@ -24,9 +24,11 @@ impl Actor for Shell {
let cwd = form.cwd.take().unwrap_or_else(|| cx.cwd().clone());
let selected: Vec<_> = cx.tab().hovered_and_selected().cloned().map(Into::into).collect();
let input = form.interactive.then(|| {
InputProxy::show(InputCfg::shell(form.block).with_value(&*form.run).with_cursor(form.cursor))
});
let input = if form.interactive {
Some(input!(cx, InputCfg::shell(form.block).with_value(&*form.run).with_cursor(form.cursor))?)
} else {
None
};
tokio::spawn(async move {
if let Some(mut rx) = input {

View file

@ -2,9 +2,9 @@ use std::borrow::Cow;
use anyhow::Result;
use yazi_config::popup::InputCfg;
use yazi_macro::{act, render, succ};
use yazi_macro::{act, input, render, succ};
use yazi_parser::mgr::TabRenameForm;
use yazi_proxy::{InputProxy, MgrProxy};
use yazi_proxy::MgrProxy;
use yazi_shared::data::Data;
use yazi_widgets::input::InputEvent;
@ -27,9 +27,11 @@ impl Actor for TabRename {
succ!(render!());
}
let mut input = InputProxy::show(
InputCfg::tab_rename().with_value(form.name.unwrap_or(Cow::Borrowed(&pref.name))),
);
let mut input = input!(
cx,
InputCfg::tab_rename().with_value(form.name.unwrap_or(Cow::Borrowed(&pref.name)))
)?;
tokio::spawn(async move {
if let Some(InputEvent::Submit(name)) = input.recv().await {
MgrProxy::tab_rename(tab, name);

View file

@ -63,13 +63,13 @@ impl FromLua for FetcherMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => return Err("expected a table of FetcherMatcher".into_lua_err()),
_ => Err("expected a table of FetcherMatcher".into_lua_err()),
}
}
}
impl IntoLua for FetcherMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.into_iter().map(Fetcher::new), None).into_lua(lua)
Iter::new(self.0.map(Fetcher::new), None).into_lua(lua)
}
}

View file

@ -73,13 +73,13 @@ impl FromLua for OpenRuleMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => return Err("expected a table of OpenRuleMatcher".into_lua_err()),
_ => Err("expected a table of OpenRuleMatcher".into_lua_err()),
}
}
}
impl IntoLua for OpenRuleMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.into_iter().map(OpenRule::new), None).into_lua(lua)
Iter::new(self.0.map(OpenRule::new), None).into_lua(lua)
}
}

View file

@ -20,7 +20,7 @@ impl UserData for OpenRules {
_ => index,
};
YAZI.open.insert(index, rule.clone().into()).into_lua_err()?;
YAZI.open.insert(index, rule.clone()).into_lua_err()?;
Ok(rule)
});

View file

@ -76,6 +76,6 @@ impl FromLua for OpenerRuleMatcher {
impl IntoLua for OpenerRuleMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.into_iter().map(OpenerRule::new), None).into_lua(lua)
Iter::new(self.0.map(OpenerRule::new), None).into_lua(lua)
}
}

View file

@ -43,7 +43,7 @@ impl UserData for OpenerRules {
_ => index,
};
me.inner.insert(index, rule.clone().into()).into_lua_err()?;
me.inner.insert(index, rule.clone()).into_lua_err()?;
Ok(rule)
});

View file

@ -63,13 +63,13 @@ impl FromLua for PreloaderMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => return Err("expected a table of PreloaderMatcher".into_lua_err()),
_ => Err("expected a table of PreloaderMatcher".into_lua_err()),
}
}
}
impl IntoLua for PreloaderMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.into_iter().map(Preloader::new), None).into_lua(lua)
Iter::new(self.0.map(Preloader::new), None).into_lua(lua)
}
}

View file

@ -73,13 +73,13 @@ impl FromLua for PreviewerMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => return Err("expected a table of PreviewerMatcher".into_lua_err()),
_ => Err("expected a table of PreviewerMatcher".into_lua_err()),
}
}
}
impl IntoLua for PreviewerMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.into_iter().map(Previewer::new), None).into_lua(lua)
Iter::new(self.0.map(Previewer::new), None).into_lua(lua)
}
}

View file

@ -63,13 +63,13 @@ impl FromLua for SpotterMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => return Err("expected a table of SpotterMatcher".into_lua_err()),
_ => Err("expected a table of SpotterMatcher".into_lua_err()),
}
}
}
impl IntoLua for SpotterMatcher {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.into_iter().map(Spotter::new), None).into_lua(lua)
Iter::new(self.0.map(Spotter::new), None).into_lua(lua)
}
}

View file

@ -7,7 +7,7 @@ pub struct Color(pub ratatui::style::Color);
impl Color {
pub fn compose(lua: &Lua) -> mlua::Result<Value> {
let new = lua.create_function(|_, (_, color): (Table, Color)| Ok(color))?;
let new = lua.create_function(|_, (_, color): (Table, Self)| Ok(color))?;
let color = lua.create_table()?;
color.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?;

View file

@ -20,7 +20,7 @@ impl From<yazi_config::Style> for Style {
}
impl From<Style> for ratatui::style::Style {
fn from(value: Style) -> ratatui::style::Style { value.0 }
fn from(value: Style) -> Self { value.0 }
}
impl From<&SyncCell<yazi_config::Style>> for Style {

View file

@ -35,7 +35,7 @@ impl Actions {
writeln!(s, " TERM_PROGRAM : {:?}", env::var_os("TERM_PROGRAM"))?;
writeln!(s, " TERM_PROGRAM_VERSION: {:?}", env::var_os("TERM_PROGRAM_VERSION"))?;
writeln!(s, " Brand.from_env : {:?}", yazi_emulator::Brand::from_env())?;
writeln!(s, " Emulator.detect : {:?}", &*yazi_emulator::EMULATOR)?;
writeln!(s, " Emulator.detect : {:?}", *yazi_emulator::EMULATOR)?;
writeln!(s, "\nAdapter")?;
writeln!(s, " Adapter.matches : {:?}", yazi_adapter::ADAPTOR)?;

View file

@ -9,7 +9,7 @@ pub(super) fn init() -> anyhow::Result<()> {
.with_context(|| format!("failed to create packages directory: {packages_dir:?}"))?;
let config_dir = Xdg::config_dir();
std::fs::create_dir_all(&config_dir)
std::fs::create_dir_all(config_dir)
.with_context(|| format!("failed to create config directory: {config_dir:?}"))?;
Ok(())

View file

@ -33,6 +33,6 @@ pub(super) fn has_serde_attr(attrs: &[Attribute], name: &str) -> bool {
a.path().is_ident("serde")
&& a
.parse_args_with(Punctuated::<Meta, Comma>::parse_terminated)
.map_or(false, |n| n.iter().any(|m| m.path().is_ident(name)))
.is_ok_and(|n| n.iter().any(|m| m.path().is_ident(name)))
})
}

View file

@ -63,7 +63,7 @@ pub fn build_flavor(light: bool, merge: bool) -> anyhow::Result<theme::Theme> {
)?;
}
Ok(preset.reshape(light)?)
preset.reshape(light)
}
fn wait_for_key(e: anyhow::Error) -> anyhow::Result<()> {

View file

@ -67,8 +67,8 @@ impl Opener {
impl DeserializeOverHook for Opener {
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
let mut inner = self.unwrap_unchecked();
for mut rules in inner.values_mut() {
*rules = Arc::try_unwrap(mem::take(&mut rules))
for rules in inner.values_mut() {
*rules = Arc::try_unwrap(mem::take(rules))
.expect("unique opener value arc")
.deserialize_over_hook()?
.into();

View file

@ -28,7 +28,7 @@ impl DeserializeOverWith for Selector {
self,
deserializer: D,
) -> Result<Self, D::Error> {
let new = Selector::deserialize(deserializer)?;
let new = Self::deserialize(deserializer)?;
Self::new(new.url.or(self.url), new.mime.or(self.mime)).map_err(de::Error::custom)
}
}

View file

@ -7,6 +7,6 @@ pub enum CustomField {
String(String),
}
impl From<&CustomField> for CustomField {
fn from(value: &CustomField) -> Self { value.clone() }
impl From<&Self> for CustomField {
fn from(value: &Self) -> Self { value.clone() }
}

View file

@ -1,7 +1,7 @@
use std::{sync::atomic::Ordering, time::{Duration, Instant}};
use anyhow::Result;
use tokio::{select, time::sleep};
use tokio::{select, sync::mpsc, time::sleep};
use yazi_actor::Ctx;
use yazi_core::Core;
use yazi_macro::act;
@ -14,62 +14,82 @@ pub(crate) struct App {
pub(crate) core: Core,
pub(crate) term: Option<Term>,
pub(crate) signals: Signals,
need_render: u8,
pub(crate) last_render: Instant,
next_render: Option<Duration>,
}
impl App {
fn make(term: Term, signals: Signals) -> Result<Self> {
Ok(Self {
core: Core::make(),
term: Some(term),
signals,
need_render: 0,
last_render: Instant::now(),
next_render: None,
})
}
pub(crate) async fn serve() -> Result<()> {
let term = Term::start()?;
let (mut rx, signals) = (Event::take(), Signals::start()?);
let mut app = Self { core: Core::make(), term: Some(term), signals };
let mut app = Self::make(term, signals)?;
app.bootstrap()?;
let mut events = Vec::with_capacity(50);
let (mut timeout, mut need_render, mut last_render) = (None, 0, Instant::now());
macro_rules! drain_events {
() => {
for event in events.drain(..) {
Dispatcher::new(&mut app).dispatch(event);
need_render = NEED_RENDER.load(Ordering::Relaxed);
if need_render == 0 {
continue;
}
timeout = Duration::from_millis(10).checked_sub(last_render.elapsed());
if timeout.is_none() {
app.render(need_render == 2)?;
last_render = Instant::now();
}
}
};
}
loop {
if let Some(t) = timeout.take() {
if let Some(t) = app.next_render.take() {
select! {
_ = sleep(t) => {
app.render(need_render == 2)?;
last_render = Instant::now();
app.render(app.need_render == 2)?;
}
n = rx.recv_many(&mut events, 50) => {
if n == 0 { break }
drain_events!();
r = app.drain(&mut rx) => if !r? {
break;
}
}
} else if rx.recv_many(&mut events, 50).await != 0 {
drain_events!();
} else {
} else if !app.drain(&mut rx).await? {
break;
}
}
Ok(())
}
fn bootstrap(&mut self) -> anyhow::Result<Data> {
fn bootstrap(&mut self) -> Result<Data> {
let cx = &mut Ctx::active(&mut self.core, &mut self.term);
act!(app:bootstrap, cx)?;
self.render(false)
}
async fn drain(&mut self, rx: &mut mpsc::UnboundedReceiver<Event>) -> Result<bool> {
let Some(event) = rx.recv().await else {
return Ok(false);
};
self.dispatch(event)?;
while let Ok(e) = rx.try_recv() {
self.dispatch(e)?;
}
Ok(true)
}
fn dispatch(&mut self, event: Event) -> Result<()> {
Dispatcher::new(self).dispatch(event);
self.need_render = NEED_RENDER.load(Ordering::Relaxed);
if self.need_render == 0 {
return Ok(());
}
self.next_render = Duration::from_millis(10).checked_sub(self.last_render.elapsed());
if self.next_render.is_none() {
self.render(self.need_render == 2)?;
}
Ok(())
}
}

View file

@ -1,4 +1,4 @@
use std::sync::atomic::{AtomicU8, Ordering};
use std::{sync::atomic::{AtomicU8, Ordering}, time::Instant};
use anyhow::Result;
use crossterm::{cursor::{MoveTo, SetCursorStyle, Show}, execute, queue, terminal::{BeginSynchronizedUpdate, EndSynchronizedUpdate}};
@ -16,6 +16,7 @@ use crate::{app::App, root::Root};
impl App {
pub(crate) fn render(&mut self, partial: bool) -> Result<Data> {
self.last_render = Instant::now();
NEED_RENDER.store(0, Ordering::Relaxed);
let Some(term) = &mut self.term else { succ!() };

View file

@ -20,8 +20,8 @@ impl<'a> Dispatcher<'a> {
pub(super) fn dispatch(&mut self, event: Event) {
let result = match event {
Event::Call(action) => self.dispatch_call(action),
Event::Seq(actions) => self.dispatch_seq(actions),
Event::Call(action) => Ok(self.dispatch_call(action)),
Event::Seq(actions) => Ok(self.dispatch_seq(actions)),
Event::Render(partial) => self.dispatch_render(partial),
Event::Key(key) => self.dispatch_key(key),
Event::Mouse(mouse) => self.dispatch_mouse(mouse),
@ -35,7 +35,7 @@ impl<'a> Dispatcher<'a> {
}
}
fn dispatch_call(&mut self, action: ActionCow) -> Result<()> {
fn dispatch_call(&mut self, action: ActionCow) {
let tx = action.replier().cloned();
let result = Executor::new(self.app).execute(action);
@ -45,17 +45,15 @@ impl<'a> Dispatcher<'a> {
if let Some(tx) = tx {
tx.send(result).ok();
}
Ok(())
}
fn dispatch_seq(&mut self, mut actions: Vec<ActionCow>) -> Result<()> {
pub(super) fn dispatch_seq(&mut self, mut actions: Vec<ActionCow>) {
if let Some(last) = actions.pop() {
self.dispatch_call(last)?;
self.dispatch_call(last);
}
if !actions.is_empty() {
emit!(Seq(actions));
}
Ok(())
}
fn dispatch_render(&mut self, partial: bool) -> Result<()> {

View file

@ -20,7 +20,7 @@ impl Logs {
}
let state_dir = Xdg::state_dir();
std::fs::create_dir_all(&state_dir)
std::fs::create_dir_all(state_dir)
.with_context(|| format!("failed to create state directory: {state_dir:?}"))?;
let log_path = state_dir.join("yazi.log");

View file

@ -1,10 +1,10 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_config::{KEYMAP, keymap::{Chord, ChordCow, Key}};
use yazi_macro::{act, emit};
use yazi_macro::act;
use yazi_shared::Layer;
use crate::app::App;
use crate::{Dispatcher, app::App};
pub(super) struct Router<'a> {
app: &'a mut App,
@ -45,7 +45,7 @@ impl<'a> Router<'a> {
let cx = &mut Ctx::active(&mut self.app.core, &mut self.app.term);
act!(which:activate, cx, (layer, key)).ok();
} else {
emit!(Seq(ChordCow::from(chord).into_seq()));
Dispatcher::new(self.app).dispatch_seq(ChordCow::from(chord).into_seq());
}
return true;
}

View file

@ -91,7 +91,7 @@ impl Xdg {
let mut p = env::var_os("XDG_RUNTIME_DIR")
.map(PathBuf::from)
.filter(|p| p.is_absolute())
.unwrap_or_else(|| env::temp_dir());
.unwrap_or_else(env::temp_dir);
let uid = unix_either!(
{

View file

@ -1,8 +1,8 @@
#[macro_export]
macro_rules! act {
(@pre $layer:ident : $name:ident, $cx:ident, $opt:ident) => {
if let Some(hook) = <act!($layer:$name) as yazi_actor::Actor>::hook($cx, &$opt) {
<act!(core:preflight)>::act($cx, (hook, yazi_parser::spark!($layer:$name, $opt))).map(|spark| spark.try_into().unwrap())
if let Some(hook) = <$crate::act!($layer:$name) as yazi_actor::Actor>::hook($cx, &$opt) {
<$crate::act!(core:preflight)>::act($cx, (hook, yazi_parser::spark!($layer:$name, $opt))).map(|spark| spark.try_into().unwrap())
} else {
Ok($opt)
}
@ -12,8 +12,8 @@ macro_rules! act {
#[cfg(debug_assertions)]
$cx.backtrace.push(concat!(stringify!($layer), ":", stringify!($name)));
let result = match act!(@pre $layer:$name, $cx, $opt) {
Ok(opt) => <act!($layer:$name) as yazi_actor::Actor>::act($cx, opt),
let result = match $crate::act!(@pre $layer:$name, $cx, $opt) {
Ok(opt) => <$crate::act!($layer:$name) as yazi_actor::Actor>::act($cx, opt),
Err(e) => Err(e),
};
@ -25,12 +25,12 @@ macro_rules! act {
}};
($layer:ident : $name:ident, $cx:ident, $action:expr) => {
<act!($layer:$name) as yazi_actor::Actor>::Form::try_from($action)
<$crate::act!($layer:$name) as yazi_actor::Actor>::Form::try_from($action)
.map_err(anyhow::Error::from)
.and_then(|opt| act!(@impl $layer:$name, $cx, opt))
.and_then(|opt| $crate::act!(@impl $layer:$name, $cx, opt))
};
($layer:ident : $name:ident, $cx:ident) => {
act!($layer:$name, $cx, <<act!($layer:$name) as yazi_actor::Actor>::Form as Default>::default())
$crate::act!($layer:$name, $cx, <<$crate::act!($layer:$name) as yazi_actor::Actor>::Form as Default>::default())
};
($layer:ident : $name:ident) => {
paste::paste! { yazi_actor::$layer::[<$name:camel>] }

10
yazi-macro/src/confirm.rs Normal file
View file

@ -0,0 +1,10 @@
#[macro_export]
macro_rules! confirm {
($cx:ident, $cfg:expr) => {{
let token = yazi_shared::CompletionToken::default();
match $crate::act!(confirm:show, $cx, yazi_parser::confirm::ShowForm { cfg: $cfg, token: token.clone() }) {
Ok(_) => Ok(token),
Err(e) => Err(e)
}
}};
}

10
yazi-macro/src/input.rs Normal file
View file

@ -0,0 +1,10 @@
#[macro_export]
macro_rules! input {
($cx:ident, $cfg:expr) => {{
let (tx, rx) = ::tokio::sync::mpsc::unbounded_channel();
match $crate::act!(input:show, $cx, yazi_widgets::input::InputOpt { cfg: $cfg, tx }) {
Ok(_) => Ok(rx),
Err(e) => Err(e)
}
}};
}

View file

@ -1,9 +1,11 @@
mod actor;
mod asset;
mod confirm;
mod context;
mod event;
mod fmt;
mod fs;
mod input;
mod log;
mod module;
mod platform;

View file

@ -83,7 +83,7 @@ impl From<&str> for Data {
impl<T> FromIterator<T> for Data
where
T: Into<Data>,
T: Into<Self>,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self::List(iter.into_iter().map(Into::into).collect())

View file

@ -8,9 +8,7 @@ pub struct NonEmptyString(String);
impl NonEmptyString {
#[inline]
pub fn new(value: String) -> Option<Self> {
Some(NonEmptyString(value)).filter(|s| !s.is_empty())
}
pub fn new(value: String) -> Option<Self> { Some(Self(value)).filter(|s| !s.is_empty()) }
}
impl Deref for NonEmptyString {