Wip: pre-diffmachine merge fork

This commit is contained in:
Jonathan Kelley 2021-06-03 13:57:41 -04:00
parent 69f5cc3802
commit 424a18137f
8 changed files with 67 additions and 231 deletions

View file

@ -45,3 +45,4 @@ namespacing
impl impl
destructured destructured
linting linting
lodash

View file

@ -85,6 +85,7 @@ TypeScript is a great addition to JavaScript, but comes with a lot of tweaking f
- inline built-in unit/integration testing - inline built-in unit/integration testing
- best-in-class error handling - best-in-class error handling
- simple and fast build system - simple and fast build system
- powerful standard library (no need for lodash or underscore)
- include_str! for integrating html/css/svg templates directly - include_str! for integrating html/css/svg templates directly
- various macros (`html!`, `rsx!`) for fast template iteration - various macros (`html!`, `rsx!`) for fast template iteration

View file

@ -1,3 +1,3 @@
{ {
"rust-analyzer.inlayHints.enable": true "rust-analyzer.inlayHints.enable": false
} }

View file

@ -1,17 +1,18 @@
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
fn main() { fn main() {
let g = rsx! {
Fragment { // let g = rsx! {
// div {} // Fragment {
// div {} // // div {}
// div {} // // div {}
// div {} // // div {}
// div {} // // div {}
// div {} // // div {}
// div {} // // div {}
// div {} // // div {}
// div {} // // div {}
} // // div {}
}; // }
// };
} }

View file

@ -1,14 +1,13 @@
use std::{ use std::{
cell::{RefCell, UnsafeCell}, cell::{RefCell, UnsafeCell},
collections::HashMap, collections::HashMap,
sync::Arc, rc::Rc,
}; };
use generational_arena::Arena; use generational_arena::Arena;
use crate::innerlude::*; use crate::innerlude::*;
type Rc<T> = Arc<T>;
#[derive(Clone)] #[derive(Clone)]
pub struct ScopeArena(Rc<RefCell<ScopeArenaInner>>); pub struct ScopeArena(Rc<RefCell<ScopeArenaInner>>);

View file

@ -1007,138 +1007,3 @@ enum KeyedPrefixResult {
// the beginning of `new` and `old` we already processed. // the beginning of `new` and `old` we already processed.
MoreWorkToDo(usize), MoreWorkToDo(usize),
} }
mod support {
// // Get or create the template.
// //
// // Upon entering this function the change list stack may be in any shape:
// //
// // [...]
// //
// // When this function returns, it leaves a freshly cloned copy of the template
// // on the top of the change list stack:
// //
// // [... template]
// #[inline]
// pub fn get_or_create_template<'a>(// cached_set: &'a CachedSet,
// // change_list: &mut ChangeListBuilder,
// // registry: &mut EventsRegistry,
// // cached_roots: &mut FxHashSet<CacheId>,
// // template_id: CacheId,
// ) -> (&'a Node<'a>, bool) {
// let (template, template_template) = cached_set.get(template_id);
// debug_assert!(
// template_template.is_none(),
// "templates should not be templated themselves"
// );
// // If we haven't already created and saved the physical DOM subtree for this
// // template, do that now.
// if change_list.has_template(template_id) {
// // Clone the template and push it onto the stack.
// //
// // [...]
// change_list.push_template(template_id);
// // [... template]
// (template, true)
// } else {
// // [...]
// create(cached_set, change_list, registry, template, cached_roots);
// // [... template]
// change_list.save_template(template_id);
// // [... template]
// (template, false)
// }
// }
// pub fn create_and_replace(
// cached_set: &CachedSet,
// change_list: &mut ChangeListBuilder,
// registry: &mut EventsRegistry,
// new_template: Option<CacheId>,
// old: &Node,
// new: &Node,
// cached_roots: &mut FxHashSet<CacheId>,
// ) {
// debug_assert!(change_list.traversal_is_committed());
// if let Some(template_id) = new_template {
// let (template, needs_listeners) = get_or_create_template(
// cached_set,
// change_list,
// registry,
// cached_roots,
// template_id,
// );
// change_list.replace_with();
// let mut old_forcing = None;
// if needs_listeners {
// old_forcing = Some(change_list.push_force_new_listeners());
// }
// diff(
// cached_set,
// change_list,
// registry,
// template,
// new,
// cached_roots,
// );
// if let Some(old) = old_forcing {
// change_list.pop_force_new_listeners(old);
// }
// change_list.commit_traversal();
// } else {
// create(cached_set, change_list, registry, new, cached_roots);
// change_list.replace_with();
// }
// registry.remove_subtree(old);
// }
// pub fn create_with_template(
// cached_set: &CachedSet,
// change_list: &mut ChangeListBuilder,
// registry: &mut EventsRegistry,
// template_id: CacheId,
// node: &Node,
// cached_roots: &mut FxHashSet<CacheId>,
// ) {
// debug_assert!(change_list.traversal_is_committed());
// // [...]
// let (template, needs_listeners) =
// get_or_create_template(cached_set, change_list, registry, cached_roots, template_id);
// // [... template]
// // Now diff the node with its template.
// //
// // We must force adding new listeners instead of updating existing ones,
// // since listeners don't get cloned in `cloneNode`.
// let mut old_forcing = None;
// if needs_listeners {
// old_forcing = Some(change_list.push_force_new_listeners());
// }
// diff(
// cached_set,
// change_list,
// registry,
// template,
// node,
// cached_roots,
// );
// if let Some(old) = old_forcing {
// change_list.pop_force_new_listeners(old);
// }
// // Make sure that we come back up to the level we were at originally.
// change_list.commit_traversal();
// }
}

View file

@ -6,6 +6,7 @@
use crate::{ use crate::{
events::VirtualEvent, events::VirtualEvent,
innerlude::{Context, Properties, Scope, ScopeIdx, FC}, innerlude::{Context, Properties, Scope, ScopeIdx, FC},
nodebuilder::text3,
}; };
use bumpalo::Bump; use bumpalo::Bump;
use std::{ use std::{
@ -28,6 +29,8 @@ pub enum VNode<'src> {
/// A text node (node type `TEXT_NODE`). /// A text node (node type `TEXT_NODE`).
Text(VText<'src>), Text(VText<'src>),
/// A fragment is a "virtual position" in the DOM
/// Fragments may have children and keys
Fragment(&'src VFragment<'src>), Fragment(&'src VFragment<'src>),
/// A "suspended component" /// A "suspended component"
@ -86,8 +89,8 @@ impl<'a> VNode<'a> {
VNode::Text(VText { text }) VNode::Text(VText { text })
} }
pub fn text_args(b: &'a Bump, f: Arguments) -> VNode<'a> { pub fn text_args(bump: &'a Bump, args: Arguments) -> VNode<'a> {
todo!() text3(bump, args)
} }
#[inline] #[inline]
@ -95,11 +98,11 @@ impl<'a> VNode<'a> {
match &self { match &self {
VNode::Text(_) => NodeKey::NONE, VNode::Text(_) => NodeKey::NONE,
VNode::Element(e) => e.key, VNode::Element(e) => e.key,
VNode::Suspended => {
todo!()
}
VNode::Fragment(frag) => frag.key, VNode::Fragment(frag) => frag.key,
VNode::Component(c) => c.key, VNode::Component(c) => c.key,
// todo suspend should be allowed to have keys
VNode::Suspended => NodeKey::NONE,
} }
} }
} }
@ -254,9 +257,6 @@ pub struct VComponent<'src> {
_p: PhantomData<&'src ()>, _p: PhantomData<&'src ()>,
} }
unsafe fn transmogrify<'a>(p: impl Properties + 'a) -> impl Properties + 'static {
todo!()
}
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:

View file

@ -154,10 +154,11 @@ impl VirtualDom {
// Make the first scope // Make the first scope
// We don't run the component though, so renderers will need to call "rebuild" when they initialize their DOM // We don't run the component though, so renderers will need to call "rebuild" when they initialize their DOM
let link = components.clone(); let link = components.clone();
let event_channel = Rc::new(move || {});
let base_scope = components let base_scope = components
.with(|arena| { .with(|arena| {
arena.insert_with(move |myidx| { arena.insert_with(move |myidx| {
Scope::new(caller_ref, myidx, None, 0, _event_queue, link) Scope::new(caller_ref, myidx, None, 0, event_channel, link, &[])
}) })
}) })
.unwrap(); .unwrap();
@ -172,6 +173,7 @@ impl VirtualDom {
} }
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
/// Currently this doesn't do what we want it to do
pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> { pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
let mut diff_machine = DiffMachine::new(); let mut diff_machine = DiffMachine::new();
@ -180,8 +182,9 @@ impl VirtualDom {
// Instead, this is done on top-level component // Instead, this is done on top-level component
let base = self.components.try_get(self.base_scope)?; let base = self.components.try_get(self.base_scope)?;
let immediate_update = self.event_queue.schedule_update(base);
immediate_update(); let update = &base.event_channel;
update();
self.progress_completely(&mut diff_machine)?; self.progress_completely(&mut diff_machine)?;
@ -319,21 +322,23 @@ impl VirtualDom {
// Insert a new scope into our component list // Insert a new scope into our component list
let idx = self.components.with(|components| { let idx = self.components.with(|components| {
components.insert_with(|f| { components.insert_with(|new_idx| {
let height = cur_height + 1;
Scope::new( Scope::new(
caller, caller,
f, new_idx,
Some(cur_component.arena_idx), Some(cur_component.arena_idx),
cur_height + 1, height,
self.event_queue.clone(), self.event_queue.new_channel(height, new_idx),
self.components.clone(), self.components.clone(),
&[],
) )
}) })
})?; })?;
{ {
let cur_component = self.components.try_get_mut(update.idx).unwrap(); let cur_component = self.components.try_get_mut(update.idx).unwrap();
let mut ch = cur_component.children.borrow_mut(); let mut ch = cur_component.descendents.borrow_mut();
ch.insert(idx); ch.insert(idx);
std::mem::drop(ch); std::mem::drop(ch);
} }
@ -444,7 +449,7 @@ impl VirtualDom {
// Accumulate all the child components that need to be removed // Accumulate all the child components that need to be removed
while let Some(child_id) = children_to_remove.pop_back() { while let Some(child_id) = children_to_remove.pop_back() {
let comp = self.components.try_get(child_id).unwrap(); let comp = self.components.try_get(child_id).unwrap();
let children = comp.children.borrow(); let children = comp.descendents.borrow();
for child in children.iter() { for child in children.iter() {
children_to_remove.push_front(*child); children_to_remove.push_front(*child);
} }
@ -471,7 +476,6 @@ impl VirtualDom {
} }
// TODO! // TODO!
//
// These impls are actually wrong. The DOM needs to have a mutex implemented. // These impls are actually wrong. The DOM needs to have a mutex implemented.
unsafe impl Sync for VirtualDom {} unsafe impl Sync for VirtualDom {}
unsafe impl Send for VirtualDom {} unsafe impl Send for VirtualDom {}
@ -488,7 +492,9 @@ pub struct Scope {
// IDs of children that this scope has created // IDs of children that this scope has created
// This enables us to drop the children and their children when this scope is destroyed // This enables us to drop the children and their children when this scope is destroyed
children: RefCell<HashSet<ScopeIdx>>, descendents: RefCell<HashSet<ScopeIdx>>,
child_nodes: &'static [VNode<'static>],
// A reference to the list of components. // A reference to the list of components.
// This lets us traverse the component list whenever we need to access our parent or children. // This lets us traverse the component list whenever we need to access our parent or children.
@ -501,8 +507,9 @@ pub struct Scope {
pub height: u32, pub height: u32,
pub event_queue: EventQueue, pub event_channel: Rc<dyn Fn() + 'static>,
// pub event_queue: EventQueue,
pub caller: Weak<OpaqueComponent<'static>>, pub caller: Weak<OpaqueComponent<'static>>,
pub hookidx: RefCell<usize>, pub hookidx: RefCell<usize>,
@ -528,6 +535,7 @@ pub struct Scope {
// We need to pin the hook so it doesn't move as we initialize the list of hooks // We need to pin the hook so it doesn't move as we initialize the list of hooks
type Hook = Pin<Box<dyn std::any::Any>>; type Hook = Pin<Box<dyn std::any::Any>>;
type EventChannel = Rc<dyn Fn()>;
impl Scope { impl Scope {
// we are being created in the scope of an existing component (where the creator_node lifetime comes into play) // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
@ -542,8 +550,9 @@ impl Scope {
arena_idx: ScopeIdx, arena_idx: ScopeIdx,
parent: Option<ScopeIdx>, parent: Option<ScopeIdx>,
height: u32, height: u32,
event_queue: EventQueue, event_channel: EventChannel,
arena_link: ScopeArena, arena_link: ScopeArena,
child_nodes: &'creator_node [VNode<'creator_node>],
) -> Self { ) -> Self {
log::debug!( log::debug!(
"New scope created, height is {}, idx is {:?}", "New scope created, height is {}, idx is {:?}",
@ -569,18 +578,19 @@ impl Scope {
}; };
Self { Self {
child_nodes: &[],
caller, caller,
parent, parent,
arena_idx, arena_idx,
height, height,
event_queue, event_channel,
arena_link, arena_link,
frames: ActiveFrame::new(), frames: ActiveFrame::new(),
hooks: Default::default(), hooks: Default::default(),
shared_contexts: Default::default(), shared_contexts: Default::default(),
listeners: Default::default(), listeners: Default::default(),
hookidx: Default::default(), hookidx: Default::default(),
children: Default::default(), descendents: Default::default(),
} }
} }
@ -732,14 +742,18 @@ pub trait Scoped<'src>: Sized {
/// Access the children elements passed into the component /// Access the children elements passed into the component
fn children(&self) -> &'src [VNode<'src>] { fn children(&self) -> &'src [VNode<'src>] {
todo!("Children API not yet implemented for component Context") // We're re-casting the nodes back out
// They don't really have a static lifetime
unsafe {
let scope = self.get_scope();
let nodes = scope.child_nodes;
nodes
}
} }
/// Create a subscription that schedules a future render for the reference component /// Create a subscription that schedules a future render for the reference component
fn schedule_update(&self) -> Rc<dyn Fn() + 'static> { fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
todo!() self.get_scope().event_channel.clone()
// pub fn schedule_update(self) -> impl Fn() + 'static {
// self.scope.event_queue.schedule_update(&self.scope)
} }
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator. /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
@ -759,22 +773,12 @@ pub trait Scoped<'src>: Sized {
///``` ///```
fn render<'a, F: for<'b> FnOnce(&'b NodeCtx<'src>) -> VNode<'src> + 'src + 'a>( fn render<'a, F: for<'b> FnOnce(&'b NodeCtx<'src>) -> VNode<'src> + 'src + 'a>(
self, self,
// &'a/ self,
lazy_nodes: LazyNodes<'src, F>, lazy_nodes: LazyNodes<'src, F>,
// lazy_nodes: LazyNodes<'src, F>,
) -> VNode<'src> { ) -> VNode<'src> {
let scope_ref = self.get_scope(); lazy_nodes.into_vnode(&NodeCtx {
let ctx = NodeCtx { scope_ref: self.get_scope(),
scope_ref,
listener_id: 0.into(), listener_id: 0.into(),
}; })
todo!()
// VNode {
// root: unsafe {
// std::mem::transmute::<VNode<'scope>, VNode<'static>>(lazy_nodes.into_vnode(&ctx))
// },
// }
} }
// impl<'scope> Context<'scope> { // impl<'scope> Context<'scope> {
@ -957,17 +961,14 @@ Any function prefixed with "use" should not be called conditionally.
// We then expose a handle to use those props for render in the form of "OpaqueComponent" // We then expose a handle to use those props for render in the form of "OpaqueComponent"
pub(crate) type OpaqueComponent<'e> = dyn for<'b> Fn(&'b Scope) -> VNode<'b> + 'e; pub(crate) type OpaqueComponent<'e> = dyn for<'b> Fn(&'b Scope) -> VNode<'b> + 'e;
#[derive(Debug, Default, Clone)] #[derive(PartialEq, Debug, Clone, Default)]
pub struct EventQueue(pub(crate) Rc<RefCell<Vec<HeightMarker>>>); pub(crate) struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
impl EventQueue { impl EventQueue {
pub fn schedule_update(&self, source: &Scope) -> impl Fn() { pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
let inner = self.clone(); let inner = self.clone();
let marker = HeightMarker { let marker = HeightMarker { height, idx };
height: source.height, Rc::new(move || inner.0.as_ref().borrow_mut().push(marker))
idx: source.arena_idx,
};
move || inner.0.as_ref().borrow_mut().push(marker)
} }
} }
@ -1107,35 +1108,3 @@ impl ActiveFrame {
} }
} }
} }
mod tests {
use super::*;
#[test]
fn simulate() {
let dom = VirtualDom::new(|ctx| {
//
ctx.render(rsx! {
div {
}
})
});
// let root = dom.components.get(dom.base_scope).unwrap();
}
// ensure the virtualdom is send + sync
// needed for use in async/await contexts
#[test]
fn is_send_sync() {
fn check_send<T: Send>(_a: T) -> T {
todo!()
}
fn check_sync<T: Sync>(_a: T) -> T {
todo!()
}
let _ = check_send(VirtualDom::new(|ctx| ctx.render(rsx! { div {}})));
let _ = check_sync(VirtualDom::new(|ctx| ctx.render(rsx! { div {}})));
}
}