From 1530445972b764a2110b0e9531f06329059f1c6b Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 22 Dec 2022 16:38:07 -0600 Subject: [PATCH] fix hot reloading rsx with nested rsx calls --- packages/core/src/create.rs | 34 +++++++++++++++++++++++++++++++- packages/core/src/diff.rs | 16 ++++++++++----- packages/core/src/virtual_dom.rs | 12 ++++++++--- packages/rsx/src/ifmt.rs | 2 +- packages/rsx/src/lib.rs | 5 +++++ 5 files changed, 59 insertions(+), 10 deletions(-) diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index f2d315034..c9e1437cd 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -354,10 +354,42 @@ impl<'b> VirtualDom { id } + /// Insert a new template into the VirtualDom's template registry + pub(crate) fn register_template_first_byte_index(&mut self, mut template: Template<'static>) { + // First, make sure we mark the template as seen, regardless if we process it + let (path, _) = template.name.rsplit_once(':').unwrap(); + if let Some((_, old_template)) = self + .templates + .entry(path) + .or_default() + .iter_mut() + .min_by_key(|(byte_index, _)| **byte_index) + { + // the byte index of the hot reloaded template could be different + template.name = old_template.name; + *old_template = template; + } else { + panic!( + "Template {path} was not registered in\n{:#?}", + self.templates + ); + } + + // If it's all dynamic nodes, then we don't need to register it + if !template.is_completely_dynamic() { + self.mutations.templates.push(template); + } + } + /// Insert a new template into the VirtualDom's template registry pub(crate) fn register_template(&mut self, template: Template<'static>) { // First, make sure we mark the template as seen, regardless if we process it - self.templates.insert(template.name, template); + let (path, byte_index) = template.name.rsplit_once(':').unwrap(); + let byte_index = byte_index.parse::().unwrap(); + self.templates + .entry(path) + .or_default() + .insert(byte_index, template); // If it's all dynamic nodes, then we don't need to register it if !template.is_completely_dynamic() { diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index ed6e1355b..1d8d2da0a 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -57,11 +57,17 @@ impl<'b> VirtualDom { fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) { // If hot reloading is enabled, we need to make sure we're using the latest template #[cfg(debug_assertions)] - if let Some(template) = self.templates.get(right_template.template.get().name) { - if *template != right_template.template.get() { - right_template.template.set(*template); - if *template != left_template.template.get() { - return self.replace(left_template, [right_template]); + { + let (path, byte_index) = right_template.template.get().name.rsplit_once(':').unwrap(); + if let Some(map) = self.templates.get(path) { + let byte_index = byte_index.parse::().unwrap(); + if let Some(&template) = map.get(&byte_index) { + if template != right_template.template.get() { + right_template.template.set(template); + if template != left_template.template.get() { + return self.replace(left_template, [right_template]); + } + } } } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index db3213609..089386584 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -142,7 +142,8 @@ use std::{any::Any, borrow::BorrowMut, cell::Cell, collections::BTreeSet, future /// } /// ``` pub struct VirtualDom { - pub(crate) templates: FxHashMap>, + // Maps a template path to a map of byteindexes to templates + pub(crate) templates: FxHashMap>>, pub(crate) scopes: Slab>, pub(crate) dirty_scopes: BTreeSet, pub(crate) scheduler: Rc, @@ -459,12 +460,16 @@ impl VirtualDom { /// This is the primitive that enables hot-reloading. /// /// The caller must ensure that the template refrences the same dynamic attributes and nodes as the original template. + /// + /// This will only replace the the parent template, not any nested templates. pub fn replace_template(&mut self, template: Template<'static>) { - self.register_template(template); + self.register_template_first_byte_index(template); // iterating a slab is very inefficient, but this is a rare operation that will only happen during development so it's fine for (_, scope) in &self.scopes { if let Some(RenderReturn::Sync(Some(sync))) = scope.try_root_node() { - if sync.template.get().name == template.name { + if sync.template.get().name.rsplit_once(':').unwrap().0 + == template.name.rsplit_once(':').unwrap().0 + { let height = scope.height; self.dirty_scopes.insert(DirtyScope { height, @@ -473,6 +478,7 @@ impl VirtualDom { } } } + assert!(!self.dirty_scopes.is_empty()); } /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch. diff --git a/packages/rsx/src/ifmt.rs b/packages/rsx/src/ifmt.rs index e94884907..13256aa74 100644 --- a/packages/rsx/src/ifmt.rs +++ b/packages/rsx/src/ifmt.rs @@ -21,7 +21,7 @@ pub struct IfmtInput { impl IfmtInput { pub fn is_static(&self) -> bool { - matches!(self.segments.as_slice(), &[Segment::Literal(_)]) + matches!(self.segments.as_slice(), &[Segment::Literal(_)] | &[]) } } diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 124eccec1..9f1890899 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -187,6 +187,9 @@ impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> { out }); + let spndbg = format!("{:?}", self.roots[0].span()); + let root_col = spndbg[9..].split("..").next().unwrap(); + // Render and release the mutable borrow on context let roots = quote! { #( #root_printer ),* }; let node_printer = &context.dynamic_nodes; @@ -202,6 +205,8 @@ impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> { line!(), ":", column!(), + ":", + #root_col ), roots: &[ #roots ], node_paths: &[ #(#node_paths),* ],