Merge pull request #648 from Demonthos/fix-non-str-attributes

fix custom attribute value support
This commit is contained in:
Jon Kelley authored and GitHub committed 2023-01-02 23:41:45 -05:00
commit 07e71956d4
36 files changed
+806 -711

No files matched your search

+1 -1
View File
@@ -44,4 +44,4 @@ pretty_assertions = "1.3.0"
[features]
default = []
serialize = ["serde"]
serialize = ["serde"]
+9 -3
View File
@@ -153,11 +153,17 @@ impl VirtualDom {
});
// Now that all the references are gone, we can safely drop our own references in our listeners.
let mut listeners = scope.listeners.borrow_mut();
let mut listeners = scope.attributes_to_drop.borrow_mut();
listeners.drain(..).for_each(|listener| {
let listener = unsafe { &*listener };
if let AttributeValue::Listener(l) = &listener.value {
_ = l.take();
match &listener.value {
AttributeValue::Listener(l) => {
_ = l.take();
}
AttributeValue::Any(a) => {
_ = a.take();
}
_ => (),
}
});
}
+12 -20
View File
@@ -1,5 +1,5 @@
use crate::any_props::AnyProps;
use crate::innerlude::{VComponent, VPlaceholder, VText};
use crate::innerlude::{BorrowedAttributeValue, VComponent, VPlaceholder, VText};
use crate::mutations::Mutation;
use crate::mutations::Mutation::*;
use crate::nodes::VNode;
@@ -285,7 +285,7 @@ impl<'b> VirtualDom {
}
}
fn write_attribute(&mut self, attribute: &crate::Attribute, id: ElementId) {
fn write_attribute(&mut self, attribute: &'b crate::Attribute<'b>, id: ElementId) {
// Make sure we set the attribute's associated id
attribute.mounted_element.set(id);
@@ -293,9 +293,17 @@ impl<'b> VirtualDom {
let unbounded_name: &str = unsafe { std::mem::transmute(attribute.name) };
match &attribute.value {
AttributeValue::Text(value) => {
AttributeValue::Listener(_) => {
self.mutations.push(NewEventListener {
// all listeners start with "on"
name: &unbounded_name[2..],
id,
})
}
_ => {
// Safety: we promise not to re-alias this text later on after committing it to the mutation
let unbounded_value: &str = unsafe { std::mem::transmute(*value) };
let value: BorrowedAttributeValue<'b> = (&attribute.value).into();
let unbounded_value = unsafe { std::mem::transmute(value) };
self.mutations.push(SetAttribute {
name: unbounded_name,
@@ -304,22 +312,6 @@ impl<'b> VirtualDom {
id,
})
}
AttributeValue::Bool(value) => self.mutations.push(SetBoolAttribute {
name: unbounded_name,
value: *value,
id,
}),
AttributeValue::Listener(_) => {
self.mutations.push(NewEventListener {
// all listeners start with "on"
name: &unbounded_name[2..],
id,
})
}
AttributeValue::Float(_) => todo!(),
AttributeValue::Int(_) => todo!(),
AttributeValue::Any(_) => todo!(),
AttributeValue::None => todo!(),
}
}
+11 -17
View File
@@ -1,7 +1,7 @@
use crate::{
any_props::AnyProps,
arena::ElementId,
innerlude::{DirtyScope, VComponent, VPlaceholder, VText},
innerlude::{BorrowedAttributeValue, DirtyScope, VComponent, VPlaceholder, VText},
mutations::Mutation,
nodes::RenderReturn,
nodes::{DynamicNode, VNode},
@@ -150,22 +150,16 @@ impl<'b> VirtualDom {
};
}
fn update_attribute(&mut self, right_attr: &Attribute, left_attr: &Attribute) {
// todo: add more types of attribute values
match right_attr.value {
AttributeValue::Text(text) => {
let name = unsafe { std::mem::transmute(left_attr.name) };
let value = unsafe { std::mem::transmute(text) };
self.mutations.push(Mutation::SetAttribute {
id: left_attr.mounted_element.get(),
ns: right_attr.namespace,
name,
value,
});
}
// todo: more types of attribute values
_ => todo!("other attribute types"),
}
fn update_attribute(&mut self, right_attr: &'b Attribute<'b>, left_attr: &'b Attribute) {
let name = unsafe { std::mem::transmute(left_attr.name) };
let value: BorrowedAttributeValue<'b> = (&right_attr.value).into();
let value = unsafe { std::mem::transmute(value) };
self.mutations.push(Mutation::SetAttribute {
id: left_attr.mounted_element.get(),
ns: right_attr.namespace,
name,
value,
});
}
fn diff_vcomponent(
+7 -7
View File
@@ -70,10 +70,10 @@ pub(crate) mod innerlude {
}
pub use crate::innerlude::{
fc_to_builder, Attribute, AttributeValue, CapturedError, Component, DynamicNode, Element,
ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation, Mutations, Properties,
RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, Template,
TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
fc_to_builder, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue, CapturedError,
Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation,
Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext,
TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VText, VirtualDom,
};
/// The purpose of this module is to alleviate imports of many common types
@@ -81,9 +81,9 @@ pub use crate::innerlude::{
/// This includes types like [`Scope`], [`Element`], and [`Component`].
pub mod prelude {
pub use crate::innerlude::{
fc_to_builder, Component, Element, Event, EventHandler, Fragment, LazyNodes, Properties,
Scope, ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode,
Throw, VNode, VirtualDom,
fc_to_builder, AnyValue, Component, Element, Event, EventHandler, Fragment,
IntoAttributeValue, LazyNodes, Properties, Scope, ScopeId, ScopeState, Scoped, TaskId,
Template, TemplateAttribute, TemplateNode, Throw, VNode, VirtualDom,
};
}
+4 -15
View File
@@ -1,6 +1,6 @@
use rustc_hash::FxHashSet;
use crate::{arena::ElementId, ScopeId, Template};
use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Template};
/// A container for all the relevant steps to modify the Real DOM
///
@@ -61,7 +61,7 @@ impl<'a> Mutations<'a> {
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type")
)]
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq)]
pub enum Mutation<'a> {
/// Add these m children to the target element
AppendChildren {
@@ -193,8 +193,9 @@ pub enum Mutation<'a> {
SetAttribute {
/// The name of the attribute to set.
name: &'a str,
/// The value of the attribute.
value: &'a str,
value: BorrowedAttributeValue<'a>,
/// The ID of the node to set the attribute of.
id: ElementId,
@@ -204,18 +205,6 @@ pub enum Mutation<'a> {
ns: Option<&'a str>,
},
/// Set the value of a node's attribute.
SetBoolAttribute {
/// The name of the attribute to set.
name: &'a str,
/// The value of the attribute.
value: bool,
/// The ID of the node to set the attribute of.
id: ElementId,
},
/// Set the textcontent of a node.
SetText {
/// The textcontent of the node
+133 -15
View File
@@ -5,8 +5,8 @@ use bumpalo::boxed::Box as BumpBox;
use bumpalo::Bump;
use std::{
any::{Any, TypeId},
cell::{Cell, RefCell, UnsafeCell},
fmt::Arguments,
cell::{self, Cell, RefCell, UnsafeCell},
fmt::{Arguments, Debug},
future::Future,
};
@@ -523,13 +523,108 @@ pub enum AttributeValue<'a> {
Listener(RefCell<Option<ListenerCb<'a>>>),
/// An arbitrary value that implements PartialEq and is static
Any(BumpBox<'a, dyn AnyValue>),
Any(RefCell<Option<BumpBox<'a, dyn AnyValue>>>),
/// A "none" value, resulting in the removal of an attribute from the dom
None,
}
type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
///
/// These varients are used to communicate what the value of an attribute is that needs to be updated
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", serde(untagged))]
pub enum BorrowedAttributeValue<'a> {
/// Text attribute
Text(&'a str),
/// A float
Float(f64),
/// Signed integer
Int(i64),
/// Boolean
Bool(bool),
/// An arbitrary value that implements PartialEq and is static
#[cfg_attr(
feature = "serialize",
serde(
deserialize_with = "deserialize_any_value",
serialize_with = "serialize_any_value"
)
)]
Any(std::cell::Ref<'a, dyn AnyValue>),
/// A "none" value, resulting in the removal of an attribute from the dom
None,
}
impl<'a> From<&'a AttributeValue<'a>> for BorrowedAttributeValue<'a> {
fn from(value: &'a AttributeValue<'a>) -> Self {
match value {
AttributeValue::Text(value) => BorrowedAttributeValue::Text(value),
AttributeValue::Float(value) => BorrowedAttributeValue::Float(*value),
AttributeValue::Int(value) => BorrowedAttributeValue::Int(*value),
AttributeValue::Bool(value) => BorrowedAttributeValue::Bool(*value),
AttributeValue::Listener(_) => {
panic!("A listener cannot be turned into a borrowed value")
}
AttributeValue::Any(value) => {
let value = value.borrow();
BorrowedAttributeValue::Any(std::cell::Ref::map(value, |value| {
&**value.as_ref().unwrap()
}))
}
AttributeValue::None => BorrowedAttributeValue::None,
}
}
}
impl Debug for BorrowedAttributeValue<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
Self::Any(_) => f.debug_tuple("Any").field(&"...").finish(),
Self::None => write!(f, "None"),
}
}
}
impl PartialEq for BorrowedAttributeValue<'_> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(&**r0),
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
#[cfg(feature = "serialize")]
fn serialize_any_value<S>(_: &cell::Ref<'_, dyn AnyValue>, _: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
panic!("Any cannot be serialized")
}
#[cfg(feature = "serialize")]
fn deserialize_any_value<'de, 'a, D>(_: D) -> Result<cell::Ref<'a, dyn AnyValue>, D::Error>
where
D: serde::Deserializer<'de>,
{
panic!("Any cannot be deserialized")
}
impl<'a> std::fmt::Debug for AttributeValue<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -553,29 +648,36 @@ impl<'a> PartialEq for AttributeValue<'a> {
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Listener(_), Self::Listener(_)) => true,
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(r0.as_ref()),
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
(Self::Any(l0), Self::Any(r0)) => {
let l0 = l0.borrow();
let r0 = r0.borrow();
l0.as_ref().unwrap().any_cmp(&**r0.as_ref().unwrap())
}
_ => false,
}
}
}
#[doc(hidden)]
pub trait AnyValue {
pub trait AnyValue: 'static {
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
fn our_typeid(&self) -> TypeId;
fn as_any(&self) -> &dyn Any;
fn type_id(&self) -> TypeId {
self.as_any().type_id()
}
}
impl<T: PartialEq + Any> AnyValue for T {
impl<T: Any + PartialEq + 'static> AnyValue for T {
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
if self.type_id() != other.our_typeid() {
return false;
if let Some(other) = other.as_any().downcast_ref() {
self == other
} else {
false
}
self == unsafe { &*(other as *const _ as *const T) }
}
fn our_typeid(&self) -> TypeId {
self.type_id()
fn as_any(&self) -> &dyn Any {
self
}
}
@@ -742,26 +844,36 @@ pub trait IntoAttributeValue<'a> {
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>;
}
impl<'a> IntoAttributeValue<'a> for AttributeValue<'a> {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
self
}
}
impl<'a> IntoAttributeValue<'a> for &'a str {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Text(self)
}
}
impl<'a> IntoAttributeValue<'a> for f64 {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Float(self)
}
}
impl<'a> IntoAttributeValue<'a> for i64 {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Int(self)
}
}
impl<'a> IntoAttributeValue<'a> for bool {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Bool(self)
}
}
impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
use bumpalo::core_alloc::fmt::Write;
@@ -770,3 +882,9 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
AttributeValue::Text(str_buf.into_bump_str())
}
}
impl<'a> IntoAttributeValue<'a> for BumpBox<'a, dyn AnyValue> {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Any(RefCell::new(Some(self)))
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ impl VirtualDom {
hook_idx: Default::default(),
shared_contexts: Default::default(),
borrowed_props: Default::default(),
listeners: Default::default(),
attributes_to_drop: Default::default(),
}))
}
+21 -6
View File
@@ -7,7 +7,7 @@ use crate::{
innerlude::{ErrorBoundary, Scheduler, SchedulerMsg},
lazynodes::LazyNodes,
nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
Attribute, AttributeValue, Element, Event, Properties, TaskId,
AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId,
};
use bumpalo::{boxed::Box as BumpBox, Bump};
use rustc_hash::{FxHashMap, FxHashSet};
@@ -88,7 +88,7 @@ pub struct ScopeState {
pub(crate) spawned_tasks: FxHashSet<TaskId>,
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
pub(crate) listeners: RefCell<Vec<*const Attribute<'static>>>,
pub(crate) attributes_to_drop: RefCell<Vec<*const Attribute<'static>>>,
pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
pub(crate) placeholder: Cell<Option<ElementId>>,
@@ -374,11 +374,15 @@ impl<'src> ScopeState {
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
let element = rsx.call(self);
let mut listeners = self.listeners.borrow_mut();
let mut listeners = self.attributes_to_drop.borrow_mut();
for attr in element.dynamic_attrs {
if let AttributeValue::Listener(_) = attr.value {
let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
listeners.push(unbounded);
match attr.value {
AttributeValue::Any(_) | AttributeValue::Listener(_) => {
let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
listeners.push(unbounded);
}
_ => (),
}
}
@@ -509,6 +513,17 @@ impl<'src> ScopeState {
AttributeValue::Listener(RefCell::new(Some(boxed)))
}
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue<'src> {
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
// This is the suggested way to build a bumpbox
//
// In theory, we could just use regular boxes
let boxed: BumpBox<'src, dyn AnyValue> =
unsafe { BumpBox::from_raw(self.bump().alloc(value)) };
AttributeValue::Any(RefCell::new(Some(boxed)))
}
/// Inject an error into the nearest error boundary and quit rendering
///
/// The error doesn't need to implement Error or any specific traits since the boundary
+1 -4
View File
@@ -641,10 +641,7 @@ impl VirtualDom {
/// Swap the current mutations with a new
fn finalize(&mut self) -> Mutations {
// todo: make this a routine
let mut out = Mutations::default();
std::mem::swap(&mut self.mutations, &mut out);
out
std::mem::take(&mut self.mutations)
}
}
+28 -4
View File
@@ -2,8 +2,10 @@
//!
//! This tests to ensure we clean it up
use bumpalo::Bump;
use dioxus::core::{ElementId, Mutation::*};
use dioxus::prelude::*;
use dioxus_core::{AttributeValue, BorrowedAttributeValue};
#[test]
fn attrs_cycle() {
@@ -22,6 +24,8 @@ fn attrs_cycle() {
}
});
let bump = Bump::new();
assert_eq!(
dom.rebuild().santize().edits,
[
@@ -36,8 +40,18 @@ fn attrs_cycle() {
[
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
AssignId { path: &[0,], id: ElementId(3,) },
SetAttribute { name: "class", value: "1", id: ElementId(3,), ns: None },
SetAttribute { name: "id", value: "1", id: ElementId(3,), ns: None },
SetAttribute {
name: "class",
value: (&*bump.alloc("1".into_value(&bump))).into(),
id: ElementId(3,),
ns: None
},
SetAttribute {
name: "id",
value: (&*bump.alloc("1".into_value(&bump))).into(),
id: ElementId(3,),
ns: None
},
ReplaceWith { id: ElementId(1,), m: 1 },
]
);
@@ -57,8 +71,18 @@ fn attrs_cycle() {
[
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
AssignId { path: &[0], id: ElementId(3) },
SetAttribute { name: "class", value: "3", id: ElementId(3), ns: None },
SetAttribute { name: "id", value: "3", id: ElementId(3), ns: None },
SetAttribute {
name: "class",
value: BorrowedAttributeValue::Text("3"),
id: ElementId(3),
ns: None
},
SetAttribute {
name: "id",
value: BorrowedAttributeValue::Text("3"),
id: ElementId(3),
ns: None
},
ReplaceWith { id: ElementId(1), m: 1 }
]
);
+10 -2
View File
@@ -1,15 +1,23 @@
use bumpalo::Bump;
use dioxus::core::{ElementId, Mutation::*};
use dioxus::prelude::*;
#[test]
fn bool_test() {
let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false })));
let bump = Bump::new();
assert_eq!(
app.rebuild().santize().edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
SetBoolAttribute { name: "hidden", value: false, id: ElementId(1,) },
SetAttribute {
name: "hidden",
value: (&*bump.alloc(false.into_value(&bump))).into(),
id: ElementId(1,),
ns: None
},
AppendChildren { m: 1, id: ElementId(0) },
]
)
);
}
+1 -1
View File
@@ -18,7 +18,7 @@ fn test_borrowed_state() {
ReplacePlaceholder { path: &[0,], m: 1 },
AppendChildren { m: 1, id: ElementId(0) },
]
)
);
}
fn Parent(cx: Scope) -> Element {
+3 -1
View File
@@ -20,7 +20,9 @@ fn app(cx: Scope) -> Element {
fn bubbles_error() {
let mut dom = VirtualDom::new(app);
let _edits = dom.rebuild().santize();
{
let _edits = dom.rebuild().santize();
}
dom.mark_dirty(ScopeId(0));
+10 -8
View File
@@ -12,14 +12,16 @@ fn cycling_elements() {
})
});
let edits = dom.rebuild().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
AppendChildren { m: 1, id: ElementId(0) },
]
);
{
let edits = dom.rebuild().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
AppendChildren { m: 1, id: ElementId(0) },
]
);
}
dom.mark_dirty(ScopeId(0));
assert_eq!(
+15 -13
View File
@@ -59,19 +59,21 @@ fn component_swap() {
}
let mut dom = VirtualDom::new(app);
let edits = dom.rebuild().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
LoadTemplate { name: "template", index: 0, id: ElementId(4) },
ReplacePlaceholder { path: &[1], m: 3 },
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
AppendChildren { m: 2, id: ElementId(0) }
]
);
{
let edits = dom.rebuild().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
LoadTemplate { name: "template", index: 0, id: ElementId(4) },
ReplacePlaceholder { path: &[1], m: 3 },
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
AppendChildren { m: 2, id: ElementId(0) }
]
);
}
dom.mark_dirty(ScopeId(0));
assert_eq!(
+20 -18
View File
@@ -20,22 +20,24 @@ fn keyed_diffing_out_of_order() {
cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
});
assert_eq!(
dom.rebuild().santize().edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
LoadTemplate { name: "template", index: 0, id: ElementId(4,) },
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
LoadTemplate { name: "template", index: 0, id: ElementId(10,) },
AppendChildren { m: 10, id: ElementId(0) },
]
);
{
assert_eq!(
dom.rebuild().santize().edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
LoadTemplate { name: "template", index: 0, id: ElementId(4,) },
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
LoadTemplate { name: "template", index: 0, id: ElementId(10,) },
AppendChildren { m: 10, id: ElementId(0) },
]
);
}
dom.mark_dirty(ScopeId(0));
assert_eq!(
@@ -44,7 +46,7 @@ fn keyed_diffing_out_of_order() {
PushRoot { id: ElementId(7,) },
InsertBefore { id: ElementId(5,), m: 1 },
]
)
);
}
/// Should result in moves only
@@ -70,7 +72,7 @@ fn keyed_diffing_out_of_order_adds() {
PushRoot { id: ElementId(4,) },
InsertBefore { id: ElementId(1,), m: 2 },
]
)
);
}
/// Should result in moves only
+68 -58
View File
@@ -315,66 +315,76 @@ fn remove_many() {
})
});
let edits = dom.rebuild().santize();
assert!(edits.templates.is_empty());
assert_eq!(
edits.edits,
[
CreatePlaceholder { id: ElementId(1,) },
AppendChildren { id: ElementId(0), m: 1 },
]
);
{
let edits = dom.rebuild().santize();
assert!(edits.templates.is_empty());
assert_eq!(
edits.edits,
[
CreatePlaceholder { id: ElementId(1,) },
AppendChildren { id: ElementId(0), m: 1 },
]
);
}
dom.mark_dirty(ScopeId(0));
let edits = dom.render_immediate().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
ReplaceWith { id: ElementId(1,), m: 1 },
]
);
{
dom.mark_dirty(ScopeId(0));
let edits = dom.render_immediate().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
ReplaceWith { id: ElementId(1,), m: 1 },
]
);
}
dom.mark_dirty(ScopeId(0));
let edits = dom.render_immediate().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) },
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) },
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) },
InsertAfter { id: ElementId(2,), m: 4 },
]
);
{
dom.mark_dirty(ScopeId(0));
let edits = dom.render_immediate().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) },
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) },
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) },
InsertAfter { id: ElementId(2,), m: 4 },
]
);
}
dom.mark_dirty(ScopeId(0));
let edits = dom.render_immediate().santize();
assert_eq!(
edits.edits,
[
CreatePlaceholder { id: ElementId(11,) },
Remove { id: ElementId(9,) },
Remove { id: ElementId(7,) },
Remove { id: ElementId(5,) },
Remove { id: ElementId(1,) },
ReplaceWith { id: ElementId(2,), m: 1 },
]
);
{
dom.mark_dirty(ScopeId(0));
let edits = dom.render_immediate().santize();
assert_eq!(
edits.edits,
[
CreatePlaceholder { id: ElementId(11,) },
Remove { id: ElementId(9,) },
Remove { id: ElementId(7,) },
Remove { id: ElementId(5,) },
Remove { id: ElementId(1,) },
ReplaceWith { id: ElementId(2,), m: 1 },
]
);
}
dom.mark_dirty(ScopeId(0));
let edits = dom.render_immediate().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
ReplaceWith { id: ElementId(11,), m: 1 },
]
)
{
dom.mark_dirty(ScopeId(0));
let edits = dom.render_immediate().santize();
assert_eq!(
edits.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
ReplaceWith { id: ElementId(11,), m: 1 },
]
)
}
}
+12 -6
View File
@@ -1,3 +1,4 @@
use bumpalo::Bump;
use dioxus::core::{ElementId, Mutation};
use dioxus::prelude::*;
@@ -26,17 +27,22 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
#[test]
fn dual_stream() {
let mut dom = VirtualDom::new(basic_syntax_is_a_template);
let bump = Bump::new();
let edits = dom.rebuild().santize();
use Mutation::*;
assert_eq!(
edits.edits,
assert_eq!(edits.edits, {
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
SetAttribute { name: "class", value: "123", id: ElementId(1), ns: None },
SetAttribute {
name: "class",
value: (&*bump.alloc("123".into_value(&bump))).into(),
id: ElementId(1),
ns: None,
},
NewEventListener { name: "click", id: ElementId(1) },
HydrateText { path: &[0, 0], value: "123", id: ElementId(2) },
AppendChildren { id: ElementId(0), m: 1 }
],
);
AppendChildren { id: ElementId(0), m: 1 },
]
});
}
+26 -24
View File
@@ -9,32 +9,34 @@ use std::time::Duration;
async fn it_works() {
let mut dom = VirtualDom::new(app);
let mutations = dom.rebuild().santize();
{
let mutations = dom.rebuild().santize();
// We should at least get the top-level template in before pausing for the children
// note: we dont test template edits anymore
// assert_eq!(
// mutations.templates,
// [
// CreateElement { name: "div" },
// CreateStaticText { value: "Waiting for child..." },
// CreateStaticPlaceholder,
// AppendChildren { m: 2 },
// SaveTemplate { name: "template", m: 1 }
// ]
// );
// We should at least get the top-level template in before pausing for the children
// note: we dont test template edits anymore
// assert_eq!(
// mutations.templates,
// [
// CreateElement { name: "div" },
// CreateStaticText { value: "Waiting for child..." },
// CreateStaticPlaceholder,
// AppendChildren { m: 2 },
// SaveTemplate { name: "template", m: 1 }
// ]
// );
// And we should load it in and assign the placeholder properly
assert_eq!(
mutations.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
// can we even?
AssignId { path: &[1], id: ElementId(3) },
AppendChildren { m: 1, id: ElementId(0) },
]
);
// And we should load it in and assign the placeholder properly
assert_eq!(
mutations.edits,
[
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
// can we even?
AssignId { path: &[1], id: ElementId(3) },
AppendChildren { m: 1, id: ElementId(0) },
]
);
}
// wait just a moment, not enough time for the boundary to resolve
+1 -2
View File
@@ -18,10 +18,9 @@ proc-macro = true
syn = { version = "1.0.11", features = ["extra-traits"] }
quote = "1.0"
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
[dev-dependencies]
dioxus = { path = "../dioxus" }
[dev-dependencies]
smallvec = "1.6"
rustc-hash = "1.1.0"
anymap = "0.12.1"
+2 -1
View File
@@ -393,7 +393,8 @@ impl<'a> StateMember<'a> {
let ty = &self.mem.ty;
let unit_type = &self.mem.unit_type;
let node_view = quote!(dioxus_native_core::node_ref::NodeView::new(unsafe{&*{&node.node_data as *const _}}, #ty::NODE_MASK));
let node_view =
quote!(dioxus_native_core::node_ref::NodeView::new(&node.node_data, #ty::NODE_MASK));
let dep_idents = self.dep_mems.iter().map(|m| &m.ident);
let impl_specific = match self.dep_kind {
DependencyKind::Node => {
@@ -43,7 +43,6 @@ impl ParentDepState for ParentDepCallCounter {
const NODE_MASK: NodeMask = NodeMask::ALL;
fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool {
self.0 += 1;
println!("ParentDepCallCounter::reduce on {:?}\n{}", _node, self.0);
true
}
}
+2 -2
View File
@@ -18,7 +18,7 @@ dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" }
taffy = "0.2.1"
smallvec = "1.6"
rustc-hash = "1.1.0"
anymap = "0.12.1"
anymap = "1.0.0-beta.2"
slab = "0.4"
parking_lot = "0.12.1"
crossbeam-deque = "0.8.2"
@@ -26,4 +26,4 @@ dashmap = "5.4.0"
[dev-dependencies]
rand = "0.8.5"
dioxus = { path = "../dioxus", version = "^0.2.1" }
dioxus = { path = "../dioxus", version = "^0.2.1" }
+2 -1
View File
@@ -1,3 +1,4 @@
use std::any::Any;
use std::hash::BuildHasherDefault;
pub use node_ref::NodeMask;
@@ -20,7 +21,7 @@ pub mod utils;
pub type RealNodeId = NodeId;
pub type FxDashMap<K, V> = dashmap::DashMap<K, V, BuildHasherDefault<FxHasher>>;
pub type FxDashSet<K> = dashmap::DashSet<K, BuildHasherDefault<FxHasher>>;
pub type SendAnyMap = anymap::Map<dyn anymap::any::Any + Send + Sync + 'static>;
pub type SendAnyMap = anymap::Map<dyn Any + Send + Sync + 'static>;
/// Used in derived state macros
#[derive(Eq, PartialEq)]
+61 -18
View File
@@ -1,43 +1,44 @@
use crate::{state::State, tree::NodeId};
use dioxus_core::ElementId;
use dioxus_core::{AnyValue, BorrowedAttributeValue, ElementId};
use rustc_hash::{FxHashMap, FxHashSet};
use std::fmt::Debug;
/// The node is stored client side and stores only basic data about the node.
#[derive(Debug, Clone)]
pub struct Node<S: State> {
pub struct Node<S: State, V: FromAnyValue = ()> {
/// The transformed state of the node.
pub state: S,
/// The raw data for the node
pub node_data: NodeData,
pub node_data: NodeData<V>,
}
#[derive(Debug, Clone)]
pub struct NodeData {
pub struct NodeData<V: FromAnyValue = ()> {
/// The id of the node
pub node_id: NodeId,
/// The id of the node in the vdom.
pub element_id: Option<ElementId>,
/// Additional inforation specific to the node type
pub node_type: NodeType,
pub node_type: NodeType<V>,
}
/// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
#[derive(Debug, Clone)]
pub enum NodeType {
pub enum NodeType<V: FromAnyValue = ()> {
Text {
text: String,
},
Element {
tag: String,
namespace: Option<String>,
attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue>,
attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
listeners: FxHashSet<String>,
},
Placeholder,
}
impl<S: State> Node<S> {
pub(crate) fn new(node_type: NodeType) -> Self {
impl<S: State, V: FromAnyValue> Node<S, V> {
pub(crate) fn new(node_type: NodeType<V>) -> Self {
Node {
state: S::default(),
node_data: NodeData {
@@ -64,24 +65,59 @@ pub struct OwnedAttributeDiscription {
/// An attribute on a DOM node, such as `id="my-thing"` or
/// `href="https://example.com"`.
#[derive(Clone, Copy, Debug)]
pub struct OwnedAttributeView<'a> {
pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> {
/// The discription of the attribute.
pub attribute: &'a OwnedAttributeDiscription,
/// The value of the attribute.
pub value: &'a OwnedAttributeValue,
pub value: &'a OwnedAttributeValue<V>,
}
#[derive(Clone, Debug)]
pub enum OwnedAttributeValue {
#[derive(Clone)]
pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
Text(String),
Float(f32),
Int(i32),
Float(f64),
Int(i64),
Bool(bool),
Custom(V),
None,
}
impl OwnedAttributeValue {
pub trait FromAnyValue {
fn from_any_value(value: &dyn AnyValue) -> Self;
}
impl FromAnyValue for () {
fn from_any_value(_: &dyn AnyValue) -> Self {}
}
impl<V: FromAnyValue> Debug for OwnedAttributeValue<V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
Self::Custom(_) => f.debug_tuple("Any").finish(),
Self::None => write!(f, "None"),
}
}
}
impl<V: FromAnyValue> From<BorrowedAttributeValue<'_>> for OwnedAttributeValue<V> {
fn from(value: BorrowedAttributeValue<'_>) -> Self {
match value {
BorrowedAttributeValue::Text(text) => Self::Text(text.to_string()),
BorrowedAttributeValue::Float(float) => Self::Float(float),
BorrowedAttributeValue::Int(int) => Self::Int(int),
BorrowedAttributeValue::Bool(bool) => Self::Bool(bool),
BorrowedAttributeValue::Any(any) => Self::Custom(V::from_any_value(&*any)),
BorrowedAttributeValue::None => Self::None,
}
}
}
impl<V: FromAnyValue> OwnedAttributeValue<V> {
pub fn as_text(&self) -> Option<&str> {
match self {
OwnedAttributeValue::Text(text) => Some(text),
@@ -89,14 +125,14 @@ impl OwnedAttributeValue {
}
}
pub fn as_float(&self) -> Option<f32> {
pub fn as_float(&self) -> Option<f64> {
match self {
OwnedAttributeValue::Float(float) => Some(*float),
_ => None,
}
}
pub fn as_int(&self) -> Option<i32> {
pub fn as_int(&self) -> Option<i64> {
match self {
OwnedAttributeValue::Int(int) => Some(*int),
_ => None,
@@ -116,4 +152,11 @@ impl OwnedAttributeValue {
_ => None,
}
}
pub fn as_custom(&self) -> Option<&V> {
match self {
OwnedAttributeValue::Custom(custom) => Some(custom),
_ => None,
}
}
}
+8 -6
View File
@@ -1,21 +1,21 @@
use dioxus_core::ElementId;
use crate::{
node::{NodeData, NodeType, OwnedAttributeView},
node::{FromAnyValue, NodeData, NodeType, OwnedAttributeView},
state::union_ordered_iter,
RealNodeId,
};
/// A view into a [VNode] with limited access.
#[derive(Debug)]
pub struct NodeView<'a> {
inner: &'a NodeData,
pub struct NodeView<'a, V: FromAnyValue = ()> {
inner: &'a NodeData<V>,
mask: NodeMask,
}
impl<'a> NodeView<'a> {
impl<'a, V: FromAnyValue> NodeView<'a, V> {
/// Create a new NodeView from a VNode, and mask.
pub fn new(node: &'a NodeData, view: NodeMask) -> Self {
pub fn new(node: &'a NodeData<V>, view: NodeMask) -> Self {
Self {
inner: node,
mask: view,
@@ -55,7 +55,9 @@ impl<'a> NodeView<'a> {
}
/// Get any attributes that are enabled in the mask
pub fn attributes<'b>(&'b self) -> Option<impl Iterator<Item = OwnedAttributeView<'a>> + 'b> {
pub fn attributes<'b>(
&'b self,
) -> Option<impl Iterator<Item = OwnedAttributeView<'a, V>> + 'b> {
match &self.inner.node_type {
NodeType::Element { attributes, .. } => Some(
attributes
+82 -38
View File
@@ -310,11 +310,65 @@ impl<T> AnyPass<T> {
}
}
struct RawPointer<T>(*mut T);
unsafe impl<T> Send for RawPointer<T> {}
unsafe impl<T> Sync for RawPointer<T> {}
pub fn resolve_passes<T, Tr: TreeView<T> + Sync + Send>(
tree: &mut Tr,
dirty_nodes: DirtyNodeStates,
passes: Vec<&AnyPass<T>>,
ctx: SendAnyMap,
) -> FxDashSet<NodeId> {
resolve_passes_single_threaded(tree, dirty_nodes, passes, ctx)
// TODO: multithreadeding has some safety issues currently that need to be resolved before it can be used
// let dirty_states = Arc::new(dirty_nodes);
// let mut resolved_passes: FxHashSet<PassId> = FxHashSet::default();
// let mut resolving = Vec::new();
// let nodes_updated = Arc::new(FxDashSet::default());
// let ctx = Arc::new(ctx);
// while !passes.is_empty() {
// let mut currently_borrowed = MemberMask::default();
// std::thread::scope(|s| {
// let mut i = 0;
// while i < passes.len() {
// let pass = &passes[i];
// let pass_id = pass.pass_id();
// let pass_mask = pass.mask();
// if pass
// .dependancies()
// .iter()
// .all(|d| resolved_passes.contains(d) || *d == pass_id)
// && !pass_mask.overlaps(currently_borrowed)
// {
// let pass = passes.remove(i);
// resolving.push(pass_id);
// currently_borrowed |= pass_mask;
// let dirty_states = dirty_states.clone();
// let nodes_updated = nodes_updated.clone();
// let ctx = ctx.clone();
// let mut dirty = DirtyNodes::default();
// // dirty_states.all_dirty(pass_id, &mut dirty, tree);
// // this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
// let tree_mut_unbounded = unsafe { &mut *(tree as *mut Tr) };
// s.spawn(move || {
// pass.resolve(
// tree_mut_unbounded,
// dirty,
// &dirty_states,
// &nodes_updated,
// &ctx,
// );
// });
// } else {
// i += 1;
// }
// }
// // all passes are resolved at the end of the scope
// });
// resolved_passes.extend(resolving.iter().copied());
// resolving.clear()
// }
// std::sync::Arc::try_unwrap(nodes_updated).unwrap()
}
pub fn resolve_passes<T, Tr: TreeView<T>>(
pub fn resolve_passes_single_threaded<T, Tr: TreeView<T>>(
tree: &mut Tr,
dirty_nodes: DirtyNodeStates,
mut passes: Vec<&AnyPass<T>>,
@@ -327,41 +381,31 @@ pub fn resolve_passes<T, Tr: TreeView<T>>(
let ctx = Arc::new(ctx);
while !passes.is_empty() {
let mut currently_borrowed = MemberMask::default();
std::thread::scope(|s| {
let mut i = 0;
while i < passes.len() {
let pass = &passes[i];
let pass_id = pass.pass_id();
let pass_mask = pass.mask();
if pass
.dependancies()
.iter()
.all(|d| resolved_passes.contains(d) || *d == pass_id)
&& !pass_mask.overlaps(currently_borrowed)
{
let pass = passes.remove(i);
resolving.push(pass_id);
currently_borrowed |= pass_mask;
let tree_mut = tree as *mut _;
let raw_ptr = RawPointer(tree_mut);
let dirty_states = dirty_states.clone();
let nodes_updated = nodes_updated.clone();
let ctx = ctx.clone();
s.spawn(move || unsafe {
// let tree_mut: &mut Tr = &mut *raw_ptr.0;
let raw = raw_ptr;
// this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
let tree_mut: &mut Tr = &mut *raw.0;
let mut dirty = DirtyNodes::default();
dirty_states.all_dirty(pass_id, &mut dirty, tree_mut);
pass.resolve(tree_mut, dirty, &dirty_states, &nodes_updated, &ctx);
});
} else {
i += 1;
}
let mut i = 0;
while i < passes.len() {
let pass = &passes[i];
let pass_id = pass.pass_id();
let pass_mask = pass.mask();
if pass
.dependancies()
.iter()
.all(|d| resolved_passes.contains(d) || *d == pass_id)
&& !pass_mask.overlaps(currently_borrowed)
{
let pass = passes.remove(i);
resolving.push(pass_id);
currently_borrowed |= pass_mask;
let dirty_states = dirty_states.clone();
let nodes_updated = nodes_updated.clone();
let ctx = ctx.clone();
// this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass
let mut dirty = DirtyNodes::default();
dirty_states.all_dirty(pass_id, &mut dirty, tree);
pass.resolve(tree, dirty, &dirty_states, &nodes_updated, &ctx);
} else {
i += 1;
}
// all passes are resolved at the end of the scope
});
}
resolved_passes.extend(resolving.iter().copied());
resolving.clear()
}
+18 -22
View File
@@ -270,26 +270,7 @@ impl<S: State> RealDom<S> {
namespace: ns.map(|s| s.to_string()),
volatile: false,
},
crate::node::OwnedAttributeValue::Text(value.to_string()),
);
mark_dirty(
node_id,
NodeMask::new_with_attrs(AttributeMask::single(name)),
&mut nodes_updated,
);
}
}
SetBoolAttribute { name, value, id } => {
let node_id = self.element_to_node_id(id);
let node = self.tree.get_mut(node_id).unwrap();
if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type {
attributes.insert(
OwnedAttributeDiscription {
name: name.to_string(),
namespace: None,
volatile: false,
},
crate::node::OwnedAttributeValue::Bool(value),
OwnedAttributeValue::from(value),
);
mark_dirty(
node_id,
@@ -358,12 +339,12 @@ impl<S: State> RealDom<S> {
}
/// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
pub fn update_state(
pub fn update_state_single_threaded(
&mut self,
nodes_updated: DirtyNodeStates,
ctx: SendAnyMap,
) -> FxDashSet<RealNodeId> {
S::update(nodes_updated, &mut self.tree, ctx)
S::update_single_threaded(nodes_updated, &mut self.tree, ctx)
}
/// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up.
@@ -415,6 +396,21 @@ impl<S: State> RealDom<S> {
}
}
impl<S: State + Sync> RealDom<S>
where
Tree<Node<S>>: Sync + Send,
{
/// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts.
/// This will resolve the state in parallel
pub fn update_state(
&mut self,
nodes_updated: DirtyNodeStates,
ctx: SendAnyMap,
) -> FxDashSet<RealNodeId> {
S::update(nodes_updated, &mut self.tree, ctx)
}
}
impl<S: State> Deref for RealDom<S> {
type Target = Tree<Node<S>>;
+22 -12
View File
@@ -1,8 +1,8 @@
use std::cmp::Ordering;
use crate::node::Node;
use crate::node::{FromAnyValue, Node};
use crate::node_ref::{NodeMask, NodeView};
use crate::passes::{resolve_passes, AnyPass, DirtyNodeStates};
use crate::passes::{resolve_passes, resolve_passes_single_threaded, AnyPass, DirtyNodeStates};
use crate::tree::TreeView;
use crate::{FxDashSet, RealNodeId, SendAnyMap};
@@ -76,7 +76,7 @@ pub(crate) fn union_ordered_iter<'a>(
/// }
/// }
/// ```
pub trait ChildDepState {
pub trait ChildDepState<V: FromAnyValue = ()> {
/// The context is passed to the [ChildDepState::reduce] when it is resolved.
type Ctx;
/// A state from each child node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
@@ -87,7 +87,7 @@ pub trait ChildDepState {
/// Resolve the state current node's state from the state of the children, the state of the node, and some external context.
fn reduce<'a>(
&mut self,
node: NodeView,
node: NodeView<'a, V>,
children: impl Iterator<Item = <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
ctx: &Self::Ctx,
) -> bool
@@ -138,7 +138,7 @@ pub trait ChildDepState {
/// }
/// }
/// ```
pub trait ParentDepState {
pub trait ParentDepState<V: FromAnyValue = ()> {
/// The context is passed to the [ParentDepState::reduce] when it is resolved.
type Ctx;
/// A state from from the parent node that this node depends on. Typically this is Self, but it could be any state that is within the state tree.
@@ -149,7 +149,7 @@ pub trait ParentDepState {
/// Resolve the state current node's state from the state of the parent node, the state of the node, and some external context.
fn reduce<'a>(
&mut self,
node: NodeView,
node: NodeView<'a, V>,
parent: Option<<Self::DepState as ElementBorrowable>::ElementBorrowed<'a>>,
ctx: &Self::Ctx,
) -> bool;
@@ -193,7 +193,7 @@ pub trait ParentDepState {
/// }
/// }
/// ```
pub trait NodeDepState {
pub trait NodeDepState<V: FromAnyValue = ()> {
/// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState], [ParentDepState] or [NodeDepState].
type DepState: ElementBorrowable;
/// The state passed to [NodeDepState::reduce] when it is resolved.
@@ -203,7 +203,7 @@ pub trait NodeDepState {
/// Resolve the state current node's state from the state of the sibling states, the state of the node, and some external context.
fn reduce<'a>(
&mut self,
node: NodeView,
node: NodeView<'a, V>,
node_state: <Self::DepState as ElementBorrowable>::ElementBorrowed<'a>,
ctx: &Self::Ctx,
) -> bool;
@@ -217,7 +217,7 @@ pub trait State: Default + Clone + 'static {
const MASKS: &'static [NodeMask];
#[doc(hidden)]
fn update<T: TreeView<Node<Self>>>(
fn update<T: TreeView<Node<Self>> + Sync + Send>(
dirty: DirtyNodeStates,
tree: &mut T,
ctx: SendAnyMap,
@@ -225,12 +225,22 @@ pub trait State: Default + Clone + 'static {
let passes = Self::PASSES.iter().collect();
resolve_passes(tree, dirty, passes, ctx)
}
#[doc(hidden)]
fn update_single_threaded<T: TreeView<Node<Self>>>(
dirty: DirtyNodeStates,
tree: &mut T,
ctx: SendAnyMap,
) -> FxDashSet<RealNodeId> {
let passes = Self::PASSES.iter().collect();
resolve_passes_single_threaded(tree, dirty, passes, ctx)
}
}
impl ChildDepState for () {
type Ctx = ();
type DepState = ();
fn reduce<'a>(&mut self, _: NodeView, _: impl Iterator<Item = ()>, _: &Self::Ctx) -> bool
fn reduce<'a>(&mut self, _: NodeView<'a>, _: impl Iterator<Item = ()>, _: &Self::Ctx) -> bool
where
Self::DepState: 'a,
{
@@ -241,7 +251,7 @@ impl ChildDepState for () {
impl ParentDepState for () {
type Ctx = ();
type DepState = ();
fn reduce<'a>(&mut self, _: NodeView, _: Option<()>, _: &Self::Ctx) -> bool {
fn reduce<'a>(&mut self, _: NodeView<'a>, _: Option<()>, _: &Self::Ctx) -> bool {
false
}
}
@@ -249,7 +259,7 @@ impl ParentDepState for () {
impl NodeDepState for () {
type DepState = ();
type Ctx = ();
fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool {
fn reduce<'a>(&mut self, _: NodeView<'a>, _sibling: (), _: &Self::Ctx) -> bool {
false
}
}
+181 -371
View File
@@ -1,11 +1,5 @@
use core::panic;
use parking_lot::lock_api::RawMutex as _;
use parking_lot::{RawMutex, RwLock};
use slab::Slab;
use std::cell::UnsafeCell;
use std::collections::VecDeque;
use std::marker::PhantomData;
use std::sync::Arc;
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
pub struct NodeId(pub usize);
@@ -93,17 +87,7 @@ pub trait TreeView<T>: Sized {
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>>;
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> {
let mut_ptr: *mut Self = self;
unsafe {
// Safety: No node has itself as a child.
(*mut_ptr).get_mut(id).and_then(|parent| {
(*mut_ptr)
.children_mut(id)
.map(|children| (parent, children))
})
}
}
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)>;
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]>;
@@ -111,28 +95,12 @@ pub trait TreeView<T>: Sized {
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T>;
fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)> {
let mut_ptr: *mut Self = self;
unsafe {
// Safety: No node has itself as a parent.
(*mut_ptr)
.get_mut(id)
.map(|node| (node, (*mut_ptr).parent_mut(id)))
}
}
fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)>;
fn parent_id(&self, id: NodeId) -> Option<NodeId>;
fn height(&self, id: NodeId) -> Option<u16>;
fn map<T2, F: Fn(&T) -> &T2, FMut: Fn(&mut T) -> &mut T2>(
&mut self,
map: F,
map_mut: FMut,
) -> TreeMap<T, T2, Self, F, FMut> {
TreeMap::new(self, map, map_mut)
}
fn size(&self) -> usize;
fn traverse_depth_first(&self, mut f: impl FnMut(&T)) {
@@ -208,58 +176,39 @@ pub trait TreeLike<T>: TreeView<T> {
fn insert_after(&mut self, id: NodeId, new: NodeId);
}
pub struct ChildNodeIterator<'a, T, Tr: TreeView<T>> {
tree: &'a Tr,
children_ids: &'a [NodeId],
pub struct ChildNodeIterator<'a, T> {
nodes: &'a Slab<Node<T>>,
children_ids: Vec<NodeId>,
index: usize,
node_type: PhantomData<T>,
}
impl<'a, T: 'a, Tr: TreeView<T>> Iterator for ChildNodeIterator<'a, T, Tr> {
impl<'a, T: 'a> Iterator for ChildNodeIterator<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.children_ids.get(self.index).map(|id| {
self.index += 1;
self.tree.get_unchecked(*id)
&self.nodes.get(id.0).unwrap().value
})
}
}
pub struct ChildNodeIteratorMut<'a, T, Tr: TreeView<T> + 'a> {
tree: *mut Tr,
children_ids: &'a [NodeId],
index: usize,
node_type: PhantomData<T>,
pub struct ChildNodeIteratorMut<'a, T> {
nodes: Vec<&'a mut Node<T>>,
}
unsafe impl<'a, T, Tr: TreeView<T> + 'a> Sync for ChildNodeIteratorMut<'a, T, Tr> {}
impl<'a, T, Tr: TreeView<T>> ChildNodeIteratorMut<'a, T, Tr> {
fn tree_mut(&mut self) -> &'a mut Tr {
unsafe { &mut *self.tree }
}
}
impl<'a, T: 'a, Tr: TreeView<T>> Iterator for ChildNodeIteratorMut<'a, T, Tr> {
impl<'a, T: 'a> Iterator for ChildNodeIteratorMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
let owned = self.children_ids.get(self.index).copied();
match owned {
Some(id) => {
self.index += 1;
Some(self.tree_mut().get_mut_unchecked(id))
}
None => None,
}
self.nodes.pop().map(|node| &mut node.value)
}
}
impl<T> TreeView<T> for Tree<T> {
type Iterator<'a> = ChildNodeIterator<'a, T, Tree<T>> where T: 'a;
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T, Tree<T>> where T: 'a;
type Iterator<'a> = ChildNodeIterator<'a, T> where T: 'a;
type IteratorMut<'a> = ChildNodeIteratorMut<'a, T> where T: 'a;
fn root(&self) -> NodeId {
self.root
@@ -275,24 +224,26 @@ impl<T> TreeView<T> for Tree<T> {
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
self.children_ids(id).map(|children_ids| ChildNodeIterator {
tree: self,
children_ids,
nodes: &self.nodes,
children_ids: children_ids.to_vec(),
index: 0,
node_type: PhantomData,
})
}
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
let raw_ptr = self as *mut Self;
unsafe {
// Safety: No node will appear as a child twice
self.children_ids(id)
.map(|children_ids| ChildNodeIteratorMut {
tree: &mut *raw_ptr,
children_ids,
index: 0,
node_type: PhantomData,
})
// Safety: No node has itself as a parent.
if let Some(children_ids) = self.children_ids(id) {
let children_ids = children_ids.to_vec();
Some(ChildNodeIteratorMut {
nodes: unsafe {
self.nodes
.get_many_mut_unchecked(children_ids.into_iter().rev().map(|id| id.0))
.unwrap()
},
})
} else {
None
}
}
@@ -340,6 +291,39 @@ impl<T> TreeView<T> for Tree<T> {
fn size(&self) -> usize {
self.nodes.len()
}
fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)> {
if let Some(parent_id) = self.parent_id(id) {
self.nodes
.get2_mut(id.0, parent_id.0)
.map(|(node, parent)| (&mut node.value, Some(&mut parent.value)))
} else {
self.nodes.get_mut(id.0).map(|node| (&mut node.value, None))
}
}
fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> {
// Safety: No node will appear as a child twice
if let Some(children_ids) = self.children_ids(id) {
debug_assert!(!children_ids.iter().any(|child_id| *child_id == id));
let mut borrowed = unsafe {
let as_vec = children_ids.to_vec();
self.nodes
.get_many_mut_unchecked(
as_vec
.into_iter()
.rev()
.map(|id| id.0)
.chain(std::iter::once(id.0)),
)
.unwrap()
};
let node = &mut borrowed.pop().unwrap().value;
Some((node, ChildNodeIteratorMut { nodes: borrowed }))
} else {
None
}
}
}
impl<T> TreeLike<T> for Tree<T> {
@@ -435,248 +419,6 @@ impl<T> TreeLike<T> for Tree<T> {
self.set_height(new, height);
}
}
pub struct TreeMap<'a, T1, T2, Tr, F, FMut>
where
Tr: TreeView<T1>,
F: Fn(&T1) -> &T2,
FMut: Fn(&mut T1) -> &mut T2,
{
tree: &'a mut Tr,
map: F,
map_mut: FMut,
in_node_type: PhantomData<T1>,
out_node_type: PhantomData<T2>,
}
impl<'a, T1, T2, Tr, F, FMut> TreeMap<'a, T1, T2, Tr, F, FMut>
where
Tr: TreeView<T1>,
F: Fn(&T1) -> &T2,
FMut: Fn(&mut T1) -> &mut T2,
{
pub fn new(tree: &'a mut Tr, map: F, map_mut: FMut) -> Self {
TreeMap {
tree,
map,
map_mut,
in_node_type: PhantomData,
out_node_type: PhantomData,
}
}
}
impl<'a, T1, T2, Tr, F, FMut> TreeView<T2> for TreeMap<'a, T1, T2, Tr, F, FMut>
where
Tr: TreeView<T1>,
F: Fn(&T1) -> &T2,
FMut: Fn(&mut T1) -> &mut T2,
{
type Iterator<'b> = ChildNodeIterator<'b, T2, TreeMap<'a, T1, T2, Tr, F, FMut>>
where
T2: 'b,
Self:'b;
type IteratorMut<'b> = ChildNodeIteratorMut<'b, T2, TreeMap<'a, T1, T2, Tr, F, FMut>>
where
T2: 'b,
Self:'b;
fn root(&self) -> NodeId {
self.tree.root()
}
fn get(&self, id: NodeId) -> Option<&T2> {
self.tree.get(id).map(|node| (self.map)(node))
}
fn get_mut(&mut self, id: NodeId) -> Option<&mut T2> {
self.tree.get_mut(id).map(|node| (self.map_mut)(node))
}
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
self.children_ids(id).map(|children_ids| ChildNodeIterator {
tree: self,
children_ids,
index: 0,
node_type: PhantomData,
})
}
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
let raw_ptr = self as *mut Self;
unsafe {
// Safety: No node can be a child twice.
self.children_ids(id)
.map(|children_ids| ChildNodeIteratorMut {
tree: &mut *raw_ptr,
children_ids,
index: 0,
node_type: PhantomData,
})
}
}
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> {
self.tree.children_ids(id)
}
fn parent(&self, id: NodeId) -> Option<&T2> {
self.tree.parent(id).map(|node| (self.map)(node))
}
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T2> {
self.tree.parent_mut(id).map(|node| (self.map_mut)(node))
}
fn parent_id(&self, id: NodeId) -> Option<NodeId> {
self.tree.parent_id(id)
}
fn height(&self, id: NodeId) -> Option<u16> {
self.tree.height(id)
}
fn get_unchecked(&self, id: NodeId) -> &T2 {
(self.map)(self.tree.get_unchecked(id))
}
fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T2 {
(self.map_mut)(self.tree.get_mut_unchecked(id))
}
fn size(&self) -> usize {
self.tree.size()
}
}
/// A view into a tree that can be shared between multiple threads. Nodes are locked invividually.
pub struct SharedView<'a, T, Tr: TreeView<T>> {
tree: Arc<UnsafeCell<&'a mut Tr>>,
node_locks: Arc<RwLock<Vec<RawMutex>>>,
node_type: PhantomData<T>,
}
impl<'a, T, Tr: TreeView<T>> SharedView<'a, T, Tr> {
/// Checks if a node is currently locked. Returns None if the node does not exist.
pub fn check_lock(&self, id: NodeId) -> Option<bool> {
let locks = self.node_locks.read();
locks.get(id.0).map(|lock| lock.is_locked())
}
}
unsafe impl<'a, T, Tr: TreeView<T>> Send for SharedView<'a, T, Tr> {}
unsafe impl<'a, T, Tr: TreeView<T>> Sync for SharedView<'a, T, Tr> {}
impl<'a, T, Tr: TreeView<T>> Clone for SharedView<'a, T, Tr> {
fn clone(&self) -> Self {
Self {
tree: self.tree.clone(),
node_locks: self.node_locks.clone(),
node_type: PhantomData,
}
}
}
impl<'a, T, Tr: TreeView<T>> SharedView<'a, T, Tr> {
pub fn new(tree: &'a mut Tr) -> Self {
let tree = Arc::new(UnsafeCell::new(tree));
let mut node_locks = Vec::new();
for _ in 0..unsafe { (*tree.get()).size() } {
node_locks.push(RawMutex::INIT);
}
Self {
tree,
node_locks: Arc::new(RwLock::new(node_locks)),
node_type: PhantomData,
}
}
fn lock_node(&self, node: NodeId) {
let read = self.node_locks.read();
let lock = read.get(node.0);
match lock {
Some(lock) => lock.lock(),
None => {
drop(read);
let mut write = self.node_locks.write();
write.resize_with(node.0 + 1, || RawMutex::INIT);
unsafe { write.get_unchecked(node.0).lock() }
}
}
}
fn unlock_node(&self, node: NodeId) {
let read = self.node_locks.read();
let lock = read.get(node.0);
match lock {
Some(lock) => unsafe { lock.unlock() },
None => {
panic!("unlocking node that was not locked")
}
}
}
fn with_node<R>(&self, node_id: NodeId, f: impl FnOnce(&'a mut Tr) -> R) -> R {
self.lock_node(node_id);
let tree = unsafe { &mut *self.tree.get() };
let r = f(tree);
self.unlock_node(node_id);
r
}
}
impl<'a, T, Tr: TreeView<T>> TreeView<T> for SharedView<'a, T, Tr> {
type Iterator<'b> = Tr::Iterator<'b> where T: 'b, Self: 'b;
type IteratorMut<'b>=Tr::IteratorMut<'b>
where
T: 'b,
Self: 'b;
fn root(&self) -> NodeId {
unsafe { (*self.tree.get()).root() }
}
fn get(&self, id: NodeId) -> Option<&T> {
self.with_node(id, |t| t.get(id))
}
fn get_mut(&mut self, id: NodeId) -> Option<&mut T> {
self.with_node(id, |t| t.get_mut(id))
}
fn children(&self, id: NodeId) -> Option<Self::Iterator<'_>> {
self.with_node(id, |t| t.children(id))
}
fn children_mut(&mut self, id: NodeId) -> Option<Self::IteratorMut<'_>> {
self.with_node(id, |t| t.children_mut(id))
}
fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> {
self.with_node(id, |t| t.children_ids(id))
}
fn parent(&self, id: NodeId) -> Option<&T> {
self.with_node(id, |t| t.get(id))
}
fn parent_mut(&mut self, id: NodeId) -> Option<&mut T> {
self.with_node(id, |t| t.parent_mut(id))
}
fn parent_id(&self, id: NodeId) -> Option<NodeId> {
self.with_node(id, |t| t.parent_id(id))
}
fn height(&self, id: NodeId) -> Option<u16> {
unsafe { (*self.tree.get()).height(id) }
}
fn size(&self) -> usize {
unsafe { (*self.tree.get()).size() }
}
}
#[test]
fn creation() {
let mut tree = Tree::new(1);
@@ -785,59 +527,6 @@ fn deletion() {
assert_eq!(tree.children_ids(parent).unwrap(), &[]);
}
#[test]
fn shared_view() {
use std::thread;
let mut tree = Tree::new(1);
let parent = tree.root();
let child = tree.create_node(0);
tree.add_child(parent, child);
let shared = SharedView::new(&mut tree);
thread::scope(|s| {
let (mut shared1, mut shared2, mut shared3) =
(shared.clone(), shared.clone(), shared.clone());
s.spawn(move || {
assert_eq!(*shared1.get_mut(parent).unwrap(), 1);
assert_eq!(*shared1.get_mut(child).unwrap(), 0);
});
s.spawn(move || {
assert_eq!(*shared2.get_mut(child).unwrap(), 0);
assert_eq!(*shared2.get_mut(parent).unwrap(), 1);
});
s.spawn(move || {
assert_eq!(*shared3.get_mut(parent).unwrap(), 1);
assert_eq!(*shared3.get_mut(child).unwrap(), 0);
});
});
}
#[test]
fn map() {
#[derive(Debug, PartialEq)]
struct Value {
value: i32,
}
impl Value {
fn new(value: i32) -> Self {
Self { value }
}
}
let mut tree = Tree::new(Value::new(1));
let parent = tree.root();
let child = tree.create_node(Value::new(0));
tree.add_child(parent, child);
let mut mapped = tree.map(|x| &x.value, |x| &mut x.value);
*mapped.get_mut(child).unwrap() = 1;
*mapped.get_mut(parent).unwrap() = 2;
assert_eq!(*tree.get(parent).unwrap(), Value::new(2));
assert_eq!(*tree.get(child).unwrap(), Value::new(1));
}
#[test]
fn traverse_depth_first() {
let mut tree = Tree::new(0);
@@ -857,3 +546,124 @@ fn traverse_depth_first() {
node_count += 1;
});
}
#[test]
fn get_node_children_mut() {
let mut tree = Tree::new(0);
let parent = tree.root();
let child1 = tree.create_node(1);
tree.add_child(parent, child1);
let child2 = tree.create_node(2);
tree.add_child(parent, child2);
let child3 = tree.create_node(3);
tree.add_child(parent, child3);
let (parent, children) = tree.parent_child_mut(parent).unwrap();
for (i, child) in children.enumerate() {
assert_eq!(*child, i + 1);
}
println!("Parent: {:#?}", parent);
}
#[test]
fn get_many_mut_unchecked() {
let mut slab = Slab::new();
let parent = slab.insert(0);
let child = slab.insert(1);
let grandchild = slab.insert(2);
let all =
unsafe { slab.get_many_mut_unchecked([parent, child, grandchild].into_iter()) }.unwrap();
println!("All: {:#?}", all);
}
#[derive(Debug)]
struct Slab<T> {
data: Vec<Option<T>>,
free: VecDeque<usize>,
}
impl<T> Default for Slab<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Slab<T> {
fn new() -> Self {
Self {
data: Vec::new(),
free: VecDeque::new(),
}
}
fn get(&self, id: usize) -> Option<&T> {
self.data.get(id).and_then(|x| x.as_ref())
}
unsafe fn get_unchecked(&self, id: usize) -> &T {
self.data.get_unchecked(id).as_ref().unwrap()
}
fn get_mut(&mut self, id: usize) -> Option<&mut T> {
self.data.get_mut(id).and_then(|x| x.as_mut())
}
unsafe fn get_unchecked_mut(&mut self, id: usize) -> &mut T {
self.data.get_unchecked_mut(id).as_mut().unwrap()
}
fn get2_mut(&mut self, id1: usize, id2: usize) -> Option<(&mut T, &mut T)> {
assert!(id1 != id2);
let ptr = self.data.as_mut_ptr();
let first = unsafe { &mut *ptr.add(id1) };
let second = unsafe { &mut *ptr.add(id2) };
if let (Some(first), Some(second)) = (first, second) {
Some((first, second))
} else {
None
}
}
unsafe fn get_many_mut_unchecked(
&mut self,
ids: impl Iterator<Item = usize>,
) -> Option<Vec<&mut T>> {
let ptr = self.data.as_mut_ptr();
let mut result = Vec::new();
for id in ids {
let item = unsafe { &mut *ptr.add(id) };
if let Some(item) = item {
result.push(item);
} else {
return None;
}
}
Some(result)
}
fn insert(&mut self, value: T) -> usize {
if let Some(id) = self.free.pop_front() {
self.data[id] = Some(value);
id
} else {
self.data.push(Some(value));
self.data.len() - 1
}
}
fn try_remove(&mut self, id: usize) -> Option<T> {
self.data.get_mut(id).and_then(|x| {
self.free.push_back(id);
x.take()
})
}
fn remove(&mut self, id: usize) -> T {
self.try_remove(id).unwrap()
}
fn len(&self) -> usize {
self.data.len() - self.free.len()
}
}
@@ -229,7 +229,7 @@ impl PersistantElementIter {
#[derive(Default, Clone, Debug)]
struct Empty {}
impl State for Empty {
const PASSES: &'static [crate::AnyPass<crate::node::Node<Self>>] = &[];
const PASSES: &'static [crate::AnyPass<crate::node::Node<Self, ()>>] = &[];
const MASKS: &'static [crate::NodeMask] = &[];
}
+1 -1
View File
@@ -27,7 +27,7 @@ futures = "0.3.19"
taffy = "0.2.1"
smallvec = "1.6"
rustc-hash = "1.1.0"
anymap = "0.12.1"
anymap = "1.0.0-beta.2"
futures-channel = "0.3.25"
[dev-dependencies]
+1 -1
View File
@@ -79,7 +79,7 @@ impl NodeDepState for Focus {
if let Some(index) = a
.value
.as_int()
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i32>().ok()))
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i64>().ok()))
{
match index.cmp(&0) {
Ordering::Less => FocusLevel::Unfocusable,
+26 -7
View File
@@ -7,7 +7,9 @@
//! - tests to ensure dyn_into works for various event types.
//! - Partial delegation?>
use dioxus_core::{ElementId, Mutation, Template, TemplateAttribute, TemplateNode};
use dioxus_core::{
BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
};
use dioxus_html::{event_bubbles, CompositionData, FormData};
use dioxus_interpreter_js::{save_template, Channel};
use futures_channel::mpsc;
@@ -175,16 +177,33 @@ impl WebsysDom {
value,
id,
ns,
} => i.set_attribute(id.0 as u32, name, value, ns.unwrap_or_default()),
SetBoolAttribute { name, value, id } => {
i.set_attribute(id.0 as u32, name, if *value { "true" } else { "false" }, "")
}
} => match value {
BorrowedAttributeValue::Text(txt) => {
i.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
}
BorrowedAttributeValue::Float(f) => {
i.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
}
BorrowedAttributeValue::Int(n) => {
i.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
}
BorrowedAttributeValue::Bool(b) => i.set_attribute(
id.0 as u32,
name,
if *b { "true" } else { "false" },
ns.unwrap_or_default(),
),
BorrowedAttributeValue::None => {
i.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
}
_ => unreachable!(),
},
SetText { value, id } => i.set_text(id.0 as u32, value),
NewEventListener { name, id, .. } => {
i.new_event_listener(name, id.0 as u32, event_bubbles(&name[2..]) as u8);
i.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
}
RemoveEventListener { name, id } => {
i.remove_event_listener(name, id.0 as u32, event_bubbles(&name[2..]) as u8)
i.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8)
}
Remove { id } => i.remove(id.0 as u32),
PushRoot { id } => i.push_root(id.0 as u32),
+5 -3
View File
@@ -189,10 +189,12 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
// if should_hydrate {
// } else {
let edits = dom.rebuild();
{
let edits = dom.rebuild();
websys_dom.load_templates(&edits.templates);
websys_dom.apply_edits(edits.edits);
websys_dom.load_templates(&edits.templates);
websys_dom.apply_edits(edits.edits);
}
// the mutations come back with nothing - we need to actually mount them
websys_dom.mount();