1
0
Эх сурвалжийг харах

fix hot reloading rsx with nested rsx calls

Evan Almloff 2 жил өмнө
parent
commit
1530445972

+ 33 - 1
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::<usize>().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() {

+ 11 - 5
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::<usize>().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]);
+                        }
+                    }
                 }
             }
         }

+ 9 - 3
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<TemplateId, Template<'static>>,
+    // Maps a template path to a map of byteindexes to templates
+    pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template<'static>>>,
     pub(crate) scopes: Slab<Box<ScopeState>>,
     pub(crate) dirty_scopes: BTreeSet<DirtyScope>,
     pub(crate) scheduler: Rc<Scheduler>,
@@ -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.

+ 1 - 1
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(_)] | &[])
     }
 }
 

+ 5 - 0
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),* ],