2022-01-31 17:49:21 +00:00
#![ warn(clippy::pedantic) ]
#![ allow(clippy::cast_possible_truncation) ]
2022-05-23 01:44:32 +00:00
//! This module contains the stateful [`DiffState`] and all methods to diff [`VNode`]s, their properties, and their children.
2022-02-01 20:00:36 +00:00
//!
//! The [`DiffState`] calculates the diffs between the old and new frames, updates the new nodes, and generates a set
2022-05-23 01:44:32 +00:00
//! of mutations for the renderer to apply.
2022-02-01 20:00:36 +00:00
//!
//! ## Notice:
//!
//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and then modified to support
2022-05-23 02:21:28 +00:00
//! Components, Fragments, Suspense, `SubTree` memoization, incremental diffing, cancellation, pausing, priority
2022-02-01 20:00:36 +00:00
//! scheduling, and additional batching operations.
//!
//! ## Implementation Details:
//!
//! ### IDs for elements
//! --------------------
2022-05-23 01:44:32 +00:00
//! All nodes are addressed by their IDs.
2022-02-01 20:00:36 +00:00
//! We don't necessarily require that DOM changes happen instantly during the diffing process, so the implementor may choose
//! to batch nodes if it is more performant for their application. The element IDs are indices into the internal element
//! array. The expectation is that implementors will use the ID as an index into a Vec of real nodes, allowing for passive
2022-05-23 02:21:28 +00:00
//! garbage collection as the [`crate::VirtualDom`] replaces old nodes.
2022-02-01 20:00:36 +00:00
//!
//! When new vnodes are created through `cx.render`, they won't know which real node they correspond to. During diffing,
//! we always make sure to copy over the ID. If we don't do this properly, the [`ElementId`] will be populated incorrectly
//! and brick the user's page.
//!
//! ### Fragment Support
//! --------------------
//! Fragments (nodes without a parent) are supported through a combination of "replace with" and anchor vnodes. Fragments
//! can be particularly challenging when they are empty, so the anchor node lets us "reserve" a spot for the empty
2022-05-23 02:21:28 +00:00
//! fragment to be replaced with when it is no longer empty. This is guaranteed by logic in the [`crate::innerlude::NodeFactory`] - it is
2022-02-01 20:00:36 +00:00
//! impossible to craft a fragment with 0 elements - they must always have at least a single placeholder element. Adding
//! "dummy" nodes _is_ inefficient, but it makes our diffing algorithm faster and the implementation is completely up to
//! the platform.
//!
//! Other implementations either don't support fragments or use a "child + sibling" pattern to represent them. Our code is
//! vastly simpler and more performant when we can just create a placeholder element while the fragment has no children.
//!
//! ### Suspense
//! ------------
//! Dioxus implements Suspense slightly differently than React. In React, each fiber is manually progressed until it runs
//! into a promise-like value. React will then work on the next "ready" fiber, checking back on the previous fiber once
//! it has finished its new work. In Dioxus, we use a similar approach, but try to completely render the tree before
//! switching sub-fibers. Instead, each future is submitted into a futures-queue and the node is manually loaded later on.
2022-05-23 02:21:28 +00:00
//! Due to the frequent calls to [`crate::virtual_dom::VirtualDom::work_with_deadline`] we can get the pure "fetch-as-you-render" behavior of React Fiber.
2022-02-01 20:00:36 +00:00
//!
//! We're able to use this approach because we use placeholder nodes - futures that aren't ready still get submitted to
//! DOM, but as a placeholder.
//!
//! Right now, the "suspense" queue is intertwined with hooks. In the future, we should allow any future to drive attributes
2022-05-23 02:21:28 +00:00
//! and contents, without the need for a `use_suspense` hook. In the interim, this is the quickest way to get Suspense working.
2022-02-01 20:00:36 +00:00
//!
//! ## Subtree Memoization
//! -----------------------
//! We also employ "subtree memoization" which saves us from having to check trees which hold no dynamic content. We can
//! detect if a subtree is "static" by checking if its children are "static". Since we dive into the tree depth-first, the
//! calls to "create" propagate this information upwards. Structures like the one below are entirely static:
//! ```rust, ignore
//! rsx!( div { class: "hello world", "this node is entirely static" } )
//! ```
//! Because the subtrees won't be diffed, their "real node" data will be stale (invalid), so it's up to the reconciler to
//! track nodes created in a scope and clean up all relevant data. Support for this is currently WIP and depends on comp-time
//! hashing of the subtree from the rsx! macro. We do a very limited form of static analysis via static string pointers as
//! a way of short-circuiting the most expensive checks.
//!
//! ## Bloom Filter and Heuristics
//! ------------------------------
//! For all components, we employ some basic heuristics to speed up allocations and pre-size bump arenas. The heuristics are
//! currently very rough, but will get better as time goes on. The information currently tracked includes the size of a
//! bump arena after first render, the number of hooks, and the number of nodes in the tree.
//!
//! ## Garbage Collection
//! ---------------------
//! Dioxus uses a passive garbage collection system to clean up old nodes once the work has been completed. This garbage
//! collection is done internally once the main diffing work is complete. After the "garbage" is collected, Dioxus will then
//! start to re-use old keys for new nodes. This results in a passive memory management system that is very efficient.
//!
//! The IDs used by the key/map are just an index into a Vec. This means that Dioxus will drive the key allocation strategy
//! so the client only needs to maintain a simple list of nodes. By default, Dioxus will not manually clean up old nodes
//! for the client. As new nodes are created, old nodes will be over-written.
//!
//! ## Further Reading and Thoughts
//! ----------------------------
//! There are more ways of increasing diff performance here that are currently not implemented.
//! - Strong memoization of subtrees.
//! - Guided diffing.
//! - Certain web-dom-specific optimizations.
//!
//! More info on how to improve this diffing algorithm:
//! - <https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/>
2022-01-31 17:49:21 +00:00
use crate ::innerlude ::{
AnyProps , ElementId , Mutations , ScopeArena , ScopeId , VComponent , VElement , VFragment , VNode ,
VPlaceholder , VText ,
} ;
2021-08-24 15:03:19 +00:00
use fxhash ::{ FxHashMap , FxHashSet } ;
2021-11-09 17:18:45 +00:00
use smallvec ::{ smallvec , SmallVec } ;
2022-01-30 23:34:24 +00:00
2022-01-31 08:27:43 +00:00
pub ( crate ) struct DiffState < ' bump > {
2021-12-13 00:47:13 +00:00
pub ( crate ) scopes : & ' bump ScopeArena ,
pub ( crate ) mutations : Mutations < ' bump > ,
pub ( crate ) force_diff : bool ,
2022-01-30 23:34:24 +00:00
pub ( crate ) element_stack : SmallVec < [ ElementId ; 10 ] > ,
pub ( crate ) scope_stack : SmallVec < [ ScopeId ; 5 ] > ,
2021-11-07 00:59:46 +00:00
}
2022-01-31 08:27:43 +00:00
impl < ' b > DiffState < ' b > {
2022-01-30 23:34:24 +00:00
pub fn new ( scopes : & ' b ScopeArena ) -> Self {
2021-11-07 00:59:46 +00:00
Self {
2021-11-09 07:11:44 +00:00
scopes ,
mutations : Mutations ::new ( ) ,
2021-11-07 04:06:00 +00:00
force_diff : false ,
2021-11-12 02:34:20 +00:00
element_stack : smallvec ! [ ] ,
2022-01-30 23:34:24 +00:00
scope_stack : smallvec ! [ ] ,
2021-11-09 17:18:45 +00:00
}
}
2022-01-30 23:34:24 +00:00
pub fn diff_scope ( & mut self , scopeid : ScopeId ) {
let ( old , new ) = ( self . scopes . wip_head ( scopeid ) , self . scopes . fin_head ( scopeid ) ) ;
let scope = self . scopes . get_scope ( scopeid ) . unwrap ( ) ;
2022-01-31 07:33:16 +00:00
2022-02-03 01:04:55 +00:00
self . scope_stack . push ( scopeid ) ;
self . element_stack . push ( scope . container ) ;
{
self . diff_node ( old , new ) ;
}
self . element_stack . pop ( ) ;
self . scope_stack . pop ( ) ;
2022-01-31 07:33:16 +00:00
self . mutations . mark_dirty_scope ( scopeid ) ;
2021-11-09 17:18:45 +00:00
}
2022-01-30 23:34:24 +00:00
pub fn diff_node ( & mut self , old_node : & ' b VNode < ' b > , new_node : & ' b VNode < ' b > ) {
2022-01-31 17:49:21 +00:00
use VNode ::{ Component , Element , Fragment , Placeholder , Text } ;
2022-01-30 23:34:24 +00:00
match ( old_node , new_node ) {
( Text ( old ) , Text ( new ) ) = > {
2022-02-03 01:04:55 +00:00
self . diff_text_nodes ( old , new , old_node , new_node ) ;
2021-11-07 00:59:46 +00:00
}
2021-11-05 20:28:08 +00:00
2022-01-30 23:34:24 +00:00
( Placeholder ( old ) , Placeholder ( new ) ) = > {
2022-02-03 01:04:55 +00:00
self . diff_placeholder_nodes ( old , new , old_node , new_node ) ;
2021-11-05 20:28:08 +00:00
}
2021-08-27 13:40:04 +00:00
2022-01-30 23:34:24 +00:00
( Element ( old ) , Element ( new ) ) = > {
2022-01-31 17:49:21 +00:00
self . diff_element_nodes ( old , new , old_node , new_node ) ;
2021-08-22 21:08:25 +00:00
}
2021-08-24 14:01:01 +00:00
2022-01-30 23:34:24 +00:00
( Component ( old ) , Component ( new ) ) = > {
2022-01-31 17:49:21 +00:00
self . diff_component_nodes ( old_node , new_node , * old , * new ) ;
2021-08-22 21:08:25 +00:00
}
2021-08-23 16:11:45 +00:00
2022-01-30 23:34:24 +00:00
( Fragment ( old ) , Fragment ( new ) ) = > {
2022-01-31 17:49:21 +00:00
self . diff_fragment_nodes ( old , new ) ;
2021-09-20 16:32:21 +00:00
}
2022-01-30 23:34:24 +00:00
(
Component ( _ ) | Fragment ( _ ) | Text ( _ ) | Element ( _ ) | Placeholder ( _ ) ,
Component ( _ ) | Fragment ( _ ) | Text ( _ ) | Element ( _ ) | Placeholder ( _ ) ,
) = > self . replace_node ( old_node , new_node ) ,
2021-08-22 21:08:25 +00:00
}
}
2022-01-30 23:34:24 +00:00
pub fn create_node ( & mut self , node : & ' b VNode < ' b > ) -> usize {
2021-08-20 15:39:13 +00:00
match node {
2021-11-09 07:11:44 +00:00
VNode ::Text ( vtext ) = > self . create_text_node ( vtext , node ) ,
2021-11-23 20:53:57 +00:00
VNode ::Placeholder ( anchor ) = > self . create_anchor_node ( anchor , node ) ,
2021-11-09 07:11:44 +00:00
VNode ::Element ( element ) = > self . create_element_node ( element , node ) ,
VNode ::Fragment ( frag ) = > self . create_fragment_node ( frag ) ,
2021-11-28 21:25:42 +00:00
VNode ::Component ( component ) = > self . create_component_node ( * component ) ,
2021-08-20 15:39:13 +00:00
}
}
2022-02-01 20:00:36 +00:00
fn create_text_node ( & mut self , text : & ' b VText < ' b > , node : & ' b VNode < ' b > ) -> usize {
2021-11-09 07:16:25 +00:00
let real_id = self . scopes . reserve_node ( node ) ;
2022-02-01 20:00:36 +00:00
text . id . set ( Some ( real_id ) ) ;
self . mutations . create_text_node ( text . text , real_id ) ;
2022-01-30 23:34:24 +00:00
1
2021-08-20 14:44:58 +00:00
}
2021-08-20 14:34:41 +00:00
2022-01-30 23:34:24 +00:00
fn create_anchor_node ( & mut self , anchor : & ' b VPlaceholder , node : & ' b VNode < ' b > ) -> usize {
2021-11-09 07:16:25 +00:00
let real_id = self . scopes . reserve_node ( node ) ;
2021-12-21 05:46:10 +00:00
anchor . id . set ( Some ( real_id ) ) ;
2022-02-01 20:00:36 +00:00
self . mutations . create_placeholder ( real_id ) ;
2022-01-30 23:34:24 +00:00
1
2021-08-20 14:44:58 +00:00
}
2022-01-30 23:34:24 +00:00
fn create_element_node ( & mut self , element : & ' b VElement < ' b > , node : & ' b VNode < ' b > ) -> usize {
2021-08-20 14:44:58 +00:00
let VElement {
2021-12-21 05:46:10 +00:00
tag : tag_name ,
2021-08-20 14:44:58 +00:00
listeners ,
attributes ,
children ,
namespace ,
2021-12-21 05:46:10 +00:00
id : dom_id ,
parent : parent_id ,
2021-08-21 17:24:47 +00:00
..
2022-02-01 20:00:36 +00:00
} = & element ;
2021-08-20 14:44:58 +00:00
2022-02-01 20:00:36 +00:00
parent_id . set ( self . element_stack . last ( ) . copied ( ) ) ;
2021-09-20 16:32:21 +00:00
2021-11-12 02:34:20 +00:00
let real_id = self . scopes . reserve_node ( node ) ;
2022-02-01 20:00:36 +00:00
2021-08-24 16:43:46 +00:00
dom_id . set ( Some ( real_id ) ) ;
2022-01-31 17:49:21 +00:00
self . element_stack . push ( real_id ) ;
2022-02-01 20:00:36 +00:00
{
self . mutations . create_element ( tag_name , * namespace , real_id ) ;
2022-01-31 17:49:21 +00:00
2022-02-03 05:35:10 +00:00
let cur_scope_id = self . current_scope ( ) ;
2021-08-22 03:04:34 +00:00
2022-02-01 20:00:36 +00:00
for listener in listeners . iter ( ) {
2021-09-02 04:10:09 +00:00
listener . mounted_node . set ( Some ( real_id ) ) ;
2021-11-09 07:11:44 +00:00
self . mutations . new_event_listener ( listener , cur_scope_id ) ;
2021-11-07 00:59:46 +00:00
}
2021-08-20 14:44:58 +00:00
2022-02-01 20:00:36 +00:00
for attr in attributes . iter ( ) {
self . mutations . set_attribute ( attr , real_id . as_u64 ( ) ) ;
}
2021-08-20 14:44:58 +00:00
2022-02-01 20:00:36 +00:00
if ! children . is_empty ( ) {
self . create_and_append_children ( children ) ;
}
2021-11-28 21:25:42 +00:00
}
2022-01-31 17:29:11 +00:00
self . element_stack . pop ( ) ;
2022-01-30 23:34:24 +00:00
1
2021-08-20 14:44:58 +00:00
}
2022-01-30 23:34:24 +00:00
fn create_fragment_node ( & mut self , frag : & ' b VFragment < ' b > ) -> usize {
self . create_children ( frag . children )
2021-08-20 14:44:58 +00:00
}
2022-01-30 23:34:24 +00:00
fn create_component_node ( & mut self , vcomponent : & ' b VComponent < ' b > ) -> usize {
2022-02-03 05:35:10 +00:00
let parent_idx = self . current_scope ( ) ;
2021-08-20 14:44:58 +00:00
2022-02-02 16:33:02 +00:00
// the component might already exist - if it does, we need to reuse it
// this makes figure out when to drop the component more complicated
let new_idx = if let Some ( idx ) = vcomponent . scope . get ( ) {
assert! ( self . scopes . get_scope ( idx ) . is_some ( ) ) ;
idx
} else {
// Insert a new scope into our component list
let props : Box < dyn AnyProps + ' b > = vcomponent . props . borrow_mut ( ) . take ( ) . unwrap ( ) ;
let props : Box < dyn AnyProps + 'static > = unsafe { std ::mem ::transmute ( props ) } ;
self . scopes . new_with_key (
vcomponent . user_fc ,
props ,
Some ( parent_idx ) ,
self . element_stack . last ( ) . copied ( ) . unwrap ( ) ,
0 ,
)
} ;
2022-02-01 20:00:36 +00:00
// Actually initialize the caller's slot with the right address
vcomponent . scope . set ( Some ( new_idx ) ) ;
2021-08-20 14:44:58 +00:00
2022-01-31 17:49:21 +00:00
log ::trace! (
2022-02-20 22:37:46 +00:00
" created component \" {} \" , id: {:?} parent {:?} " ,
2022-01-31 07:33:16 +00:00
vcomponent . fn_name ,
2022-01-30 19:08:03 +00:00
new_idx ,
parent_idx ,
) ;
2022-01-31 17:49:21 +00:00
// if vcomponent.can_memoize {
// // todo: implement promotion logic. save us from boxing props that we don't need
// } else {
// // track this component internally so we know the right drop order
// }
2021-08-20 14:44:58 +00:00
2022-01-30 23:34:24 +00:00
self . enter_scope ( new_idx ) ;
2022-02-01 20:00:36 +00:00
let created = {
// Run the scope for one iteration to initialize it
self . scopes . run_scope ( new_idx ) ;
self . mutations . mark_dirty_scope ( new_idx ) ;
2021-12-19 04:03:59 +00:00
2022-02-01 20:00:36 +00:00
// Take the node that was just generated from running the component
let nextnode = self . scopes . fin_head ( new_idx ) ;
self . create_node ( nextnode )
} ;
2021-08-19 06:50:35 +00:00
2022-01-30 23:34:24 +00:00
self . leave_scope ( ) ;
2021-08-20 15:39:13 +00:00
2022-01-30 23:34:24 +00:00
created
2021-08-20 15:39:13 +00:00
}
2021-08-19 06:50:35 +00:00
2022-02-03 01:04:55 +00:00
pub ( crate ) fn diff_text_nodes (
& mut self ,
old : & ' b VText < ' b > ,
new : & ' b VText < ' b > ,
_old_node : & ' b VNode < ' b > ,
new_node : & ' b VNode < ' b > ,
) {
if std ::ptr ::eq ( old , new ) {
return ;
}
// if the node is comming back not assigned, that means it was borrowed but removed
let root = match old . id . get ( ) {
Some ( id ) = > id ,
None = > self . scopes . reserve_node ( new_node ) ,
} ;
if old . text ! = new . text {
self . mutations . set_text ( new . text , root . as_u64 ( ) ) ;
}
self . scopes . update_node ( new_node , root ) ;
new . id . set ( Some ( root ) ) ;
}
pub ( crate ) fn diff_placeholder_nodes (
& mut self ,
old : & ' b VPlaceholder ,
new : & ' b VPlaceholder ,
_old_node : & ' b VNode < ' b > ,
new_node : & ' b VNode < ' b > ,
) {
if std ::ptr ::eq ( old , new ) {
return ;
}
// if the node is comming back not assigned, that means it was borrowed but removed
let root = match old . id . get ( ) {
Some ( id ) = > id ,
None = > self . scopes . reserve_node ( new_node ) ,
} ;
self . scopes . update_node ( new_node , root ) ;
new . id . set ( Some ( root ) ) ;
}
2021-09-20 16:32:21 +00:00
fn diff_element_nodes (
2021-11-09 07:11:44 +00:00
& mut self ,
2022-01-30 23:34:24 +00:00
old : & ' b VElement < ' b > ,
new : & ' b VElement < ' b > ,
old_node : & ' b VNode < ' b > ,
new_node : & ' b VNode < ' b > ,
2021-09-20 16:32:21 +00:00
) {
2022-02-03 01:04:55 +00:00
if std ::ptr ::eq ( old , new ) {
return ;
}
2022-02-03 00:04:37 +00:00
// if the node is comming back not assigned, that means it was borrowed but removed
let root = match old . id . get ( ) {
Some ( id ) = > id ,
None = > self . scopes . reserve_node ( new_node ) ,
} ;
2021-08-19 06:50:35 +00:00
2021-08-20 15:39:13 +00:00
// If the element type is completely different, the element needs to be re-rendered completely
// This is an optimization React makes due to how users structure their code
//
// This case is rather rare (typically only in non-keyed lists)
2021-12-21 05:46:10 +00:00
if new . tag ! = old . tag | | new . namespace ! = old . namespace {
2022-01-30 23:34:24 +00:00
self . replace_node ( old_node , new_node ) ;
2021-08-20 15:39:13 +00:00
return ;
}
2021-11-12 04:55:57 +00:00
self . scopes . update_node ( new_node , root ) ;
2021-12-21 05:46:10 +00:00
new . id . set ( Some ( root ) ) ;
new . parent . set ( old . parent . get ( ) ) ;
2021-08-19 06:50:35 +00:00
2021-10-04 06:54:20 +00:00
// todo: attributes currently rely on the element on top of the stack, but in theory, we only need the id of the
// element to modify its attributes.
// it would result in fewer instructions if we just set the id directly.
// it would also clean up this code some, but that's not very important anyways
2021-08-20 15:39:13 +00:00
// Diff Attributes
//
// It's extraordinarily rare to have the number/order of attributes change
// In these cases, we just completely erase the old set and make a new set
//
// TODO: take a more efficient path than this
if old . attributes . len ( ) = = new . attributes . len ( ) {
for ( old_attr , new_attr ) in old . attributes . iter ( ) . zip ( new . attributes . iter ( ) ) {
2021-10-18 00:18:30 +00:00
if old_attr . value ! = new_attr . value | | new_attr . is_volatile {
2021-11-09 07:11:44 +00:00
self . mutations . set_attribute ( new_attr , root . as_u64 ( ) ) ;
2021-08-19 06:50:35 +00:00
}
}
2021-08-20 15:39:13 +00:00
} else {
for attribute in old . attributes {
2021-11-09 07:11:44 +00:00
self . mutations . remove_attribute ( attribute , root . as_u64 ( ) ) ;
2021-08-20 15:39:13 +00:00
}
for attribute in new . attributes {
2022-01-31 17:49:21 +00:00
self . mutations . set_attribute ( attribute , root . as_u64 ( ) ) ;
2021-08-20 15:39:13 +00:00
}
}
2021-08-19 06:50:35 +00:00
2021-08-20 15:39:13 +00:00
// Diff listeners
//
// It's extraordinarily rare to have the number/order of listeners change
// In the cases where the listeners change, we completely wipe the data attributes and add new ones
//
// We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
//
// TODO: take a more efficient path than this
2022-02-03 05:35:10 +00:00
let cur_scope_id = self . current_scope ( ) ;
if old . listeners . len ( ) = = new . listeners . len ( ) {
for ( old_l , new_l ) in old . listeners . iter ( ) . zip ( new . listeners . iter ( ) ) {
2022-06-29 14:48:16 +00:00
new_l . mounted_node . set ( old_l . mounted_node . get ( ) ) ;
2022-02-03 05:35:10 +00:00
if old_l . event ! = new_l . event {
2021-11-09 07:11:44 +00:00
self . mutations
2022-02-03 05:35:10 +00:00
. remove_event_listener ( old_l . event , root . as_u64 ( ) ) ;
self . mutations . new_event_listener ( new_l , cur_scope_id ) ;
2021-08-19 06:50:35 +00:00
}
2022-02-03 05:35:10 +00:00
}
} else {
for listener in old . listeners {
self . mutations
. remove_event_listener ( listener . event , root . as_u64 ( ) ) ;
}
for listener in new . listeners {
listener . mounted_node . set ( Some ( root ) ) ;
self . mutations . new_event_listener ( listener , cur_scope_id ) ;
2021-08-19 06:50:35 +00:00
}
2021-08-20 15:39:13 +00:00
}
2021-08-19 06:50:35 +00:00
2022-01-30 23:34:24 +00:00
match ( old . children . len ( ) , new . children . len ( ) ) {
( 0 , 0 ) = > { }
( 0 , _ ) = > {
2022-03-26 17:53:24 +00:00
self . mutations . push_root ( root ) ;
2022-03-26 18:35:23 +00:00
let created = self . create_children ( new . children ) ;
2022-01-30 23:34:24 +00:00
self . mutations . append_children ( created as u32 ) ;
2022-04-04 16:18:51 +00:00
self . mutations . pop_root ( ) ;
2022-01-30 23:34:24 +00:00
}
2022-01-31 17:49:21 +00:00
( _ , _ ) = > self . diff_children ( old . children , new . children ) ,
2022-01-30 23:34:24 +00:00
} ;
2021-08-19 06:50:35 +00:00
}
2021-08-20 15:39:13 +00:00
fn diff_component_nodes (
2021-11-09 07:11:44 +00:00
& mut self ,
2022-01-30 23:34:24 +00:00
old_node : & ' b VNode < ' b > ,
new_node : & ' b VNode < ' b > ,
old : & ' b VComponent < ' b > ,
new : & ' b VComponent < ' b > ,
2021-08-20 15:39:13 +00:00
) {
2022-02-01 20:00:36 +00:00
let scope_addr = old
. scope
. get ( )
. expect ( " existing component nodes should have a scope " ) ;
2022-01-05 05:27:22 +00:00
if std ::ptr ::eq ( old , new ) {
return ;
}
2021-11-11 21:36:51 +00:00
2021-08-20 15:39:13 +00:00
// Make sure we're dealing with the same component (by function pointer)
if old . user_fc = = new . user_fc {
2022-01-30 23:34:24 +00:00
self . enter_scope ( scope_addr ) ;
2022-02-01 20:00:36 +00:00
{
// Make sure the new component vnode is referencing the right scope id
new . scope . set ( Some ( scope_addr ) ) ;
// make sure the component's caller function is up to date
let scope = self
. scopes
. get_scope ( scope_addr )
. unwrap_or_else ( | | panic! ( " could not find {:?} " , scope_addr ) ) ;
// take the new props out regardless
// when memoizing, push to the existing scope if memoization happens
let new_props = new
2021-12-21 04:31:33 +00:00
. props
2022-02-01 20:00:36 +00:00
. borrow_mut ( )
. take ( )
. expect ( " new component props should exist " ) ;
let should_diff = {
if old . can_memoize {
// safety: we trust the implementation of "memoize"
let props_are_the_same = unsafe {
let new_ref = new_props . as_ref ( ) ;
scope . props . borrow ( ) . as_ref ( ) . unwrap ( ) . memoize ( new_ref )
} ;
! props_are_the_same | | self . force_diff
} else {
true
}
} ;
2021-09-21 22:13:09 +00:00
2022-02-01 20:00:36 +00:00
if should_diff {
let _old_props = scope
. props
. replace ( unsafe { std ::mem ::transmute ( Some ( new_props ) ) } ) ;
2022-01-05 05:27:22 +00:00
2022-02-01 20:00:36 +00:00
// this should auto drop the previous props
self . scopes . run_scope ( scope_addr ) ;
self . mutations . mark_dirty_scope ( scope_addr ) ;
2021-06-21 04:52:37 +00:00
2022-02-01 20:00:36 +00:00
self . diff_node (
self . scopes . wip_head ( scope_addr ) ,
self . scopes . fin_head ( scope_addr ) ,
) ;
} else {
// memoization has taken place
drop ( new_props ) ;
} ;
}
2022-01-30 23:34:24 +00:00
self . leave_scope ( ) ;
2021-08-20 15:39:13 +00:00
} else {
2022-01-30 23:34:24 +00:00
self . replace_node ( old_node , new_node ) ;
2021-02-12 08:07:35 +00:00
}
2021-03-03 07:27:26 +00:00
}
2021-07-11 18:49:52 +00:00
2022-01-30 23:34:24 +00:00
fn diff_fragment_nodes ( & mut self , old : & ' b VFragment < ' b > , new : & ' b VFragment < ' b > ) {
2022-02-03 01:04:55 +00:00
if std ::ptr ::eq ( old , new ) {
return ;
}
2021-08-20 15:39:13 +00:00
// This is the case where options or direct vnodes might be used.
// In this case, it's faster to just skip ahead to their diff
2022-01-31 17:49:21 +00:00
if old . children . len ( ) = = 1 & & new . children . len ( ) = = 1 {
if ! std ::ptr ::eq ( old , new ) {
self . diff_node ( & old . children [ 0 ] , & new . children [ 0 ] ) ;
}
return ;
}
2021-07-29 01:46:53 +00:00
2021-10-18 00:18:30 +00:00
debug_assert! ( ! old . children . is_empty ( ) ) ;
debug_assert! ( ! new . children . is_empty ( ) ) ;
2021-10-11 02:27:08 +00:00
2021-11-09 07:11:44 +00:00
self . diff_children ( old . children , new . children ) ;
2021-07-29 01:46:53 +00:00
}
2021-03-03 07:27:26 +00:00
// Diff the given set of old and new children.
//
// The parent must be on top of the change list stack when this function is
// entered:
//
// [... parent]
//
// the change list stack is in the same state when this function returns.
2021-07-29 01:46:53 +00:00
//
// If old no anchors are provided, then it's assumed that we can freely append to the parent.
//
// Remember, non-empty lists does not mean that there are real elements, just that there are virtual elements.
2021-08-22 03:04:34 +00:00
//
2021-10-24 17:30:36 +00:00
// Fragment nodes cannot generate empty children lists, so we can assume that when a list is empty, it belongs only
2021-08-22 03:04:34 +00:00
// to an element, and appending makes sense.
2022-01-30 23:34:24 +00:00
fn diff_children ( & mut self , old : & ' b [ VNode < ' b > ] , new : & ' b [ VNode < ' b > ] ) {
2022-01-31 06:24:11 +00:00
if std ::ptr ::eq ( old , new ) {
return ;
}
2021-08-22 03:04:34 +00:00
// Remember, fragments can never be empty (they always have a single child)
2021-08-23 17:41:47 +00:00
match ( old , new ) {
( [ ] , [ ] ) = > { }
2022-01-30 23:34:24 +00:00
( [ ] , _ ) = > self . create_and_append_children ( new ) ,
( _ , [ ] ) = > self . remove_nodes ( old , true ) ,
2021-08-23 17:41:47 +00:00
_ = > {
let new_is_keyed = new [ 0 ] . key ( ) . is_some ( ) ;
let old_is_keyed = old [ 0 ] . key ( ) . is_some ( ) ;
2021-02-14 23:03:16 +00:00
2021-08-23 17:41:47 +00:00
debug_assert! (
new . iter ( ) . all ( | n | n . key ( ) . is_some ( ) = = new_is_keyed ) ,
" all siblings must be keyed or all siblings must be non-keyed "
) ;
debug_assert! (
old . iter ( ) . all ( | o | o . key ( ) . is_some ( ) = = old_is_keyed ) ,
" all siblings must be keyed or all siblings must be non-keyed "
) ;
2021-07-29 22:56:09 +00:00
2021-08-23 17:41:47 +00:00
if new_is_keyed & & old_is_keyed {
2021-11-09 07:11:44 +00:00
self . diff_keyed_children ( old , new ) ;
2021-08-23 17:41:47 +00:00
} else {
2021-11-09 07:11:44 +00:00
self . diff_non_keyed_children ( old , new ) ;
2021-08-23 17:41:47 +00:00
}
}
}
}
2021-07-29 22:56:09 +00:00
2021-08-23 17:41:47 +00:00
// Diff children that are not keyed.
//
// The parent must be on the top of the change list stack when entering this
// function:
//
// [... parent]
//
// the change list stack is in the same state when this function returns.
2022-01-30 23:34:24 +00:00
fn diff_non_keyed_children ( & mut self , old : & ' b [ VNode < ' b > ] , new : & ' b [ VNode < ' b > ] ) {
2022-01-31 17:49:21 +00:00
use std ::cmp ::Ordering ;
2021-08-23 17:41:47 +00:00
// Handled these cases in `diff_children` before calling this function.
debug_assert! ( ! new . is_empty ( ) ) ;
debug_assert! ( ! old . is_empty ( ) ) ;
2021-07-29 22:04:09 +00:00
2021-09-21 22:13:09 +00:00
match old . len ( ) . cmp ( & new . len ( ) ) {
2021-11-09 07:11:44 +00:00
Ordering ::Greater = > self . remove_nodes ( & old [ new . len ( ) .. ] , true ) ,
2022-01-30 23:34:24 +00:00
Ordering ::Less = > self . create_and_insert_after ( & new [ old . len ( ) .. ] , old . last ( ) . unwrap ( ) ) ,
Ordering ::Equal = > { }
}
for ( new , old ) in new . iter ( ) . zip ( old . iter ( ) ) {
self . diff_node ( old , new ) ;
2021-07-29 01:46:53 +00:00
}
2021-02-12 08:07:35 +00:00
}
2021-03-03 07:27:26 +00:00
// Diffing "keyed" children.
//
// With keyed children, we care about whether we delete, move, or create nodes
// versus mutate existing nodes in place. Presumably there is some sort of CSS
// transition animation that makes the virtual DOM diffing algorithm
// observable. By specifying keys for nodes, we know which virtual DOM nodes
// must reuse (or not reuse) the same physical DOM nodes.
//
// This is loosely based on Inferno's keyed patching implementation. However, we
// have to modify the algorithm since we are compiling the diff down into change
// list instructions that will be executed later, rather than applying the
// changes to the DOM directly as we compare virtual DOMs.
//
// https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
//
2021-07-30 20:07:42 +00:00
// The stack is empty upon entry.
2022-01-30 23:34:24 +00:00
fn diff_keyed_children ( & mut self , old : & ' b [ VNode < ' b > ] , new : & ' b [ VNode < ' b > ] ) {
2021-06-27 02:13:57 +00:00
if cfg! ( debug_assertions ) {
let mut keys = fxhash ::FxHashSet ::default ( ) ;
2022-01-30 23:34:24 +00:00
let mut assert_unique_keys = | children : & ' b [ VNode < ' b > ] | {
2021-06-27 02:13:57 +00:00
keys . clear ( ) ;
for child in children {
2021-08-20 14:34:41 +00:00
let key = child . key ( ) ;
2021-06-27 02:13:57 +00:00
debug_assert! (
key . is_some ( ) ,
" if any sibling is keyed, all siblings must be keyed "
) ;
keys . insert ( key ) ;
}
debug_assert_eq! (
children . len ( ) ,
keys . len ( ) ,
" keyed siblings must each have a unique key "
) ;
} ;
assert_unique_keys ( old ) ;
assert_unique_keys ( new ) ;
}
2021-03-03 07:27:26 +00:00
// First up, we diff all the nodes with the same key at the beginning of the
// children.
//
// `shared_prefix_count` is the count of how many nodes at the start of
// `new` and `old` share the same keys.
2021-11-09 07:11:44 +00:00
let ( left_offset , right_offset ) = match self . diff_keyed_ends ( old , new ) {
2021-08-23 19:35:26 +00:00
Some ( count ) = > count ,
None = > return ,
2021-03-03 07:27:26 +00:00
} ;
// Ok, we now hopefully have a smaller range of children in the middle
// within which to re-order nodes with the same keys, remove old nodes with
// now-unused keys, and create new nodes with fresh keys.
2021-10-11 02:27:08 +00:00
let old_middle = & old [ left_offset .. ( old . len ( ) - right_offset ) ] ;
let new_middle = & new [ left_offset .. ( new . len ( ) - right_offset ) ] ;
debug_assert! (
2021-10-18 00:18:30 +00:00
! ( ( old_middle . len ( ) = = new_middle . len ( ) ) & & old_middle . is_empty ( ) ) ,
2021-10-11 02:27:08 +00:00
" keyed children must have the same number of children "
2021-03-03 07:27:26 +00:00
) ;
2022-01-30 23:34:24 +00:00
2021-10-18 00:18:30 +00:00
if new_middle . is_empty ( ) {
2021-10-11 02:27:08 +00:00
// remove the old elements
2021-11-09 07:11:44 +00:00
self . remove_nodes ( old_middle , true ) ;
2021-10-18 00:18:30 +00:00
} else if old_middle . is_empty ( ) {
2021-10-11 02:27:08 +00:00
// there were no old elements, so just create the new elements
2021-10-24 17:30:36 +00:00
// we need to find the right "foothold" though - we shouldn't use the "append" at all
2021-10-11 02:27:08 +00:00
if left_offset = = 0 {
// insert at the beginning of the old list
let foothold = & old [ old . len ( ) - right_offset ] ;
2022-01-30 23:34:24 +00:00
self . create_and_insert_before ( new_middle , foothold ) ;
2021-10-11 02:27:08 +00:00
} else if right_offset = = 0 {
// insert at the end the old list
let foothold = old . last ( ) . unwrap ( ) ;
2022-01-30 23:34:24 +00:00
self . create_and_insert_after ( new_middle , foothold ) ;
2021-10-11 02:27:08 +00:00
} else {
// inserting in the middle
let foothold = & old [ left_offset - 1 ] ;
2022-01-30 23:34:24 +00:00
self . create_and_insert_after ( new_middle , foothold ) ;
2021-10-11 02:27:08 +00:00
}
} else {
2021-11-09 07:11:44 +00:00
self . diff_keyed_middle ( old_middle , new_middle ) ;
2021-10-11 02:27:08 +00:00
}
2021-02-12 08:07:35 +00:00
}
2021-08-23 19:35:26 +00:00
/// Diff both ends of the children that share keys.
///
/// Returns a left offset and right offset of that indicates a smaller section to pass onto the middle diffing.
///
/// If there is no offset, then this function returns None and the diffing is complete.
fn diff_keyed_ends (
2021-11-09 07:11:44 +00:00
& mut self ,
2022-01-30 23:34:24 +00:00
old : & ' b [ VNode < ' b > ] ,
new : & ' b [ VNode < ' b > ] ,
2021-08-23 19:35:26 +00:00
) -> Option < ( usize , usize ) > {
let mut left_offset = 0 ;
2021-02-12 08:07:35 +00:00
2021-07-30 14:35:47 +00:00
for ( old , new ) in old . iter ( ) . zip ( new . iter ( ) ) {
2021-07-23 21:03:51 +00:00
// abort early if we finally run into nodes with different keys
if old . key ( ) ! = new . key ( ) {
break ;
}
2022-01-30 23:34:24 +00:00
self . diff_node ( old , new ) ;
2021-08-23 19:35:26 +00:00
left_offset + = 1 ;
2021-07-23 21:03:51 +00:00
}
2021-02-12 08:07:35 +00:00
2021-07-23 21:03:51 +00:00
// If that was all of the old children, then create and append the remaining
// new children and we're finished.
2021-08-23 19:35:26 +00:00
if left_offset = = old . len ( ) {
2022-01-30 23:34:24 +00:00
self . create_and_insert_after ( & new [ left_offset .. ] , old . last ( ) . unwrap ( ) ) ;
2021-08-23 19:35:26 +00:00
return None ;
2021-07-23 21:03:51 +00:00
}
2021-02-12 08:07:35 +00:00
2021-07-23 21:03:51 +00:00
// And if that was all of the new children, then remove all of the remaining
// old children and we're finished.
2021-08-23 19:35:26 +00:00
if left_offset = = new . len ( ) {
2021-11-09 07:11:44 +00:00
self . remove_nodes ( & old [ left_offset .. ] , true ) ;
2021-08-23 19:35:26 +00:00
return None ;
2021-07-23 21:03:51 +00:00
}
2021-02-12 08:07:35 +00:00
2021-08-23 19:35:26 +00:00
// if the shared prefix is less than either length, then we need to walk backwards
let mut right_offset = 0 ;
for ( old , new ) in old . iter ( ) . rev ( ) . zip ( new . iter ( ) . rev ( ) ) {
// abort early if we finally run into nodes with different keys
if old . key ( ) ! = new . key ( ) {
break ;
}
2021-11-09 07:11:44 +00:00
self . diff_node ( old , new ) ;
2021-08-23 19:35:26 +00:00
right_offset + = 1 ;
}
Some ( ( left_offset , right_offset ) )
2021-07-12 06:23:46 +00:00
}
2021-03-03 07:27:26 +00:00
// The most-general, expensive code path for keyed children diffing.
//
// We find the longest subsequence within `old` of children that are relatively
// ordered the same way in `new` (via finding a longest-increasing-subsequence
// of the old child's index within `new`). The children that are elements of
// this subsequence will remain in place, minimizing the number of DOM moves we
// will have to do.
//
2021-07-30 14:35:47 +00:00
// Upon entry to this function, the change list stack must be empty.
2021-03-03 07:27:26 +00:00
//
2021-07-30 14:35:47 +00:00
// This function will load the appropriate nodes onto the stack and do diffing in place.
2021-03-03 07:27:26 +00:00
//
2021-11-09 07:11:44 +00:00
// Upon exit from this function, it will be restored to that same self.
2022-01-31 17:49:21 +00:00
#[ allow(clippy::too_many_lines) ]
2022-01-30 23:34:24 +00:00
fn diff_keyed_middle ( & mut self , old : & ' b [ VNode < ' b > ] , new : & ' b [ VNode < ' b > ] ) {
2021-08-22 03:04:34 +00:00
/*
2021-10-24 17:30:36 +00:00
1. Map the old keys into a numerical ordering based on indices .
2021-08-22 03:04:34 +00:00
2. Create a map of old key to its index
3. Map each new key to the old key , carrying over the old index .
- IE if we have ABCD becomes BACD , our sequence would be 1 , 0 , 2 , 3
- if we have ABCD to ABDE , our sequence would be 0 , 1 , 3 , MAX because E doesn ' t exist
now , we should have a list of integers that indicates where in the old list the new items map to .
4. Compute the LIS of this list
- this indicates the longest list of new children that won ' t need to be moved .
5. Identify which nodes need to be removed
6. Identify which nodes will need to be diffed
7. Going along each item in the new list , create it and insert it before the next closest item in the LIS .
- if the item already existed , just move it to the right place .
8. Finally , generate instructions to remove any old children .
9. Generate instructions to finally diff children that are the same between both
* /
// 0. Debug sanity checks
2021-07-29 22:04:09 +00:00
// Should have already diffed the shared-key prefixes and suffixes.
2022-01-31 17:49:21 +00:00
debug_assert_ne! ( new . first ( ) . map ( VNode ::key ) , old . first ( ) . map ( VNode ::key ) ) ;
debug_assert_ne! ( new . last ( ) . map ( VNode ::key ) , old . last ( ) . map ( VNode ::key ) ) ;
2021-07-29 22:04:09 +00:00
2021-10-24 17:30:36 +00:00
// 1. Map the old keys into a numerical ordering based on indices.
2021-08-22 03:04:34 +00:00
// 2. Create a map of old key to its index
2021-07-29 22:04:09 +00:00
// IE if the keys were A B C, then we would have (A, 1) (B, 2) (C, 3).
2021-08-22 03:04:34 +00:00
let old_key_to_old_index = old
2021-07-29 22:04:09 +00:00
. iter ( )
. enumerate ( )
2021-07-30 20:07:42 +00:00
. map ( | ( i , o ) | ( o . key ( ) . unwrap ( ) , i ) )
2021-07-29 22:04:09 +00:00
. collect ::< FxHashMap < _ , _ > > ( ) ;
2021-06-21 04:52:37 +00:00
2021-07-29 22:04:09 +00:00
let mut shared_keys = FxHashSet ::default ( ) ;
2021-06-21 04:52:37 +00:00
2021-08-22 03:04:34 +00:00
// 3. Map each new key to the old key, carrying over the old index.
let new_index_to_old_index = new
2021-07-29 22:04:09 +00:00
. iter ( )
2021-08-23 19:35:26 +00:00
. map ( | node | {
let key = node . key ( ) . unwrap ( ) ;
2021-08-22 03:04:34 +00:00
if let Some ( & index ) = old_key_to_old_index . get ( & key ) {
shared_keys . insert ( key ) ;
index
} else {
u32 ::MAX as usize
2021-07-29 22:04:09 +00:00
}
} )
. collect ::< Vec < _ > > ( ) ;
2021-08-23 19:35:26 +00:00
// If none of the old keys are reused by the new children, then we remove all the remaining old children and
// create the new children afresh.
if shared_keys . is_empty ( ) {
2021-11-11 16:49:07 +00:00
if let Some ( first_old ) = old . get ( 0 ) {
self . remove_nodes ( & old [ 1 .. ] , true ) ;
2022-01-30 23:34:24 +00:00
let nodes_created = self . create_children ( new ) ;
self . replace_inner ( first_old , nodes_created ) ;
2021-11-11 16:49:07 +00:00
} else {
2022-01-30 23:34:24 +00:00
// I think this is wrong - why are we appending?
// only valid of the if there are no trailing elements
self . create_and_append_children ( new ) ;
2021-11-11 16:49:07 +00:00
}
2021-07-29 22:04:09 +00:00
return ;
}
2022-02-27 19:49:47 +00:00
// remove any old children that are not shared
// todo: make this an iterator
for child in old {
let key = child . key ( ) . unwrap ( ) ;
if ! shared_keys . contains ( & key ) {
self . remove_nodes ( [ child ] , true ) ;
}
}
2021-08-22 03:04:34 +00:00
// 4. Compute the LIS of this list
2021-08-24 14:01:01 +00:00
let mut lis_sequence = Vec ::default ( ) ;
lis_sequence . reserve ( new_index_to_old_index . len ( ) ) ;
2021-07-30 20:07:42 +00:00
2021-07-30 14:35:47 +00:00
let mut predecessors = vec! [ 0 ; new_index_to_old_index . len ( ) ] ;
let mut starts = vec! [ 0 ; new_index_to_old_index . len ( ) ] ;
2021-07-30 20:07:42 +00:00
2021-07-30 14:35:47 +00:00
longest_increasing_subsequence ::lis_with (
& new_index_to_old_index ,
2021-08-24 14:01:01 +00:00
& mut lis_sequence ,
2021-07-30 14:35:47 +00:00
| a , b | a < b ,
& mut predecessors ,
& mut starts ,
) ;
2021-08-24 15:03:19 +00:00
// the lis comes out backwards, I think. can't quite tell.
2021-08-24 14:01:01 +00:00
lis_sequence . sort_unstable ( ) ;
// if a new node gets u32 max and is at the end, then it might be part of our LIS (because u32 max is a valid LIS)
if lis_sequence . last ( ) . map ( | f | new_index_to_old_index [ * f ] ) = = Some ( u32 ::MAX as usize ) {
lis_sequence . pop ( ) ;
}
2021-07-30 14:35:47 +00:00
2022-01-31 17:49:21 +00:00
for idx in & lis_sequence {
2022-01-30 23:34:24 +00:00
self . diff_node ( & old [ new_index_to_old_index [ * idx ] ] , & new [ * idx ] ) ;
}
2021-08-24 14:01:01 +00:00
2022-01-30 23:34:24 +00:00
let mut nodes_created = 0 ;
// add mount instruction for the first items not covered by the lis
let last = * lis_sequence . last ( ) . unwrap ( ) ;
if last < ( new . len ( ) - 1 ) {
for ( idx , new_node ) in new [ ( last + 1 ) .. ] . iter ( ) . enumerate ( ) {
let new_idx = idx + last + 1 ;
let old_index = new_index_to_old_index [ new_idx ] ;
if old_index = = u32 ::MAX as usize {
nodes_created + = self . create_node ( new_node ) ;
} else {
self . diff_node ( & old [ old_index ] , new_node ) ;
2022-02-23 13:09:16 +00:00
nodes_created + = self . push_all_real_nodes ( new_node ) ;
2022-01-30 23:34:24 +00:00
}
2021-08-24 14:01:01 +00:00
}
2022-01-30 23:34:24 +00:00
self . mutations . insert_after (
self . find_last_element ( & new [ last ] ) . unwrap ( ) ,
nodes_created as u32 ,
) ;
nodes_created = 0 ;
2021-08-24 14:01:01 +00:00
}
// for each spacing, generate a mount instruction
let mut lis_iter = lis_sequence . iter ( ) . rev ( ) ;
let mut last = * lis_iter . next ( ) . unwrap ( ) ;
2021-09-21 22:13:09 +00:00
for next in lis_iter {
2021-08-24 14:01:01 +00:00
if last - next > 1 {
2022-01-30 23:34:24 +00:00
for ( idx , new_node ) in new [ ( next + 1 ) .. last ] . iter ( ) . enumerate ( ) {
let new_idx = idx + next + 1 ;
let old_index = new_index_to_old_index [ new_idx ] ;
if old_index = = u32 ::MAX as usize {
nodes_created + = self . create_node ( new_node ) ;
} else {
self . diff_node ( & old [ old_index ] , new_node ) ;
2022-02-23 13:09:16 +00:00
nodes_created + = self . push_all_real_nodes ( new_node ) ;
2022-01-30 23:34:24 +00:00
}
2021-08-23 19:35:26 +00:00
}
2022-01-30 23:34:24 +00:00
self . mutations . insert_before (
self . find_first_element ( & new [ last ] ) . unwrap ( ) ,
nodes_created as u32 ,
) ;
nodes_created = 0 ;
2021-07-30 14:35:47 +00:00
}
2021-09-21 22:13:09 +00:00
last = * next ;
2021-07-30 14:35:47 +00:00
}
2021-03-03 07:27:26 +00:00
2022-01-30 23:34:24 +00:00
// add mount instruction for the last items not covered by the lis
let first_lis = * lis_sequence . first ( ) . unwrap ( ) ;
if first_lis > 0 {
for ( idx , new_node ) in new [ .. first_lis ] . iter ( ) . enumerate ( ) {
let old_index = new_index_to_old_index [ idx ] ;
if old_index = = u32 ::MAX as usize {
nodes_created + = self . create_node ( new_node ) ;
} else {
self . diff_node ( & old [ old_index ] , new_node ) ;
2022-02-23 13:09:16 +00:00
nodes_created + = self . push_all_real_nodes ( new_node ) ;
2022-01-30 23:34:24 +00:00
}
2021-08-24 14:01:01 +00:00
}
2022-01-30 23:34:24 +00:00
self . mutations . insert_before (
self . find_first_element ( & new [ first_lis ] ) . unwrap ( ) ,
nodes_created as u32 ,
) ;
2021-08-24 14:01:01 +00:00
}
2021-03-03 07:27:26 +00:00
}
2021-02-14 23:03:16 +00:00
2022-01-30 23:34:24 +00:00
fn replace_node ( & mut self , old : & ' b VNode < ' b > , new : & ' b VNode < ' b > ) {
let nodes_created = self . create_node ( new ) ;
self . replace_inner ( old , nodes_created ) ;
2021-07-30 14:35:47 +00:00
}
2022-01-30 23:34:24 +00:00
fn replace_inner ( & mut self , old : & ' b VNode < ' b > , nodes_created : usize ) {
2021-11-11 16:49:07 +00:00
match old {
2021-11-11 21:36:51 +00:00
VNode ::Element ( el ) = > {
2021-11-12 02:34:20 +00:00
let id = old
. try_mounted_id ( )
. unwrap_or_else ( | | panic! ( " broke on {:?} " , old ) ) ;
2021-11-11 21:36:51 +00:00
self . mutations . replace_with ( id , nodes_created as u32 ) ;
self . remove_nodes ( el . children , false ) ;
2022-01-30 23:34:24 +00:00
self . scopes . collect_garbage ( id ) ;
2021-11-11 21:36:51 +00:00
}
2021-11-23 20:53:57 +00:00
VNode ::Text ( _ ) | VNode ::Placeholder ( _ ) = > {
2021-11-12 02:34:20 +00:00
let id = old
. try_mounted_id ( )
. unwrap_or_else ( | | panic! ( " broke on {:?} " , old ) ) ;
2021-11-11 16:49:07 +00:00
self . mutations . replace_with ( id , nodes_created as u32 ) ;
2022-01-30 23:34:24 +00:00
self . scopes . collect_garbage ( id ) ;
2021-11-11 16:49:07 +00:00
}
VNode ::Fragment ( f ) = > {
2022-01-30 23:34:24 +00:00
self . replace_inner ( & f . children [ 0 ] , nodes_created ) ;
2021-11-11 16:49:07 +00:00
self . remove_nodes ( f . children . iter ( ) . skip ( 1 ) , true ) ;
}
VNode ::Component ( c ) = > {
2022-01-31 17:49:21 +00:00
log ::trace! ( " Replacing component {:?} " , old ) ;
2022-01-31 07:33:16 +00:00
let scope_id = c . scope . get ( ) . unwrap ( ) ;
let node = self . scopes . fin_head ( scope_id ) ;
self . enter_scope ( scope_id ) ;
2022-02-01 20:00:36 +00:00
{
self . replace_inner ( node , nodes_created ) ;
2022-01-31 07:33:16 +00:00
2022-02-01 20:00:36 +00:00
log ::trace! ( " Replacing component x2 {:?} " , old ) ;
2021-11-11 21:36:51 +00:00
2022-02-03 00:04:37 +00:00
let scope = self . scopes . get_scope ( scope_id ) . unwrap ( ) ;
c . scope . set ( None ) ;
let props = scope . props . take ( ) . unwrap ( ) ;
c . props . borrow_mut ( ) . replace ( props ) ;
2022-07-03 04:11:28 +00:00
self . scopes . try_remove ( scope_id ) ;
2022-01-05 21:34:24 +00:00
}
2022-01-31 07:33:16 +00:00
self . leave_scope ( ) ;
2021-11-11 16:49:07 +00:00
}
2021-08-23 14:43:49 +00:00
}
}
2021-07-29 22:56:09 +00:00
2022-01-30 23:34:24 +00:00
pub fn remove_nodes ( & mut self , nodes : impl IntoIterator < Item = & ' b VNode < ' b > > , gen_muts : bool ) {
2021-08-23 14:43:49 +00:00
for node in nodes {
match node {
VNode ::Text ( t ) = > {
2021-11-11 16:49:07 +00:00
// this check exists because our null node will be removed but does not have an ID
2021-12-21 05:46:10 +00:00
if let Some ( id ) = t . id . get ( ) {
2022-01-30 23:34:24 +00:00
self . scopes . collect_garbage ( id ) ;
2022-02-03 00:04:37 +00:00
t . id . set ( None ) ;
2021-10-25 19:05:17 +00:00
2021-11-11 16:49:07 +00:00
if gen_muts {
self . mutations . remove ( id . as_u64 ( ) ) ;
}
2021-09-21 22:13:09 +00:00
}
2021-07-29 22:56:09 +00:00
}
2021-11-23 20:53:57 +00:00
VNode ::Placeholder ( a ) = > {
2021-12-21 05:46:10 +00:00
let id = a . id . get ( ) . unwrap ( ) ;
2022-01-30 23:34:24 +00:00
self . scopes . collect_garbage ( id ) ;
2022-02-03 00:04:37 +00:00
a . id . set ( None ) ;
2021-10-25 19:05:17 +00:00
if gen_muts {
2021-11-09 07:11:44 +00:00
self . mutations . remove ( id . as_u64 ( ) ) ;
2021-09-21 22:13:09 +00:00
}
2021-08-23 14:43:49 +00:00
}
VNode ::Element ( e ) = > {
2021-12-21 05:46:10 +00:00
let id = e . id . get ( ) . unwrap ( ) ;
2021-10-25 19:05:17 +00:00
if gen_muts {
2021-11-09 07:11:44 +00:00
self . mutations . remove ( id . as_u64 ( ) ) ;
2021-09-21 22:13:09 +00:00
}
2021-10-25 19:05:17 +00:00
2022-01-30 23:34:24 +00:00
self . scopes . collect_garbage ( id ) ;
2022-02-03 00:04:37 +00:00
e . id . set ( None ) ;
2022-01-30 19:08:03 +00:00
2022-01-30 23:34:24 +00:00
self . remove_nodes ( e . children , false ) ;
2021-08-23 14:43:49 +00:00
}
2021-10-31 00:28:58 +00:00
2021-08-23 14:43:49 +00:00
VNode ::Fragment ( f ) = > {
2021-11-09 07:11:44 +00:00
self . remove_nodes ( f . children , gen_muts ) ;
2021-08-23 14:43:49 +00:00
}
2021-08-24 15:03:19 +00:00
2021-08-23 14:43:49 +00:00
VNode ::Component ( c ) = > {
2022-01-31 07:33:16 +00:00
self . enter_scope ( c . scope . get ( ) . unwrap ( ) ) ;
2022-02-01 20:00:36 +00:00
{
let scope_id = c . scope . get ( ) . unwrap ( ) ;
let root = self . scopes . root_node ( scope_id ) ;
self . remove_nodes ( [ root ] , gen_muts ) ;
2022-02-03 00:04:37 +00:00
let scope = self . scopes . get_scope ( scope_id ) . unwrap ( ) ;
c . scope . set ( None ) ;
let props = scope . props . take ( ) . unwrap ( ) ;
c . props . borrow_mut ( ) . replace ( props ) ;
2022-07-03 04:11:28 +00:00
self . scopes . try_remove ( scope_id ) ;
2022-01-05 21:34:24 +00:00
}
2022-01-31 07:33:16 +00:00
self . leave_scope ( ) ;
2021-07-29 22:56:09 +00:00
}
}
}
2021-08-23 14:43:49 +00:00
}
2022-01-30 23:34:24 +00:00
fn create_children ( & mut self , nodes : & ' b [ VNode < ' b > ] ) -> usize {
let mut created = 0 ;
for node in nodes {
created + = self . create_node ( node ) ;
}
created
}
fn create_and_append_children ( & mut self , nodes : & ' b [ VNode < ' b > ] ) {
let created = self . create_children ( nodes ) ;
self . mutations . append_children ( created as u32 ) ;
}
fn create_and_insert_after ( & mut self , nodes : & ' b [ VNode < ' b > ] , after : & ' b VNode < ' b > ) {
let created = self . create_children ( nodes ) ;
let last = self . find_last_element ( after ) . unwrap ( ) ;
self . mutations . insert_after ( last , created as u32 ) ;
}
fn create_and_insert_before ( & mut self , nodes : & ' b [ VNode < ' b > ] , before : & ' b VNode < ' b > ) {
let created = self . create_children ( nodes ) ;
let first = self . find_first_element ( before ) . unwrap ( ) ;
self . mutations . insert_before ( first , created as u32 ) ;
}
2022-02-03 05:35:10 +00:00
fn current_scope ( & self ) -> ScopeId {
self . scope_stack . last ( ) . copied ( ) . expect ( " no current scope " )
2022-01-30 23:34:24 +00:00
}
fn enter_scope ( & mut self , scope : ScopeId ) {
self . scope_stack . push ( scope ) ;
}
fn leave_scope ( & mut self ) {
self . scope_stack . pop ( ) ;
}
fn find_last_element ( & self , vnode : & ' b VNode < ' b > ) -> Option < ElementId > {
let mut search_node = Some ( vnode ) ;
loop {
match & search_node . take ( ) . unwrap ( ) {
VNode ::Text ( t ) = > break t . id . get ( ) ,
VNode ::Element ( t ) = > break t . id . get ( ) ,
VNode ::Placeholder ( t ) = > break t . id . get ( ) ,
2022-01-31 17:49:21 +00:00
VNode ::Fragment ( frag ) = > search_node = frag . children . last ( ) ,
2022-01-30 23:34:24 +00:00
VNode ::Component ( el ) = > {
let scope_id = el . scope . get ( ) . unwrap ( ) ;
search_node = Some ( self . scopes . root_node ( scope_id ) ) ;
}
}
}
}
fn find_first_element ( & self , vnode : & ' b VNode < ' b > ) -> Option < ElementId > {
let mut search_node = Some ( vnode ) ;
loop {
2022-01-31 17:49:21 +00:00
match & search_node . take ( ) . expect ( " search node to have an ID " ) {
2022-01-30 23:34:24 +00:00
VNode ::Text ( t ) = > break t . id . get ( ) ,
VNode ::Element ( t ) = > break t . id . get ( ) ,
VNode ::Placeholder ( t ) = > break t . id . get ( ) ,
2022-01-31 17:49:21 +00:00
VNode ::Fragment ( frag ) = > search_node = Some ( & frag . children [ 0 ] ) ,
VNode ::Component ( el ) = > {
let scope = el . scope . get ( ) . expect ( " element to have a scope assigned " ) ;
search_node = Some ( self . scopes . root_node ( scope ) ) ;
}
2022-01-30 23:34:24 +00:00
}
}
}
// recursively push all the nodes of a tree onto the stack and return how many are there
2022-02-23 13:09:16 +00:00
fn push_all_real_nodes ( & mut self , node : & ' b VNode < ' b > ) -> usize {
2022-01-30 23:34:24 +00:00
match node {
2022-02-23 13:09:16 +00:00
VNode ::Text ( _ ) | VNode ::Placeholder ( _ ) | VNode ::Element ( _ ) = > {
2022-01-30 23:34:24 +00:00
self . mutations . push_root ( node . mounted_id ( ) ) ;
1
}
2022-01-31 17:49:21 +00:00
VNode ::Fragment ( frag ) = > {
2022-01-30 23:34:24 +00:00
let mut added = 0 ;
2022-01-31 17:49:21 +00:00
for child in frag . children {
2022-02-23 13:09:16 +00:00
added + = self . push_all_real_nodes ( child ) ;
2022-01-30 23:34:24 +00:00
}
added
}
2022-01-31 17:49:21 +00:00
VNode ::Component ( c ) = > {
let scope_id = c . scope . get ( ) . unwrap ( ) ;
let root = self . scopes . root_node ( scope_id ) ;
2022-02-23 13:09:16 +00:00
self . push_all_real_nodes ( root )
2022-01-30 23:34:24 +00:00
}
}
}
2021-09-01 04:57:04 +00:00
}