Jonathan Kelley 1 周之前
父节点
当前提交
5dedc38c83
共有 3 个文件被更改,包括 259 次插入280 次删除
  1. 6 9
      packages/core/src/arena.rs
  2. 251 269
      packages/core/src/diff.rs
  3. 2 2
      packages/core/src/virtual_dom.rs

+ 6 - 9
packages/core/src/arena.rs

@@ -1,6 +1,3 @@
-use crate::innerlude::ScopeOrder;
-use crate::{virtual_dom::VirtualDom, ScopeId};
-
 /// An Element's unique identifier.
 ///
 /// `ElementId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
@@ -54,6 +51,12 @@ pub struct ElementPath {
     pub(crate) path: &'static [u8],
 }
 
+impl PartialEq<&[u8]> for ElementPath {
+    fn eq(&self, other: &&[u8]) -> bool {
+        self.path.eq(*other)
+    }
+}
+
 impl ElementPath {
     pub(crate) fn is_descendant(&self, small: &[u8]) -> bool {
         small.len() <= self.path.len() && small == &self.path[..small.len()]
@@ -75,9 +78,3 @@ fn is_descendant() {
     assert!(!event_path.is_descendant(&[1, 2, 3, 4, 5, 6]));
     assert!(!event_path.is_descendant(&[2, 3, 4]));
 }
-
-impl PartialEq<&[u8]> for ElementPath {
-    fn eq(&self, other: &&[u8]) -> bool {
-        self.path.eq(*other)
-    }
-}

+ 251 - 269
packages/core/src/diff.rs

@@ -7,13 +7,6 @@
 
 #![allow(clippy::too_many_arguments)]
 
-use std::{
-    any::TypeId,
-    iter::Peekable,
-    ops::{Deref, DerefMut},
-    rc::Rc,
-};
-
 use crate::{
     any_props::AnyProps,
     arena::ElementPath,
@@ -21,13 +14,18 @@ use crate::{
         ElementRef, MountId, ScopeOrder, SuspenseBoundaryProps, SuspenseBoundaryPropsWithOwner,
     },
     nodes::{AsVNode, VNode, VNodeMount},
-    prelude::{RuntimeGuard, SuspenseBoundary, SuspenseContext},
+    prelude::{RuntimeGuard, SuspenseContext},
     scopes::ScopeId,
-    Attribute, AttributeValue, DynamicNode, Element, ElementId, Runtime, ScopeState, TemplateNode,
-    VComponent, VText, VirtualDom, WriteMutations,
+    Attribute, AttributeValue, DynamicNode, Element, ElementId, Runtime, TemplateNode, VComponent,
+    VText, VirtualDom, WriteMutations,
 };
 use rustc_hash::{FxHashMap, FxHashSet};
-use slab::Slab;
+use std::{
+    any::TypeId,
+    iter::Peekable,
+    ops::{Deref, DerefMut},
+    rc::Rc,
+};
 
 /// A fiber progresses a given work tree by running scopes and diffing nodes.
 /// It queues work internally such that suspended scopes can be paused and resumed.
@@ -56,8 +54,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
     pub(crate) fn run_and_diff_scope(&mut self, scope_id: ScopeId) {
         let scope = &mut self.dom.scopes[scope_id.0];
         if SuspenseBoundaryProps::downcast_from_props(&mut *scope.props).is_some() {
-            todo!()
-            // SuspenseBoundaryProps::diff(scope_id)
+            self.diff_suspense(scope_id)
         } else {
             let new_nodes = self.dom.run_scope(scope_id);
             self.diff_scope(scope_id, new_nodes);
@@ -70,7 +67,9 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             let Ok(new_real_nodes) = &new_nodes else {
                 return;
             };
+
             let scope_state = &mut self.dom.scopes[scope.0];
+
             // Load the old and new rendered nodes
             let old = scope_state.last_rendered_node.take().unwrap();
 
@@ -81,10 +80,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             self.dom.scopes[scope.0].last_rendered_node = Some(new_nodes);
 
             if self.write && self.runtime.scope_should_render(scope) {
-                self.runtime
-                    .get_state(scope)
-                    .unwrap()
-                    .mount(&mut self.runtime);
+                self.runtime.get_state(scope).unwrap().mount(self.runtime);
             }
         })
     }
@@ -98,11 +94,10 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
         new_nodes: Element,
         parent: Option<ElementRef>,
     ) -> usize {
+        // If there are suspended scopes, we need to check if the scope is suspended before we diff it
+        // If it is suspended, we need to diff it but write the mutations nothing
+        // Note: It is important that we still diff the scope even if it is suspended, because the scope may render other child components which may change between renders
         self.runtime.clone().with_scope_on_stack(scope, || {
-            // If there are suspended scopes, we need to check if the scope is suspended before we diff it
-            // If it is suspended, we need to diff it but write the mutations nothing
-            // Note: It is important that we still diff the scope even if it is suspended, because the scope may render other child components which may change between renders
-
             // Create the node
             let nodes = self.create(new_nodes.as_vnode(), parent);
 
@@ -114,7 +109,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
                     .runtime
                     .get_state(scope)
                     .unwrap()
-                    .mount(&mut self.runtime);
+                    .mount(self.runtime);
             }
 
             nodes
@@ -128,7 +123,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
         replace_with: Option<usize>,
     ) {
         // If this is a suspense boundary, remove the suspended nodes as well
-        Self::remove_suspended_nodes(self, scope_id, destroy_component_state);
+        self.remove_suspended_nodes(scope_id, destroy_component_state);
 
         // Remove the component from the dom
         if let Some(node) = self.dom.scopes[scope_id.0].last_rendered_node.as_ref() {
@@ -139,8 +134,8 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             );
         };
 
+        // Now drop all the resources
         if destroy_component_state {
-            // Now drop all the resources
             self.drop_scope(scope_id);
         }
     }
@@ -282,7 +277,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
     /// If there is no offset, then this function returns None and the diffing is complete.
     fn diff_keyed_ends(
         &mut self,
-        mut old: &[VNode],
+        old: &[VNode],
         new: &[VNode],
         parent: Option<ElementRef>,
     ) -> Option<(usize, usize)> {
@@ -536,7 +531,6 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
 
     fn create_and_insert_before(
         &mut self,
-
         new: &[VNode],
         before: &VNode,
         parent: Option<ElementRef>,
@@ -546,17 +540,14 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
     }
 
     fn insert_before(&mut self, new: usize, before: &VNode) {
-        if self.write {
-            if new > 0 {
-                let id = self.find_first_element(before);
-                self.to.insert_nodes_before(id, new);
-            }
+        if self.write && new > 0 {
+            self.to
+                .insert_nodes_before(self.find_first_element(before), new);
         }
     }
 
     fn create_and_insert_after(
         &mut self,
-
         new: &[VNode],
         after: &VNode,
         parent: Option<ElementRef>,
@@ -566,53 +557,12 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
     }
 
     fn insert_after(&mut self, new: usize, after: &VNode) {
-        if self.write {
-            if new > 0 {
-                let id = self.find_last_element(after);
-                self.to.insert_nodes_after(id, new);
-            }
+        if self.write && new > 0 {
+            self.to
+                .insert_nodes_after(self.find_last_element(after), new);
         }
     }
 
-    fn create_children(&mut self, nodes: &[VNode], parent: Option<ElementRef>) -> usize {
-        nodes.iter().map(|child| self.create(child, parent)).sum()
-    }
-
-    fn get_mounted_parent(&self, mount: MountId) -> Option<ElementRef> {
-        let mounts = self.runtime.mounts.borrow();
-        mounts[mount.0].parent
-    }
-
-    fn get_mounted_dyn_node(&self, mount: MountId, dyn_node_idx: usize) -> usize {
-        let mounts = self.runtime.mounts.borrow();
-        mounts[mount.0].mounted_dynamic_nodes[dyn_node_idx]
-    }
-
-    fn set_mounted_dyn_node(&self, mount: MountId, dyn_node_idx: usize, value: usize) {
-        let mut mounts = self.runtime.mounts.borrow_mut();
-        mounts[mount.0].mounted_dynamic_nodes[dyn_node_idx] = value;
-    }
-
-    fn get_mounted_dyn_attr(&self, mount: MountId, dyn_attr_idx: usize) -> ElementId {
-        let mounts = self.runtime.mounts.borrow();
-        mounts[mount.0].mounted_attributes[dyn_attr_idx]
-    }
-
-    fn set_mounted_dyn_attr(&self, mount: MountId, dyn_attr_idx: usize, value: ElementId) {
-        let mut mounts = self.runtime.mounts.borrow_mut();
-        mounts[mount.0].mounted_attributes[dyn_attr_idx] = value;
-    }
-
-    fn get_mounted_root_node(&self, mount: MountId, root_idx: usize) -> ElementId {
-        let mounts = self.runtime.mounts.borrow();
-        mounts[mount.0].root_ids[root_idx]
-    }
-
-    fn set_mounted_root_node(&self, mount: MountId, root_idx: usize, value: ElementId) {
-        let mut mounts = self.runtime.mounts.borrow_mut();
-        mounts[mount.0].root_ids[root_idx] = value;
-    }
-
     /// Remove these nodes from the dom
     /// Wont generate mutations for the inner nodes
     fn remove_nodes(&mut self, nodes: &[VNode], replace_with: Option<usize>) {
@@ -721,29 +671,29 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
         self.create_scope(scope, new_node, parent)
     }
 
-    fn diff_node(&mut self, node: &VNode, new: &VNode) {
+    fn diff_node(&mut self, old: &VNode, new: &VNode) {
         // The node we are diffing from should always be mounted
         debug_assert!(
             self.dom
                 .runtime
                 .mounts
                 .borrow()
-                .get(node.mount.get().0)
+                .get(old.mount.get().0)
                 .is_some()
                 || !self.write
         );
 
         // If the templates are different, we need to replace the entire template
-        if node.template != new.template {
-            let mount_id = node.mount.get();
+        if old.template != new.template {
+            let mount_id = old.mount.get();
             let parent = self.get_mounted_parent(mount_id);
-            return self.replace(node, std::slice::from_ref(new), parent);
+            return self.replace(old, std::slice::from_ref(new), parent);
         }
 
-        self.move_mount_to(node, new);
+        self.move_mount_to(old, new);
 
         // If the templates are the same, we don't need to do anything, except copy over the mount information
-        if node == new {
+        if old == new {
             return;
         }
 
@@ -751,18 +701,18 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
         // Start with the attributes
         // Since the attributes are only side effects, we can skip diffing them entirely if the node is suspended and we aren't outputting mutations
         if self.write {
-            self.diff_attributes(node, new);
+            self.diff_attributes(old, new);
         }
 
         // Now diff the dynamic nodes
         let mount_id = new.mount.get();
-        for (dyn_node_idx, (old, new)) in node
+        for (dyn_node_idx, (old_dyn, new_dyn)) in old
             .dynamic_nodes
             .iter()
             .zip(new.dynamic_nodes.iter())
             .enumerate()
         {
-            self.diff_dynamic_node(node, mount_id, dyn_node_idx, old, new)
+            self.diff_dynamic_node(old, mount_id, dyn_node_idx, old_dyn, new_dyn)
         }
     }
 
@@ -861,8 +811,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             Some((id, Component(_))) => {
                 let scope = ScopeId(self.get_mounted_dyn_node(mount_id, id));
                 self.find_first_element(
-                    &self
-                        .dom
+                    self.dom
                         .get_scope(scope)
                         .expect("Scope should exist")
                         .root_node(),
@@ -1009,8 +958,8 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
                     } else {
                         self.to.remove_node(id);
                     }
+                    self.reclaim(id);
                 }
-                self.reclaim(id);
             }
         }
     }
@@ -1025,11 +974,11 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
         for (idx, dyn_node) in node.dynamic_nodes.iter().enumerate() {
             let path_len = template.node_paths.get(idx).map(|path| path.len());
             // Roots are cleaned up automatically above and nodes with a empty path are placeholders
-            if let Some(1) = path_len {
-                continue;
+            if let Some(2..) = path_len {
+                self.write = false;
+                self.remove_dynamic_node(mount, destroy_component_state, idx, dyn_node, None);
+                self.write = true;
             }
-
-            self.remove_dynamic_node(mount, destroy_component_state, idx, dyn_node, None)
         }
     }
 
@@ -1469,6 +1418,45 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
         }
     }
 
+    fn create_children(&mut self, nodes: &[VNode], parent: Option<ElementRef>) -> usize {
+        nodes.iter().map(|child| self.create(child, parent)).sum()
+    }
+
+    fn get_mounted_parent(&self, mount: MountId) -> Option<ElementRef> {
+        let mounts = self.runtime.mounts.borrow();
+        mounts[mount.0].parent
+    }
+
+    fn get_mounted_dyn_node(&self, mount: MountId, dyn_node_idx: usize) -> usize {
+        let mounts = self.runtime.mounts.borrow();
+        mounts[mount.0].mounted_dynamic_nodes[dyn_node_idx]
+    }
+
+    fn set_mounted_dyn_node(&self, mount: MountId, dyn_node_idx: usize, value: usize) {
+        let mut mounts = self.runtime.mounts.borrow_mut();
+        mounts[mount.0].mounted_dynamic_nodes[dyn_node_idx] = value;
+    }
+
+    fn get_mounted_dyn_attr(&self, mount: MountId, dyn_attr_idx: usize) -> ElementId {
+        let mounts = self.runtime.mounts.borrow();
+        mounts[mount.0].mounted_attributes[dyn_attr_idx]
+    }
+
+    fn set_mounted_dyn_attr(&self, mount: MountId, dyn_attr_idx: usize, value: ElementId) {
+        let mut mounts = self.runtime.mounts.borrow_mut();
+        mounts[mount.0].mounted_attributes[dyn_attr_idx] = value;
+    }
+
+    fn get_mounted_root_node(&self, mount: MountId, root_idx: usize) -> ElementId {
+        let mounts = self.runtime.mounts.borrow();
+        mounts[mount.0].root_ids[root_idx]
+    }
+
+    fn set_mounted_root_node(&self, mount: MountId, root_idx: usize, value: ElementId) {
+        let mut mounts = self.runtime.mounts.borrow_mut();
+        mounts[mount.0].root_ids[root_idx] = value;
+    }
+
     fn load_template_root(&mut self, node: &VNode, mount: MountId, root_idx: usize) -> ElementId {
         // Get an ID for this root since it's a real root
         let this_id = self.next_element();
@@ -1536,14 +1524,14 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
                         Some((_, DynamicNode::Fragment(nodes))) => {
                             let mut accumulated = 0;
                             for node in nodes {
-                                accumulated += push_all_inner(&node, dom, to);
+                                accumulated += push_all_inner(node, dom, to);
                             }
                             accumulated
                         }
                         Some((idx, DynamicNode::Component(_))) => {
                             let scope = ScopeId(mount.mounted_dynamic_nodes[idx]);
                             let node = dom.get_scope(scope).unwrap().root_node();
-                            push_all_inner(&node, dom, to)
+                            push_all_inner(node, dom, to)
                         }
                         // This is a static root node or a single dynamic node, just push it
                         None | Some((_, DynamicNode::Placeholder(_) | DynamicNode::Text(_))) => {
@@ -1555,15 +1543,16 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
                 .sum()
         }
 
-        push_all_inner(node, &self.dom, self.to)
+        push_all_inner(node, self.dom, self.to)
     }
 
     fn remove_suspended_nodes(&mut self, scope_id: ScopeId, destroy_component_state: bool) {
         let Some(scope) =
-            SuspenseContext::downcast_suspense_boundary_from_scope(&mut self.runtime, scope_id)
+            SuspenseContext::downcast_suspense_boundary_from_scope(self.runtime, scope_id)
         else {
             return;
         };
+
         // Remove the suspended nodes
         if let Some(node) = scope.take_suspended_nodes() {
             self.remove_node_inner(&node, destroy_component_state, None)
@@ -1656,7 +1645,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             let children = props.children.clone();
 
             // First always render the children in the background. Rendering the children may cause this boundary to suspend
-            suspense_context.under_suspense_boundary(&self.dom.runtime(), || {
+            suspense_context.under_suspense_boundary(self.runtime, || {
                 let write = self.write;
                 self.write = false;
                 self.create(children.as_vnode(), parent);
@@ -1679,7 +1668,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             // If there are suspended futures, render the fallback
             let nodes_created = if !suspense_context.suspended_futures().is_empty() {
                 let (node, nodes_created) =
-                    suspense_context.in_suspense_placeholder(&self.dom.runtime(), || {
+                    suspense_context.in_suspense_placeholder(self.runtime, || {
                         let scope_state = &mut self.dom.scopes[scope_id.0];
                         let props =
                             SuspenseBoundaryProps::downcast_from_props(&mut *scope_state.props)
@@ -1705,19 +1694,16 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             } else {
                 // Otherwise just render the children in the real dom
                 debug_assert!(children.as_vnode().mount.get().mounted());
-                let nodes_created = suspense_context
-                    .under_suspense_boundary(&self.dom.runtime(), || {
-                        self.create(children.as_vnode(), parent)
-                    });
+                let nodes_created = suspense_context.under_suspense_boundary(self.runtime, || {
+                    self.create(children.as_vnode(), parent)
+                });
                 let scope_state = &mut self.dom.scopes[scope_id.0];
                 scope_state.last_rendered_node = Some(children);
-                let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
-                    &self.dom.runtime(),
-                    scope_id,
-                )
-                .unwrap();
+                let suspense_context =
+                    SuspenseContext::downcast_suspense_boundary_from_scope(self.runtime, scope_id)
+                        .unwrap();
                 suspense_context.take_suspended_nodes();
-                mark_suspense_resolved(&suspense_context, self.dom, scope_id);
+                self.mark_suspense_resolved(&suspense_context, scope_id);
 
                 nodes_created
             };
@@ -1725,6 +1711,158 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
         })
     }
 
+    pub(crate) fn diff_suspense(&mut self, scope_id: ScopeId) {
+        self.runtime.clone().with_scope_on_stack(scope_id, || {
+            let scope = &mut self.dom.scopes[scope_id.0];
+            let myself = SuspenseBoundaryProps::downcast_from_props(&mut *scope.props)
+                .unwrap()
+                .clone();
+
+            let last_rendered_node = scope.last_rendered_node.as_ref().unwrap().clone();
+
+            let SuspenseBoundaryProps {
+                fallback, children, ..
+            } = myself;
+
+            let suspense_context = scope.state().suspense_boundary().unwrap().clone();
+            let suspended_nodes = suspense_context.suspended_nodes();
+            let suspended = !suspense_context.suspended_futures().is_empty();
+            match (suspended_nodes, suspended) {
+                // We already have suspended nodes that still need to be suspended
+                // Just diff the normal and suspended nodes
+                (Some(suspended_nodes), true) => {
+                    let new_suspended_nodes: VNode = children.into();
+
+                    // Diff the placeholder nodes in the dom
+                    let new_placeholder =
+                        suspense_context.in_suspense_placeholder(self.runtime, || {
+                            let old_placeholder = last_rendered_node;
+                            let new_placeholder = fallback.call(suspense_context.clone());
+
+                            self.write = true;
+                            self.diff_node(old_placeholder.as_vnode(), new_placeholder.as_vnode());
+                            self.write = false;
+
+                            new_placeholder
+                        });
+
+                    // Set the last rendered node to the placeholder
+                    self.dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
+
+                    // Diff the suspended nodes in the background
+                    suspense_context.under_suspense_boundary(self.runtime, || {
+                        self.write = false;
+                        self.diff_node(&suspended_nodes, &new_suspended_nodes);
+                        self.write = false;
+                    });
+
+                    let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
+                        self.runtime,
+                        scope_id,
+                    )
+                    .unwrap();
+                    suspense_context.set_suspended_nodes(new_suspended_nodes);
+                }
+
+                // We have no suspended nodes, and we are not suspended. Just diff the children like normal
+                (None, false) => {
+                    let old_children = last_rendered_node;
+                    let new_children = children;
+
+                    suspense_context.under_suspense_boundary(self.runtime, || {
+                        self.write = true;
+                        self.diff_node(old_children.as_vnode(), new_children.as_vnode());
+                        self.write = false;
+                    });
+
+                    // Set the last rendered node to the new children
+                    self.dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
+                }
+
+                // We have no suspended nodes, but we just became suspended. Move the children to the background
+                (None, true) => {
+                    let old_children = last_rendered_node.as_vnode();
+                    let new_children: VNode = children.into();
+
+                    let new_placeholder = fallback.call(suspense_context.clone());
+
+                    // Move the children to the background
+                    let parent = self.get_mounted_parent(old_children.mount.get());
+
+                    suspense_context.in_suspense_placeholder(self.runtime, || {
+                        self.write = false;
+                        self.move_node_to_background(
+                            old_children,
+                            std::slice::from_ref(new_placeholder.as_vnode()),
+                            parent,
+                        );
+                        self.write = true;
+                    });
+
+                    // Then diff the new children in the background
+                    suspense_context.under_suspense_boundary(self.runtime, || {
+                        self.write = false;
+                        self.diff_node(old_children, &new_children);
+                        self.write = true;
+                    });
+
+                    // Set the last rendered node to the new suspense placeholder
+                    self.dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
+
+                    let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
+                        self.runtime,
+                        scope_id,
+                    )
+                    .unwrap();
+                    suspense_context.set_suspended_nodes(new_children);
+
+                    // Move from a resolved suspense state to an suspended state
+                    self.dom.resolved_scopes.retain(|&id| id != scope_id);
+                }
+
+                // We have suspended nodes, but we just got out of suspense. Move the suspended nodes to the foreground
+                (Some(_), false) => {
+                    // Take the suspended nodes out of the suspense boundary so the children know that the boundary is not suspended while diffing
+                    let old_suspended_nodes = suspense_context.take_suspended_nodes().unwrap();
+                    let old_placeholder = last_rendered_node;
+                    let new_children = children;
+
+                    // First diff the two children nodes in the background
+                    suspense_context.under_suspense_boundary(self.runtime, || {
+                        self.write = false;
+                        self.diff_node(&old_suspended_nodes, new_children.as_vnode());
+                        self.write = true;
+
+                        // Then replace the placeholder with the new children
+                        let mount = old_placeholder.as_vnode().mount.get();
+                        let parent = self.get_mounted_parent(mount);
+                        self.replace(
+                            old_placeholder.as_vnode(),
+                            std::slice::from_ref(new_children.as_vnode()),
+                            parent,
+                        );
+                    });
+
+                    // Set the last rendered node to the new children
+                    self.dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
+                    self.mark_suspense_resolved(&suspense_context, scope_id);
+                }
+            }
+        })
+    }
+
+    /// Move to a resolved suspense state
+    pub(crate) fn mark_suspense_resolved(
+        &mut self,
+        suspense_context: &SuspenseContext,
+        scope_id: ScopeId,
+    ) {
+        self.dom.resolved_scopes.push(scope_id);
+
+        // Run any closures that were waiting for the suspense to resolve
+        suspense_context.run_resolved_closures(self.runtime);
+    }
+
     #[doc(hidden)]
     /// Manually rerun the children of this suspense boundary without diffing against the old nodes.
     ///
@@ -1767,7 +1905,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             // Unmount any children to reset any scopes under this suspense boundary
             let children = props.children.clone();
             let suspense_context =
-                SuspenseContext::downcast_suspense_boundary_from_scope(&self.runtime, scope_id)
+                SuspenseContext::downcast_suspense_boundary_from_scope(self.runtime, scope_id)
                     .unwrap();
 
             // Take the suspended nodes out of the suspense boundary so the children know that the boundary is not suspended while diffing
@@ -1789,7 +1927,7 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             children.as_vnode().mount.take();
 
             // First always render the children in the background. Rendering the children may cause this boundary to suspend
-            suspense_context.under_suspense_boundary(&self.runtime, || {
+            suspense_context.under_suspense_boundary(self.runtime, || {
                 self.write = true;
                 self.create(children.as_vnode(), parent);
                 self.write = false;
@@ -1803,146 +1941,9 @@ impl<'a, 'b, M: WriteMutations> Fiber<'a, 'b, M> {
             scope_state.last_rendered_node = Some(children);
 
             // Run any closures that were waiting for the suspense to resolve
-            suspense_context.run_resolved_closures(&self.runtime);
+            suspense_context.run_resolved_closures(self.runtime);
         })
     }
-
-    pub(crate) fn diff_suspense(&mut self, scope_id: ScopeId) {
-        todo!()
-        // self.runtime.clone().with_scope_on_stack(scope_id, || {
-        //     let scope = &mut dom.scopes[scope_id.0];
-        //     let myself = SuspenseBoundaryProps::downcast_from_props(&mut *scope.props)
-        //         .unwrap()
-        //         .clone();
-
-        //     let last_rendered_node = scope.last_rendered_node.as_ref().unwrap().clone();
-
-        //     let SuspenseBoundaryProps {
-        //         fallback, children, ..
-        //     } = myself;
-
-        //     let suspense_context = scope.state().suspense_boundary().unwrap().clone();
-        //     let suspended_nodes = suspense_context.suspended_nodes();
-        //     let suspended = !suspense_context.suspended_futures().is_empty();
-        //     match (suspended_nodes, suspended) {
-        //         // We already have suspended nodes that still need to be suspended
-        //         // Just diff the normal and suspended nodes
-        //         (Some(suspended_nodes), true) => {
-        //             let new_suspended_nodes: VNode = children.into();
-
-        //             // Diff the placeholder nodes in the dom
-        //             let new_placeholder =
-        //                 suspense_context.in_suspense_placeholder(&dom.runtime(), || {
-        //                     let old_placeholder = last_rendered_node;
-        //                     let new_placeholder = fallback.call(suspense_context.clone());
-
-        //                     old_placeholder.as_vnode().diff_node(
-        //                         new_placeholder.as_vnode(),
-        //                         dom,
-        //                         to,
-        //                         true,
-        //                     );
-        //                     new_placeholder
-        //                 });
-
-        //             // Set the last rendered node to the placeholder
-        //             dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
-
-        //             // Diff the suspended nodes in the background
-        //             suspense_context.under_suspense_boundary(&dom.runtime(), || {
-        //                 suspended_nodes.diff_node(&new_suspended_nodes, dom, to, false);
-        //             });
-
-        //             let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
-        //                 &dom.runtime,
-        //                 scope_id,
-        //             )
-        //             .unwrap();
-        //             suspense_context.set_suspended_nodes(new_suspended_nodes);
-        //         }
-        //         // We have no suspended nodes, and we are not suspended. Just diff the children like normal
-        //         (None, false) => {
-        //             let old_children = last_rendered_node;
-        //             let new_children = children;
-
-        //             suspense_context.under_suspense_boundary(&dom.runtime(), || {
-        //                 old_children
-        //                     .as_vnode()
-        //                     .diff_node(new_children.as_vnode(), dom, to, true);
-        //             });
-
-        //             // Set the last rendered node to the new children
-        //             dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
-        //         }
-        //         // We have no suspended nodes, but we just became suspended. Move the children to the background
-        //         (None, true) => {
-        //             let old_children = last_rendered_node.as_vnode();
-        //             let new_children: VNode = children.into();
-
-        //             let new_placeholder = fallback.call(suspense_context.clone());
-
-        //             // Move the children to the background
-        //             let mount = old_children.mount.get();
-        //             let parent = dom.get_mounted_parent(mount);
-
-        //             suspense_context.in_suspense_placeholder(&dom.runtime(), || {
-        //                 old_children.move_node_to_background(
-        //                     std::slice::from_ref(new_placeholder.as_vnode()),
-        //                     parent,
-        //                     dom,
-        //                     to,
-        //                     false,
-        //                 );
-        //             });
-
-        //             // Then diff the new children in the background
-        //             suspense_context.under_suspense_boundary(&dom.runtime(), || {
-        //                 old_children.diff_node(&new_children, dom, to, false);
-        //             });
-
-        //             // Set the last rendered node to the new suspense placeholder
-        //             dom.scopes[scope_id.0].last_rendered_node = Some(new_placeholder);
-
-        //             let suspense_context = SuspenseContext::downcast_suspense_boundary_from_scope(
-        //                 &dom.runtime,
-        //                 scope_id,
-        //             )
-        //             .unwrap();
-        //             suspense_context.set_suspended_nodes(new_children);
-
-        //             un_resolve_suspense(dom, scope_id);
-        //         }
-        //         // We have suspended nodes, but we just got out of suspense. Move the suspended nodes to the foreground
-        //         (Some(_), false) => {
-        //             // Take the suspended nodes out of the suspense boundary so the children know that the boundary is not suspended while diffing
-        //             let old_suspended_nodes = suspense_context.take_suspended_nodes().unwrap();
-        //             let old_placeholder = last_rendered_node;
-        //             let new_children = children;
-
-        //             // First diff the two children nodes in the background
-        //             suspense_context.under_suspense_boundary(&dom.runtime(), || {
-        //                 old_suspended_nodes.diff_node(new_children.as_vnode(), dom, to, false);
-
-        //                 // Then replace the placeholder with the new children
-        //                 let mount = old_placeholder.as_vnode().mount.get();
-        //                 let parent = dom.get_mounted_parent(mount);
-        //                 old_placeholder.as_vnode().replace(
-        //                     std::slice::from_ref(new_children.as_vnode()),
-        //                     parent,
-        //                     dom,
-        //                     to,
-        //                     true,
-        //                 );
-        //             });
-
-        //             // Set the last rendered node to the new children
-        //             dom.scopes[scope_id.0].last_rendered_node = Some(new_children);
-
-        //             mark_suspense_resolved(&suspense_context, dom, scope_id);
-        //         }
-        //     }
-        // })
-    }
 }
 
 /// We can apply various optimizations to dynamic nodes that are the single child of their parent.
@@ -1974,27 +1975,8 @@ fn is_dyn_node_only_child(node: &VNode, idx: usize) -> bool {
 }
 
 /// Try to get the dynamic node and its index for a root node
-fn get_dynamic_root_node_and_id<'n>(
-    node: &'n VNode,
-    root_idx: usize,
-) -> Option<(usize, &'n DynamicNode)> {
+fn get_dynamic_root_node_and_id(node: &VNode, root_idx: usize) -> Option<(usize, &DynamicNode)> {
     node.template.roots[root_idx]
         .dynamic_id()
         .map(|id| (id, &node.dynamic_nodes[id]))
 }
-
-/// Move to a resolved suspense state
-pub(crate) fn mark_suspense_resolved(
-    suspense_context: &SuspenseContext,
-    dom: &mut VirtualDom,
-    scope_id: ScopeId,
-) {
-    dom.resolved_scopes.push(scope_id);
-    // Run any closures that were waiting for the suspense to resolve
-    suspense_context.run_resolved_closures(&dom.runtime);
-}
-
-/// Move from a resolved suspense state to an suspended state
-fn un_resolve_suspense(dom: &mut VirtualDom, scope_id: ScopeId) {
-    dom.resolved_scopes.retain(|&id| id != scope_id);
-}

+ 2 - 2
packages/core/src/virtual_dom.rs

@@ -722,8 +722,8 @@ impl VirtualDom {
                     if run_scope {
                         // If the scope is dirty, run the scope and get the mutations
                         self.runtime.clone().while_rendering(|| {
-                            todo!()
-                            // self.run_and_diff_scope(None::<&mut NoOpMutations>, scope_id);
+                            Fiber::new(&self.runtime.clone(), self, &mut NoOpMutations, false)
+                                .run_and_diff_scope(scope_id);
                         });
 
                         tracing::trace!("Ran scope {:?} during suspense", scope_id);