Explorar o código

Feat: move out scope into its own file

Jonathan Kelley %!s(int64=4) %!d(string=hai) anos
pai
achega
c9d95dd1dc

+ 23 - 0
packages/core/architecture.md

@@ -0,0 +1,23 @@
+# This module includes all life-cycle related mechanics, including the virtual DOM, scopes, properties, and lifecycles.
+---
+The VirtualDom is designed as so:
+
+VDOM contains:
+- An arena of component scopes.
+    - A scope contains
+        - lifecycle data
+        - hook data
+- Event queue
+    - An event
+
+A VDOM is
+- constructed from anything that implements "component"
+
+A "Component" is anything (normally functions) that can be ran with a context to produce VNodes
+- Must implement properties-builder trait which produces a properties builder
+
+A Context
+- Is a consumable struct
+    - Made of references to properties
+    - Holds a reference (lockable) to the underlying scope
+    - Is partially thread-safe

+ 1 - 94
packages/core/examples/dummy.rs

@@ -1,102 +1,9 @@
 #![allow(unused, non_upper_case_globals)]
 use bumpalo::Bump;
+use dioxus_core::nodebuilder::*;
 use dioxus_core::prelude::VNode;
 use dioxus_core::prelude::*;
-use dioxus_core::{nodebuilder::*, virtual_dom::Properties};
 use once_cell::sync::{Lazy, OnceCell};
 use std::{collections::HashMap, future::Future, marker::PhantomData};
 
 fn main() {}
-
-// struct VC<P, F = fn(Context<P>) -> VNode> {
-//     f: F,
-//     _a: std::marker::PhantomData<(P, F)>, // cell: OnceCell<T>,
-//                                           // init: Cell<Option<F>>
-// }
-// impl<P, F> VC<P, F> {
-//     const fn new(init: F) -> VC<P, F> {
-//         Self {
-//             _a: std::marker::PhantomData {},
-//             f: init,
-//         }
-//     }
-//     fn builder() -> P {
-//         // P::new()
-//     }
-// }
-
-// // Build a new functional component
-// static SomeComp: VC<()> = VC::new(|ctx| {
-//     // This is a component, apparently
-//     // still not useful because we can't have bounds
-
-//     ctx.view(html! {
-//         <div>
-
-//         </div>
-//     })
-// });
-
-/*
-
-
-
-
-
-
-
-
-
-
-
-*/
-static BILL: Lazy<fn(Context<()>) -> String> = Lazy::new(|| {
-    //
-    |c| "BLAH".to_string()
-});
-
-// struct FUNC<F = fn() -> T> {}
-
-struct SomeBuilder {}
-
-// struct DummyRenderer {
-//     alloc: Bump,
-// }
-
-// impl DummyRenderer {
-//     // "Renders" a domtree by logging its children and outputs
-//     fn render() {}
-
-//     // Takes a domtree, an initial value, a new value, and produces the diff list
-//     fn produce_diffs() {}
-// }
-
-// struct Props<'a> {
-//     name: &'a str,
-// }
-
-// /// This component does "xyz things"
-// /// This is sample documentation
-// static Component: FC<Props> = |ctx| {
-//     // This block mimics that output of the html! macro
-
-//     DomTree::new(move |bump| {
-//         // parse into RSX structures
-//         // regurgetate as rust types
-
-//         // <div> "Child 1" "Child 2"</div>
-//         div(bump)
-//             .attr("class", "edit")
-//             .child(text("Child 1"))
-//             .child(text("Child 2"))
-//             .finish()
-//     })
-// };
-
-// /*
-// source
-//     |> c1 -> VNode
-//     |> c2 -> VNode
-//     |> c3 -> VNode
-//     |> c4 -> VNode
-// */

+ 9 - 22
packages/core/examples/macrosrc.rs

@@ -1,7 +1,7 @@
 #![allow(unused, non_upper_case_globals, non_snake_case)]
 use bumpalo::Bump;
+use dioxus_core::nodebuilder::*;
 use dioxus_core::prelude::*;
-use dioxus_core::{nodebuilder::*, virtual_dom::Properties};
 use std::{collections::HashMap, future::Future, marker::PhantomData};
 
 fn main() {
@@ -9,7 +9,7 @@ fn main() {
         component,
         Props {
             blah: false,
-            text: "blah",
+            text: "blah".into(),
         },
     );
 
@@ -45,11 +45,12 @@ fn main() {
 
 // ~~~ Text shared between components via props can be done with lifetimes! ~~~
 // Super duper efficient :)
-struct Props<'src> {
+struct Props {
     blah: bool,
-    text: &'src str,
+    text: String,
+    // text: &'src str,
 }
-impl<'src> Properties for Props<'src> {
+impl Properties for Props {
     fn new() -> Self {
         todo!()
     }
@@ -88,7 +89,7 @@ fn BuilderComp<'a>(ctx: &'a Context<'a, Props>) -> VNode<'a> {
         div(bump)
             .attr("class", "edit")
             .child(text("Hello"))
-            .child(text(ctx.props.text))
+            .child(text(ctx.props.text.as_ref()))
             .finish()
     })
 }
@@ -99,23 +100,7 @@ fn EffcComp(ctx: &Context, name: &str) -> VNode {
     // However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
     // We can "view" them with Context for ultimate speed while inside components
     // use "phase" style allocation;
-    /*
-    nodes...
-    text...
-    attrs...
-    <div> // node0
-        <div> </div> // node1
-        {// support some expression} // node 2
-    </div>
-    let node0;
-    let node1;
-    let node2 = evaluate{}.into();
-    let g= |bump| {1};
-    g(bump).into()
-
-    */
 
-    // should we automatically view the output or leave it?
     ctx.view(html! {
         <div>
             // your template goes here
@@ -129,6 +114,8 @@ fn FullySuspended<'a>(ctx: &'a Context<Props>) -> VNode<'a> {
         let i: i32 = 0;
 
         // full suspended works great with just returning VNodes!
+        // Feel free to capture the html! macro directly
+        // Anything returned here is automatically viewed
         let tex = match i {
             1 => html! { <div> </div> },
             2 => html! { <div> </div> },

+ 2 - 2
packages/core/examples/sketch.rs

@@ -1,8 +1,8 @@
 use bumpalo::Bump;
 use dioxus_core as dioxus;
 use dioxus_core::{
-    prelude::{html, Context, VElement, VNode, FC},
-    virtual_dom::{Properties, Scope},
+    prelude::{html, Context, Properties, VElement, VNode, FC},
+    scope::Scope,
 };
 use std::{
     any::Any,

+ 6 - 8
packages/core/examples/step.rs

@@ -5,17 +5,15 @@
 //!     render it again
 //!     consume the diffs and write that to a renderer
 
-use dioxus_core::{
-    prelude::*,
-    virtual_dom::{Properties, Scope},
-};
+use dioxus_core::{prelude::*, scope::Scope};
 
-fn main() {
-    let mut scope = Scope::new(Example);
-    let ctx = scope.create_context::<Props>();
+fn main() -> Result<(), ()> {
     let p1 = Props { name: "bob".into() };
 
-    let p2 = Props { name: "bob".into() };
+    let mut vdom = VirtualDom::new_with_props(Example, p1);
+    vdom.progress()?;
+
+    Ok(())
 }
 
 struct Props {

+ 0 - 0
packages/core/src/old.rs → packages/core/old/old.rs


+ 0 - 0
packages/core/src/virtual_node.rs → packages/core/old/virtual_node.rs


+ 58 - 0
packages/core/src/component.rs

@@ -0,0 +1,58 @@
+use crate::inner::*;
+use crate::prelude::bumpalo::Bump;
+
+/// The `Component` trait refers to any struct or funciton that can be used as a component
+/// We automatically implement Component for FC<T>
+pub trait Component {
+    type Props: Properties;
+    fn builder(&'static self) -> Self::Props;
+}
+
+// Auto implement component for a FC
+// Calling the FC is the same as "rendering" it
+impl<P: Properties> Component for FC<P> {
+    type Props = P;
+
+    fn builder(&self) -> Self::Props {
+        todo!()
+    }
+}
+
+/// The `Properties` trait defines any struct that can be constructed using a combination of default / optional fields.
+/// Components take a "properties" object
+pub trait Properties: 'static {
+    fn new() -> Self;
+}
+
+// Auto implement for no-prop components
+impl Properties for () {
+    fn new() -> Self {
+        ()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    fn test_static_fn<'a, P: Properties>(b: &'a Bump, r: FC<P>) -> VNode<'a> {
+        todo!()
+    }
+
+    fn test_component(ctx: Context<()>) -> VNode {
+        ctx.view(html! {<div> </div> })
+    }
+
+    fn test_component2(ctx: Context<()>) -> VNode {
+        ctx.view(|bump: &Bump| VNode::text("blah"))
+    }
+
+    #[test]
+    fn ensure_types_work() {
+        let bump = Bump::new();
+
+        // Happiness! The VNodes are now allocated onto the bump vdom
+        let _ = test_static_fn(&bump, test_component);
+        let _ = test_static_fn(&bump, test_component2);
+    }
+}

+ 0 - 0
packages/core/src/context.rs


+ 35 - 0
packages/core/src/debug_renderer.rs

@@ -0,0 +1,35 @@
+//! Debug virtual doms!
+//! This renderer comes built in with dioxus core and shows how to implement a basic renderer.
+//!
+//! Renderers don't actually need to own the virtual dom (it's up to the implementer).
+
+use crate::prelude::{Properties, VirtualDom};
+
+pub struct DebugRenderer<'a, P: Properties> {
+    vdom: &'a mut VirtualDom<P>,
+}
+
+impl<'a, P: Properties> DebugRenderer<'a, P> {
+    pub fn new(vdom: &'a mut VirtualDom<P>) -> Self {
+        Self { vdom }
+    }
+
+    pub async fn run(&mut self) -> Result<(), ()> {
+        Ok(())
+    }
+
+    pub fn log_dom(&self) {}
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::prelude::*;
+
+    #[test]
+    fn ensure_creation() -> Result<(), ()> {
+        let mut dom = VirtualDom::new(|ctx| ctx.view(html! { <div>"hello world" </div> }));
+        dom.progress()?;
+        Ok(())
+    }
+}

+ 26 - 0
packages/core/src/events.rs

@@ -0,0 +1,26 @@
+//! Virtual Events
+//! This module provides a wrapping of platform-specific events with a list of events easier to work with.
+//!
+//! 3rd party renderers are responsible for forming this virtual events from events
+//!
+//! The goal here is to provide a consistent event interface across all renderer types
+
+pub enum VirtualEvent {
+    ClipboardEvent,
+    CompositionEvent,
+    KeyboardEvent,
+    FocusEvent,
+    FormEvent,
+    GenericEvent,
+    MouseEvent,
+    PointerEvent,
+    SelectionEvent,
+    TouchEvent,
+    UIEvent,
+    WheelEvent,
+    MediaEvent,
+    ImageEvent,
+    AnimationEvent,
+    TransitionEvent,
+    OtherEvent,
+}

+ 35 - 11
packages/core/src/lib.rs

@@ -65,8 +65,13 @@
 //! - dioxus-liveview (SSR + StringRenderer)
 //!
 
+pub mod component;
+pub mod context;
+pub mod debug_renderer;
+pub mod events;
 pub mod nodebuilder;
 pub mod nodes;
+pub mod scope;
 pub mod validation;
 pub mod virtual_dom;
 
@@ -74,33 +79,52 @@ pub mod builder {
     pub use super::nodebuilder::*;
 }
 
+// types used internally that are important
+pub(crate) mod inner {
+    pub use crate::component::{Component, Properties};
+    use crate::nodes;
+    pub use crate::scope::{Context, Hook, Scope};
+    pub use crate::virtual_dom::VirtualDom;
+    pub use nodes::*;
+
+    // pub use nodes::iterables::IterableNodes;
+    /// This type alias is an internal way of abstracting over the static functions that represent components.
+    pub type FC<P> = for<'a> fn(Context<'a, P>) -> VNode<'a>;
+
+    // TODO @Jon, fix this
+    // hack the VNode type until VirtualNode is fixed in the macro crate
+    pub type VirtualNode<'a> = VNode<'a>;
+
+    // Re-export the FC macro
+    pub use crate as dioxus;
+    pub use crate::nodebuilder as builder;
+    pub use dioxus_core_macro::fc;
+    pub use dioxus_html_2::html;
+}
+
 /// Re-export common types for ease of development use.
 /// Essential when working with the html! macro
 pub mod prelude {
+    pub use crate::component::{Component, Properties};
     use crate::nodes;
-    pub use crate::virtual_dom::{Context, VirtualDom};
+    pub use crate::scope::Context;
+    pub use crate::virtual_dom::VirtualDom;
     pub use nodes::*;
-    // pub use nodes::iterables::IterableNodes;
 
+    // pub use nodes::iterables::IterableNodes;
     /// This type alias is an internal way of abstracting over the static functions that represent components.
     pub type FC<P> = for<'a> fn(Context<'a, P>) -> VNode<'a>;
-    // pub type FC<P> = for<'a> fn(Context<'a, P>) -> VNode<'a>;
 
     // TODO @Jon, fix this
     // hack the VNode type until VirtualNode is fixed in the macro crate
     pub type VirtualNode<'a> = VNode<'a>;
 
-    // Re-export from the macro crate
-    // pub use dodrio_derive::html;
-
+    // expose our bumpalo type
     pub use bumpalo;
-    // pub use dioxus_html_macro::html;
 
     // Re-export the FC macro
-    pub use dioxus_core_macro::fc;
-    pub use dioxus_html_2::html;
-
     pub use crate as dioxus;
-
     pub use crate::nodebuilder as builder;
+    pub use dioxus_core_macro::fc;
+    pub use dioxus_html_2::html;
 }

+ 1 - 1
packages/core/src/nodes.rs

@@ -330,7 +330,7 @@ mod vtext {
 /// Virtual Components for custom user-defined components
 /// Only supports the functional syntax
 mod vcomponent {
-    use crate::virtual_dom::Properties;
+    use crate::prelude::Properties;
     use std::{any::TypeId, fmt, future::Future};
 
     use super::VNode;

+ 225 - 0
packages/core/src/scope.rs

@@ -0,0 +1,225 @@
+use crate::nodes::VNode;
+use crate::prelude::*;
+use any::Any;
+use bumpalo::Bump;
+use generational_arena::{Arena, Index};
+use std::{
+    any::{self, TypeId},
+    cell::{RefCell, UnsafeCell},
+    future::Future,
+    marker::PhantomData,
+    sync::atomic::AtomicUsize,
+};
+
+/// The Scope that wraps a functional component
+/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components
+/// The actualy contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
+pub struct Scope {
+    arena: typed_arena::Arena<Hook>,
+    hooks: RefCell<Vec<*mut Hook>>,
+    props_type: TypeId,
+    caller: *const i32,
+}
+
+impl Scope {
+    // create a new scope from a function
+    pub fn new<T: 'static>(f: FC<T>) -> Self {
+        // Capture the props type
+        let props_type = TypeId::of::<T>();
+        let arena = typed_arena::Arena::new();
+        let hooks = RefCell::new(Vec::new());
+
+        let caller = f as *const i32;
+
+        Self {
+            arena,
+            hooks,
+            props_type,
+            caller,
+        }
+    }
+
+    pub fn create_context<T: Properties>(&mut self) -> Context<T> {
+        Context {
+            _p: PhantomData {},
+            arena: &self.arena,
+            hooks: &self.hooks,
+            idx: 0.into(),
+            props: T::new(),
+        }
+    }
+
+    /// Create a new context and run the component with references from the Virtual Dom
+    /// This function downcasts the function pointer based on the stored props_type
+    fn run<T: 'static>(&self, f: FC<T>) {}
+
+    fn call<T: Properties + 'static>(&mut self, val: T) {
+        if self.props_type == TypeId::of::<T>() {
+            /*
+            SAFETY ALERT
+
+            This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
+            We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
+            we transmute the function back using the props as reference.
+
+            This is safe because we check that the generic type matches before casting.
+            */
+            let caller = unsafe { std::mem::transmute::<*const i32, FC<T>>(self.caller) };
+            let ctx = self.create_context::<T>();
+            // TODO: do something with these nodes
+            let nodes = caller(ctx);
+        } else {
+            panic!("Do not try to use `call` on Scopes with the wrong props type")
+        }
+    }
+}
+
+/// Components in Dioxus use the "Context" object to interact with their lifecycle.
+/// This lets components schedule updates, integrate hooks, and expose their context via the context api.
+///
+/// Properties passed down from the parent component are also directly accessible via the exposed "props" field.
+///
+/// ```ignore
+/// #[derive(Properties)]
+/// struct Props {
+///     name: String
+///
+/// }
+///
+/// fn example(ctx: &Context<Props>) -> VNode {
+///     html! {
+///         <div> "Hello, {ctx.props.name}" </div>
+///     }
+/// }
+/// ```
+// todo: force lifetime of source into T as a valid lifetime too
+// it's definitely possible, just needs some more messing around
+pub struct Context<'src, T> {
+    /// Direct access to the properties used to create this component.
+    pub props: T,
+    pub idx: AtomicUsize,
+
+    // Borrowed from scope
+    arena: &'src typed_arena::Arena<Hook>,
+    hooks: &'src RefCell<Vec<*mut Hook>>,
+
+    // holder for the src lifetime
+    // todo @jon remove this
+    pub _p: std::marker::PhantomData<&'src ()>,
+}
+
+impl<'a, T> Context<'a, T> {
+    /// Access the children elements passed into the component
+    pub fn children(&self) -> Vec<VNode> {
+        todo!("Children API not yet implemented for component Context")
+    }
+
+    /// Access a parent context
+    pub fn parent_context<C>(&self) -> C {
+        todo!("Context API is not ready yet")
+    }
+
+    /// Create a subscription that schedules a future render for the reference component
+    pub fn subscribe(&self) -> impl FnOnce() -> () {
+        todo!("Subscription API is not ready yet");
+        || {}
+    }
+
+    /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
+    ///
+    /// ```ignore
+    /// fn Component(ctx: Context<Props>) -> VNode {
+    ///     // Lazy assemble the VNode tree
+    ///     let lazy_tree = html! {<div>"Hello World"</div>};
+    ///     
+    ///     // Actually build the tree and allocate it
+    ///     ctx.view(lazy_tree)
+    /// }
+    ///```
+    pub fn view(&self, v: impl FnOnce(&'a Bump) -> VNode<'a>) -> VNode<'a> {
+        todo!()
+    }
+
+    /// Create a suspended component from a future.
+    ///
+    /// When the future completes, the component will be renderered
+    pub fn suspend(
+        &self,
+        fut: impl Future<Output = impl FnOnce(&'a Bump) -> VNode<'a>>,
+    ) -> VNode<'a> {
+        todo!()
+    }
+
+    /// use_hook provides a way to store data between renders for functional components.
+    pub fn use_hook<'comp, InternalHookState: 'static, Output: 'comp>(
+        &'comp self,
+        // The closure that builds the hook state
+        initializer: impl FnOnce() -> InternalHookState,
+        // The closure that takes the hookstate and returns some value
+        runner: impl FnOnce(&'comp mut InternalHookState, ()) -> Output,
+        // The closure that cleans up whatever mess is left when the component gets torn down
+        // TODO: add this to the "clean up" group for when the component is dropped
+        cleanup: impl FnOnce(InternalHookState),
+    ) -> Output {
+        let raw_hook = {
+            let idx = self.idx.load(std::sync::atomic::Ordering::Relaxed);
+
+            // Mutate hook list if necessary
+            let mut hooks = self.hooks.borrow_mut();
+
+            // Initialize the hook by allocating it in the typed arena.
+            // We get a reference from the arena which is owned by the component scope
+            // This is valid because "Context" is only valid while the scope is borrowed
+            if idx >= hooks.len() {
+                let new_state = initializer();
+                let boxed_state: Box<dyn std::any::Any> = Box::new(new_state);
+                let hook = self.arena.alloc(Hook::new(boxed_state));
+
+                // Push the raw pointer instead of the &mut
+                // A "poor man's OwningRef"
+                hooks.push(hook);
+            }
+            self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
+
+            *hooks.get(idx).unwrap()
+        };
+
+        /*
+        ** UNSAFETY ALERT **
+        Here, we dereference a raw pointer. Normally, we aren't guaranteed that this is okay.
+
+        However, typed-arena gives a mutable reference to the stored data which is stable for any inserts
+        into the arena. During the first call of the function, we need to add the mutable reference given to us by
+        the arena into our list of hooks. The arena provides stability of the &mut references and is only deallocated
+        when the component itself is deallocated.
+
+        This is okay because:
+        - The lifetime of the component arena is tied to the lifetime of these raw hooks
+        - Usage of the raw hooks is tied behind the Vec refcell
+        - Output is static, meaning it can't take a reference to the data
+        - We don't expose the raw hook pointer outside of the scope of use_hook
+        - The reference is tied to context, meaning it can only be used while ctx is around to free it
+        */
+        let borrowed_hook: &'comp mut _ = unsafe { raw_hook.as_mut().unwrap() };
+
+        let internal_state = borrowed_hook
+            .state
+            .downcast_mut::<InternalHookState>()
+            .unwrap();
+
+        // todo: set up an updater with the subscription API
+        let updater = ();
+
+        runner(internal_state, updater)
+    }
+}
+
+pub struct Hook {
+    state: Box<dyn std::any::Any>,
+}
+
+impl Hook {
+    fn new(state: Box<dyn std::any::Any>) -> Self {
+        Self { state }
+    }
+}

+ 21 - 339
packages/core/src/virtual_dom.rs

@@ -21,34 +21,9 @@ static Component: FC = |ctx| {
     ctx.view(html! {<div> "hello world" </div>})
 }
 ```
-
-
-This module includes all life-cycle related mechanics, including the virtual dom, scopes, properties, and lifecycles.
----
-The VirtualDom is designed as so:
-
-VDOM contains:
-    - An arena of component scopes.
-        - A scope contains
-            - lifecycle data
-            - hook data
-    - Event queue
-        - An event
-
-A VDOM is
-    - constructed from anything that implements "component"
-
-A "Component" is anything (normally functions) that can be ran with a context to produce VNodes
-    - Must implement properties-builder trait which produces a properties builder
-
-A Context
-    - Is a consumable struct
-        - Made of references to properties
-        - Holds a reference (lockable) to the underlying scope
-        - Is partially threadsafe
 */
+use crate::inner::*;
 use crate::nodes::VNode;
-use crate::prelude::*;
 use any::Any;
 use bumpalo::Bump;
 use generational_arena::{Arena, Index};
@@ -67,15 +42,12 @@ pub struct VirtualDom<P: Properties> {
     /// A generational arean is used to re-use slots of deleted scopes without having to resize the underlying arena.
     components: Arena<Scope>,
 
+    /// The index of the root component.
     base_scope: Index,
 
     /// Components generate lifecycle events
     event_queue: Vec<LifecycleEvent>,
 
-    buffers: [Bump; 2],
-
-    selected_buf: u8,
-
     root_props: P,
 }
 
@@ -98,12 +70,10 @@ impl<P: Properties + 'static> VirtualDom<P> {
     /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
     /// to toss out the entire tree.
     pub fn new_with_props(root: FC<P>, root_props: P) -> Self {
-        // 1. Create the buffers
-        // 2. Create the component arena
-        // 3. Create the base scope (can never be removed)
-        // 4. Create the lifecycle queue
-        // 5. Create the event queue
-        let buffers = [Bump::new(), Bump::new()];
+        // 1. Create the component arena
+        // 2. Create the base scope (can never be removed)
+        // 3. Create the lifecycle queue
+        // 4. Create the event queue
 
         // Arena allocate all the components
         // This should make it *really* easy to store references in events and such
@@ -112,16 +82,17 @@ impl<P: Properties + 'static> VirtualDom<P> {
         // Create a reference to the component in the arena
         let base_scope = components.insert(Scope::new(root));
 
+        // Create a new mount event with no root container
+        let first_event = LifecycleEvent::mount(base_scope, None, 0);
+
         // Create an event queue with a mount for the base scope
-        let event_queue = vec![];
+        let event_queue = vec![first_event];
 
         Self {
             components,
             base_scope,
             event_queue,
-            buffers,
             root_props,
-            selected_buf: 0,
         }
     }
 
@@ -133,9 +104,15 @@ impl<P: Properties + 'static> VirtualDom<P> {
 
         match event_type {
             // Component needs to be mounted to the virtual dom
-            LifecycleType::Mount {} => {
+            LifecycleType::Mount { to, under } => {
                 // todo! run the FC with the bump allocator
                 // Run it with its properties
+                if let Some(other) = to {
+                    // mount to another component
+                    let p = ();
+                } else {
+                    // mount to the root
+                }
             }
 
             // The parent for this component generated new props and the component needs update
@@ -150,10 +127,6 @@ impl<P: Properties + 'static> VirtualDom<P> {
                 let f = self.components.remove(index);
             }
 
-            // Component was moved around in the DomTree
-            // Doesn't generate any event but interesting to keep track of
-            LifecycleType::Moved {} => {}
-
             // Component was messaged via the internal subscription service
             LifecycleType::Messaged => {}
         }
@@ -183,309 +156,18 @@ pub struct LifecycleEvent {
     pub event_type: LifecycleType,
 }
 impl LifecycleEvent {
-    fn mount(index: Index) -> Self {
+    fn mount(which: Index, to: Option<Index>, under: usize) -> Self {
         Self {
-            index,
-            event_type: LifecycleType::Mount,
+            index: which,
+            event_type: LifecycleType::Mount { to, under },
         }
     }
 }
 /// The internal lifecycle event system is managed by these
 pub enum LifecycleType {
-    Mount,
+    Mount { to: Option<Index>, under: usize },
     PropsChanged,
     Mounted,
     Removed,
-    Moved,
     Messaged,
 }
-
-/// The `Component` trait refers to any struct or funciton that can be used as a component
-/// We automatically implement Component for FC<T>
-pub trait Component {
-    type Props: Properties;
-    fn builder(&'static self) -> Self::Props;
-}
-
-// Auto implement component for a FC
-// Calling the FC is the same as "rendering" it
-impl<P: Properties> Component for FC<P> {
-    type Props = P;
-
-    fn builder(&self) -> Self::Props {
-        todo!()
-    }
-}
-
-/// The `Properties` trait defines any struct that can be constructed using a combination of default / optional fields.
-/// Components take a "properties" object
-pub trait Properties {
-    fn new() -> Self;
-}
-
-// Auto implement for no-prop components
-impl Properties for () {
-    fn new() -> Self {
-        ()
-    }
-}
-
-// ============================================
-// Compile Tests for FC/Component/Properties
-// ============================================
-#[cfg(test)]
-mod fc_test {
-    use super::*;
-    use crate::prelude::*;
-
-    // Make sure this function builds properly.
-    fn test_static_fn<'a, P: Properties>(b: &'a Bump, r: FC<P>) -> VNode<'a> {
-        todo!()
-        // let p = P::new(); // new props
-        // let c = Context { props: &p }; // new context with props
-        // let g = r(&c); // calling function with context
-        // g
-    }
-
-    fn test_component(ctx: Context<()>) -> VNode {
-        // todo: helper should be part of html! macro
-        todo!()
-        // ctx.view(|bump| html! {bump,  <div> </div> })
-    }
-
-    fn test_component2(ctx: Context<()>) -> VNode {
-        ctx.view(|bump: &Bump| VNode::text("blah"))
-    }
-    // fn test_component2<'a>(ctx: &'a Context<()>) -> VNode<'a> {
-    //     ctx.view(|bump: &Bump| VNode::text("blah"))
-    // }
-
-    #[test]
-    fn ensure_types_work() {
-        // TODO: Get the whole casting thing to work properly.
-        // For whatever reason, FC is not auto-implemented, depsite it being a static type
-        let b = Bump::new();
-
-        // Happiness! The VNodes are now allocated onto the bump vdom
-        let nodes0 = test_static_fn(&b, test_component);
-
-        let nodes1 = test_static_fn(&b, test_component2);
-    }
-}
-
-/// The Scope that wraps a functional component
-/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components
-/// The actualy contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
-pub struct Scope {
-    arena: typed_arena::Arena<Hook>,
-    hooks: RefCell<Vec<*mut Hook>>,
-    props_type: TypeId,
-    caller: *const (),
-}
-
-impl Scope {
-    // create a new scope from a function
-    pub fn new<T: 'static>(f: FC<T>) -> Self {
-        // Capture the props type
-        let props_type = TypeId::of::<T>();
-        let arena = typed_arena::Arena::new();
-        let hooks = RefCell::new(Vec::new());
-
-        let caller = f as *const ();
-
-        Self {
-            arena,
-            hooks,
-            props_type,
-            caller,
-        }
-    }
-
-    pub fn create_context<T: Properties>(&mut self) -> Context<T> {
-        Context {
-            _p: PhantomData {},
-            arena: &self.arena,
-            hooks: &self.hooks,
-            idx: 0.into(),
-            props: T::new(),
-        }
-    }
-
-    /// Create a new context and run the component with references from the Virtual Dom
-    /// This function downcasts the function pointer based on the stored props_type
-    fn run<T: 'static>(&self, f: FC<T>) {}
-
-    fn call<T: Properties + 'static>(&mut self, val: T) {
-        if self.props_type == TypeId::of::<T>() {
-            /*
-            SAFETY ALERT
-
-            This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
-            We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
-            we transmute the function back using the props as reference.
-
-            This is safe because we check that the generic type matches before casting.
-            */
-            let caller = unsafe { std::mem::transmute::<*const (), FC<T>>(self.caller) };
-            let ctx = self.create_context::<T>();
-            let nodes = caller(ctx);
-        } else {
-            panic!("Do not try to use `call` on Scopes with the wrong props type")
-        }
-    }
-}
-
-/// Components in Dioxus use the "Context" object to interact with their lifecycle.
-/// This lets components schedule updates, integrate hooks, and expose their context via the context api.
-///
-/// Properties passed down from the parent component are also directly accessible via the exposed "props" field.
-///
-/// ```ignore
-/// #[derive(Properties)]
-/// struct Props {
-///     name: String
-///
-/// }
-///
-/// fn example(ctx: &Context<Props>) -> VNode {
-///     html! {
-///         <div> "Hello, {ctx.props.name}" </div>
-///     }
-/// }
-/// ```
-// todo: force lifetime of source into T as a valid lifetime too
-// it's definitely possible, just needs some more messing around
-pub struct Context<'src, T> {
-    /// Direct access to the properties used to create this component.
-    pub props: T,
-    pub idx: AtomicUsize,
-
-    // Borrowed from scope
-    arena: &'src typed_arena::Arena<Hook>,
-    hooks: &'src RefCell<Vec<*mut Hook>>,
-
-    // holder for the src lifetime
-    // todo @jon remove this
-    pub _p: std::marker::PhantomData<&'src ()>,
-}
-
-impl<'a, T> Context<'a, T> {
-    /// Access the children elements passed into the component
-    pub fn children(&self) -> Vec<VNode> {
-        todo!("Children API not yet implemented for component Context")
-    }
-
-    /// Access a parent context
-    pub fn parent_context<C>(&self) -> C {
-        todo!("Context API is not ready yet")
-    }
-
-    /// Create a subscription that schedules a future render for the reference component
-    pub fn subscribe(&self) -> impl FnOnce() -> () {
-        todo!("Subscription API is not ready yet");
-        || {}
-    }
-
-    /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
-    ///
-    /// ```ignore
-    /// fn Component(ctx: Context<Props>) -> VNode {
-    ///     // Lazy assemble the VNode tree
-    ///     let lazy_tree = html! {<div>"Hello World"</div>};
-    ///     
-    ///     // Actually build the tree and allocate it
-    ///     ctx.view(lazy_tree)
-    /// }
-    ///```
-    pub fn view(&self, v: impl FnOnce(&'a Bump) -> VNode<'a>) -> VNode<'a> {
-        todo!()
-    }
-
-    /// Create a suspended component from a future.
-    ///
-    /// When the future completes, the component will be renderered
-    pub fn suspend(
-        &self,
-        fut: impl Future<Output = impl FnOnce(&'a Bump) -> VNode<'a>>,
-    ) -> VNode<'a> {
-        todo!()
-    }
-
-    /// use_hook provides a way to store data between renders for functional components.
-    pub fn use_hook<'comp, InternalHookState: 'static, Output: 'comp>(
-        &'comp self,
-        // The closure that builds the hook state
-        initializer: impl FnOnce() -> InternalHookState,
-        // The closure that takes the hookstate and returns some value
-        runner: impl FnOnce(&'comp mut InternalHookState, ()) -> Output,
-        // The closure that cleans up whatever mess is left when the component gets torn down
-        // TODO: add this to the "clean up" group for when the component is dropped
-        cleanup: impl FnOnce(InternalHookState),
-    ) -> Output {
-        let raw_hook = {
-            let idx = self.idx.load(std::sync::atomic::Ordering::Relaxed);
-
-            // Mutate hook list if necessary
-            let mut hooks = self.hooks.borrow_mut();
-
-            // Initialize the hook by allocating it in the typed arena.
-            // We get a reference from the arena which is owned by the component scope
-            // This is valid because "Context" is only valid while the scope is borrowed
-            if idx >= hooks.len() {
-                let new_state = initializer();
-                let boxed_state: Box<dyn std::any::Any> = Box::new(new_state);
-                let hook = self.arena.alloc(Hook::new(boxed_state));
-
-                // Push the raw pointer instead of the &mut
-                // A "poor man's OwningRef"
-                hooks.push(hook);
-            }
-            self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
-
-            *hooks.get(idx).unwrap()
-        };
-
-        /*
-        ** UNSAFETY ALERT **
-        Here, we dereference a raw pointer. Normally, we aren't guaranteed that this is okay.
-
-        However, typed-arena gives a mutable reference to the stored data which is stable for any inserts
-        into the arena. During the first call of the function, we need to add the mutable reference given to us by
-        the arena into our list of hooks. The arena provides stability of the &mut references and is only deallocated
-        when the component itself is deallocated.
-
-        This is okay because:
-        - The lifetime of the component arena is tied to the lifetime of these raw hooks
-        - Usage of the raw hooks is tied behind the Vec refcell
-        - Output is static, meaning it can't take a reference to the data
-        - We don't expose the raw hook pointer outside of the scope of use_hook
-        - The reference is tied to context, meaning it can only be used while ctx is around to free it
-        */
-        let borrowed_hook: &'comp mut _ = unsafe { raw_hook.as_mut().unwrap() };
-
-        let internal_state = borrowed_hook
-            .state
-            .downcast_mut::<InternalHookState>()
-            .unwrap();
-
-        // todo: set up an updater with the subscription API
-        let updater = ();
-
-        runner(internal_state, updater)
-    }
-}
-
-pub struct Hook {
-    state: Box<dyn std::any::Any>,
-}
-
-impl Hook {
-    fn new(state: Box<dyn std::any::Any>) -> Self {
-        Self { state }
-    }
-}
-
-/// A CallbackEvent wraps any event returned from the renderer's event system.
-pub struct CallbackEvent {}
-
-pub struct EventListener {}