From 8e33b8e02711cc4108f4d05d2cf95d647ad697d7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 16 Jun 2022 09:11:02 -0500 Subject: [PATCH] improve speed when reloading the site --- src/server/hot_reload.rs | 91 +++++++++++++++++++++-------- src/server/mod.rs | 120 +++++++++++++++++++++------------------ 2 files changed, 132 insertions(+), 79 deletions(-) diff --git a/src/server/hot_reload.rs b/src/server/hot_reload.rs index 2011b8958..14f9ffbc6 100644 --- a/src/server/hot_reload.rs +++ b/src/server/hot_reload.rs @@ -2,12 +2,13 @@ use axum::{ extract::{ws::Message, Extension, TypedHeader, WebSocketUpgrade}, response::IntoResponse, }; +use dioxus_rsx_interpreter::SetRsxMessage; use std::{path::PathBuf, sync::Arc}; pub use crate::hot_reload::{find_rsx, DiffResult}; use crate::CrateConfig; -pub use dioxus_rsx_interpreter::{error::Error, CodeLocation, SetRsxMessage}; +pub use dioxus_rsx_interpreter::{error::Error, CodeLocation, SetManyRsxMessage}; pub use proc_macro2::TokenStream; pub use std::collections::HashMap; pub use std::sync::Mutex; @@ -19,7 +20,7 @@ use syn::spanned::Spanned; use tokio::sync::broadcast; pub struct HotReloadState { - pub messages: broadcast::Sender, + pub messages: broadcast::Sender, pub update: broadcast::Sender, pub last_file_rebuild: Arc>, pub watcher_config: CrateConfig, @@ -32,32 +33,76 @@ pub struct FileMap { impl FileMap { pub fn new(path: PathBuf) -> Self { + log::info!("Searching files for changes since last compile..."); fn find_rs_files(root: PathBuf) -> io::Result> { let mut files = HashMap::new(); if root.is_dir() { - let mut handles = Vec::new(); for entry in fs::read_dir(root)? { if let Ok(entry) = entry { let path = entry.path(); - handles.push(std::thread::spawn(move || find_rs_files(path))); + files.extend(find_rs_files(path)?); } } - for handle in handles { - files.extend(handle.join().unwrap()?); - } } else { if root.extension().map(|s| s.to_str()).flatten() == Some("rs") { - let mut file = File::open(root.clone()).unwrap(); - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - files.insert(root, src); + if let Ok(mut file) = File::open(root.clone()) { + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + files.insert(root, src); + } } } Ok(files) } - Self { - last_updated_time: SystemTime::now(), + + let last_updated_time = SystemTime::now(); + let result = Self { + last_updated_time, map: find_rs_files(path).unwrap(), + }; + log::info!("Files updated"); + result + } + + pub fn update(&mut self, path: PathBuf) { + log::info!("Updating files that changed since last compile..."); + self.last_updated_time = SystemTime::now(); + self.update_inner(path); + log::info!("Files updated"); + } + + fn update_inner(&mut self, path: PathBuf) { + if path.is_dir() { + if let Ok(files) = fs::read_dir(path) { + for entry in files { + if let Ok(entry) = entry { + if let Ok(md) = entry.metadata() { + if let Ok(time) = md.modified() { + if time < self.last_updated_time { + return; + } + } + } + let path = entry.path(); + self.update(path); + } + } + } + } else { + if path.extension().map(|s| s.to_str()).flatten() == Some("rs") { + if let Ok(mut file) = File::open(path.clone()) { + if let Ok(md) = file.metadata() { + if let Ok(time) = md.modified() { + if time < self.last_updated_time { + return; + } + } + } + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + self.map.insert(path, src); + } + } } } } @@ -70,7 +115,6 @@ pub async fn hot_reload_handler( ws.on_upgrade(|mut socket| async move { log::info!("🔥 Hot Reload WebSocket connected"); { - log::info!("Searching files for changes since last run..."); // update any files that changed before the websocket connected. let mut messages = Vec::new(); @@ -123,16 +167,14 @@ pub async fn hot_reload_handler( } } - for msg in &messages { - if socket - .send(Message::Text(serde_json::to_string(msg).unwrap())) - .await - .is_err() - { - return; - } + let msg = SetManyRsxMessage(messages); + if socket + .send(Message::Text(serde_json::to_string(&msg).unwrap())) + .await + .is_err() + { + return; } - log::info!("Updated page"); } let mut rx = state.messages.subscribe(); @@ -149,8 +191,7 @@ pub async fn hot_reload_handler( Error::ParseError(parse_error) => { log::error!("parse error:\n--> at {}:{}:{}\n\t{:?}", parse_error.location.file, parse_error.location.line, parse_error.location.column, parse_error.message); }, - Error::RecompileRequiredError(reason) => { - log::info!("{:?}", reason); + Error::RecompileRequiredError(_) => { if state.update.send("reload".to_string()).is_err() { break; } diff --git a/src/server/mod.rs b/src/server/mod.rs index 3397beac6..19791fd79 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,6 +6,7 @@ use axum::{ routing::{get, get_service}, Router, }; +use dioxus_rsx_interpreter::SetRsxMessage; use notify::{RecommendedWatcher, Watcher}; use syn::spanned::Spanned; @@ -67,67 +68,75 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> { .unwrap_or_else(|| vec![PathBuf::from("src")]); let mut watcher = RecommendedWatcher::new(move |evt: notify::Result| { - if let Ok(evt) = evt { - for path in evt.paths { - let mut file = File::open(path.clone()).unwrap(); - if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { - continue; - } - let mut src = String::new(); - file.read_to_string(&mut src).expect("Unable to read file"); - if src.is_empty() { - continue; - } - // find changes to the rsx in the file - if let Ok(syntax) = syn::parse_file(&src) { - let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); - if let Some(old_str) = last_file_rebuild.map.get(&path) { - if let Ok(old) = syn::parse_file(&old_str) { - match find_rsx(&syntax, &old) { - DiffResult::CodeChanged => { - log::info!("reload required"); - if chrono::Local::now().timestamp() > last_update_time { - let _ = reload_tx.send("reload".into()); - last_update_time = chrono::Local::now().timestamp(); + if chrono::Local::now().timestamp() > last_update_time { + if let Ok(evt) = evt { + let mut messages = Vec::new(); + for path in evt.paths { + let mut file = File::open(path.clone()).unwrap(); + if path.extension().map(|p| p.to_str()).flatten() != Some("rs") { + continue; + } + let mut src = String::new(); + file.read_to_string(&mut src).expect("Unable to read file"); + if src.is_empty() { + continue; + } + // find changes to the rsx in the file + if let Ok(syntax) = syn::parse_file(&src) { + let mut last_file_rebuild = last_file_rebuild.lock().unwrap(); + if let Some(old_str) = last_file_rebuild.map.get(&path) { + if let Ok(old) = syn::parse_file(&old_str) { + match find_rsx(&syntax, &old) { + DiffResult::CodeChanged => { + log::info!("reload required"); + let _ = reload_tx.send("reload time".into()); + break; } - } - DiffResult::RsxChanged(changed) => { - log::info!("reloading rsx"); - for (old, new) in changed.into_iter() { - let hr = get_location( - &path.strip_prefix(&crate_dir).unwrap().to_path_buf(), - old.to_token_stream(), - ); - // get the original source code to preserve whitespace - let span = new.span(); - let start = span.start(); - let end = span.end(); - let mut lines: Vec<_> = src - .lines() - .skip(start.line - 1) - .take(end.line - start.line + 1) - .collect(); - if let Some(first) = lines.first_mut() { - *first = first.split_at(start.column).1; + DiffResult::RsxChanged(changed) => { + log::info!("reloading rsx"); + for (old, new) in changed.into_iter() { + let hr = get_location( + &path + .strip_prefix(&crate_dir) + .unwrap() + .to_path_buf(), + old.to_token_stream(), + ); + // get the original source code to preserve whitespace + let span = new.span(); + let start = span.start(); + let end = span.end(); + let mut lines: Vec<_> = src + .lines() + .skip(start.line - 1) + .take(end.line - start.line + 1) + .collect(); + if let Some(first) = lines.first_mut() { + *first = first.split_at(start.column).1; + } + if let Some(last) = lines.last_mut() { + *last = last.split_at(end.column).0; + } + let rsx = lines.join("\n"); + messages.push(SetRsxMessage { + location: hr, + new_text: rsx, + }); } - if let Some(last) = lines.last_mut() { - *last = last.split_at(end.column).0; - } - let rsx = lines.join("\n"); - let _ = hot_reload_tx.send(SetRsxMessage { - location: hr, - new_text: rsx, - }); } } } + } else { + // if this is a new file, rebuild the project + *last_file_rebuild = FileMap::new(crate_dir.clone()); } - } else { - // if this is a new file, rebuild the project - *last_file_rebuild = FileMap::new(crate_dir.clone()); } } + if !messages.is_empty() { + let _ = hot_reload_tx.send(SetManyRsxMessage(messages)); + } } + last_update_time = chrono::Local::now().timestamp(); } }) .unwrap(); @@ -313,7 +322,7 @@ async fn ws_handler( _: Option>, Extension(state): Extension>, ) -> impl IntoResponse { - ws.on_upgrade(|mut socket| async move { + ws.max_send_queue(1).on_upgrade(|mut socket| async move { let mut rx = state.update.subscribe(); let reload_watcher = tokio::spawn(async move { loop { @@ -335,7 +344,7 @@ async fn ws_handler( } if let Some(file_map) = &state.last_file_rebuild { let mut write = file_map.lock().unwrap(); - *write = FileMap::new(state.watcher_config.crate_dir.clone()); + write.update(state.watcher_config.crate_dir.clone()); } } // ignore the error @@ -346,6 +355,9 @@ async fn ws_handler( { break; } + + // flush the errors after recompling + rx = rx.resubscribe(); } } });