From 9b845a69c345f51c8b53f4f0cb1a790b78d6a5e0 Mon Sep 17 00:00:00 2001 From: tiye Date: Sun, 29 May 2022 04:08:31 +0800 Subject: [PATCH] allow Rc inside tree for sharing; tag 0.0.6 --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/app/task.rs | 93 +++++++++++++++++++++++---------------------- src/respo.rs | 8 +++- src/respo/diff.rs | 32 ++++++++++++++++ src/respo/primes.rs | 33 ++++++++++++++++ 6 files changed, 122 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 015cacc..83bc072 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,7 +99,7 @@ dependencies = [ [[package]] name = "respo" -version = "0.0.5" +version = "0.0.6" dependencies = [ "cirru_parser", "console_error_panic_hook", diff --git a/Cargo.toml b/Cargo.toml index 8e703cb..4916cd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "respo" -version = "0.0.5" +version = "0.0.6" edition = "2021" description = "a tiny virtual DOM library migrated from ClojureScript" license = "Apache-2.0" diff --git a/src/app/task.rs b/src/app/task.rs index 1cebb53..c11d959 100644 --- a/src/app/task.rs +++ b/src/app/task.rs @@ -58,51 +58,54 @@ pub fn comp_task( Ok(()) }; - Ok(RespoNode::Component( - "tasks".to_owned(), - vec![RespoEffect::new( - vec![cast_into_json(task)], - move |args, effect_type, _el| -> Result<(), String> { - let t: Task = cast_from_json(&args[0]); - util::log!("effect {:?} task: {:?}", effect_type, t); - // TODO - Ok(()) - }, - )], - Box::new( - div() - .class_list(&[ui_row_middle(), style_task_container()]) - .add_children([ - div() - .class(style_done_button()) - .add_style(if task.done { - RespoStyle::default().background_color(CssColor::Blue).to_owned() - } else { - RespoStyle::default() - }) - .on_click(on_toggle) - .to_owned(), - div().inner_text(task.content.to_owned()).to_owned(), - span() - .class_list(&[ui_center(), style_remove_button()]) - .inner_text("✕") - .on_click(on_remove) - .to_owned(), - div() - .add_style(RespoStyle::default().margin4(0.0, 0.0, 0.0, 20.0).to_owned()) - .to_owned(), - input() - .class(ui_input()) - .insert_attr("value", state.draft) - .insert_attr("placeholder", "something to update...") - .on_input(on_input) - .to_owned(), - space(Some(8), None), - button().class(ui_button()).inner_text("Update").on_click(on_update).to_owned(), - ]) - .to_owned(), - ), - )) + Ok( + RespoNode::Component( + "tasks".to_owned(), + vec![RespoEffect::new( + vec![cast_into_json(task)], + move |args, effect_type, _el| -> Result<(), String> { + let t: Task = cast_from_json(&args[0]); + util::log!("effect {:?} task: {:?}", effect_type, t); + // TODO + Ok(()) + }, + )], + Box::new( + div() + .class_list(&[ui_row_middle(), style_task_container()]) + .add_children([ + div() + .class(style_done_button()) + .add_style(if task.done { + RespoStyle::default().background_color(CssColor::Blue).to_owned() + } else { + RespoStyle::default() + }) + .on_click(on_toggle) + .to_owned(), + div().inner_text(task.content.to_owned()).to_owned(), + span() + .class_list(&[ui_center(), style_remove_button()]) + .inner_text("✕") + .on_click(on_remove) + .to_owned(), + div() + .add_style(RespoStyle::default().margin4(0.0, 0.0, 0.0, 20.0).to_owned()) + .to_owned(), + input() + .class(ui_input()) + .insert_attr("value", state.draft) + .insert_attr("placeholder", "something to update...") + .on_input(on_input) + .to_owned(), + space(Some(8), None), + button().class(ui_button()).inner_text("Update").on_click(on_update).to_owned(), + ]) + .to_owned(), + ), + ) + .share_with_ref(), + ) } static_styles!( diff --git a/src/respo.rs b/src/respo.rs index 2bb25ef..cffaa65 100644 --- a/src/respo.rs +++ b/src/respo.rs @@ -118,7 +118,10 @@ where { // util::log!("looking for {:?}\n {}", coord, &tree); if coord.is_empty() { - Ok(tree.to_owned()) + match tree { + RespoNode::Referenced(cell) => Ok((**cell).clone()), + _ => Ok(tree.clone()), + } } else { let branch = coord.first().ok_or("to get first branch of coord")?; match (tree, branch) { @@ -144,6 +147,7 @@ where (RespoNode::Element { .. }, RespoCoord::Comp(..)) => { Err(format!("expected component at {:?}, found target being an element", coord)) } + (RespoNode::Referenced(cell), _) => load_coord_target_tree(cell, coord), } } } @@ -160,6 +164,7 @@ where Some(v) => Ok((*v).to_owned()), None => Err(format!("no handler for event:{} on {} {:?}", &event_name, tag_name, event,)), }, + RespoNode::Referenced(cell) => request_for_target_handler(&cell, event_name, coord), } } @@ -214,5 +219,6 @@ where Ok(element.dyn_ref::().expect("converting to Node").clone()) } + RespoNode::Referenced(cell) => build_dom_tree(cell, coord, handle_event), } } diff --git a/src/respo/diff.rs b/src/respo/diff.rs index b403edf..34ad657 100644 --- a/src/respo/diff.rs +++ b/src/respo/diff.rs @@ -1,5 +1,6 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; +use std::rc::Rc; use crate::respo::primes::*; @@ -109,6 +110,20 @@ where diff_children(children, old_children, coord, dom_path, changes)?; } } + (RespoNode::Referenced(new_cell), RespoNode::Referenced(old_cell)) => { + // pointer compare https://stackoverflow.com/a/60241585/883571 + if Rc::ptr_eq(new_cell, old_cell) { + return Ok(()); + } else { + diff_tree(new_cell, old_cell, coord, dom_path, changes)?; + } + } + (RespoNode::Referenced(new_cell), b) => { + diff_tree(new_cell, b, coord, dom_path, changes)?; + } + (a, RespoNode::Referenced(old_cell)) => { + diff_tree(a, old_cell, coord, dom_path, changes)?; + } } Ok(()) @@ -378,6 +393,10 @@ where } Ok(()) } + RespoNode::Referenced(cell) => { + collect_effects_outside_in_as(cell, coord, dom_path, effect_type, changes)?; + Ok(()) + } } } @@ -413,6 +432,11 @@ where } Ok(()) } + + RespoNode::Referenced(cell) => { + collect_effects_inside_out_as(cell, coord, dom_path, effect_type, changes)?; + Ok(()) + } } } @@ -448,6 +472,10 @@ where } Ok(()) } + RespoNode::Referenced(cell) => { + nested_effects_outside_in_as(cell, coord, dom_path, effect_type, operations)?; + Ok(()) + } } } @@ -483,5 +511,9 @@ where } Ok(()) } + RespoNode::Referenced(cell) => { + nested_effects_inside_out_as(cell, coord, dom_path, effect_type, operations)?; + Ok(()) + } } } diff --git a/src/respo/primes.rs b/src/respo/primes.rs index f621d06..f59b923 100644 --- a/src/respo/primes.rs +++ b/src/respo/primes.rs @@ -30,6 +30,7 @@ where /// they are used in diffing, so it's better to be distinct, although not required to be. children: Vec<(RespoIndexKey, RespoNode)>, }, + Referenced(Rc>), } impl From> for Cirru @@ -48,6 +49,7 @@ where .collect(), ), ]), + RespoNode::Referenced(cell) => (*cell).to_owned().into(), } } } @@ -106,6 +108,9 @@ where style.0.push((k.to_owned(), v.to_owned())); } } + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } self } @@ -122,6 +127,9 @@ where RespoNode::Element { ref mut attrs, .. } => { attrs.insert(property.into(), value.into()); } + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } self } @@ -140,6 +148,9 @@ where attrs.insert(k.into(), v.into()); } } + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } self } @@ -154,6 +165,9 @@ where RespoNode::Element { ref mut event, .. } => { event.insert("click".into(), RespoEventHandler::new(handler)); } + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } self } @@ -168,6 +182,9 @@ where RespoNode::Element { ref mut event, .. } => { event.insert("input".into(), RespoEventHandler::new(handler)); } + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } self } @@ -185,6 +202,9 @@ where event.insert(k.into(), v.to_owned()); } } + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } self } @@ -202,6 +222,9 @@ where children.push((idx.into(), v)); } } + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } self } @@ -218,6 +241,9 @@ where children.push((idx, v)); } } + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } self } @@ -232,6 +258,9 @@ where self } RespoNode::Element { .. } => unreachable!("effects are on components"), + RespoNode::Referenced(_) => { + unreachable!("should not be called on a referenced node"); + } } } /// attach a class name for adding styles @@ -269,6 +298,10 @@ where self.insert_attr("innerHTML", content.into()); self } + /// wrap with a `Rc>` to enable memory reuse and skipping in diff + pub fn share_with_ref(&self) -> Self { + Self::Referenced(Rc::new(self.clone())) + } } pub(crate) type StrDict = HashMap;