From 6eff4438cfb5981e69e641c6d6b880b4f4eab291 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 11 Jan 2023 14:20:38 -0600 Subject: [PATCH] implement hot reloading for TUI --- packages/tui/Cargo.toml | 9 ++++++++- packages/tui/src/hot_reload.rs | 31 ++++++++++++++++++++++++++++++ packages/tui/src/lib.rs | 35 ++++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 packages/tui/src/hot_reload.rs diff --git a/packages/tui/Cargo.toml b/packages/tui/Cargo.toml index 5341d40f4..1e35f8b3e 100644 --- a/packages/tui/Cargo.toml +++ b/packages/tui/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT/Apache-2.0" [dependencies] dioxus = { path = "../dioxus", version = "^0.3.0" } -dioxus-core = { path = "../core", version = "^0.3.0" } +dioxus-core = { path = "../core", version = "^0.3.0", features = ["serialize"] } dioxus-html = { path = "../html", version = "^0.3.0" } dioxus-native-core = { path = "../native-core", version = "^0.2.0" } dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.2.0" } @@ -29,6 +29,9 @@ smallvec = "1.6" rustc-hash = "1.1.0" anymap = "1.0.0-beta.2" futures-channel = "0.3.25" +interprocess = { version = "1.2.1", optional = true } +serde = { version = "1.0.136", optional = true } +serde_json = { version = "1.0.79", optional = true } [dev-dependencies] dioxus = { path = "../dioxus" } @@ -38,3 +41,7 @@ criterion = "0.3.5" [[bench]] name = "update" harness = false + +[features] +default = ["hot-reload"] +hot-reload = ["interprocess", "serde", "serde_json"] diff --git a/packages/tui/src/hot_reload.rs b/packages/tui/src/hot_reload.rs new file mode 100644 index 000000000..8385879e0 --- /dev/null +++ b/packages/tui/src/hot_reload.rs @@ -0,0 +1,31 @@ +#![allow(dead_code)] + +use dioxus_core::Template; + +use interprocess::local_socket::LocalSocketStream; +use std::io::{BufRead, BufReader}; +use tokio::sync::mpsc::UnboundedSender; + +pub(crate) fn init(proxy: UnboundedSender>) { + std::thread::spawn(move || { + let temp_file = std::env::temp_dir().join("@dioxusin"); + if let Ok(socket) = LocalSocketStream::connect(temp_file.as_path()) { + let mut buf_reader = BufReader::new(socket); + loop { + let mut buf = String::new(); + match buf_reader.read_line(&mut buf) { + Ok(_) => { + let template: Template<'static> = + serde_json::from_str(Box::leak(buf.into_boxed_str())).unwrap(); + proxy.send(template).unwrap(); + } + Err(err) => { + if err.kind() != std::io::ErrorKind::WouldBlock { + break; + } + } + } + } + } + }); +} diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 4c8d8e739..6af02e993 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -22,11 +22,14 @@ use std::{ use std::{io, time::Duration}; use taffy::Taffy; pub use taffy::{geometry::Point, prelude::*}; +use tokio::{select, sync::mpsc::unbounded_channel}; use tui::{backend::CrosstermBackend, layout::Rect, Terminal}; mod config; mod focus; mod hooks; +#[cfg(all(feature = "hot-reload", debug_assertions))] +mod hot_reload; mod layout; mod node; pub mod prelude; @@ -144,6 +147,12 @@ fn render_vdom( .enable_all() .build()? .block_on(async { + #[cfg(all(feature = "hot-reload", debug_assertions))] + let mut hot_reload_rx = { + let (hot_reload_tx, hot_reload_rx) = unbounded_channel::>(); + hot_reload::init(hot_reload_tx); + hot_reload_rx + }; let mut terminal = (!cfg.headless).then(|| { enable_raw_mode().unwrap(); let mut stdout = std::io::stdout(); @@ -223,16 +232,21 @@ fn render_vdom( } } - use futures::future::{select, Either}; + let mut new_templete = None; { let wait = vdom.wait_for_work(); + #[cfg(all(feature = "hot-reload", debug_assertions))] + let hot_reload_wait = hot_reload_rx.recv(); + #[cfg(not(all(feature = "hot-reload", debug_assertions)))] + let hot_reload_wait = std::future::pending(); + pin_mut!(wait); - match select(wait, event_reciever.next()).await { - Either::Left((_a, _b)) => { - // - } - Either::Right((evt, _o)) => { + select! { + _ = wait => { + + }, + evt = event_reciever.next() => { match evt.as_ref().unwrap() { InputEvent::UserInput(event) => match event { TermEvent::Key(key) => { @@ -252,10 +266,19 @@ fn render_vdom( if let InputEvent::UserInput(evt) = evt.unwrap() { register_event(evt); } + }, + Some(template) = hot_reload_wait => { + new_templete = Some(template); } } } + // if we have a new template, replace the old one + if let Some(template) = new_templete { + // println!("reloading template"); + vdom.replace_template(template); + } + { let evts = { let mut rdom = rdom.borrow_mut();