Feat: implement vcomp fully
This commit is contained in:
1 parent
5abda91892
commit
29751a4bab
4 files changed
+104
-195
No files matched your search
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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,
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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.
|
||||||
Reference in new issue
Block a user