Feat: implement vcomp fully

This commit is contained in:
Jonathan Kelley committed 2021-03-10 19:42:10 -05:00
1 parent 5abda91892
commit 29751a4bab
4 files changed
+104 -195

No files matched your search

-20
View File
@@ -55,7 +55,6 @@ use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
/// subscriptions and props changes. /// subscriptions and props changes.
pub struct DiffMachine<'a> { pub struct DiffMachine<'a> {
pub change_list: EditMachine<'a>, pub change_list: EditMachine<'a>,
pub diffed: FxHashSet<ScopeIdx>, pub diffed: FxHashSet<ScopeIdx>,
pub need_to_diff: FxHashSet<ScopeIdx>, pub need_to_diff: FxHashSet<ScopeIdx>,
} }
@@ -125,25 +124,6 @@ impl<'a> DiffMachine<'a> {
} }
(_, VNode::Component(new)) => { (_, VNode::Component(new)) => {
// let VComponent {
// props,
// props_type,
// comp,
// caller,
// assigned_scope,
// ..
// } = *new;
// make the component
// let idx = unsafe {
// // let vdom = &mut *self.vdom;
// vdom.insert_with(|f| {
// todo!()
// //
// // create_scoped(caller, props, myidx, parent)
// })
// };
// we have no stable reference to work from // we have no stable reference to work from
// push the lifecycle event onto the queue // push the lifecycle event onto the queue
// self.lifecycle_events // self.lifecycle_events
+1 -2
View File
@@ -89,10 +89,10 @@ pub mod builder {
// types used internally that are important // types used internally that are important
pub(crate) mod innerlude { pub(crate) mod innerlude {
// pub(crate) use crate::component::Properties; // pub(crate) use crate::component::Properties;
pub(crate) use crate::component::Properties; pub(crate) use crate::component::Properties;
pub(crate) use crate::context::Context; pub(crate) use crate::context::Context;
pub(crate) use crate::error::{Error, Result}; pub(crate) use crate::error::{Error, Result};
pub use crate::events::EventTrigger;
use crate::nodes; use crate::nodes;
pub(crate) use crate::scope::Scope; pub(crate) use crate::scope::Scope;
pub(crate) use crate::virtual_dom::VirtualDom; pub(crate) use crate::virtual_dom::VirtualDom;
@@ -100,7 +100,6 @@ pub(crate) mod innerlude {
pub use crate::component::ScopeIdx; pub use crate::component::ScopeIdx;
pub use crate::diff::DiffMachine; pub use crate::diff::DiffMachine;
pub use crate::events::EventTrigger;
pub use crate::patch::{EditList, EditMachine}; pub use crate::patch::{EditList, EditMachine};
// pub use crate::patchdx; // pub use crate::patchdx;
// pub use crate::patchtList; // pub use crate::patchtList;
+100 -169
View File
@@ -3,27 +3,35 @@
//! //!
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick. //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
use crate::{
events::VirtualEvent,
innerlude::{Context, Properties, ScopeIdx, FC},
};
use bumpalo::Bump; use bumpalo::Bump;
pub use vcomponent::VComponent; use std::fmt::Debug;
pub use velement::VElement; use std::{
pub use velement::{Attribute, Listener, NodeKey}; any::{Any, TypeId},
pub use vnode::VNode; cell::RefCell,
pub use vtext::VText; marker::PhantomData,
rc::Rc,
};
/// A domtree represents the result of "Viewing" the context /// A domtree represents the result of "Viewing" the context
/// It's a placeholder over vnodes, to make working with lifetimes easier /// It's a placeholder over vnodes, to make working with lifetimes easier
pub struct DomTree; pub struct DomTree;
// ==============================
// VNODES
// ==============================
/// Tools for the base unit of the virtual dom - the VNode /// Tools for the base unit of the virtual dom - the VNode
/// VNodes are intended to be quickly-allocated, lightweight enum values. /// VNodes are intended to be quickly-allocated, lightweight enum values.
/// ///
/// Components will be generating a lot of these very quickly, so we want to /// Components will be generating a lot of these very quickly, so we want to
/// limit the amount of heap allocations / overly large enum sizes. /// limit the amount of heap allocations / overly large enum sizes.
mod vnode { #[derive(Debug)]
use super::*; pub enum VNode<'src> {
#[derive(Debug)]
pub enum VNode<'src> {
/// An element node (node type `ELEMENT_NODE`). /// An element node (node type `ELEMENT_NODE`).
Element(&'src VElement<'src>), Element(&'src VElement<'src>),
@@ -41,9 +49,9 @@ mod vnode {
/// A User-defined componen node (node type COMPONENT_NODE) /// A User-defined componen node (node type COMPONENT_NODE)
Component(VComponent<'src>), Component(VComponent<'src>),
} }
impl<'a> VNode<'a> { impl<'a> VNode<'a> {
/// Low-level constructor for making a new `Node` of type element with given /// Low-level constructor for making a new `Node` of type element with given
/// parts. /// parts.
/// ///
@@ -90,20 +98,13 @@ mod vnode {
} }
} }
} }
}
} }
mod velement { // ========================================================
// VElement (div, h1, etc), attrs, keys, listener handle
// use crate::{events::VirtualEvent, innerlude::CbIdx}; // ========================================================
#[derive(Debug)]
use crate::{events::VirtualEvent, innerlude::ScopeIdx}; pub struct VElement<'a> {
use super::*;
use std::fmt::Debug;
#[derive(Debug)]
pub struct VElement<'a> {
/// Elements have a tag name, zero or more attributes, and zero or more /// Elements have a tag name, zero or more attributes, and zero or more
pub key: NodeKey, pub key: NodeKey,
pub tag_name: &'a str, pub tag_name: &'a str,
@@ -132,9 +133,9 @@ mod velement {
// /// The children of this `VNode`. So a <div> <em></em> </div> structure would // /// The children of this `VNode`. So a <div> <em></em> </div> structure would
// /// have a parent div and one child, em. // /// have a parent div and one child, em.
// pub children: Vec<VNode>, // pub children: Vec<VNode>,
} }
impl<'a> VElement<'a> { impl<'a> VElement<'a> {
// The tag of a component MUST be known at compile time // The tag of a component MUST be known at compile time
pub fn new(_tag: &'a str) -> Self { pub fn new(_tag: &'a str) -> Self {
todo!() todo!()
@@ -146,17 +147,17 @@ mod velement {
// children: vec![], // children: vec![],
// } // }
} }
} }
/// An attribute on a DOM node, such as `id="my-thing"` or /// An attribute on a DOM node, such as `id="my-thing"` or
/// `href="https://example.com"`. /// `href="https://example.com"`.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Attribute<'a> { pub struct Attribute<'a> {
pub name: &'static str, pub name: &'static str,
pub value: &'a str, pub value: &'a str,
} }
impl<'a> Attribute<'a> { impl<'a> Attribute<'a> {
/// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`. /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
#[inline] #[inline]
pub fn name(&self) -> &'a str { pub fn name(&self) -> &'a str {
@@ -180,16 +181,16 @@ mod velement {
_ => false, _ => false,
} }
} }
} }
pub struct ListenerHandle { pub struct ListenerHandle {
pub event: &'static str, pub event: &'static str,
pub scope: ScopeIdx, pub scope: ScopeIdx,
pub id: usize, pub id: usize,
} }
/// An event listener. /// An event listener.
pub struct Listener<'bump> { pub struct Listener<'bump> {
/// The type of event to listen for. /// The type of event to listen for.
pub(crate) event: &'static str, pub(crate) event: &'static str,
@@ -200,52 +201,29 @@ mod velement {
// #[serde(skip_serializing, skip_deserializing, default="")] // #[serde(skip_serializing, skip_deserializing, default="")]
// /// The callback to invoke when the event happens. // /// The callback to invoke when the event happens.
pub(crate) callback: &'bump (dyn Fn(VirtualEvent)), pub(crate) callback: &'bump (dyn Fn(VirtualEvent)),
} }
impl Debug for Listener<'_> { impl Debug for Listener<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Listener") f.debug_struct("Listener")
.field("event", &self.event) .field("event", &self.event)
.finish() .finish()
} }
} }
pub(crate) type ListenerCallback<'a> = &'a (dyn Fn(VirtualEvent));
union CallbackFatPtr<'a> {
callback: ListenerCallback<'a>,
parts: (u32, u32),
}
impl Listener<'_> {
#[inline]
pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
assert_eq!(
std::mem::size_of::<ListenerCallback>(),
std::mem::size_of::<CallbackFatPtr>()
);
unsafe { /// The key for keyed children.
let fat = CallbackFatPtr { ///
callback: self.callback, /// Keys must be unique among siblings.
}; ///
let (a, b) = fat.parts; /// If any sibling is keyed, then they all must be keyed.
debug_assert!(a != 0); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
(a, b) pub struct NodeKey(pub(crate) u32);
}
}
}
/// The key for keyed children. impl Default for NodeKey {
///
/// Keys must be unique among siblings.
///
/// If any sibling is keyed, then they all must be keyed.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NodeKey(pub(crate) u32);
impl Default for NodeKey {
fn default() -> NodeKey { fn default() -> NodeKey {
NodeKey::NONE NodeKey::NONE
} }
} }
impl NodeKey { impl NodeKey {
/// The default, lack of a key. /// The default, lack of a key.
pub const NONE: NodeKey = NodeKey(u32::MAX); pub const NONE: NodeKey = NodeKey(u32::MAX);
@@ -269,128 +247,81 @@ mod velement {
debug_assert_ne!(key, u32::MAX); debug_assert_ne!(key, u32::MAX);
NodeKey(key) NodeKey(key)
} }
}
// todo
// use zst enum for element type. Something like ValidElements::div
} }
mod vtext { #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub struct VText<'bump> {
pub struct VText<'bump> {
pub text: &'bump str, pub text: &'bump str,
} }
impl<'a> VText<'a> { impl<'a> VText<'a> {
// / Create an new `VText` instance with the specified text. // / Create an new `VText` instance with the specified text.
pub fn new(text: &'a str) -> Self pub fn new(text: &'a str) -> Self
// pub fn new<S>(text: S) -> Self // pub fn new(text: Into<str>) -> Self
// where
// S: Into<str>,
{ {
VText { text: text.into() } VText { text: text.into() }
} }
}
} }
// ==============================
// Custom components
// ==============================
/// Virtual Components for custom user-defined components /// Virtual Components for custom user-defined components
/// Only supports the functional syntax /// Only supports the functional syntax
mod vcomponent { pub type StableScopeAddres = Rc<RefCell<Option<usize>>>;
use crate::innerlude::{Context, Properties, ScopeIdx, FC};
use std::{
any::{Any, TypeId},
cell::RefCell,
marker::PhantomData,
rc::Rc,
};
use super::DomTree; #[derive(Debug)]
pub struct VComponent<'src> {
pub type StableScopeAddres = Rc<RefCell<Option<ScopeIdx>>>; pub stable_addr: StableScopeAddres,
pub comparator: Comparator,
#[derive(Debug)] pub caller: Caller,
pub struct VComponent<'src> {
_p: PhantomData<&'src ()>, _p: PhantomData<&'src ()>,
// pub(crate) props: Box<dyn std::any::Any>, }
// pub(crate) props_type: TypeId, pub struct Comparator(Box<dyn Fn(&dyn Any) -> bool>);
// pub(crate) comp: *const (), impl std::fmt::Debug for Comparator {
// pub(crate) caller: Caller,
pub(crate) comparator: Comparator,
// once a component gets mounted, its parent gets a stable address.
// this way we can carry the scope index from between renders
// genius, really!
// pub assigned_scope: StableScopeAddres,
}
pub struct Comparator(Box<dyn Fn(&dyn Any) -> bool>);
// pub struct Comparator<'src>(&'src dyn Fn(&dyn Any) -> bool);
pub struct Caller(Box<dyn Fn(Context) -> DomTree>);
impl std::fmt::Debug for Caller {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!() Ok(())
}
} }
}
impl std::fmt::Debug for Comparator { pub struct Caller(Box<dyn Fn(Context) -> DomTree>);
impl std::fmt::Debug for Caller {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!() Ok(())
}
} }
}
impl<'a> VComponent<'a> { impl<'a> VComponent<'a> {
// use the type parameter on props creation and move it into a portable context // use the type parameter on props creation and move it into a portable context
// this lets us keep scope generic *and* downcast its props when we need to: // this lets us keep scope generic *and* downcast its props when we need to:
// - perform comparisons when diffing (memoization) // - perform comparisons when diffing (memoization)
// - // TODO: lift the requirement that props need to be static
pub fn new<P: Properties + 'static>(caller: FC<P>, comp: P) -> Self { // we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
todo!() pub fn new<P: Properties + 'static>(caller: FC<P>, props: P) -> Self {
// let p = Rc::new(props); let props = Rc::new(props);
// let props_comparator = move |new_props: &dyn Any| -> bool { // used for memoization
// new_props let p1 = props.clone();
// .downcast_ref::<P>() let props_comparator = move |new_props: &dyn Any| -> bool {
// .map(|new_p| p.as_ref() == new_p) new_props
// .unwrap_or_else(|| { .downcast_ref::<P>()
// log::debug!("downcasting failed, this receovered but SHOULD NOT FAIL"); .map(|new_p| p1.as_ref() == new_p)
// false .unwrap_or_else(|| {
// }) log::debug!("downcasting failed, this receovered but SHOULD NOT FAIL");
// }; false
})
};
// Self { // used for actually rendering the custom component
// _p: PhantomData, let p2 = props.clone();
// comparator: Comparator(Box::new(props_comparator)), let caller = move |ctx: Context| -> DomTree { caller(ctx, p2.as_ref()) };
// }
// let caller = move |ctx: Context| {
// let t = comp(ctx, &props);
// t
// };
// let _caller = comp as *const ();
// let _props = Box::new(props);
// cast to static? Self {
// how do we save the type of props ? _p: PhantomData,
// do it in the caller function? comparator: Comparator(Box::new(props_comparator)),
// something like caller: Caller(Box::new(caller)),
stable_addr: Rc::new(RefCell::new(None)),
// the "true" entrpypoint
//
// |caller| caller.call(fn, Props {})
// fn call<P>(f, new_props) {
// let old_props = old.downcast_ref::<P>();
// if new_props == old_props {
// return;
// } else {
// // set the new props
// // call the fn
// }
// }
// todo!()
// Self {
// _p: PhantomData {},
// props,
// caller,
// }
} }
} }
} }
+3 -4
View File
@@ -185,8 +185,7 @@ impl VirtualDom {
} }
enum LifeCycleEvent { enum LifeCycleEvent {
// Mount { Mount {},
// props: &dyn Properties,
// // f: FC<dyn Properties>,
// },
} }
// todo: add some "handle" to the vdom. Or a way of giving out closures that can mutate the vdoms internal data.