implement hot reloading for TUI

This commit is contained in:
Evan Almloff 2023-01-11 14:20:38 -06:00
parent 1073574896
commit 6eff4438cf
3 changed files with 68 additions and 7 deletions

View file

@ -14,7 +14,7 @@ license = "MIT/Apache-2.0"
[dependencies] [dependencies]
dioxus = { path = "../dioxus", version = "^0.3.0" } 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-html = { path = "../html", version = "^0.3.0" }
dioxus-native-core = { path = "../native-core", version = "^0.2.0" } dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
dioxus-native-core-macro = { path = "../native-core-macro", 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" rustc-hash = "1.1.0"
anymap = "1.0.0-beta.2" anymap = "1.0.0-beta.2"
futures-channel = "0.3.25" 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] [dev-dependencies]
dioxus = { path = "../dioxus" } dioxus = { path = "../dioxus" }
@ -38,3 +41,7 @@ criterion = "0.3.5"
[[bench]] [[bench]]
name = "update" name = "update"
harness = false harness = false
[features]
default = ["hot-reload"]
hot-reload = ["interprocess", "serde", "serde_json"]

View file

@ -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<Template<'static>>) {
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;
}
}
}
}
}
});
}

View file

@ -22,11 +22,14 @@ use std::{
use std::{io, time::Duration}; use std::{io, time::Duration};
use taffy::Taffy; use taffy::Taffy;
pub use taffy::{geometry::Point, prelude::*}; pub use taffy::{geometry::Point, prelude::*};
use tokio::{select, sync::mpsc::unbounded_channel};
use tui::{backend::CrosstermBackend, layout::Rect, Terminal}; use tui::{backend::CrosstermBackend, layout::Rect, Terminal};
mod config; mod config;
mod focus; mod focus;
mod hooks; mod hooks;
#[cfg(all(feature = "hot-reload", debug_assertions))]
mod hot_reload;
mod layout; mod layout;
mod node; mod node;
pub mod prelude; pub mod prelude;
@ -144,6 +147,12 @@ fn render_vdom(
.enable_all() .enable_all()
.build()? .build()?
.block_on(async { .block_on(async {
#[cfg(all(feature = "hot-reload", debug_assertions))]
let mut hot_reload_rx = {
let (hot_reload_tx, hot_reload_rx) = unbounded_channel::<Template<'static>>();
hot_reload::init(hot_reload_tx);
hot_reload_rx
};
let mut terminal = (!cfg.headless).then(|| { let mut terminal = (!cfg.headless).then(|| {
enable_raw_mode().unwrap(); enable_raw_mode().unwrap();
let mut stdout = std::io::stdout(); 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(); 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); pin_mut!(wait);
match select(wait, event_reciever.next()).await { select! {
Either::Left((_a, _b)) => { _ = wait => {
//
} },
Either::Right((evt, _o)) => { evt = event_reciever.next() => {
match evt.as_ref().unwrap() { match evt.as_ref().unwrap() {
InputEvent::UserInput(event) => match event { InputEvent::UserInput(event) => match event {
TermEvent::Key(key) => { TermEvent::Key(key) => {
@ -252,10 +266,19 @@ fn render_vdom(
if let InputEvent::UserInput(evt) = evt.unwrap() { if let InputEvent::UserInput(evt) = evt.unwrap() {
register_event(evt); 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 evts = {
let mut rdom = rdom.borrow_mut(); let mut rdom = rdom.borrow_mut();