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

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;
}