wip: tests added for new iterative
This commit is contained in:
1 parent
2ce0752a9c
commit
24572b63de
6 files changed
+191
-69
No files matched your search
@@ -50,6 +50,8 @@ indexmap = "1.7.0"
|
|||||||
anyhow = "1.0.42"
|
anyhow = "1.0.42"
|
||||||
async-std = { version = "1.9.0", features = ["attributes"] }
|
async-std = { version = "1.9.0", features = ["attributes"] }
|
||||||
dioxus-html = { path = "../html" }
|
dioxus-html = { path = "../html" }
|
||||||
|
fern = { version = "0.6.0", features = ["colored"] }
|
||||||
|
simple_logger = "1.13.0"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
+55
-41
@@ -100,7 +100,7 @@ pub struct DiffMachine<'bump> {
|
|||||||
|
|
||||||
pub nodes_created_stack: SmallVec<[usize; 10]>,
|
pub nodes_created_stack: SmallVec<[usize; 10]>,
|
||||||
|
|
||||||
pub node_stack: SmallVec<[DiffInstruction<'bump>; 10]>,
|
pub instructions: SmallVec<[DiffInstruction<'bump>; 10]>,
|
||||||
|
|
||||||
pub scope_stack: SmallVec<[ScopeId; 5]>,
|
pub scope_stack: SmallVec<[ScopeId; 5]>,
|
||||||
|
|
||||||
@@ -113,6 +113,7 @@ pub struct DiffMachine<'bump> {
|
|||||||
///
|
///
|
||||||
/// Right now, we insert an instruction for every child node we want to create and diff. This can be less efficient than
|
/// Right now, we insert an instruction for every child node we want to create and diff. This can be less efficient than
|
||||||
/// a custom iterator type - but this is current easier to implement. In the future, let's try interact with the stack less.
|
/// a custom iterator type - but this is current easier to implement. In the future, let's try interact with the stack less.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum DiffInstruction<'a> {
|
pub enum DiffInstruction<'a> {
|
||||||
DiffNode {
|
DiffNode {
|
||||||
old: &'a VNode<'a>,
|
old: &'a VNode<'a>,
|
||||||
@@ -121,7 +122,8 @@ pub enum DiffInstruction<'a> {
|
|||||||
|
|
||||||
DiffChildren {
|
DiffChildren {
|
||||||
progress: usize,
|
progress: usize,
|
||||||
children: &'a [VNode<'a>],
|
old: &'a [VNode<'a>],
|
||||||
|
new: &'a [VNode<'a>],
|
||||||
},
|
},
|
||||||
|
|
||||||
Create {
|
Create {
|
||||||
@@ -148,7 +150,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
shared: &'bump SharedResources,
|
shared: &'bump SharedResources,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node_stack: smallvec![],
|
instructions: smallvec![],
|
||||||
nodes_created_stack: smallvec![],
|
nodes_created_stack: smallvec![],
|
||||||
mutations: edits,
|
mutations: edits,
|
||||||
scope_stack: smallvec![cur_scope],
|
scope_stack: smallvec![cur_scope],
|
||||||
@@ -158,6 +160,12 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_headless(shared: &'bump SharedResources) -> Self {
|
||||||
|
let edits = Mutations::new();
|
||||||
|
let cur_scope = ScopeId(0);
|
||||||
|
Self::new(edits, cur_scope, shared)
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
pub async fn diff_scope(&mut self, id: ScopeId) -> Result<()> {
|
pub async fn diff_scope(&mut self, id: ScopeId) -> Result<()> {
|
||||||
let component = self.get_scope_mut(&id).ok_or_else(|| Error::NotMounted)?;
|
let component = self.get_scope_mut(&id).ok_or_else(|| Error::NotMounted)?;
|
||||||
@@ -176,50 +184,60 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
// todo: don't move the reused instructions around
|
// todo: don't move the reused instructions around
|
||||||
// defer to individual functions so the compiler produces better code
|
// defer to individual functions so the compiler produces better code
|
||||||
// large functions tend to be difficult for the compiler to work with
|
// large functions tend to be difficult for the compiler to work with
|
||||||
let mut should_pop = false;
|
while let Some(instruction) = self.instructions.last_mut() {
|
||||||
while let Some(make) = self.node_stack.last_mut() {
|
log::debug!("Handling diff instruction: {:?}", instruction);
|
||||||
should_pop = false;
|
match instruction {
|
||||||
match make {
|
|
||||||
DiffInstruction::DiffNode { old, new, .. } => {
|
DiffInstruction::DiffNode { old, new, .. } => {
|
||||||
let (old, new) = (*old, *new);
|
let (old, new) = (*old, *new);
|
||||||
|
self.instructions.pop();
|
||||||
self.diff_node(old, new);
|
self.diff_node(old, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::DiffChildren { progress, children } => {
|
// this is slightly more complicated, we need to find a way to pause our LIS code
|
||||||
//
|
DiffInstruction::DiffChildren { progress, old, new } => {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::Create { node, .. } => {
|
DiffInstruction::Create { node, .. } => {
|
||||||
let node = *node;
|
let node = *node;
|
||||||
|
self.instructions.pop();
|
||||||
self.create_node(node);
|
self.create_node(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::CreateChildren { progress, children } => {
|
DiffInstruction::CreateChildren { progress, children } => {
|
||||||
if let Some(child) = (children).get(*progress) {
|
if let Some(child) = children.get(*progress) {
|
||||||
*progress += 1;
|
*progress += 1;
|
||||||
self.create_node(child);
|
self.create_node(child);
|
||||||
} else {
|
} else {
|
||||||
should_pop = true;
|
self.instructions.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::Append {} => {
|
DiffInstruction::Append => {
|
||||||
let many = self.nodes_created_stack.pop().unwrap();
|
let many = self.nodes_created_stack.pop().unwrap();
|
||||||
self.edit_append_children(many as u32);
|
self.edit_append_children(many as u32);
|
||||||
|
self.instructions.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::Replace { with } => {
|
DiffInstruction::Replace { with } => {
|
||||||
let with = *with;
|
let with = *with;
|
||||||
let many = self.nodes_created_stack.pop().unwrap();
|
let many = self.nodes_created_stack.pop().unwrap();
|
||||||
|
|
||||||
self.edit_replace_with(with as u32, many as u32);
|
self.edit_replace_with(with as u32, many as u32);
|
||||||
|
self.instructions.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::InsertAfter => todo!(),
|
DiffInstruction::InsertAfter => {
|
||||||
DiffInstruction::InsertBefore => todo!(),
|
let n = self.nodes_created_stack.pop().unwrap();
|
||||||
}
|
self.edit_insert_after(n as u32);
|
||||||
if should_pop {
|
self.instructions.pop();
|
||||||
self.node_stack.pop();
|
}
|
||||||
}
|
|
||||||
|
DiffInstruction::InsertBefore => {
|
||||||
|
let n = self.nodes_created_stack.pop().unwrap();
|
||||||
|
self.edit_insert_before(n as u32);
|
||||||
|
self.instructions.pop();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -268,15 +286,13 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
attributes,
|
attributes,
|
||||||
children,
|
children,
|
||||||
namespace,
|
namespace,
|
||||||
static_attrs: _,
|
|
||||||
static_children: _,
|
|
||||||
static_listeners: _,
|
|
||||||
dom_id,
|
dom_id,
|
||||||
key,
|
..
|
||||||
} = element;
|
} = element;
|
||||||
|
|
||||||
let real_id = self.vdom.reserve_node();
|
let real_id = self.vdom.reserve_node();
|
||||||
self.edit_create_element(tag_name, *namespace, real_id);
|
self.edit_create_element(tag_name, *namespace, real_id);
|
||||||
|
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||||
dom_id.set(Some(real_id));
|
dom_id.set(Some(real_id));
|
||||||
|
|
||||||
let cur_scope = self.current_scope().unwrap();
|
let cur_scope = self.current_scope().unwrap();
|
||||||
@@ -285,30 +301,26 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
self.fix_listener(listener);
|
self.fix_listener(listener);
|
||||||
listener.mounted_node.set(Some(real_id));
|
listener.mounted_node.set(Some(real_id));
|
||||||
self.edit_new_event_listener(listener, cur_scope.clone());
|
self.edit_new_event_listener(listener, cur_scope.clone());
|
||||||
|
|
||||||
// if the node has an event listener, then it must be visited ?
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for attr in *attributes {
|
for attr in *attributes {
|
||||||
self.edit_set_attribute(attr);
|
self.edit_set_attribute(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: the append child edit
|
self.instructions.push(DiffInstruction::Append);
|
||||||
|
|
||||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
|
||||||
|
|
||||||
// push every child onto the stack
|
|
||||||
self.nodes_created_stack.push(0);
|
self.nodes_created_stack.push(0);
|
||||||
for child in *children {
|
self.instructions.push(DiffInstruction::CreateChildren {
|
||||||
self.node_stack
|
children,
|
||||||
.push(DiffInstruction::Create { node: child })
|
progress: 0,
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
|
fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
|
||||||
for node in frag.children {
|
self.instructions.push(DiffInstruction::CreateChildren {
|
||||||
self.node_stack.push(DiffInstruction::Create { node })
|
children: frag.children,
|
||||||
}
|
progress: 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
|
fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
|
||||||
@@ -348,7 +360,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
|
|
||||||
// Run the scope for one iteration to initialize it
|
// Run the scope for one iteration to initialize it
|
||||||
match new_component.run_scope() {
|
match new_component.run_scope() {
|
||||||
Ok(_) => {
|
Ok(_g) => {
|
||||||
// all good, new nodes exist
|
// all good, new nodes exist
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -361,11 +373,13 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
// Take the node that was just generated from running the component
|
// Take the node that was just generated from running the component
|
||||||
let nextnode = new_component.frames.fin_head();
|
let nextnode = new_component.frames.fin_head();
|
||||||
|
|
||||||
// Push the new scope onto the stack
|
// // Push the new scope onto the stack
|
||||||
self.scope_stack.push(new_idx);
|
// self.scope_stack.push(new_idx);
|
||||||
|
|
||||||
todo!();
|
|
||||||
// // Run the creation algorithm with this scope on the stack
|
// // Run the creation algorithm with this scope on the stack
|
||||||
|
self.instructions
|
||||||
|
.push(DiffInstruction::Create { node: nextnode });
|
||||||
|
|
||||||
// let meta = self.create_vnode(nextnode);
|
// let meta = self.create_vnode(nextnode);
|
||||||
|
|
||||||
// // pop the scope off the stack
|
// // pop the scope off the stack
|
||||||
@@ -376,7 +390,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// // Finally, insert this scope as a seen node.
|
// // Finally, insert this scope as a seen node.
|
||||||
// self.seen_scopes.insert(new_idx);
|
self.seen_scopes.insert(new_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================
|
// =================================
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ pub enum VNode<'src> {
|
|||||||
impl<'src> VNode<'src> {
|
impl<'src> VNode<'src> {
|
||||||
pub fn key(&self) -> Option<&'src str> {
|
pub fn key(&self) -> Option<&'src str> {
|
||||||
match &self {
|
match &self {
|
||||||
VNode::Text(_) => todo!(),
|
VNode::Element(el) => el.key,
|
||||||
VNode::Element(_) => todo!(),
|
VNode::Component(c) => c.key,
|
||||||
VNode::Fragment(_) => todo!(),
|
VNode::Fragment(f) => f.key,
|
||||||
VNode::Component(_) => todo!(),
|
VNode::Text(_t) => None,
|
||||||
VNode::Suspended(_) => todo!(),
|
VNode::Suspended(s) => None,
|
||||||
VNode::Anchor(_) => todo!(),
|
VNode::Anchor(f) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn direct_id(&self) -> ElementId {
|
pub fn direct_id(&self) -> ElementId {
|
||||||
|
|||||||
@@ -159,23 +159,53 @@ impl VirtualDom {
|
|||||||
.get_scope_mut(&self.base_scope)
|
.get_scope_mut(&self.base_scope)
|
||||||
.expect("The base scope should never be moved");
|
.expect("The base scope should never be moved");
|
||||||
|
|
||||||
todo!();
|
// todo!();
|
||||||
|
|
||||||
// // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
|
// // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
|
||||||
// if cur_component.run_scope().is_ok() {
|
if cur_component.run_scope().is_ok() {
|
||||||
// let meta = diff_machine.create_vnode(cur_component.frames.fin_head());
|
diff_machine.nodes_created_stack.push(0);
|
||||||
// diff_machine.edit_append_children(meta.added_to_stack);
|
diff_machine.instructions.push(DiffInstruction::Create {
|
||||||
// } else {
|
node: cur_component.frames.fin_head(),
|
||||||
// // todo: should this be a hard error?
|
});
|
||||||
// log::warn!(
|
} else {
|
||||||
// "Component failed to run succesfully during rebuild.
|
// todo: should this be a hard error?
|
||||||
// This does not result in a failed rebuild, but indicates a logic failure within your app."
|
log::warn!(
|
||||||
// );
|
"Component failed to run succesfully during rebuild.
|
||||||
// }
|
This does not result in a failed rebuild, but indicates a logic failure within your app."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(diff_machine.mutations)
|
Ok(diff_machine.mutations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn rebuild_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
|
||||||
|
let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
|
||||||
|
|
||||||
|
let cur_component = diff_machine
|
||||||
|
.get_scope_mut(&self.base_scope)
|
||||||
|
.expect("The base scope should never be moved");
|
||||||
|
|
||||||
|
// todo!();
|
||||||
|
|
||||||
|
// // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
|
||||||
|
if cur_component.run_scope().is_ok() {
|
||||||
|
diff_machine.nodes_created_stack.push(0);
|
||||||
|
diff_machine.instructions.push(DiffInstruction::Create {
|
||||||
|
node: cur_component.frames.fin_head(),
|
||||||
|
});
|
||||||
|
diff_machine.work().await.unwrap();
|
||||||
|
let many = diff_machine.nodes_created_stack.pop().unwrap() as u32;
|
||||||
|
diff_machine.edit_append_children(many);
|
||||||
|
} else {
|
||||||
|
// todo: should this be a hard error?
|
||||||
|
log::warn!(
|
||||||
|
"Component failed to run succesfully during rebuild.
|
||||||
|
This does not result in a failed rebuild, but indicates a logic failure within your app."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(diff_machine.mutations)
|
||||||
|
}
|
||||||
/// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
|
/// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
|
||||||
///
|
///
|
||||||
/// This method will not wait for any suspended nodes to complete.
|
/// This method will not wait for any suspended nodes to complete.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use dioxus::{
|
|||||||
scheduler::Mutations,
|
scheduler::Mutations,
|
||||||
DomEdit,
|
DomEdit,
|
||||||
};
|
};
|
||||||
|
mod test_logging;
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
@@ -29,29 +30,55 @@ fn test_original_diff() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_iterative_diff() {
|
async fn test_iterative_create() {
|
||||||
static App: FC<()> = |cx| {
|
static App: FC<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
"Hello, world!"
|
"Hello, world!"
|
||||||
|
div {
|
||||||
|
div {
|
||||||
|
Fragment {
|
||||||
|
"hello"
|
||||||
|
"world"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let shared = SharedResources::new();
|
test_logging::set_up_logging();
|
||||||
|
|
||||||
let mut machine = DiffMachine::new_headless(&shared);
|
let mut dom = VirtualDom::new(App);
|
||||||
let a = machine.work().await.unwrap();
|
let mutations = dom.rebuild_async().await.unwrap();
|
||||||
|
dbg!(mutations);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn websys_loop() {
|
async fn test_iterative_create_list() {
|
||||||
///loop {
|
static App: FC<()> = |cx| {
|
||||||
/// let deadline = request_idle_callback().await;
|
cx.render(rsx! {
|
||||||
/// let edits = dom.work(deadline);
|
{(0..10).map(|f| rsx!{ div {
|
||||||
/// request_animation_frame().await;
|
"hello"
|
||||||
/// apply(edits);
|
}})}
|
||||||
///}
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
test_logging::set_up_logging();
|
||||||
|
|
||||||
|
let mut dom = VirtualDom::new(App);
|
||||||
|
let mutations = dom.rebuild_async().await.unwrap();
|
||||||
|
dbg!(mutations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[async_std::test]
|
||||||
|
// async fn websys_loop() {
|
||||||
|
// ///loop {
|
||||||
|
// /// let deadline = request_idle_callback().await;
|
||||||
|
// /// let edits = dom.work(deadline);
|
||||||
|
// /// request_animation_frame().await;
|
||||||
|
// /// apply(edits);
|
||||||
|
// ///}
|
||||||
|
// }
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
pub fn set_up_logging() {
|
||||||
|
use fern::colors::{Color, ColoredLevelConfig};
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
|
// configure colors for the whole line
|
||||||
|
let colors_line = ColoredLevelConfig::new()
|
||||||
|
.error(Color::Red)
|
||||||
|
.warn(Color::Yellow)
|
||||||
|
// we actually don't need to specify the color for debug and info, they are white by default
|
||||||
|
.info(Color::White)
|
||||||
|
.debug(Color::White)
|
||||||
|
// depending on the terminals color scheme, this is the same as the background color
|
||||||
|
.trace(Color::BrightBlack);
|
||||||
|
|
||||||
|
// configure colors for the name of the level.
|
||||||
|
// since almost all of them are the same as the color for the whole line, we
|
||||||
|
// just clone `colors_line` and overwrite our changes
|
||||||
|
let colors_level = colors_line.clone().info(Color::Green);
|
||||||
|
// here we set up our fern Dispatch
|
||||||
|
fern::Dispatch::new()
|
||||||
|
.format(move |out, message, record| {
|
||||||
|
out.finish(format_args!(
|
||||||
|
"{color_line}[{level}{color_line}] {message}\x1B[0m",
|
||||||
|
color_line = format_args!(
|
||||||
|
"\x1B[{}m",
|
||||||
|
colors_line.get_color(&record.level()).to_fg_str()
|
||||||
|
),
|
||||||
|
level = colors_level.color(record.level()),
|
||||||
|
message = message,
|
||||||
|
));
|
||||||
|
})
|
||||||
|
// set the default log level. to filter out verbose log messages from dependencies, set
|
||||||
|
// this to Warn and overwrite the log level for your crate.
|
||||||
|
.level(log::LevelFilter::Debug)
|
||||||
|
// .level(log::LevelFilter::Warn)
|
||||||
|
// change log levels for individual modules. Note: This looks for the record's target
|
||||||
|
// field which defaults to the module path but can be overwritten with the `target`
|
||||||
|
// parameter:
|
||||||
|
// `info!(target="special_target", "This log message is about special_target");`
|
||||||
|
// .level_for("dioxus", log::LevelFilter::Debug)
|
||||||
|
// .level_for("dioxus", log::LevelFilter::Info)
|
||||||
|
// .level_for("pretty_colored", log::LevelFilter::Trace)
|
||||||
|
// output to stdout
|
||||||
|
.chain(std::io::stdout())
|
||||||
|
.apply()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
debug!("finished setting up logging! yay!");
|
||||||
|
}
|
||||||
Reference in new issue
Block a user