diff --git a/Cargo.toml b/Cargo.toml index f5623f7b4..e84eb31ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ desktop = ["dioxus-desktop"] router = ["dioxus-router"] tui = ["dioxus-tui"] liveview = ["dioxus-liveview"] -hot_reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter"] +hot_reload = ["dioxus-core-macro/hot_reload", "dioxus-rsx-interpreter", "dioxus-desktop?/hot_reload"] native-core = ["dioxus-native-core", "dioxus-native-core-macro"] diff --git a/examples/hot_reload.rs b/examples/hot_reload.rs index 17e2b00b5..41ef5b1c7 100644 --- a/examples/hot_reload.rs +++ b/examples/hot_reload.rs @@ -5,92 +5,7 @@ fn main() { } fn app(cx: Scope) -> Element { - let rsx_code = use_state(&cx, || { - r##"div { - width: "100%", - height: "500px", - onclick: move |_| { - count.modify(|count| *count + 10); - }, - - p { - "High-Five counter: {count.to_string():?}", - } - - div { - width: "{count}px", - height: "10px", - background_color: "red", - } - - Comp { - color: "#083289" - } - - Comp { - color: "green" - } - - { - (0..10).map(|i| { - cx.render(rsx!{p {"{i}"}}) - }) - } - }"## - .to_string() - }); - let submitted_rsx_code = use_state(&cx, || None); - - cx.render(rsx! { - div { - display: "flex", - flex_direction: "row", - width: "100%", - height: "100%", - Editable{ - current_code: submitted_rsx_code.get().clone(), - }, - - textarea { - width: "50em", - height: "50em", - value: rsx_code, - oninput: move |evt| { - rsx_code.set(evt.value.clone()); - }, - } - - button { - height: "100%", - width: "10%", - onclick: move |_|{ - submitted_rsx_code.set(Some(rsx_code.get().clone())); - }, - "submit" - } - } - }) -} - -#[derive(PartialEq, Props)] -struct EditableProps { - #[props(!optional)] - current_code: Option, -} - -fn Editable(cx: Scope) -> Element { let count = use_state(&cx, || 170); - if let Some(code) = cx.props.current_code.as_ref() { - let rsx_index: RsxContext = cx.consume_context().unwrap(); - rsx_index.insert( - CodeLocation { - file: r"examples\hot_reload.rs".to_string(), - line: 94, - column: 15, - }, - code.clone(), - ); - } cx.render(rsx! { div { width: "100%", @@ -98,7 +13,7 @@ fn Editable(cx: Scope) -> Element { onclick: move |_| { count.modify(|count| *count + 10); }, - + p{"{__line_num:?}"} p { "High-Five counter: {count.to_string():?}", } diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 31755e396..6cba99ace 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -186,7 +186,7 @@ pub fn rsx(s: TokenStream) -> TokenStream { Ok(body) => { #[cfg(feature = "hot_reload")] { - use dioxus_rsx_interperter::captuered_context::CapturedContextBuilder; + use dioxus_rsx_interpreter::captuered_context::CapturedContextBuilder; match CapturedContextBuilder::from_call_body(body) { Ok(captured) => { diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 443b0a402..de07bb88a 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -15,6 +15,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] dioxus-core = { path = "../core", version = "^0.2.1", features = ["serialize"] } dioxus-html = { path = "../html", features = ["serialize"], version = "^0.2.1" } dioxus-interpreter-js = { path = "../interpreter", version = "^0.2.1" } +dioxus-rsx-interpreter = { path = "../rsx_interpreter", optional = true } serde = "1.0.136" serde_json = "1.0.79" @@ -32,6 +33,8 @@ webbrowser = "0.7.1" mime_guess = "2.0.3" dunce = "1.0.2" +interprocess = { version = "1.1.1", optional = true } + [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9.3" @@ -41,6 +44,7 @@ tokio_runtime = ["tokio"] fullscreen = ["wry/fullscreen"] transparent = ["wry/transparent"] tray = ["wry/tray"] +hot_reload = ["dioxus-rsx-interpreter", "interprocess"] [dev-dependencies] dioxus-core-macro = { path = "../core-macro" } diff --git a/packages/desktop/src/controller.rs b/packages/desktop/src/controller.rs index 2fa1355d2..9329e25f7 100644 --- a/packages/desktop/src/controller.rs +++ b/packages/desktop/src/controller.rs @@ -1,4 +1,5 @@ use crate::desktop_context::{DesktopContext, UserWindowEvent}; + use dioxus_core::*; use std::{ collections::HashMap, @@ -49,6 +50,96 @@ impl DesktopController { dom.base_scope().provide_context(window_context); + // allow other proccesses to send the new rsx text to the @dioxus ipc channel + #[cfg(feature = "hot_reload")] + { + use dioxus_rsx_interpreter::{ + error::Error, ErrorHandler, RsxContext, RsxData, SetRsxMessage, + }; + use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; + use std::io::{BufRead, BufReader, Write}; + use std::time::Duration; + + fn handle_error( + connection: std::io::Result, + ) -> Option { + connection + .map_err(|error| eprintln!("Incoming connection failed: {}", error)) + .ok() + } + + let latest_connection: Arc>>> = + Arc::new(Mutex::new(None)); + let latest_connection_handle = latest_connection.clone(); + let latest_connection_handle2 = latest_connection.clone(); + + struct DesktopErrorHandler { + latest_connection: Arc>>>, + } + impl ErrorHandler for DesktopErrorHandler { + fn handle_error(&self, err: Error) { + println!("reporting err: {err:?}"); + if let Some(conn) = &mut *self.latest_connection.lock().unwrap() { + if let Error::RecompileRequiredError(reason) = err { + conn.get_mut() + .write_all( + (serde_json::to_string(&reason).unwrap() + "\n") + .as_bytes(), + ) + .unwrap(); + println!("sending error"); + } + } + } + } + + let context = dom + .base_scope() + .provide_root_context(RsxContext::new(RsxData { + hm: HashMap::new(), + error_handler: Box::new(DesktopErrorHandler { + latest_connection: latest_connection_handle, + }), + })); + + std::thread::spawn(move || { + if let Ok(listener) = LocalSocketListener::bind("@dioxus") { + for conn in listener.incoming().filter_map(handle_error) { + // conn.set_nonblocking(true); + println!("connected"); + *latest_connection_handle2.lock().unwrap() = + Some(BufReader::new(conn)); + } + } + }); + + std::thread::spawn(move || { + loop { + if let Some(conn) = &mut *latest_connection.lock().unwrap() { + let mut buf = String::new(); + println!("reading"); + match conn.read_line(&mut buf) { + Ok(_) => { + println!("read"); + let message: SetRsxMessage = + serde_json::from_str(&buf).unwrap(); + println!("{:?}", message.location); + context.insert(message.location, message.new_text); + } + Err(err) => { + if err.kind() != std::io::ErrorKind::WouldBlock { + println!("{err}"); + break; + } + } + } + } + // give the error handler time to take the mutex + std::thread::sleep(Duration::from_millis(10)); + } + }); + } + let edits = dom.rebuild(); edit_queue diff --git a/packages/rsx_interpreter/src/lib.rs b/packages/rsx_interpreter/src/lib.rs index b4f05ab0b..7150b888e 100644 --- a/packages/rsx_interpreter/src/lib.rs +++ b/packages/rsx_interpreter/src/lib.rs @@ -1,5 +1,4 @@ use dioxus_core::{Component, Element, LazyNodes, Scope, VNode}; -use dioxus_hooks::*; use error::Error; use interperter::build; use serde::{Deserialize, Serialize}; @@ -11,7 +10,7 @@ use syn::parse_str; mod attributes; pub mod captuered_context; mod elements; -mod error; +pub mod error; mod interperter; #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] @@ -50,7 +49,7 @@ pub struct RsxContext { pub struct RsxData { pub hm: HashMap, - error_handle: Box, + pub error_handler: Box, } impl std::fmt::Debug for RsxData { @@ -60,6 +59,12 @@ impl std::fmt::Debug for RsxData { } impl RsxContext { + pub fn new(data: RsxData) -> Self { + Self { + data: Arc::new(RwLock::new(data)), + } + } + pub fn insert(&self, loc: CodeLocation, text: String) { self.data.write().unwrap().hm.insert(loc, text); } @@ -69,6 +74,16 @@ impl RsxContext { } pub fn report_error(&self, error: Error) { - (self.data.write().unwrap().error_handle)(error) + self.data.write().unwrap().error_handler.handle_error(error) } } + +pub trait ErrorHandler { + fn handle_error(&self, err: Error); +} + +#[derive(Serialize, Deserialize)] +pub struct SetRsxMessage { + pub location: CodeLocation, + pub new_text: String, +} diff --git a/src/lib.rs b/src/lib.rs index 3ca3f6b21..d9e723f3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,9 @@ pub mod events { pub use dioxus_rsx as rsx; +#[cfg(feature = "hot_reload")] +pub use dioxus_rsx_interpreter as rsx_interpreter; + pub mod prelude { pub use crate::hooks::*; pub use dioxus_core::prelude::*; @@ -60,7 +63,7 @@ pub mod prelude { pub use fermi::{use_atom_ref, use_init_atom_root, use_read, use_set, Atom, AtomRef}; #[cfg(feature = "hot_reload")] - pub use dioxus_rsx_interperter::{ + pub use dioxus_rsx_interpreter::{ captuered_context::{CapturedContext, FormattedArg, IfmtArgs}, get_line_num, interpert_rsx, CodeLocation, RsxContext, };