feat: enable children properly
This commit is contained in:
1 parent
dbb981d500
commit
b997b8ebbb
10 files changed
+128
-131
No files matched your search
@@ -17,7 +17,7 @@ pub fn format_args_f(input: TokenStream) -> TokenStream {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(Props, attributes(builder))]
|
#[proc_macro_derive(Props, attributes(props))]
|
||||||
pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as syn::DeriveInput);
|
let input = parse_macro_input!(input as syn::DeriveInput);
|
||||||
match props::impl_my_derive(&input) {
|
match props::impl_my_derive(&input) {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
//! However, it has been adopted to fit the Dioxus Props builder pattern.
|
//! However, it has been adopted to fit the Dioxus Props builder pattern.
|
||||||
//!
|
//!
|
||||||
//! For dioxus, we make a few changes:
|
//! For dioxus, we make a few changes:
|
||||||
//! - automatically implement Into<Option> on the setters (IE the strip setter option)
|
//! - [ ] automatically implement Into<Option> on the setters (IE the strip setter option)
|
||||||
//! - automatically implement a default of none for optional fields (those explicitly wrapped with Option<T>)
|
//! - [ ] automatically implement a default of none for optional fields (those explicitly wrapped with Option<T>)
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
|
||||||
@@ -194,6 +194,14 @@ mod field_info {
|
|||||||
field_defaults: FieldBuilderAttr,
|
field_defaults: FieldBuilderAttr,
|
||||||
) -> Result<FieldInfo, Error> {
|
) -> Result<FieldInfo, Error> {
|
||||||
if let Some(ref name) = field.ident {
|
if let Some(ref name) = field.ident {
|
||||||
|
let mut builder_attr = field_defaults.with(&field.attrs)?;
|
||||||
|
|
||||||
|
// children field is automatically defaulted to None
|
||||||
|
if name == "children" {
|
||||||
|
builder_attr.default =
|
||||||
|
Some(syn::parse(quote!(Default::default()).into()).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(FieldInfo {
|
Ok(FieldInfo {
|
||||||
ordinal,
|
ordinal,
|
||||||
name,
|
name,
|
||||||
@@ -202,7 +210,7 @@ mod field_info {
|
|||||||
proc_macro2::Span::call_site(),
|
proc_macro2::Span::call_site(),
|
||||||
),
|
),
|
||||||
ty: &field.ty,
|
ty: &field.ty,
|
||||||
builder_attr: field_defaults.with(&field.attrs)?,
|
builder_attr,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::new(field.span(), "Nameless field in struct"))
|
Err(Error::new(field.span(), "Nameless field in struct"))
|
||||||
@@ -285,7 +293,7 @@ mod field_info {
|
|||||||
pub fn with(mut self, attrs: &[syn::Attribute]) -> Result<Self, Error> {
|
pub fn with(mut self, attrs: &[syn::Attribute]) -> Result<Self, Error> {
|
||||||
let mut skip_tokens = None;
|
let mut skip_tokens = None;
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if path_to_single_string(&attr.path).as_deref() != Some("builder") {
|
if path_to_single_string(&attr.path).as_deref() != Some("props") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +324,7 @@ mod field_info {
|
|||||||
if self.setter.skip && self.default.is_none() {
|
if self.setter.skip && self.default.is_none() {
|
||||||
return Err(Error::new_spanned(
|
return Err(Error::new_spanned(
|
||||||
skip_tokens.unwrap(),
|
skip_tokens.unwrap(),
|
||||||
"#[builder(skip)] must be accompanied by default or default_code",
|
"#[props(skip)] must be accompanied by default or default_code",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +333,7 @@ mod field_info {
|
|||||||
|
|
||||||
pub fn apply_meta(&mut self, expr: syn::Expr) -> Result<(), Error> {
|
pub fn apply_meta(&mut self, expr: syn::Expr) -> Result<(), Error> {
|
||||||
match expr {
|
match expr {
|
||||||
|
// #[props(default = "...")]
|
||||||
syn::Expr::Assign(assign) => {
|
syn::Expr::Assign(assign) => {
|
||||||
let name = expr_to_single_string(&assign.left)
|
let name = expr_to_single_string(&assign.left)
|
||||||
.ok_or_else(|| Error::new_spanned(&assign.left, "Expected identifier"))?;
|
.ok_or_else(|| Error::new_spanned(&assign.left, "Expected identifier"))?;
|
||||||
@@ -356,6 +365,8 @@ mod field_info {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uh not sure
|
||||||
syn::Expr::Path(path) => {
|
syn::Expr::Path(path) => {
|
||||||
let name = path_to_single_string(&path.path)
|
let name = path_to_single_string(&path.path)
|
||||||
.ok_or_else(|| Error::new_spanned(&path, "Expected identifier"))?;
|
.ok_or_else(|| Error::new_spanned(&path, "Expected identifier"))?;
|
||||||
@@ -371,6 +382,8 @@ mod field_info {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
syn::Expr::Call(call) => {
|
syn::Expr::Call(call) => {
|
||||||
let subsetting_name = if let syn::Expr::Path(path) = &*call.func {
|
let subsetting_name = if let syn::Expr::Path(path) = &*call.func {
|
||||||
path_to_single_string(&path.path)
|
path_to_single_string(&path.path)
|
||||||
@@ -398,6 +411,7 @@ mod field_info {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
syn::Expr::Unary(syn::ExprUnary {
|
syn::Expr::Unary(syn::ExprUnary {
|
||||||
op: syn::UnOp::Not(_),
|
op: syn::UnOp::Not(_),
|
||||||
expr,
|
expr,
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_core_macro::*;
|
||||||
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut dom = VirtualDom::new(parent);
|
||||||
|
let edits = dom.rebuild();
|
||||||
|
dbg!(edits);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent(cx: Scope<()>) -> Element {
|
||||||
|
let value = cx.use_hook(|_| String::new(), |f| f);
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
child(
|
||||||
|
name: value,
|
||||||
|
h1 {"hi"}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
struct ChildProps<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
children: Element<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
"it's nested {cx.props.name}"
|
||||||
|
{&cx.props.children}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
+23
-25
@@ -88,8 +88,6 @@
|
|||||||
//! More info on how to improve this diffing algorithm:
|
//! More info on how to improve this diffing algorithm:
|
||||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
||||||
|
|
||||||
use std::borrow::BorrowMut;
|
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
@@ -295,7 +293,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
for child in el.children.iter() {
|
for child in el.children.iter() {
|
||||||
num_on_stack += self.push_all_nodes(child);
|
num_on_stack += self.push_all_nodes(child);
|
||||||
}
|
}
|
||||||
self.mutations.push_root(el.dom_id.get().unwrap());
|
self.mutations.push_root(el.id.get().unwrap());
|
||||||
|
|
||||||
num_on_stack + 1
|
num_on_stack + 1
|
||||||
}
|
}
|
||||||
@@ -351,7 +349,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
let real_id = self.scopes.reserve_node(node);
|
let real_id = self.scopes.reserve_node(node);
|
||||||
|
|
||||||
self.mutations.create_text_node(vtext.text, real_id);
|
self.mutations.create_text_node(vtext.text, real_id);
|
||||||
vtext.dom_id.set(Some(real_id));
|
vtext.id.set(Some(real_id));
|
||||||
self.stack.add_child_count(1);
|
self.stack.add_child_count(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,20 +357,20 @@ impl<'bump> DiffState<'bump> {
|
|||||||
let real_id = self.scopes.reserve_node(node);
|
let real_id = self.scopes.reserve_node(node);
|
||||||
|
|
||||||
self.mutations.create_placeholder(real_id);
|
self.mutations.create_placeholder(real_id);
|
||||||
anchor.dom_id.set(Some(real_id));
|
anchor.id.set(Some(real_id));
|
||||||
|
|
||||||
self.stack.add_child_count(1);
|
self.stack.add_child_count(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_element_node(&mut self, element: &'bump VElement<'bump>, node: &'bump VNode<'bump>) {
|
fn create_element_node(&mut self, element: &'bump VElement<'bump>, node: &'bump VNode<'bump>) {
|
||||||
let VElement {
|
let VElement {
|
||||||
tag_name,
|
tag: tag_name,
|
||||||
listeners,
|
listeners,
|
||||||
attributes,
|
attributes,
|
||||||
children,
|
children,
|
||||||
namespace,
|
namespace,
|
||||||
dom_id,
|
id: dom_id,
|
||||||
parent_id,
|
parent: parent_id,
|
||||||
..
|
..
|
||||||
} = element;
|
} = element;
|
||||||
|
|
||||||
@@ -479,9 +477,9 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
(Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
|
(Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
|
||||||
(Placeholder(old), Placeholder(new)) => {
|
(Placeholder(old), Placeholder(new)) => {
|
||||||
if let Some(root) = old.dom_id.get() {
|
if let Some(root) = old.id.get() {
|
||||||
self.scopes.update_node(new_node, root);
|
self.scopes.update_node(new_node, root);
|
||||||
new.dom_id.set(Some(root))
|
new.id.set(Some(root))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,13 +513,13 @@ impl<'bump> DiffState<'bump> {
|
|||||||
_old_node: &'bump VNode<'bump>,
|
_old_node: &'bump VNode<'bump>,
|
||||||
new_node: &'bump VNode<'bump>,
|
new_node: &'bump VNode<'bump>,
|
||||||
) {
|
) {
|
||||||
if let Some(root) = old.dom_id.get() {
|
if let Some(root) = old.id.get() {
|
||||||
if old.text != new.text {
|
if old.text != new.text {
|
||||||
self.mutations.set_text(new.text, root.as_u64());
|
self.mutations.set_text(new.text, root.as_u64());
|
||||||
}
|
}
|
||||||
self.scopes.update_node(new_node, root);
|
self.scopes.update_node(new_node, root);
|
||||||
|
|
||||||
new.dom_id.set(Some(root));
|
new.id.set(Some(root));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,13 +530,13 @@ impl<'bump> DiffState<'bump> {
|
|||||||
old_node: &'bump VNode<'bump>,
|
old_node: &'bump VNode<'bump>,
|
||||||
new_node: &'bump VNode<'bump>,
|
new_node: &'bump VNode<'bump>,
|
||||||
) {
|
) {
|
||||||
let root = old.dom_id.get().unwrap();
|
let root = old.id.get().unwrap();
|
||||||
|
|
||||||
// If the element type is completely different, the element needs to be re-rendered completely
|
// 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 is an optimization React makes due to how users structure their code
|
||||||
//
|
//
|
||||||
// This case is rather rare (typically only in non-keyed lists)
|
// This case is rather rare (typically only in non-keyed lists)
|
||||||
if new.tag_name != old.tag_name || new.namespace != old.namespace {
|
if new.tag != old.tag || new.namespace != old.namespace {
|
||||||
// maybe make this an instruction?
|
// maybe make this an instruction?
|
||||||
// issue is that we need the "vnode" but this method only has the velement
|
// issue is that we need the "vnode" but this method only has the velement
|
||||||
self.stack.push_nodes_created(0);
|
self.stack.push_nodes_created(0);
|
||||||
@@ -551,8 +549,8 @@ impl<'bump> DiffState<'bump> {
|
|||||||
|
|
||||||
self.scopes.update_node(new_node, root);
|
self.scopes.update_node(new_node, root);
|
||||||
|
|
||||||
new.dom_id.set(Some(root));
|
new.id.set(Some(root));
|
||||||
new.parent_id.set(old.parent_id.get());
|
new.parent.set(old.parent.get());
|
||||||
|
|
||||||
// todo: attributes currently rely on the element on top of the stack, but in theory, we only need the id of the
|
// 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.
|
// element to modify its attributes.
|
||||||
@@ -1182,9 +1180,9 @@ impl<'bump> DiffState<'bump> {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
match &search_node.take().unwrap() {
|
match &search_node.take().unwrap() {
|
||||||
VNode::Text(t) => break t.dom_id.get(),
|
VNode::Text(t) => break t.id.get(),
|
||||||
VNode::Element(t) => break t.dom_id.get(),
|
VNode::Element(t) => break t.id.get(),
|
||||||
VNode::Placeholder(t) => break t.dom_id.get(),
|
VNode::Placeholder(t) => break t.id.get(),
|
||||||
VNode::Fragment(frag) => {
|
VNode::Fragment(frag) => {
|
||||||
search_node = frag.children.last();
|
search_node = frag.children.last();
|
||||||
}
|
}
|
||||||
@@ -1209,9 +1207,9 @@ impl<'bump> DiffState<'bump> {
|
|||||||
let scope_id = el.scope.get().unwrap();
|
let scope_id = el.scope.get().unwrap();
|
||||||
search_node = Some(self.scopes.root_node(scope_id));
|
search_node = Some(self.scopes.root_node(scope_id));
|
||||||
}
|
}
|
||||||
VNode::Text(t) => break t.dom_id.get(),
|
VNode::Text(t) => break t.id.get(),
|
||||||
VNode::Element(t) => break t.dom_id.get(),
|
VNode::Element(t) => break t.id.get(),
|
||||||
VNode::Placeholder(t) => break t.dom_id.get(),
|
VNode::Placeholder(t) => break t.id.get(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1263,7 +1261,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
match node {
|
match node {
|
||||||
VNode::Text(t) => {
|
VNode::Text(t) => {
|
||||||
// this check exists because our null node will be removed but does not have an ID
|
// this check exists because our null node will be removed but does not have an ID
|
||||||
if let Some(id) = t.dom_id.get() {
|
if let Some(id) = t.id.get() {
|
||||||
self.scopes.collect_garbage(id);
|
self.scopes.collect_garbage(id);
|
||||||
|
|
||||||
if gen_muts {
|
if gen_muts {
|
||||||
@@ -1272,7 +1270,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
VNode::Placeholder(a) => {
|
VNode::Placeholder(a) => {
|
||||||
let id = a.dom_id.get().unwrap();
|
let id = a.id.get().unwrap();
|
||||||
self.scopes.collect_garbage(id);
|
self.scopes.collect_garbage(id);
|
||||||
|
|
||||||
if gen_muts {
|
if gen_muts {
|
||||||
@@ -1280,7 +1278,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
VNode::Element(e) => {
|
VNode::Element(e) => {
|
||||||
let id = e.dom_id.get().unwrap();
|
let id = e.id.get().unwrap();
|
||||||
|
|
||||||
if gen_muts {
|
if gen_muts {
|
||||||
self.mutations.remove(id.as_u64());
|
self.mutations.remove(id.as_u64());
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ pub(crate) mod innerlude {
|
|||||||
pub use crate::mutations::*;
|
pub use crate::mutations::*;
|
||||||
pub use crate::nodes::*;
|
pub use crate::nodes::*;
|
||||||
pub use crate::scopes::*;
|
pub use crate::scopes::*;
|
||||||
pub(crate) use crate::scopes::*;
|
|
||||||
pub use crate::virtual_dom::*;
|
pub use crate::virtual_dom::*;
|
||||||
|
|
||||||
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
|
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
|
||||||
|
|||||||
+27
-80
@@ -141,9 +141,9 @@ impl<'src> VNode<'src> {
|
|||||||
/// Returns None if the VNode is not mounted, or if the VNode cannot be presented by a mounted ID (Fragment/Component)
|
/// Returns None if the VNode is not mounted, or if the VNode cannot be presented by a mounted ID (Fragment/Component)
|
||||||
pub fn try_mounted_id(&self) -> Option<ElementId> {
|
pub fn try_mounted_id(&self) -> Option<ElementId> {
|
||||||
match &self {
|
match &self {
|
||||||
VNode::Text(el) => el.dom_id.get(),
|
VNode::Text(el) => el.id.get(),
|
||||||
VNode::Element(el) => el.dom_id.get(),
|
VNode::Element(el) => el.id.get(),
|
||||||
VNode::Placeholder(el) => el.dom_id.get(),
|
VNode::Placeholder(el) => el.id.get(),
|
||||||
VNode::Fragment(_) => None,
|
VNode::Fragment(_) => None,
|
||||||
VNode::Component(_) => None,
|
VNode::Component(_) => None,
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ impl Debug for VNode<'_> {
|
|||||||
match &self {
|
match &self {
|
||||||
VNode::Element(el) => s
|
VNode::Element(el) => s
|
||||||
.debug_struct("VNode::VElement")
|
.debug_struct("VNode::VElement")
|
||||||
.field("name", &el.tag_name)
|
.field("name", &el.tag)
|
||||||
.field("key", &el.key)
|
.field("key", &el.key)
|
||||||
.field("attrs", &el.attributes)
|
.field("attrs", &el.attributes)
|
||||||
.field("children", &el.children)
|
.field("children", &el.children)
|
||||||
@@ -187,7 +187,6 @@ impl Debug for VNode<'_> {
|
|||||||
write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
|
write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
|
||||||
}
|
}
|
||||||
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
||||||
// VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,13 +215,13 @@ fn empty_cell() -> Cell<Option<ElementId>> {
|
|||||||
|
|
||||||
/// A placeholder node only generated when Fragments don't have any children.
|
/// A placeholder node only generated when Fragments don't have any children.
|
||||||
pub struct VPlaceholder {
|
pub struct VPlaceholder {
|
||||||
pub dom_id: Cell<Option<ElementId>>,
|
pub id: Cell<Option<ElementId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bump-allocated string slice and metadata.
|
/// A bump-allocated string slice and metadata.
|
||||||
pub struct VText<'src> {
|
pub struct VText<'src> {
|
||||||
pub text: &'src str,
|
pub text: &'src str,
|
||||||
pub dom_id: Cell<Option<ElementId>>,
|
pub id: Cell<Option<ElementId>>,
|
||||||
pub is_static: bool,
|
pub is_static: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,11 +237,11 @@ pub struct VFragment<'src> {
|
|||||||
|
|
||||||
/// An element like a "div" with children, listeners, and attributes.
|
/// An element like a "div" with children, listeners, and attributes.
|
||||||
pub struct VElement<'a> {
|
pub struct VElement<'a> {
|
||||||
pub tag_name: &'static str,
|
pub tag: &'static str,
|
||||||
pub namespace: Option<&'static str>,
|
pub namespace: Option<&'static str>,
|
||||||
pub key: Option<&'a str>,
|
pub key: Option<&'a str>,
|
||||||
pub dom_id: Cell<Option<ElementId>>,
|
pub id: Cell<Option<ElementId>>,
|
||||||
pub parent_id: Cell<Option<ElementId>>,
|
pub parent: Cell<Option<ElementId>>,
|
||||||
pub listeners: &'a [Listener<'a>],
|
pub listeners: &'a [Listener<'a>],
|
||||||
pub attributes: &'a [Attribute<'a>],
|
pub attributes: &'a [Attribute<'a>],
|
||||||
pub children: &'a [VNode<'a>],
|
pub children: &'a [VNode<'a>],
|
||||||
@@ -251,11 +250,11 @@ pub struct VElement<'a> {
|
|||||||
impl Debug for VElement<'_> {
|
impl Debug for VElement<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("VElement")
|
f.debug_struct("VElement")
|
||||||
.field("tag_name", &self.tag_name)
|
.field("tag_name", &self.tag)
|
||||||
.field("namespace", &self.namespace)
|
.field("namespace", &self.namespace)
|
||||||
.field("key", &self.key)
|
.field("key", &self.key)
|
||||||
.field("dom_id", &self.dom_id)
|
.field("id", &self.id)
|
||||||
.field("parent_id", &self.parent_id)
|
.field("parent", &self.parent)
|
||||||
.field("listeners", &self.listeners.len())
|
.field("listeners", &self.listeners.len())
|
||||||
.field("attributes", &self.attributes)
|
.field("attributes", &self.attributes)
|
||||||
.field("children", &self.children)
|
.field("children", &self.children)
|
||||||
@@ -363,20 +362,13 @@ pub struct VComponent<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct VComponentProps<P> {
|
pub(crate) struct VComponentProps<P> {
|
||||||
// Props start local
|
|
||||||
// If they are static, then they can get promoted onto the heap
|
|
||||||
pub render_fn: Component<P>,
|
pub render_fn: Component<P>,
|
||||||
pub props: P,
|
|
||||||
pub memo: unsafe fn(&P, &P) -> bool,
|
pub memo: unsafe fn(&P, &P) -> bool,
|
||||||
|
pub props: P,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AnyProps {
|
pub trait AnyProps {
|
||||||
fn as_ptr(&self) -> *const ();
|
fn as_ptr(&self) -> *const ();
|
||||||
fn release(&self);
|
|
||||||
fn replace_heap(&self, new: Box<dyn Any>);
|
|
||||||
|
|
||||||
// move the props from the bump arena onto the heap
|
|
||||||
fn promote(&self);
|
|
||||||
fn render<'a>(&'a self, bump: &'a ScopeState) -> Element<'a>;
|
fn render<'a>(&'a self, bump: &'a ScopeState) -> Element<'a>;
|
||||||
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
|
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
|
||||||
}
|
}
|
||||||
@@ -384,16 +376,9 @@ pub trait AnyProps {
|
|||||||
impl<P> AnyProps for VComponentProps<P> {
|
impl<P> AnyProps for VComponentProps<P> {
|
||||||
fn as_ptr(&self) -> *const () {
|
fn as_ptr(&self) -> *const () {
|
||||||
&self.props as *const _ as *const ()
|
&self.props as *const _ as *const ()
|
||||||
// // todo: maybe move this into an enum
|
|
||||||
// let heap_props = self.heap_props.borrow();
|
|
||||||
// if let Some(b) = heap_props.as_ref() {
|
|
||||||
// b.as_ref() as *const _ as *const ()
|
|
||||||
// } else {
|
|
||||||
// let local_props = self.local_props.borrow();
|
|
||||||
// local_props.as_ref().unwrap() as *const _ as *const ()
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Safety:
|
||||||
// this will downcat the other ptr as our swallowed type!
|
// this will downcat the other ptr as our swallowed type!
|
||||||
// you *must* make this check *before* calling this method
|
// you *must* make this check *before* calling this method
|
||||||
// if your functions are not the same, then you will downcast a pointer into a different type (UB)
|
// if your functions are not the same, then you will downcast a pointer into a different type (UB)
|
||||||
@@ -403,41 +388,9 @@ impl<P> AnyProps for VComponentProps<P> {
|
|||||||
(self.memo)(real_us, real_other)
|
(self.memo)(real_us, real_other)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn release(&self) {
|
|
||||||
panic!("don't release props anymore");
|
|
||||||
// if let Some(heap_props) = self.heap_props.borrow_mut().take() {
|
|
||||||
// dbg!("releasing heap");
|
|
||||||
// drop(heap_props)
|
|
||||||
// } else if let Some(local_props) = self.local_props.borrow_mut().take() {
|
|
||||||
// dbg!("releasing local");
|
|
||||||
// drop(local_props)
|
|
||||||
// } else {
|
|
||||||
// dbg!("trying to do a double release");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn promote(&self) {
|
|
||||||
panic!("don't promote props anymore");
|
|
||||||
// dbg!("promoting props");
|
|
||||||
// if let Some(props) = self.local_props.borrow_mut().take() {
|
|
||||||
// let mut heap_slot = self.heap_props.borrow_mut();
|
|
||||||
// *heap_slot = Some(Box::new(props));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render<'a>(&'a self, scope: &'a ScopeState) -> Element<'a> {
|
fn render<'a>(&'a self, scope: &'a ScopeState) -> Element<'a> {
|
||||||
let props: &P = &self.props;
|
let props = unsafe { std::mem::transmute::<&P, &P>(&self.props) };
|
||||||
let inline_props = unsafe { std::mem::transmute::<&P, &P>(props) };
|
(self.render_fn)(Scope { scope, props })
|
||||||
|
|
||||||
(self.render_fn)(Scope {
|
|
||||||
scope,
|
|
||||||
props: inline_props,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn replace_heap(&self, new: Box<dyn Any>) {
|
|
||||||
// let new = new.downcast::<P>().unwrap();
|
|
||||||
// self.heap_props.borrow_mut().replace(new);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,7 +416,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
/// Directly pass in text blocks without the need to use the format_args macro.
|
/// Directly pass in text blocks without the need to use the format_args macro.
|
||||||
pub fn static_text(&self, text: &'static str) -> VNode<'a> {
|
pub fn static_text(&self, text: &'static str) -> VNode<'a> {
|
||||||
VNode::Text(self.bump.alloc(VText {
|
VNode::Text(self.bump.alloc(VText {
|
||||||
dom_id: empty_cell(),
|
id: empty_cell(),
|
||||||
text,
|
text,
|
||||||
is_static: true,
|
is_static: true,
|
||||||
}))
|
}))
|
||||||
@@ -492,7 +445,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
VNode::Text(self.bump.alloc(VText {
|
VNode::Text(self.bump.alloc(VText {
|
||||||
text,
|
text,
|
||||||
is_static,
|
is_static,
|
||||||
dom_id: empty_cell(),
|
id: empty_cell(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,14 +498,14 @@ impl<'a> NodeFactory<'a> {
|
|||||||
let key = key.map(|f| self.raw_text(f).0);
|
let key = key.map(|f| self.raw_text(f).0);
|
||||||
|
|
||||||
VNode::Element(self.bump.alloc(VElement {
|
VNode::Element(self.bump.alloc(VElement {
|
||||||
tag_name,
|
tag: tag_name,
|
||||||
key,
|
key,
|
||||||
namespace,
|
namespace,
|
||||||
listeners,
|
listeners,
|
||||||
attributes,
|
attributes,
|
||||||
children,
|
children,
|
||||||
dom_id: empty_cell(),
|
id: empty_cell(),
|
||||||
parent_id: empty_cell(),
|
parent: empty_cell(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,9 +574,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nodes.is_empty() {
|
if nodes.is_empty() {
|
||||||
VNode::Placeholder(self.bump.alloc(VPlaceholder {
|
VNode::Placeholder(self.bump.alloc(VPlaceholder { id: empty_cell() }))
|
||||||
dom_id: empty_cell(),
|
|
||||||
}))
|
|
||||||
} else {
|
} else {
|
||||||
VNode::Fragment(VFragment {
|
VNode::Fragment(VFragment {
|
||||||
children: nodes.into_bump_slice(),
|
children: nodes.into_bump_slice(),
|
||||||
@@ -643,9 +594,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nodes.is_empty() {
|
if nodes.is_empty() {
|
||||||
VNode::Placeholder(self.bump.alloc(VPlaceholder {
|
VNode::Placeholder(self.bump.alloc(VPlaceholder { id: empty_cell() }))
|
||||||
dom_id: empty_cell(),
|
|
||||||
}))
|
|
||||||
} else {
|
} else {
|
||||||
let children = nodes.into_bump_slice();
|
let children = nodes.into_bump_slice();
|
||||||
|
|
||||||
@@ -686,9 +635,9 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nodes.is_empty() {
|
if nodes.is_empty() {
|
||||||
Some(VNode::Placeholder(self.bump.alloc(VPlaceholder {
|
Some(VNode::Placeholder(
|
||||||
dom_id: empty_cell(),
|
self.bump.alloc(VPlaceholder { id: empty_cell() }),
|
||||||
})))
|
))
|
||||||
} else {
|
} else {
|
||||||
let children = nodes.into_bump_slice();
|
let children = nodes.into_bump_slice();
|
||||||
|
|
||||||
@@ -766,9 +715,7 @@ impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
|
|||||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||||
match self {
|
match self {
|
||||||
Some(lazy) => lazy.call(cx),
|
Some(lazy) => lazy.call(cx),
|
||||||
None => VNode::Placeholder(cx.bump.alloc(VPlaceholder {
|
None => VNode::Placeholder(cx.bump.alloc(VPlaceholder { id: empty_cell() })),
|
||||||
dom_id: empty_cell(),
|
|
||||||
})),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,11 +44,11 @@ impl ScopeArena {
|
|||||||
// this will *never* show up in the diffing process
|
// this will *never* show up in the diffing process
|
||||||
// todo: figure out why this is necessary. i forgot. whoops.
|
// todo: figure out why this is necessary. i forgot. whoops.
|
||||||
let el = bump.alloc(VElement {
|
let el = bump.alloc(VElement {
|
||||||
tag_name: "root",
|
tag: "root",
|
||||||
namespace: None,
|
namespace: None,
|
||||||
key: None,
|
key: None,
|
||||||
dom_id: Cell::new(Some(ElementId(0))),
|
id: Cell::new(Some(ElementId(0))),
|
||||||
parent_id: Default::default(),
|
parent: Default::default(),
|
||||||
listeners: &[],
|
listeners: &[],
|
||||||
attributes: &[],
|
attributes: &[],
|
||||||
children: &[],
|
children: &[],
|
||||||
@@ -279,7 +279,7 @@ impl ScopeArena {
|
|||||||
let node = frame
|
let node = frame
|
||||||
.bump
|
.bump
|
||||||
.alloc(VNode::Placeholder(frame.bump.alloc(VPlaceholder {
|
.alloc(VNode::Placeholder(frame.bump.alloc(VPlaceholder {
|
||||||
dom_id: Default::default(),
|
id: Default::default(),
|
||||||
})));
|
})));
|
||||||
frame.node.set(unsafe { extend_vnode(node) });
|
frame.node.set(unsafe { extend_vnode(node) });
|
||||||
}
|
}
|
||||||
@@ -310,7 +310,7 @@ impl ScopeArena {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_el = real_el.parent_id.get();
|
cur_el = real_el.parent.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -869,7 +869,7 @@ impl BumpFrame {
|
|||||||
|
|
||||||
let node = &*bump.alloc(VText {
|
let node = &*bump.alloc(VText {
|
||||||
text: "placeholdertext",
|
text: "placeholdertext",
|
||||||
dom_id: Default::default(),
|
id: Default::default(),
|
||||||
is_static: false,
|
is_static: false,
|
||||||
});
|
});
|
||||||
let node = bump.alloc(VNode::Text(unsafe { std::mem::transmute(node) }));
|
let node = bump.alloc(VNode::Text(unsafe { std::mem::transmute(node) }));
|
||||||
@@ -881,7 +881,7 @@ impl BumpFrame {
|
|||||||
self.bump.reset();
|
self.bump.reset();
|
||||||
let node = self.bump.alloc(VText {
|
let node = self.bump.alloc(VText {
|
||||||
text: "placeholdertext",
|
text: "placeholdertext",
|
||||||
dom_id: Default::default(),
|
id: Default::default(),
|
||||||
is_static: false,
|
is_static: false,
|
||||||
});
|
});
|
||||||
let node = self
|
let node = self
|
||||||
|
|||||||
@@ -109,14 +109,14 @@ fn components_generate() {
|
|||||||
*render_phase += 1;
|
*render_phase += 1;
|
||||||
|
|
||||||
cx.render(match *render_phase {
|
cx.render(match *render_phase {
|
||||||
0 => rsx!("Text0"),
|
1 => rsx!("Text0"),
|
||||||
1 => rsx!(div {}),
|
2 => rsx!(div {}),
|
||||||
2 => rsx!("Text2"),
|
3 => rsx!("Text2"),
|
||||||
3 => rsx!(Child {}),
|
4 => rsx!(Child {}),
|
||||||
4 => rsx!({ None as Option<()> }),
|
5 => rsx!({ None as Option<()> }),
|
||||||
5 => rsx!("text 3"),
|
6 => rsx!("text 3"),
|
||||||
6 => rsx!({ (0..2).map(|f| rsx!("text {f}")) }),
|
7 => rsx!({ (0..2).map(|f| rsx!("text {f}")) }),
|
||||||
7 => rsx!(Child {}),
|
8 => rsx!(Child {}),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@@ -220,7 +220,6 @@ fn components_generate() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn component_swap() {
|
fn component_swap() {
|
||||||
// simple_logger::init();
|
|
||||||
static App: Component<()> = |cx| {
|
static App: Component<()> = |cx| {
|
||||||
let render_phase = cx.use_hook(|_| 0, |f| f);
|
let render_phase = cx.use_hook(|_| 0, |f| f);
|
||||||
*render_phase += 1;
|
*render_phase += 1;
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ impl DesktopController {
|
|||||||
runtime.block_on(async move {
|
runtime.block_on(async move {
|
||||||
// LocalSet::new().block_on(&runtime, async move {
|
// LocalSet::new().block_on(&runtime, async move {
|
||||||
let mut dom =
|
let mut dom =
|
||||||
VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver);
|
VirtualDom::new_with_props_and_scheduler(root, props, (sender, receiver));
|
||||||
|
|
||||||
let edits = dom.rebuild();
|
let edits = dom.rebuild();
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ impl<'a> TextRenderer<'a, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "<{}", el.tag_name)?;
|
write!(f, "<{}", el.tag)?;
|
||||||
|
|
||||||
let mut inner_html = None;
|
let mut inner_html = None;
|
||||||
let mut attr_iter = el.attributes.iter().peekable();
|
let mut attr_iter = el.attributes.iter().peekable();
|
||||||
@@ -233,7 +233,7 @@ impl<'a> TextRenderer<'a, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "</{}>", el.tag_name)?;
|
write!(f, "</{}>", el.tag)?;
|
||||||
if self.cfg.newline {
|
if self.cfg.newline {
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user