Fix nested rsx expansion by not using template titles (#2799)

* Fix nested rsx expansion by not using template titles

* fix writers with nameless templates

* fix clippy

* dont commit vscode fix

* fix release mode, pull out __template_name

* fix axum_desktop

* Fix core tests

* Make most fields of HotReloadedTemplate public for testing

* wip: formatting, compare all diff cases

* slightly smarter diffing for dynamic nodes

* add a comment about generic node diffing

* clean up mutations a bit

* fix load template

* simplify maybe_rebuild

* Restore write mutations flag in web

* write_mutations -> skip_mutations

---------

Co-authored-by: Evan Almloff <evanalmloff@gmail.com>
This commit is contained in:
Jonathan KelleyandEvan Almloff authored and GitHub committed 2024-08-13 01:59:04 +00:00
1 parent a2180b92e9
commit 0de3bf7aeb
40 files changed
+597 -1038

No files matched your search

+6 -95
View File
@@ -1,6 +1,4 @@
use rustc_hash::FxHashSet;
use crate::{arena::ElementId, AttributeValue, ScopeId, Template};
use crate::{arena::ElementId, AttributeValue, Template};
/// Something that can handle the mutations that are generated by the diffing process and apply them to the Real DOM
///
@@ -14,9 +12,6 @@ use crate::{arena::ElementId, AttributeValue, ScopeId, Template};
///
/// Mutations are the only link between the RealDOM and the VirtualDOM.
pub trait WriteMutations {
/// Register a template with the renderer
fn register_template(&mut self, template: Template);
/// Add these m children to the target element
///
/// Id: The ID of the element being mounted to
@@ -45,15 +40,6 @@ pub trait WriteMutations {
/// Id: The ID we're assigning to this specific text nodes. This will be used later to modify the element or replace it with another element.
fn create_text_node(&mut self, value: &str, id: ElementId);
/// Hydrate an existing text node at the given path with the given text.
///
/// Assign this text node the given ID since we will likely need to modify this text at a later point
///
/// Path: The path of the child of the topmost node on the stack. A path of `[]` represents the topmost node. A path of `[0]` represents the first child. `[0,1,2]` represents 1st child's 2nd child's 3rd child.
/// Value: The value of the textnode that we want to set the placeholder with
/// Id: The ID we're assigning to this specific text nodes. This will be used later to modify the element or replace it with another element.
fn hydrate_text_node(&mut self, path: &'static [u8], value: &str, id: ElementId);
/// Load and clone an existing node from a template saved under that specific name
///
/// Dioxus guarantees that the renderer will have already been provided the template.
@@ -62,7 +48,7 @@ pub trait WriteMutations {
/// Name: The unique "name" of the template based on the template location. When paired with `rsx!`, this is autogenerated
/// Index: The index root we loading from the template. The template is stored as a list of nodes. This index represents the position of that root
/// Id: The ID we're assigning to this element being loaded from the template (This will be used later to move the element around in lists)
fn load_template(&mut self, name: &'static str, index: usize, id: ElementId);
fn load_template(&mut self, template: Template, index: usize, id: ElementId);
/// Replace the target element (given by its ID) with the topmost m nodes on the stack
///
@@ -129,12 +115,6 @@ pub trait WriteMutations {
///
/// Id: The ID of the root node to push.
fn push_root(&mut self, id: ElementId);
/// Swap to a new subtree
fn swap_subtree(&mut self, _subtree_index: usize) {}
/// Mark a scope as dirty
fn mark_scope_dirty(&mut self, _scope_id: ScopeId) {}
}
/// A `Mutation` represents a single instruction for the renderer to use to modify the UI tree to match the state
@@ -156,8 +136,6 @@ pub enum Mutation {
///
/// The path is in the form of a list of indices based on children. Templates cannot have more than 255 children per
/// element, hence the use of a single byte.
///
///
AssignId {
/// The path of the child of the topmost node on the stack
///
@@ -192,33 +170,11 @@ pub enum Mutation {
id: ElementId,
},
/// Hydrate an existing text node at the given path with the given text.
///
/// Assign this text node the given ID since we will likely need to modify this text at a later point
HydrateText {
/// The path of the child of the topmost node on the stack
///
/// A path of `[]` represents the topmost node. A path of `[0]` represents the first child.
/// `[0,1,2]` represents 1st child's 2nd child's 3rd child.
path: &'static [u8],
/// The value of the textnode that we want to set the placeholder with
value: String,
/// The ID we're assigning to this specific text nodes
///
/// This will be used later to modify the element or replace it with another element.
id: ElementId,
},
/// Load and clone an existing node from a template saved under that specific name
/// Load and clone an existing node from a template with a given ID
///
/// Dioxus guarantees that the renderer will have already been provided the template.
/// When the template is picked up in the template list, it should be saved under its "name" - here, the name
LoadTemplate {
/// The "name" of the template. When paired with `rsx!`, this is autogenerated
name: &'static str,
/// Which root are we loading from the template?
///
/// The template is stored as a list of nodes. This index represents the position of that root
@@ -328,38 +284,11 @@ pub enum Mutation {
/// A static list of mutations that can be applied to the DOM. Note: this list does not contain any `Any` attribute values
#[derive(Debug, PartialEq, Default)]
pub struct Mutations {
/// The list of Scopes that were diffed, created, and removed during the Diff process.
pub dirty_scopes: FxHashSet<ScopeId>,
/// Any templates encountered while diffing the DOM.
///
/// These must be loaded into a cache before applying the edits
pub templates: Vec<Template>,
/// Any mutations required to patch the renderer to match the layout of the VirtualDom
pub edits: Vec<Mutation>,
}
impl Mutations {
/// Rewrites IDs to just be "template", so you can compare the mutations
///
/// Used really only for testing
pub fn sanitize(mut self) -> Self {
for edit in self.edits.iter_mut() {
if let Mutation::LoadTemplate { name, .. } = edit {
*name = "template"
}
}
self
}
}
impl WriteMutations for Mutations {
fn register_template(&mut self, template: Template) {
self.templates.push(template)
}
fn append_children(&mut self, id: ElementId, m: usize) {
self.edits.push(Mutation::AppendChildren { id, m })
}
@@ -379,16 +308,8 @@ impl WriteMutations for Mutations {
})
}
fn hydrate_text_node(&mut self, path: &'static [u8], value: &str, id: ElementId) {
self.edits.push(Mutation::HydrateText {
path,
value: value.into(),
id,
})
}
fn load_template(&mut self, name: &'static str, index: usize, id: ElementId) {
self.edits.push(Mutation::LoadTemplate { name, index, id })
fn load_template(&mut self, _template: Template, index: usize, id: ElementId) {
self.edits.push(Mutation::LoadTemplate { index, id })
}
fn replace_node_with(&mut self, id: ElementId, m: usize) {
@@ -457,20 +378,12 @@ impl WriteMutations for Mutations {
fn push_root(&mut self, id: ElementId) {
self.edits.push(Mutation::PushRoot { id })
}
fn swap_subtree(&mut self, _subtree_index: usize) {}
fn mark_scope_dirty(&mut self, scope_id: ScopeId) {
self.dirty_scopes.insert(scope_id);
}
}
/// A struct that ignores all mutations
pub struct NoOpMutations;
impl WriteMutations for NoOpMutations {
fn register_template(&mut self, _: Template) {}
fn append_children(&mut self, _: ElementId, _: usize) {}
fn assign_node_id(&mut self, _: &'static [u8], _: ElementId) {}
@@ -479,9 +392,7 @@ impl WriteMutations for NoOpMutations {
fn create_text_node(&mut self, _: &str, _: ElementId) {}
fn hydrate_text_node(&mut self, _: &'static [u8], _: &str, _: ElementId) {}
fn load_template(&mut self, _: &'static str, _: usize, _: ElementId) {}
fn load_template(&mut self, _: Template, _: usize, _: ElementId) {}
fn replace_node_with(&mut self, _: ElementId, _: usize) {}