feat: upgrade syntax
This commit is contained in:
1 parent
574d7fdb9e
commit
fd93ee89c1
64 files changed
+522
-437
No files matched your search
@@ -173,7 +173,7 @@ impl ToTokens for Component {
|
||||
if !self.children.is_empty() {
|
||||
let childs = &self.children;
|
||||
toks.append_all(quote! {
|
||||
.children(ScopeChildren::new(__cx.fragment_from_iter([ #( #childs ),* ])))
|
||||
.children(__cx.create_children([ #( #childs ),* ]))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+22
-24
@@ -321,14 +321,15 @@ impl<'bump> DiffState<'bump> {
|
||||
}
|
||||
|
||||
MountType::Replace { old } => {
|
||||
// todo: a bug here where we remove a node that is alread being replaced
|
||||
if let Some(old_id) = old.try_mounted_id() {
|
||||
self.mutations.replace_with(old_id, nodes_created as u32);
|
||||
self.remove_nodes(Some(old), true);
|
||||
self.remove_nodes(Some(old), false);
|
||||
} else {
|
||||
if let Some(id) = self.find_first_element_id(old) {
|
||||
self.mutations.replace_with(id, nodes_created as u32);
|
||||
}
|
||||
self.remove_nodes(Some(old), true);
|
||||
self.remove_nodes(Some(old), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,7 +488,11 @@ impl<'bump> DiffState<'bump> {
|
||||
}
|
||||
|
||||
fn create_linked_node(&mut self, link: &'bump NodeLink) {
|
||||
todo!()
|
||||
if let Some(cur_scope) = self.stack.current_scope() {
|
||||
link.scope_id.set(Some(cur_scope));
|
||||
let node: &'bump VNode<'static> = unsafe { &*link.node };
|
||||
self.create_node(unsafe { std::mem::transmute(node) });
|
||||
}
|
||||
}
|
||||
|
||||
// =================================
|
||||
@@ -532,7 +537,6 @@ impl<'bump> DiffState<'bump> {
|
||||
|
||||
fn diff_element_nodes(
|
||||
&mut self,
|
||||
|
||||
old: &'bump VElement<'bump>,
|
||||
new: &'bump VElement<'bump>,
|
||||
old_node: &'bump VNode<'bump>,
|
||||
@@ -629,10 +633,8 @@ impl<'bump> DiffState<'bump> {
|
||||
|
||||
fn diff_component_nodes(
|
||||
&mut self,
|
||||
|
||||
old_node: &'bump VNode<'bump>,
|
||||
new_node: &'bump VNode<'bump>,
|
||||
|
||||
old: &'bump VComponent<'bump>,
|
||||
new: &'bump VComponent<'bump>,
|
||||
) {
|
||||
@@ -648,21 +650,20 @@ impl<'bump> DiffState<'bump> {
|
||||
new.associated_scope.set(Some(scope_addr));
|
||||
|
||||
// make sure the component's caller function is up to date
|
||||
let scope = self.scopes.get_scope(&scope_addr).unwrap();
|
||||
let mut items = scope.items.borrow_mut();
|
||||
let scope = unsafe { self.scopes.get_scope_mut(&scope_addr).unwrap() };
|
||||
scope.caller = unsafe { std::mem::transmute(new.caller) };
|
||||
|
||||
// React doesn't automatically memoize, but we do.
|
||||
let props_are_the_same = todo!("reworking component memoization");
|
||||
// let props_are_the_same = todo!("reworking component memoization");
|
||||
// let props_are_the_same = old.comparator.unwrap();
|
||||
let props_are_the_same = old.comparator.unwrap();
|
||||
|
||||
// if self.cfg.force_diff || !props_are_the_same(new) {
|
||||
// let succeeded = scope.run_scope(self);
|
||||
|
||||
// if succeeded {
|
||||
// self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
|
||||
// }
|
||||
// }
|
||||
if self.force_diff || !props_are_the_same(new) {
|
||||
if self.scopes.run_scope(&scope_addr) {
|
||||
self.diff_node(
|
||||
self.scopes.wip_head(&scope_addr),
|
||||
self.scopes.fin_head(&scope_addr),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.stack.scope_stack.pop();
|
||||
} else {
|
||||
@@ -1198,7 +1199,6 @@ impl<'bump> DiffState<'bump> {
|
||||
/// remove can happen whenever
|
||||
fn remove_nodes(
|
||||
&mut self,
|
||||
|
||||
nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
|
||||
gen_muts: bool,
|
||||
) {
|
||||
@@ -1244,18 +1244,16 @@ impl<'bump> DiffState<'bump> {
|
||||
}
|
||||
|
||||
VNode::Linked(l) => {
|
||||
todo!()
|
||||
let node = unsafe { std::mem::transmute(&*l.node) };
|
||||
self.remove_nodes(Some(node), gen_muts);
|
||||
}
|
||||
|
||||
VNode::Component(c) => {
|
||||
let scope_id = c.associated_scope.get().unwrap();
|
||||
// let scope = self.scopes.get_scope(&scope_id).unwrap();
|
||||
let root = self.scopes.root_node(&scope_id);
|
||||
self.remove_nodes(Some(root), gen_muts);
|
||||
|
||||
log::debug!("Destroying scope {:?}", scope_id);
|
||||
let mut s = self.scopes.try_remove(&scope_id).unwrap();
|
||||
s.hooks.clear_hooks();
|
||||
self.scopes.try_remove(&scope_id).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ fn it_drops() {
|
||||
|
||||
simple_logger::init().unwrap();
|
||||
|
||||
let factory = NodeFactory { bump: &bump };
|
||||
// let factory = NodeFactory { scope: &bump };
|
||||
|
||||
struct DropInner {
|
||||
id: i32,
|
||||
|
||||
@@ -3,21 +3,32 @@
|
||||
//! This module contains an internal API to generate these instructions.
|
||||
|
||||
use crate::innerlude::*;
|
||||
use std::any::Any;
|
||||
use std::{any::Any, fmt::Debug};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mutations<'a> {
|
||||
pub edits: Vec<DomEdit<'a>>,
|
||||
pub noderefs: Vec<NodeRefMutation<'a>>,
|
||||
pub effects: Vec<&'a dyn FnMut()>,
|
||||
}
|
||||
impl Debug for Mutations<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Mutations")
|
||||
.field("edits", &self.edits)
|
||||
.field("noderefs", &self.noderefs)
|
||||
// .field("effects", &self.effects)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
use DomEdit::*;
|
||||
|
||||
impl<'a> Mutations<'a> {
|
||||
pub(crate) fn new() -> Self {
|
||||
let edits = Vec::new();
|
||||
let noderefs = Vec::new();
|
||||
Self { edits, noderefs }
|
||||
Self {
|
||||
edits: Vec::new(),
|
||||
noderefs: Vec::new(),
|
||||
effects: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation
|
||||
|
||||
+114
-32
@@ -181,7 +181,7 @@ impl<'src> VNode<'src> {
|
||||
pub(crate) fn children(&self) -> &[VNode<'src>] {
|
||||
match &self {
|
||||
VNode::Fragment(f) => f.children,
|
||||
VNode::Component(c) => todo!("children are not accessible through this"),
|
||||
VNode::Component(_c) => todo!("children are not accessible through this"),
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
@@ -199,9 +199,9 @@ impl<'src> VNode<'src> {
|
||||
key: f.key,
|
||||
}),
|
||||
VNode::Linked(c) => VNode::Linked(NodeLink {
|
||||
gen_id: c.gen_id,
|
||||
scope_id: c.scope_id,
|
||||
link_idx: c.link_idx,
|
||||
scope_id: c.scope_id.clone(),
|
||||
link_idx: c.link_idx.clone(),
|
||||
node: c.node.clone(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -224,11 +224,7 @@ impl Debug for VNode<'_> {
|
||||
}
|
||||
VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
|
||||
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
||||
VNode::Linked(c) => write!(
|
||||
s,
|
||||
"VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
|
||||
c.gen_id, c.scope_id
|
||||
),
|
||||
VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,9 +415,25 @@ pub struct VSuspended<'a> {
|
||||
/// an `rsx!` call.
|
||||
#[derive(Debug)]
|
||||
pub struct NodeLink {
|
||||
pub(crate) link_idx: usize,
|
||||
pub(crate) gen_id: u32,
|
||||
pub(crate) scope_id: ScopeId,
|
||||
pub(crate) link_idx: Cell<usize>,
|
||||
pub(crate) scope_id: Cell<Option<ScopeId>>,
|
||||
pub(crate) node: *const VNode<'static>,
|
||||
}
|
||||
|
||||
impl PartialEq for NodeLink {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node == other.node
|
||||
}
|
||||
}
|
||||
impl NodeLink {
|
||||
// we don't want to let users clone NodeLinks
|
||||
pub(crate) fn clone_inner(&self) -> Self {
|
||||
Self {
|
||||
link_idx: self.link_idx.clone(),
|
||||
scope_id: self.scope_id.clone(),
|
||||
node: self.node.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct provides an ergonomic API to quickly build VNodes.
|
||||
@@ -440,7 +452,7 @@ impl<'a> NodeFactory<'a> {
|
||||
|
||||
#[inline]
|
||||
pub fn bump(&self) -> &'a bumpalo::Bump {
|
||||
self.bump
|
||||
&self.bump
|
||||
}
|
||||
|
||||
/// Directly pass in text blocks without the need to use the format_args macro.
|
||||
@@ -460,7 +472,7 @@ impl<'a> NodeFactory<'a> {
|
||||
Some(static_str) => (static_str, true),
|
||||
None => {
|
||||
use bumpalo::core_alloc::fmt::Write;
|
||||
let mut str_buf = bumpalo::collections::String::new_in(self.bump());
|
||||
let mut str_buf = bumpalo::collections::String::new_in(self.bump);
|
||||
str_buf.write_fmt(args).unwrap();
|
||||
(str_buf.into_bump_str(), false)
|
||||
}
|
||||
@@ -516,18 +528,18 @@ impl<'a> NodeFactory<'a> {
|
||||
A: 'a + AsRef<[Attribute<'a>]>,
|
||||
V: 'a + AsRef<[VNode<'a>]>,
|
||||
{
|
||||
let listeners: &'a L = self.bump().alloc(listeners);
|
||||
let listeners: &'a L = self.bump.alloc(listeners);
|
||||
let listeners = listeners.as_ref();
|
||||
|
||||
let attributes: &'a A = self.bump().alloc(attributes);
|
||||
let attributes: &'a A = self.bump.alloc(attributes);
|
||||
let attributes = attributes.as_ref();
|
||||
|
||||
let children: &'a V = self.bump().alloc(children);
|
||||
let children: &'a V = self.bump.alloc(children);
|
||||
let children = children.as_ref();
|
||||
|
||||
let key = key.map(|f| self.raw_text(f).0);
|
||||
|
||||
VNode::Element(self.bump().alloc(VElement {
|
||||
VNode::Element(self.bump.alloc(VElement {
|
||||
tag_name,
|
||||
key,
|
||||
namespace,
|
||||
@@ -565,7 +577,7 @@ impl<'a> NodeFactory<'a> {
|
||||
where
|
||||
P: Properties + 'a,
|
||||
{
|
||||
let bump = self.bump();
|
||||
let bump = self.bump;
|
||||
let props = bump.alloc(props);
|
||||
let bump_props = props as *mut P as *mut ();
|
||||
let user_fc = component as *const ();
|
||||
@@ -598,7 +610,6 @@ impl<'a> NodeFactory<'a> {
|
||||
|
||||
let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
|
||||
move || unsafe {
|
||||
log::debug!("dropping props!");
|
||||
if !has_dropped {
|
||||
let real_other = bump_props as *mut _ as *mut P;
|
||||
let b = BumpBox::from_raw(real_other);
|
||||
@@ -620,7 +631,6 @@ impl<'a> NodeFactory<'a> {
|
||||
|
||||
let caller: &'a mut dyn Fn(&'a Scope) -> Element =
|
||||
bump.alloc(move |scope: &Scope| -> Element {
|
||||
log::debug!("calling component renderr {:?}", scope.our_arena_idx);
|
||||
let props: &'_ P = unsafe { &*(bump_props as *const P) };
|
||||
let res = component(scope, props);
|
||||
res
|
||||
@@ -657,7 +667,7 @@ impl<'a> NodeFactory<'a> {
|
||||
self,
|
||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
|
||||
) -> VNode<'a> {
|
||||
let bump = self.bump();
|
||||
let bump = self.bump;
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(bump);
|
||||
|
||||
for node in node_iter {
|
||||
@@ -697,6 +707,58 @@ impl<'a> NodeFactory<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
// this isn't quite feasible yet
|
||||
// I think we need some form of interior mutability or state on nodefactory that stores which subtree was created
|
||||
pub fn create_children(
|
||||
self,
|
||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
|
||||
) -> Element {
|
||||
let bump = self.bump;
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(bump);
|
||||
|
||||
for node in node_iter {
|
||||
nodes.push(node.into_vnode(self));
|
||||
}
|
||||
|
||||
if nodes.is_empty() {
|
||||
nodes.push(VNode::Anchor(bump.alloc(VAnchor {
|
||||
dom_id: empty_cell(),
|
||||
})));
|
||||
}
|
||||
|
||||
let children = nodes.into_bump_slice();
|
||||
|
||||
// TODO
|
||||
// We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
|
||||
//
|
||||
// if cfg!(debug_assertions) {
|
||||
// if children.len() > 1 {
|
||||
// if children.last().unwrap().key().is_none() {
|
||||
// log::error!(
|
||||
// r#"
|
||||
// Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
// Not providing a key will lead to poor performance with lists.
|
||||
// See docs.rs/dioxus for more information.
|
||||
// ---
|
||||
// To help you identify where this error is coming from, we've generated a backtrace.
|
||||
// "#,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
let frag = VNode::Fragment(VFragment {
|
||||
children,
|
||||
key: None,
|
||||
});
|
||||
let ptr = self.bump.alloc(frag) as *const _;
|
||||
Some(NodeLink {
|
||||
link_idx: Default::default(),
|
||||
scope_id: Default::default(),
|
||||
node: unsafe { std::mem::transmute(ptr) },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn annotate_lazy<'z, 'b>(
|
||||
f: impl FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b,
|
||||
) -> Option<LazyNodes<'z, 'b>> {
|
||||
@@ -796,28 +858,48 @@ impl IntoVNode<'_> for Arguments<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// called cx.render from a helper function
|
||||
impl IntoVNode<'_> for Option<NodeLink> {
|
||||
fn into_vnode(self, cx: NodeFactory) -> VNode {
|
||||
todo!()
|
||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
||||
match self {
|
||||
Some(node) => VNode::Linked(node),
|
||||
None => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// essentially passing elements through props
|
||||
// just build a new element in place
|
||||
impl IntoVNode<'_> for &Option<NodeLink> {
|
||||
fn into_vnode(self, cx: NodeFactory) -> VNode {
|
||||
todo!()
|
||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
||||
match self {
|
||||
Some(node) => VNode::Linked(NodeLink {
|
||||
link_idx: node.link_idx.clone(),
|
||||
scope_id: node.scope_id.clone(),
|
||||
node: node.node,
|
||||
}),
|
||||
None => {
|
||||
//
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conveniently, we also support "None"
|
||||
impl IntoVNode<'_> for NodeLink {
|
||||
fn into_vnode(self, cx: NodeFactory) -> VNode {
|
||||
todo!()
|
||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
||||
VNode::Linked(self)
|
||||
}
|
||||
}
|
||||
|
||||
// Conveniently, we also support "None"
|
||||
impl IntoVNode<'_> for &NodeLink {
|
||||
fn into_vnode(self, cx: NodeFactory) -> VNode {
|
||||
todo!()
|
||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
||||
VNode::Linked(NodeLink {
|
||||
link_idx: self.link_idx.clone(),
|
||||
scope_id: self.scope_id.clone(),
|
||||
node: self.node,
|
||||
})
|
||||
}
|
||||
}
|
||||
+45
-22
@@ -2,6 +2,7 @@ use crate::innerlude::*;
|
||||
|
||||
use futures_channel::mpsc::UnboundedSender;
|
||||
use fxhash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
@@ -62,9 +63,6 @@ pub struct Scope {
|
||||
// The double-buffering situation that we will use
|
||||
pub(crate) frames: [BumpFrame; 2],
|
||||
|
||||
pub(crate) old_root: RefCell<Option<NodeLink>>,
|
||||
pub(crate) new_root: RefCell<Option<NodeLink>>,
|
||||
|
||||
pub(crate) caller: *const dyn Fn(&Scope) -> Element,
|
||||
|
||||
/*
|
||||
@@ -241,19 +239,22 @@ impl Scope {
|
||||
/// }
|
||||
///```
|
||||
pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
|
||||
let bump = &self.wip_frame().bump;
|
||||
let frame = self.wip_frame();
|
||||
let bump = &frame.bump;
|
||||
let factory = NodeFactory { bump };
|
||||
let node = lazy_nodes.map(|f| f.call(factory))?;
|
||||
let node = bump.alloc(node);
|
||||
|
||||
let idx = self
|
||||
.wip_frame()
|
||||
.add_node(unsafe { std::mem::transmute(node) });
|
||||
let node_ptr = node as *mut _;
|
||||
let node_ptr = unsafe { std::mem::transmute(node_ptr) };
|
||||
|
||||
Some(NodeLink {
|
||||
gen_id: self.generation.get(),
|
||||
scope_id: self.our_arena_idx,
|
||||
link_idx: idx,
|
||||
})
|
||||
let link = NodeLink {
|
||||
scope_id: Cell::new(Some(self.our_arena_idx)),
|
||||
link_idx: Cell::new(0),
|
||||
node: node_ptr,
|
||||
};
|
||||
|
||||
Some(link)
|
||||
}
|
||||
|
||||
/// Push an effect to be ran after the component has been successfully mounted to the dom
|
||||
@@ -435,6 +436,7 @@ impl Scope {
|
||||
false => &self.frames[1],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fin_frame(&self) -> &BumpFrame {
|
||||
match self.generation.get() & 1 == 1 {
|
||||
true => &self.frames[0],
|
||||
@@ -496,28 +498,42 @@ impl Scope {
|
||||
effect();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_node(&self) -> &VNode {
|
||||
let node = *self.wip_frame().nodes.borrow().get(0).unwrap();
|
||||
unsafe { std::mem::transmute(&*node) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BumpFrame {
|
||||
pub(crate) struct BumpFrame {
|
||||
pub bump: Bump,
|
||||
pub nodes: RefCell<Vec<VNode<'static>>>,
|
||||
pub nodes: RefCell<Vec<*const VNode<'static>>>,
|
||||
}
|
||||
impl BumpFrame {
|
||||
pub fn new() -> Self {
|
||||
let bump = Bump::new();
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
let bump = Bump::with_capacity(capacity);
|
||||
|
||||
let node = &*bump.alloc(VText {
|
||||
text: "asd",
|
||||
dom_id: Default::default(),
|
||||
is_static: false,
|
||||
});
|
||||
let nodes = RefCell::new(vec![VNode::Text(unsafe { std::mem::transmute(node) })]);
|
||||
let node = bump.alloc(VNode::Text(unsafe { std::mem::transmute(node) }));
|
||||
let nodes = RefCell::new(vec![node as *const _]);
|
||||
Self { bump, nodes }
|
||||
}
|
||||
fn add_node(&self, node: VNode<'static>) -> usize {
|
||||
|
||||
pub fn allocated_bytes(&self) -> usize {
|
||||
self.bump.allocated_bytes()
|
||||
}
|
||||
|
||||
pub fn assign_nodelink(&self, node: &NodeLink) {
|
||||
let mut nodes = self.nodes.borrow_mut();
|
||||
nodes.push(node);
|
||||
nodes.len() - 1
|
||||
|
||||
let len = nodes.len();
|
||||
nodes.push(node.node);
|
||||
|
||||
node.link_idx.set(len);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,11 +547,18 @@ impl BumpFrame {
|
||||
#[derive(Default)]
|
||||
pub(crate) struct HookList {
|
||||
arena: Bump,
|
||||
vals: RefCell<Vec<*mut dyn Any>>,
|
||||
vals: RefCell<SmallVec<[*mut dyn Any; 5]>>,
|
||||
idx: Cell<usize>,
|
||||
}
|
||||
|
||||
impl HookList {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
arena: Bump::with_capacity(capacity),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
|
||||
self.vals.borrow().get(self.idx.get()).and_then(|inn| {
|
||||
self.idx.set(self.idx.get() + 1);
|
||||
@@ -571,7 +594,7 @@ impl HookList {
|
||||
self.cur_idx() >= self.len()
|
||||
}
|
||||
|
||||
pub fn clear_hooks(&mut self) {
|
||||
pub fn clear(&mut self) {
|
||||
self.vals.borrow_mut().drain(..).for_each(|state| {
|
||||
let as_mut = unsafe { &mut *state };
|
||||
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
|
||||
|
||||
+104
-73
@@ -1,3 +1,4 @@
|
||||
use fxhash::FxHashMap;
|
||||
use slab::Slab;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
@@ -7,7 +8,6 @@ use futures_channel::mpsc::UnboundedSender;
|
||||
use crate::innerlude::*;
|
||||
|
||||
pub type FcSlot = *const ();
|
||||
// pub heuristics: FxHashMap<FcSlot, Heuristic>,
|
||||
|
||||
pub struct Heuristic {
|
||||
hook_arena_size: usize,
|
||||
@@ -21,6 +21,7 @@ pub struct Heuristic {
|
||||
pub(crate) struct ScopeArena {
|
||||
bump: Bump,
|
||||
scopes: RefCell<Vec<*mut Scope>>,
|
||||
pub heuristics: RefCell<FxHashMap<FcSlot, Heuristic>>,
|
||||
free_scopes: RefCell<Vec<ScopeId>>,
|
||||
nodes: RefCell<Slab<*const VNode<'static>>>,
|
||||
pub(crate) sender: UnboundedSender<SchedulerMsg>,
|
||||
@@ -31,6 +32,7 @@ impl ScopeArena {
|
||||
Self {
|
||||
bump: Bump::new(),
|
||||
scopes: RefCell::new(Vec::new()),
|
||||
heuristics: RefCell::new(FxHashMap::default()),
|
||||
free_scopes: RefCell::new(Vec::new()),
|
||||
nodes: RefCell::new(Slab::new()),
|
||||
sender,
|
||||
@@ -68,33 +70,53 @@ impl ScopeArena {
|
||||
} else {
|
||||
let scope_id = ScopeId(self.scopes.borrow().len());
|
||||
|
||||
let old_root = NodeLink {
|
||||
link_idx: 0,
|
||||
gen_id: 0,
|
||||
scope_id,
|
||||
};
|
||||
let new_root = NodeLink {
|
||||
link_idx: 0,
|
||||
gen_id: 0,
|
||||
scope_id,
|
||||
let (node_capacity, hook_capacity) = {
|
||||
let heuristics = self.heuristics.borrow();
|
||||
if let Some(heuristic) = heuristics.get(&fc_ptr) {
|
||||
(heuristic.node_arena_size, heuristic.hook_arena_size)
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
};
|
||||
|
||||
let new_scope = Scope {
|
||||
let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
|
||||
|
||||
frames[0].nodes.get_mut().push({
|
||||
let vnode = frames[0]
|
||||
.bump
|
||||
.alloc(VNode::Text(frames[0].bump.alloc(VText {
|
||||
dom_id: Default::default(),
|
||||
is_static: false,
|
||||
text: "",
|
||||
})));
|
||||
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||
});
|
||||
|
||||
frames[1].nodes.get_mut().push({
|
||||
let vnode = frames[1]
|
||||
.bump
|
||||
.alloc(VNode::Text(frames[1].bump.alloc(VText {
|
||||
dom_id: Default::default(),
|
||||
is_static: false,
|
||||
text: "",
|
||||
})));
|
||||
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||
});
|
||||
|
||||
let scope = self.bump.alloc(Scope {
|
||||
sender: self.sender.clone(),
|
||||
parent_scope,
|
||||
our_arena_idx: scope_id,
|
||||
height,
|
||||
subtree: Cell::new(subtree),
|
||||
is_subtree_root: Cell::new(false),
|
||||
frames: [BumpFrame::new(), BumpFrame::new()],
|
||||
frames,
|
||||
|
||||
hooks: Default::default(),
|
||||
hooks: HookList::new(hook_capacity),
|
||||
shared_contexts: Default::default(),
|
||||
caller,
|
||||
generation: 0.into(),
|
||||
|
||||
old_root: RefCell::new(Some(old_root)),
|
||||
new_root: RefCell::new(Some(new_root)),
|
||||
items: RefCell::new(SelfReferentialItems {
|
||||
listeners: Default::default(),
|
||||
borrowed_props: Default::default(),
|
||||
@@ -102,16 +124,42 @@ impl ScopeArena {
|
||||
tasks: Default::default(),
|
||||
pending_effects: Default::default(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let stable = self.bump.alloc(new_scope);
|
||||
self.scopes.borrow_mut().push(stable);
|
||||
self.scopes.borrow_mut().push(scope);
|
||||
scope_id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_remove(&self, id: &ScopeId) -> Option<Scope> {
|
||||
todo!()
|
||||
pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
|
||||
self.ensure_drop_safety(id);
|
||||
|
||||
let mut scope = unsafe { &mut *self.get_scope_raw(id)? };
|
||||
|
||||
// we're just reusing scopes so we need to clear it out
|
||||
scope.hooks.clear();
|
||||
scope.shared_contexts.get_mut().clear();
|
||||
scope.parent_scope = None;
|
||||
scope.generation.set(0);
|
||||
scope.is_subtree_root.set(false);
|
||||
scope.subtree.set(0);
|
||||
|
||||
let SelfReferentialItems {
|
||||
borrowed_props,
|
||||
listeners,
|
||||
pending_effects,
|
||||
suspended_nodes,
|
||||
tasks,
|
||||
} = scope.items.get_mut();
|
||||
|
||||
borrowed_props.clear();
|
||||
listeners.clear();
|
||||
pending_effects.clear();
|
||||
suspended_nodes.clear();
|
||||
tasks.clear();
|
||||
|
||||
self.free_scopes.borrow_mut().push(*id);
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn reserve_node(&self, node: &VNode) -> ElementId {
|
||||
@@ -123,16 +171,10 @@ impl ScopeArena {
|
||||
let node = unsafe { std::mem::transmute::<*const VNode, *const VNode>(node) };
|
||||
entry.insert(node);
|
||||
id
|
||||
|
||||
// let nodes = self.nodes.borrow_mut();
|
||||
// let id = nodes.insert(());
|
||||
// let node_id = ElementId(id);
|
||||
// node = Some(node_id);
|
||||
// node_id
|
||||
}
|
||||
|
||||
pub fn collect_garbage(&self, id: ElementId) {
|
||||
todo!()
|
||||
self.nodes.borrow_mut().remove(id.0);
|
||||
}
|
||||
|
||||
// These methods would normally exist on `scope` but they need access to *all* of the scopes
|
||||
@@ -152,35 +194,25 @@ impl ScopeArena {
|
||||
pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
|
||||
let scope = self.get_scope(scope_id).unwrap();
|
||||
|
||||
let mut items = scope.items.borrow_mut();
|
||||
|
||||
// make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
|
||||
// run the hooks (which hold an &mut Reference)
|
||||
// right now, we don't drop
|
||||
scope
|
||||
.items
|
||||
.borrow_mut()
|
||||
.borrowed_props
|
||||
.drain(..)
|
||||
.for_each(|comp| {
|
||||
// First drop the component's undropped references
|
||||
let scope_id = comp
|
||||
.associated_scope
|
||||
.get()
|
||||
.expect("VComponents should be associated with a valid Scope");
|
||||
// recursively call ensure_drop_safety on all children
|
||||
items.borrowed_props.drain(..).for_each(|comp| {
|
||||
let scope_id = comp
|
||||
.associated_scope
|
||||
.get()
|
||||
.expect("VComponents should be associated with a valid Scope");
|
||||
|
||||
todo!("move this onto virtualdom");
|
||||
// let scope = unsafe { &mut *scope_id };
|
||||
self.ensure_drop_safety(&scope_id);
|
||||
|
||||
// scope.ensure_drop_safety();
|
||||
|
||||
todo!("drop the component's props");
|
||||
// let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
|
||||
// drop_props();
|
||||
});
|
||||
let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
|
||||
drop_props();
|
||||
});
|
||||
|
||||
// Now that all the references are gone, we can safely drop our own references in our listeners.
|
||||
scope
|
||||
.items
|
||||
.borrow_mut()
|
||||
items
|
||||
.listeners
|
||||
.drain(..)
|
||||
.for_each(|listener| drop(listener.callback.borrow_mut().take()));
|
||||
@@ -212,59 +244,58 @@ impl ScopeArena {
|
||||
|
||||
// just forget about our suspended nodes while we're at it
|
||||
items.suspended_nodes.clear();
|
||||
items.tasks.clear();
|
||||
items.pending_effects.clear();
|
||||
|
||||
// guarantee that we haven't screwed up - there should be no latent references anywhere
|
||||
debug_assert!(items.listeners.is_empty());
|
||||
debug_assert!(items.suspended_nodes.is_empty());
|
||||
debug_assert!(items.borrowed_props.is_empty());
|
||||
debug_assert!(items.suspended_nodes.is_empty());
|
||||
debug_assert!(items.tasks.is_empty());
|
||||
debug_assert!(items.pending_effects.is_empty());
|
||||
|
||||
log::debug!("Borrowed stuff is successfully cleared");
|
||||
|
||||
// temporarily cast the vcomponent to the right lifetime
|
||||
// let vcomp = scope.load_vcomp();
|
||||
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
||||
scope.wip_frame().nodes.borrow_mut().clear();
|
||||
}
|
||||
|
||||
let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller };
|
||||
|
||||
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
||||
scope.wip_frame().nodes.borrow_mut().clear();
|
||||
if let Some(key) = render(scope) {
|
||||
dbg!(key);
|
||||
if let Some(link) = render(scope) {
|
||||
// right now, it's a panic to render a nodelink from another scope
|
||||
// todo: enable this. it should (reasonably) work even if it doesnt make much sense
|
||||
assert_eq!(link.scope_id.get(), Some(*id));
|
||||
|
||||
dbg!(&scope.wip_frame().nodes.borrow_mut());
|
||||
// let mut old = scope.old_root.borrow_mut();
|
||||
// let mut new = scope.new_root.borrow_mut();
|
||||
// nodelinks are not assigned when called and must be done so through the create/diff phase
|
||||
// however, we need to link this one up since it will never be used in diffing
|
||||
scope.wip_frame().assign_nodelink(&link);
|
||||
debug_assert_eq!(scope.wip_frame().nodes.borrow().len(), 1);
|
||||
|
||||
// let new_old = new.clone();
|
||||
// *old = new_old;
|
||||
// *new = Some(key);
|
||||
if !scope.items.borrow().tasks.is_empty() {
|
||||
// self.
|
||||
}
|
||||
|
||||
// dbg!(&old);
|
||||
// dbg!(&new);
|
||||
|
||||
// the user's component succeeded. We can safely cycle to the next frame
|
||||
// scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
|
||||
scope.cycle_frame();
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// The head of the bumpframe is the first linked NodeLink
|
||||
pub fn wip_head(&self, id: &ScopeId) -> &VNode {
|
||||
let scope = self.get_scope(id).unwrap();
|
||||
let wip_frame = scope.wip_frame();
|
||||
let nodes = wip_frame.nodes.borrow();
|
||||
let node = nodes.get(0).unwrap();
|
||||
let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
|
||||
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
|
||||
}
|
||||
|
||||
// The head of the bumpframe is the first linked NodeLink
|
||||
pub fn fin_head(&self, id: &ScopeId) -> &VNode {
|
||||
let scope = self.get_scope(id).unwrap();
|
||||
let wip_frame = scope.fin_frame();
|
||||
let nodes = wip_frame.nodes.borrow();
|
||||
let node: &VNode = nodes.get(0).unwrap();
|
||||
let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
|
||||
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ use std::{any::Any, collections::VecDeque};
|
||||
|
||||
/// A virtual node system that progresses user events and diffs UI trees.
|
||||
///
|
||||
///
|
||||
/// ## Guide
|
||||
///
|
||||
/// Components are defined as simple functions that take [`Context`] and a [`Properties`] type and return an [`Element`].
|
||||
///
|
||||
/// ```rust
|
||||
@@ -105,14 +108,16 @@ pub struct VirtualDom {
|
||||
|
||||
_root_caller: *mut dyn Fn(&Scope) -> Element,
|
||||
|
||||
pub(crate) scopes: Box<ScopeArena>,
|
||||
scopes: Box<ScopeArena>,
|
||||
|
||||
receiver: UnboundedReceiver<SchedulerMsg>,
|
||||
pub(crate) sender: UnboundedSender<SchedulerMsg>,
|
||||
|
||||
// Every component that has futures that need to be polled
|
||||
sender: UnboundedSender<SchedulerMsg>,
|
||||
|
||||
pending_futures: FxHashSet<ScopeId>,
|
||||
|
||||
pending_messages: VecDeque<SchedulerMsg>,
|
||||
|
||||
dirty_scopes: IndexSet<ScopeId>,
|
||||
}
|
||||
|
||||
@@ -130,7 +135,7 @@ impl VirtualDom {
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// fn Example(cx: Context<()>) -> DomTree {
|
||||
/// fn Example(cx: Context, props: &()) -> Element {
|
||||
/// cx.render(rsx!( div { "hello world" } ))
|
||||
/// }
|
||||
///
|
||||
@@ -159,7 +164,7 @@ impl VirtualDom {
|
||||
/// name: &'static str
|
||||
/// }
|
||||
///
|
||||
/// fn Example(cx: Context<SomeProps>) -> DomTree {
|
||||
/// fn Example(cx: Context, props: &SomeProps) -> Element {
|
||||
/// cx.render(rsx!{ div{ "hello {cx.name}" } })
|
||||
/// }
|
||||
///
|
||||
@@ -172,7 +177,7 @@ impl VirtualDom {
|
||||
/// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
|
||||
/// let mutations = dom.rebuild();
|
||||
/// ```
|
||||
pub fn new_with_props<P: 'static + Send>(root: FC<P>, root_props: P) -> Self {
|
||||
pub fn new_with_props<P: 'static>(root: FC<P>, root_props: P) -> Self {
|
||||
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
|
||||
Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
|
||||
}
|
||||
@@ -230,8 +235,10 @@ impl VirtualDom {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
|
||||
self.sender.clone()
|
||||
}
|
||||
@@ -240,8 +247,10 @@ impl VirtualDom {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
pub fn has_any_work(&self) -> bool {
|
||||
!(self.dirty_scopes.is_empty() && self.pending_messages.is_empty())
|
||||
}
|
||||
@@ -347,7 +356,9 @@ impl VirtualDom {
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// static App: FC<()> = |cx, props|rsx!(cx, div {"hello"} );
|
||||
/// fn App(cx: Context, props: &()) -> Element {
|
||||
/// cx.render(rsx!( div {"hello"} ))
|
||||
/// }
|
||||
///
|
||||
/// let mut dom = VirtualDom::new(App);
|
||||
///
|
||||
@@ -368,6 +379,7 @@ impl VirtualDom {
|
||||
/// applied the edits.
|
||||
///
|
||||
/// Mutations are the only link between the RealDOM and the VirtualDOM.
|
||||
///
|
||||
pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
|
||||
let mut committed_mutations = vec![];
|
||||
|
||||
@@ -436,10 +448,7 @@ impl VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
// let scopes = &mut self.scopes;
|
||||
let work_completed = diff_state.work(&mut deadline);
|
||||
// let work_completed = crate::diff::work(&mut diff_state, &mut deadline);
|
||||
// let work_completed = crate::diff::work(&mut diff_state, &mut deadline);
|
||||
|
||||
if work_completed {
|
||||
let DiffState {
|
||||
@@ -454,7 +463,7 @@ impl VirtualDom {
|
||||
}
|
||||
|
||||
// I think the stack should be empty at the end of diffing?
|
||||
debug_assert_eq!(stack.scope_stack.len(), 0);
|
||||
debug_assert_eq!(stack.scope_stack.len(), 1);
|
||||
|
||||
committed_mutations.push(mutations);
|
||||
} else {
|
||||
@@ -536,74 +545,45 @@ impl VirtualDom {
|
||||
/// let edits = dom.diff();
|
||||
/// ```
|
||||
pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
|
||||
log::debug!("hard diff {:?}", scope_id);
|
||||
|
||||
let mut diff_machine = DiffState::new(&self.scopes);
|
||||
if self.scopes.run_scope(scope_id) {
|
||||
let mut diff_machine = DiffState::new(&self.scopes);
|
||||
|
||||
diff_machine.force_diff = true;
|
||||
|
||||
diff_machine.work(|| false);
|
||||
// let scopes = &mut self.scopes;
|
||||
// crate::diff::diff_scope(&mut diff_machine, scope_id);
|
||||
// self.scopes.diff_scope(&mut diff_machine, scope_id);
|
||||
|
||||
// dbg!(&diff_machine.mutations);
|
||||
// let DiffState {
|
||||
// mutations,
|
||||
// stack,
|
||||
// seen_scopes,
|
||||
// force_diff,
|
||||
// ..
|
||||
// } = mutations;
|
||||
|
||||
// let mutations = diff_machine.mutations;
|
||||
|
||||
// Some(unsafe { std::mem::transmute(mutations) })
|
||||
todo!()
|
||||
} else {
|
||||
None
|
||||
diff_machine.diff_scope(scope_id);
|
||||
}
|
||||
Some(diff_machine.mutations)
|
||||
}
|
||||
|
||||
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||
///
|
||||
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
||||
todo!()
|
||||
let scope = self.scopes.get_scope(&self.base_scope).unwrap();
|
||||
let frame = scope.wip_frame();
|
||||
let factory = NodeFactory { bump: &frame.bump };
|
||||
let node = lazy_nodes.unwrap().call(factory);
|
||||
frame.bump.alloc(node)
|
||||
}
|
||||
|
||||
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||
///
|
||||
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||
pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
|
||||
todo!()
|
||||
// let mutations = Mutations::new();
|
||||
// let mut machine: DiffState = todo!();
|
||||
// // let mut machine = DiffState::new(mutations);
|
||||
// // let mut machine = DiffState::new(mutations);
|
||||
// machine.stack.push(DiffInstruction::Diff { new, old });
|
||||
// machine.mutations
|
||||
let mut machine = DiffState::new(&self.scopes);
|
||||
machine.stack.push(DiffInstruction::Diff { new, old });
|
||||
machine.stack.scope_stack.push(self.base_scope);
|
||||
machine.work(|| false);
|
||||
machine.mutations
|
||||
}
|
||||
|
||||
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||
///
|
||||
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||
pub fn create_vnodes<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
|
||||
todo!()
|
||||
// let old = self.bump.alloc(self.render_direct(left));
|
||||
|
||||
// let mut machine: DiffState = todo!();
|
||||
// // let mut machine = DiffState::new(Mutations::new());
|
||||
// // let mut machine = DiffState::new(Mutations::new());
|
||||
|
||||
// machine.stack.create_node(old, MountType::Append);
|
||||
|
||||
// todo!()
|
||||
|
||||
// // machine.work(&mut || false);
|
||||
|
||||
// // machine.mutations
|
||||
let nodes = self.render_vnodes(left);
|
||||
let mut machine = DiffState::new(&self.scopes);
|
||||
machine.stack.create_node(nodes, MountType::Append);
|
||||
machine.work(|| false);
|
||||
machine.mutations
|
||||
}
|
||||
|
||||
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||
@@ -616,26 +596,17 @@ impl VirtualDom {
|
||||
) -> (Mutations<'a>, Mutations<'a>) {
|
||||
let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
|
||||
|
||||
// let mut machine: DiffState = todo!();
|
||||
// let mut machine = DiffState::new(Mutations::new());
|
||||
let mut create = DiffState::new(&self.scopes);
|
||||
create.stack.scope_stack.push(self.base_scope);
|
||||
create.stack.create_node(old, MountType::Append);
|
||||
create.work(|| false);
|
||||
|
||||
// machine.stack.create_node(old, MountType::Append);
|
||||
let mut edit = DiffState::new(&self.scopes);
|
||||
create.stack.scope_stack.push(self.base_scope);
|
||||
edit.stack.push(DiffInstruction::Diff { old, new });
|
||||
edit.work(&mut || false);
|
||||
|
||||
todo!()
|
||||
|
||||
// machine.work(|| false);
|
||||
// let create_edits = machine.mutations;
|
||||
|
||||
// let mut machine: DiffState = todo!();
|
||||
// // let mut machine = DiffState::new(Mutations::new());
|
||||
|
||||
// machine.stack.push(DiffInstruction::Diff { old, new });
|
||||
|
||||
// machine.work(&mut || false);
|
||||
|
||||
// let edits = machine.mutations;
|
||||
|
||||
// (create_edits, edits)
|
||||
(create.mutations, edit.mutations)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,12 +215,12 @@ fn create_components() {
|
||||
})
|
||||
};
|
||||
|
||||
#[derive(Props)]
|
||||
struct ChildProps<'a> {
|
||||
children: ScopeChildren<'a>,
|
||||
#[derive(Props, PartialEq)]
|
||||
struct ChildProps {
|
||||
children: Element,
|
||||
}
|
||||
|
||||
fn Child<'a>(cx: Context<'a>, props: &ChildProps<'a>) -> Element {
|
||||
fn Child(cx: Context, props: &ChildProps) -> Element {
|
||||
cx.render(rsx! {
|
||||
h1 {}
|
||||
div { {&props.children} }
|
||||
@@ -299,21 +299,3 @@ fn anchors() {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suspended() {
|
||||
todo!()
|
||||
// static App: FC<()> = |cx, props| {
|
||||
// let val = use_suspense(cx, || async {}, |p| todo!());
|
||||
|
||||
// cx.render(rsx! { {val} })
|
||||
// };
|
||||
|
||||
// let mut dom = new_dom(App, ());
|
||||
// let mutations = dom.rebuild();
|
||||
|
||||
// assert_eq!(
|
||||
// mutations.edits,
|
||||
// [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
|
||||
// );
|
||||
}
|
||||
@@ -257,12 +257,12 @@ fn many_items_become_fragment() {
|
||||
]
|
||||
);
|
||||
|
||||
// hmmmmmmmmm worried about reusing IDs that we shouldnt be
|
||||
// note: the ID gets reused
|
||||
assert_eq!(
|
||||
change.edits,
|
||||
[
|
||||
Remove { root: 2 },
|
||||
CreatePlaceholder { root: 4 },
|
||||
CreatePlaceholder { root: 3 },
|
||||
ReplaceWith { root: 0, m: 1 },
|
||||
]
|
||||
);
|
||||
@@ -433,7 +433,7 @@ fn keyed_diffing_order() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!(
|
||||
// {(0..5).map(|f| {rsx! { div { key: "{f}" }}})}
|
||||
{(0..5).map(|f| {rsx! { div { key: "{f}" }}})}
|
||||
p {"e"}
|
||||
);
|
||||
let right = rsx!(
|
||||
@@ -802,22 +802,3 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suspense() {
|
||||
let dom = new_dom();
|
||||
|
||||
todo!()
|
||||
// let edits = dom.create_vnodes(Some(LazyNodes::new(|f| {
|
||||
// use std::cell::{Cell, RefCell};
|
||||
// VNode::Suspended(f.bump().alloc(VSuspended {
|
||||
// task_id: 0,
|
||||
// callback: RefCell::new(None),
|
||||
// dom_id: Cell::new(None),
|
||||
// }))
|
||||
// })));
|
||||
// assert_eq!(
|
||||
// edits.edits,
|
||||
// [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
|
||||
// );
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use dioxus::prelude::Scope;
|
||||
use dioxus::ScopeChildren;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::{Context, Element, LazyNodes, NodeFactory, Properties};
|
||||
use dioxus_core_macro::Props;
|
||||
@@ -44,7 +43,7 @@ pub struct WebviewWindowProps<'a> {
|
||||
/// focuse me
|
||||
onfocused: &'a dyn FnMut(()),
|
||||
|
||||
children: ScopeChildren<'a>,
|
||||
children: Element,
|
||||
}
|
||||
|
||||
/// A handle to a
|
||||
@@ -57,7 +56,7 @@ pub struct WebviewWindowProps<'a> {
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn WebviewWindow((cx, props): Scope<WebviewWindowProps>) -> Element {
|
||||
pub fn WebviewWindow(cx: Context, props: &WebviewWindowProps) -> Element {
|
||||
let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?;
|
||||
|
||||
cx.use_hook(
|
||||
@@ -91,7 +90,7 @@ fn syntax_works() {
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
static App: FC<()> = |(cx, props)| {
|
||||
static App: FC<()> = |cx, props| {
|
||||
cx.render(rsx! {
|
||||
// left window
|
||||
WebviewWindow {
|
||||
|
||||
@@ -166,7 +166,7 @@ pub fn run<T: 'static + Send + Sync>(
|
||||
sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
|
||||
|
||||
if let Some(BridgeEvent::Update(edits)) = rx.blocking_recv() {
|
||||
log::info!("bridge received message {:?}", edits);
|
||||
log::info!("bridge received message");
|
||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||
} else {
|
||||
log::info!("none received message");
|
||||
@@ -276,7 +276,7 @@ pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
|
||||
|
||||
runtime.block_on(async move {
|
||||
let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver);
|
||||
let _ = vir.get_event_sender();
|
||||
let _ = vir.get_scheduler_channel();
|
||||
|
||||
let edits = vir.rebuild();
|
||||
|
||||
@@ -298,14 +298,13 @@ pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
|
||||
// todo: maybe we want to schedule ourselves in
|
||||
// on average though, the virtualdom running natively is stupid fast
|
||||
|
||||
let mut muts = vir.run_with_deadline(|| false);
|
||||
let mut muts = vir.work_with_deadline(|| false);
|
||||
|
||||
log::debug!("finished running with deadline");
|
||||
|
||||
let mut edits = vec![];
|
||||
|
||||
while let Some(edit) = muts.pop() {
|
||||
log::debug!("sending message on channel with edit {:?}", edit);
|
||||
let edit_string = serde_json::to_value(Evt { edits: edit.edits })
|
||||
.expect("serializing edits should never fail");
|
||||
edits.push(edit_string);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
asd
|
||||
+11
-9
@@ -14,7 +14,7 @@ use std::fmt::{Display, Formatter};
|
||||
|
||||
use dioxus_core::exports::bumpalo;
|
||||
use dioxus_core::exports::bumpalo::Bump;
|
||||
use dioxus_core::nodes::IntoVNode;
|
||||
use dioxus_core::IntoVNode;
|
||||
use dioxus_core::*;
|
||||
|
||||
/// A memory pool for rendering
|
||||
@@ -94,7 +94,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
|
||||
"{:}",
|
||||
TextRenderer {
|
||||
cfg: SsrConfig::default(),
|
||||
root: vdom.get_scope(scope).unwrap().root_node(),
|
||||
root: vdom.get_scope(&scope).unwrap().root_node(),
|
||||
vdom: Some(vdom)
|
||||
}
|
||||
))
|
||||
@@ -158,6 +158,9 @@ impl<'a> TextRenderer<'a, '_> {
|
||||
}
|
||||
write!(f, "<!-- -->")?;
|
||||
}
|
||||
VNode::Linked(link) => {
|
||||
todo!();
|
||||
}
|
||||
VNode::Element(el) => {
|
||||
if self.cfg.indent {
|
||||
for _ in 0..il {
|
||||
@@ -244,7 +247,7 @@ impl<'a> TextRenderer<'a, '_> {
|
||||
let idx = vcomp.associated_scope.get().unwrap();
|
||||
|
||||
if let (Some(vdom), false) = (self.vdom, self.cfg.skip_components) {
|
||||
let new_node = vdom.get_scope(idx).unwrap().root_node();
|
||||
let new_node = vdom.get_scope(&idx).unwrap().root_node();
|
||||
self.html_render(new_node, f, il + 1)?;
|
||||
} else {
|
||||
}
|
||||
@@ -297,18 +300,17 @@ impl SsrConfig {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
static SIMPLE_APP: FC<()> = |(cx, _)| {
|
||||
static SIMPLE_APP: FC<()> = |cx, _| {
|
||||
cx.render(rsx!(div {
|
||||
"hello world!"
|
||||
}))
|
||||
};
|
||||
|
||||
static SLIGHTLY_MORE_COMPLEX: FC<()> = |(cx, _)| {
|
||||
static SLIGHTLY_MORE_COMPLEX: FC<()> = |cx, _| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
title: "About W3Schools"
|
||||
@@ -327,14 +329,14 @@ mod tests {
|
||||
})
|
||||
};
|
||||
|
||||
static NESTED_APP: FC<()> = |(cx, _)| {
|
||||
static NESTED_APP: FC<()> = |cx, _| {
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
SIMPLE_APP {}
|
||||
}
|
||||
))
|
||||
};
|
||||
static FRAGMENT_APP: FC<()> = |(cx, _)| {
|
||||
static FRAGMENT_APP: FC<()> = |cx, _| {
|
||||
cx.render(rsx!(
|
||||
div { "f1" }
|
||||
div { "f2" }
|
||||
@@ -390,7 +392,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn styles() {
|
||||
static STLYE_APP: FC<()> = |(cx, _)| {
|
||||
static STLYE_APP: FC<()> = |cx, _| {
|
||||
cx.render(rsx! {
|
||||
div { color: "blue", font_size: "46px" }
|
||||
})
|
||||
|
||||
+22
-34
@@ -7,17 +7,12 @@
|
||||
//! - tests to ensure dyn_into works for various event types.
|
||||
//! - Partial delegation?>
|
||||
|
||||
use dioxus_core::{
|
||||
events::{KeyCode, UserEvent},
|
||||
mutations::NodeRefMutation,
|
||||
scheduler::SchedulerMsg,
|
||||
DomEdit, ElementId, ScopeId,
|
||||
};
|
||||
use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
|
||||
use fxhash::FxHashMap;
|
||||
use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc};
|
||||
use std::{any::Any, fmt::Debug, rc::Rc};
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{
|
||||
Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
|
||||
CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
|
||||
HtmlOptionElement, HtmlTextAreaElement, Node, NodeList,
|
||||
};
|
||||
|
||||
@@ -55,7 +50,7 @@ impl WebsysDom {
|
||||
let document = load_document();
|
||||
|
||||
let mut nodes = NodeSlab::new(2000);
|
||||
let mut listeners = FxHashMap::default();
|
||||
let listeners = FxHashMap::default();
|
||||
|
||||
// re-hydrate the page - only supports one virtualdom per page
|
||||
if cfg.hydrate {
|
||||
@@ -90,17 +85,17 @@ impl WebsysDom {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
|
||||
for item in refs {
|
||||
if let Some(bla) = &item.element {
|
||||
let node = self.nodes[item.element_id.as_u64() as usize]
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.clone();
|
||||
bla.set(Box::new(node)).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
// pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
|
||||
// for item in refs {
|
||||
// if let Some(bla) = &item.element {
|
||||
// let node = self.nodes[item.element_id.as_u64() as usize]
|
||||
// .as_ref()
|
||||
// .unwrap()
|
||||
// .clone();
|
||||
// bla.set(Box::new(node)).unwrap();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) {
|
||||
for edit in edits.drain(..) {
|
||||
@@ -309,8 +304,8 @@ impl WebsysDom {
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_event_listener(&mut self, event: &str, root: u64) {
|
||||
// todo!()
|
||||
fn remove_event_listener(&mut self, _event: &str, _root: u64) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_text(&mut self, text: &str, root: u64) {
|
||||
@@ -489,8 +484,9 @@ unsafe impl Sync for DioxusWebsysEvent {}
|
||||
// todo: some of these events are being casted to the wrong event type.
|
||||
// We need tests that simulate clicks/etc and make sure every event type works.
|
||||
fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send> {
|
||||
use crate::events::*;
|
||||
use dioxus_core::events::on::*;
|
||||
use dioxus_html::on::*;
|
||||
use dioxus_html::KeyCode;
|
||||
// use dioxus_core::events::on::*;
|
||||
match event.type_().as_str() {
|
||||
"copy" | "cut" | "paste" => Box::new(ClipboardEvent {}),
|
||||
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||
@@ -682,15 +678,6 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
|
||||
|
||||
let typ = event.type_();
|
||||
|
||||
// TODO: clean this up
|
||||
if cfg!(debug_assertions) {
|
||||
let attrs = target.attributes();
|
||||
for x in 0..attrs.length() {
|
||||
let attr: Attr = attrs.item(x).unwrap();
|
||||
// log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value());
|
||||
}
|
||||
}
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
|
||||
@@ -716,7 +703,8 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
|
||||
name: event_name_from_typ(&typ),
|
||||
event: virtual_event_from_websys_event(event.clone()),
|
||||
mounted_dom_id: Some(ElementId(real_id as usize)),
|
||||
scope: ScopeId(triggered_scope as usize),
|
||||
scope_id: ScopeId(triggered_scope as usize),
|
||||
priority: dioxus_core::EventPriority::Medium,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
//! Ported events into Dioxus Synthetic Event system
|
||||
|
||||
use dioxus_core::events::on::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Event, UiEvent};
|
||||
@@ -57,8 +57,8 @@ use std::rc::Rc;
|
||||
pub use crate::cfg::WebConfig;
|
||||
use crate::dom::load_document;
|
||||
use cache::intern_cached_strings;
|
||||
use dioxus::prelude::Properties;
|
||||
use dioxus::virtual_dom::VirtualDom;
|
||||
use dioxus::SchedulerMsg;
|
||||
use dioxus::VirtualDom;
|
||||
pub use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::FC;
|
||||
use futures_util::FutureExt;
|
||||
@@ -66,7 +66,6 @@ use futures_util::FutureExt;
|
||||
mod cache;
|
||||
mod cfg;
|
||||
mod dom;
|
||||
mod events;
|
||||
mod nodeslab;
|
||||
mod ric_raf;
|
||||
|
||||
@@ -144,9 +143,10 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
|
||||
|
||||
let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
|
||||
|
||||
let tasks = dom.get_event_sender();
|
||||
let tasks = dom.get_scheduler_channel();
|
||||
|
||||
let sender_callback = Rc::new(move |event| tasks.unbounded_send(event).unwrap());
|
||||
let sender_callback: Rc<dyn Fn(SchedulerMsg)> =
|
||||
Rc::new(move |event| tasks.unbounded_send(event).unwrap());
|
||||
|
||||
let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
|
||||
|
||||
@@ -170,13 +170,12 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
|
||||
let mut deadline = work_loop.wait_for_idle_time().await;
|
||||
|
||||
// run the virtualdom work phase until the frame deadline is reached
|
||||
let mutations = dom.run_with_deadline(|| (&mut deadline).now_or_never().is_some());
|
||||
let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some());
|
||||
|
||||
// wait for the animation frame to fire so we can apply our changes
|
||||
work_loop.wait_for_raf().await;
|
||||
|
||||
for mut edit in mutations {
|
||||
// log::debug!("edits are {:#?}", edit);
|
||||
// actually apply our changes during the animation frame
|
||||
websys_dom.process_edits(&mut edit.edits);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user