Browse Source

implement hot reloading for TUI

Evan Almloff 2 years ago
parent
commit
6eff4438cf
3 changed files with 68 additions and 7 deletions
  1. 8 1
      packages/tui/Cargo.toml
  2. 31 0
      packages/tui/src/hot_reload.rs
  3. 29 6
      packages/tui/src/lib.rs

+ 8 - 1
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"]

+ 31 - 0
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<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;
+                        }
+                    }
+                }
+            }
+        }
+    });
+}

+ 29 - 6
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::<Template<'static>>();
+                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();