Skip to content

Commit

Permalink
improve debug tools; fix prepend; alpha tag 0.0.12-a2
Browse files Browse the repository at this point in the history
  • Loading branch information
tiye committed Jun 7, 2022
1 parent e25e94d commit cfa5fbb
Show file tree
Hide file tree
Showing 8 changed files with 394 additions and 143 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "respo"
version = "0.0.12-a1"
version = "0.0.12-a2"
edition = "2021"
description = "a tiny virtual DOM library migrated from ClojureScript"
license = "Apache-2.0"
Expand All @@ -18,8 +18,8 @@ console_error_panic_hook = "0.1.7"
lazy_static = "1.4.0"
serde = { version = "1.0.137", features = [ "derive" ] }
serde_json = "1.0.81"
uuid = { version = "1.0.0", features = [ "v4", "js" ] }
cirru_parser = "0.1.20"
uuid = { version = "1.1.1", features = [ "v4", "js" ] }
cirru_parser = "0.1.22"
rust-hsluv = "0.1.4"

[lib]
Expand Down
3 changes: 1 addition & 2 deletions src/app/panel.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::fmt::Debug;

use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
use web_sys::console::log_1;

Expand Down Expand Up @@ -58,7 +57,7 @@ pub fn comp_panel(states: &StatesTree) -> Result<RespoNode<ActionOp>, String> {
])
.to_owned(),
)
.effect(&vec![] as &Vec<Value>, move |_, _dispatch, _el| {
.stable_effect(move |_, _dispatch, _el| {
log_1(&format!("panel effect {:?}", cursor).into());
Ok(())
})
Expand Down
14 changes: 13 additions & 1 deletion src/respo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,24 @@ where

let to_prev_tree = prev_tree.clone();
util::raf_loop_slow(Box::new(move || -> Result<(), String> {
// let to_prev_tree2 = to_prev_tree.clone();
if drain_rerender_status() {
let new_tree = renderer()?;
let mut changes: Vec<DomChange<T>> = vec![];
diff_tree(&new_tree, &to_prev_tree.borrow(), &Vec::new(), &Vec::new(), &mut changes)?;

// util::log!("changes: {:?}", changes);
// util::log!(
// "prev tree: {}",
// cirru_parser::format(
// &[to_prev_tree2.borrow().to_owned().into()],
// cirru_parser::CirruWriterOptions { use_inline: true }
// )
// .unwrap()
// );
// util::log!(
// "changes: {}",
// cirru_parser::format(&[changes_to_cirru(&changes)], cirru_parser::CirruWriterOptions { use_inline: true }).unwrap()
// );

let handler = handle_event.clone();
patch_tree(&new_tree, &prev_tree.borrow(), &mount_target, &changes, handler)?;
Expand Down
22 changes: 15 additions & 7 deletions src/respo/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ where
for op in changes {
// crate::util::log!("op: {:?}", op);
let coord = op.get_coord();
let target = find_coord_dom_target(&mount_target.first_child().ok_or("to get first child")?, &op.get_dom_path())?;
let target = find_coord_dom_target(&mount_target.first_child().ok_or("mount position")?, &op.get_dom_path())?;
match op {
DomChange::ModifyAttrs { set, unset, .. } => {
let el = target.dyn_ref::<Element>().expect("load as element");
Expand Down Expand Up @@ -128,12 +128,20 @@ where
let mut next_coord = coord.to_owned();
next_coord.push(RespoCoord::Key(k.to_owned()));
let new_element = build_dom_tree(node, &next_coord, handler).expect("new element");
let base = target.dyn_ref::<Node>().expect("to node").first_child().expect("to first child");
target
.dyn_ref::<Node>()
.expect("to node")
.insert_before(&new_element, Some(&base))
.expect("element appended");
if target.child_nodes().length() == 0 {
target
.dyn_ref::<Node>()
.expect("to node")
.append_child(&new_element)
.expect("element appended");
} else {
let base = target.dyn_ref::<Node>().expect("to node").first_child().ok_or("to first child")?;
target
.dyn_ref::<Node>()
.expect("to node")
.insert_before(&new_element, Some(&base))
.expect("element appended");
}
}
ChildDomOp::RemoveAt(idx) => {
let child = target
Expand Down
195 changes: 72 additions & 123 deletions src/respo/primes.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
mod dom_change;

use std::boxed::Box;
use std::collections::HashSet;
use std::fmt::Display;
use std::rc::Rc;
use std::{collections::HashMap, fmt::Debug};

use cirru_parser::{Cirru, CirruWriterOptions};
use cirru_parser::Cirru;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::Value;
Expand All @@ -14,6 +15,8 @@ use crate::{MaybeState, StatesTree};

use super::css::RespoStyle;

pub use dom_change::{changes_to_cirru, ChildDomOp, DomChange, RespoCoord};

/// an `Element` or a `Component`
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RespoNode<T>
Expand Down Expand Up @@ -43,16 +46,16 @@ where
{
fn from(value: RespoNode<T>) -> Self {
match value {
RespoNode::Component(name, _eff, tree) => Cirru::List(vec![Cirru::Leaf(name.into()), (*tree).into()]),
RespoNode::Element { name, children, .. } => Cirru::List(vec![
Cirru::Leaf(name.into()),
Cirru::List(
children
.iter()
.map(|(k, child)| Cirru::List(vec![Cirru::Leaf(k.0.to_owned().into()), (*child).to_owned().into()]))
.collect(),
),
]),
RespoNode::Component(name, _eff, tree) => {
Cirru::List(vec![Cirru::Leaf("::Component".into()), Cirru::Leaf(name.into()), (*tree).into()])
}
RespoNode::Element { name, children, .. } => {
let mut xs = vec![Cirru::Leaf(name.into())];
for (k, child) in children {
xs.push(Cirru::List(vec![Cirru::Leaf(k.to_string().into()), child.to_owned().into()]));
}
Cirru::List(xs)
}
RespoNode::Referenced(cell) => (*cell).to_owned().into(),
}
}
Expand All @@ -63,31 +66,43 @@ where
T: Debug + Clone,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match cirru_parser::format(&[self.to_owned().into()], CirruWriterOptions { use_inline: true }) {
Ok(s) => write!(f, "{}", s),
Err(e) => write!(f, "{}", e),
}
write!(f, "{}", self)
}
}

/// a key for referencing a child node, use a value that can be converted to string
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct RespoIndexKey(String);

impl<T> From<T> for RespoIndexKey
where
T: Display + Clone + Debug,
{
fn from(data: T) -> Self {
impl From<usize> for RespoIndexKey {
fn from(data: usize) -> Self {
Self(data.to_string())
}
}

// impl Display for RespoIndexKey {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "{}", self.0)
// }
// }
impl From<String> for RespoIndexKey {
fn from(s: String) -> Self {
Self(s)
}
}

impl From<&str> for RespoIndexKey {
fn from(s: &str) -> Self {
Self(s.to_owned())
}
}

impl Display for RespoIndexKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

impl From<RespoIndexKey> for Cirru {
fn from(k: RespoIndexKey) -> Cirru {
k.to_string().into()
}
}

impl<T> RespoNode<T>
where
Expand Down Expand Up @@ -306,6 +321,22 @@ where
}
}
}
/// add an empty args effect on component, which does not update
pub fn stable_effect<U>(&mut self, handler: U) -> &mut Self
where
U: Fn(Vec<RespoEffectArg>, RespoEffectType, &Node) -> Result<(), String> + 'static,
{
match self {
RespoNode::Component(_, ref mut effects, _) => {
effects.push(RespoEffect::new(vec![] as Vec<()>, handler));
self
}
RespoNode::Element { .. } => unreachable!("effects are on components"),
RespoNode::Referenced(_) => {
unreachable!("should not be called on a referenced node");
}
}
}
/// add a list of effects on component
pub fn effects<U>(&mut self, more: U) -> &mut Self
where
Expand Down Expand Up @@ -373,6 +404,14 @@ where

pub(crate) type StrDict = HashMap<String, String>;

fn str_dict_to_cirrus_dict(dict: &StrDict) -> Cirru {
let mut xs = vec![];
for (k, v) in dict {
xs.push(vec![k.to_owned(), v.to_owned()].into());
}
Cirru::List(xs)
}

/// (internal) struct to store event handler function on the tree
#[derive(Clone)]
pub struct RespoListenerFn<T>(Rc<dyn Fn(RespoEvent, DispatchFn<T>) -> Result<(), String>>)
Expand Down Expand Up @@ -415,14 +454,6 @@ where
}
}

/// coordinate system on RespoNode, to lookup among elements and components
#[derive(Debug, Clone)]
pub enum RespoCoord {
Key(RespoIndexKey),
/// for indexing by component name, even though there's only one of that
Comp(String),
}

/// marks on virtual DOM to declare that there's an event
/// event handler is HIDDEN from this mark.
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -542,99 +573,17 @@ pub enum RespoEffectType {
BeforeUnmount,
}

/// DOM operations used for diff/patching
/// performance is not optimial since looking up the DOM via dom_path has repetitive operations,
/// might need to fix in future is overhead observed.
#[derive(Debug, Clone)]
pub enum DomChange<T>
where
T: Debug + Clone,
{
ReplaceElement {
coord: Vec<RespoCoord>,
dom_path: Vec<u32>,
node: RespoNode<T>,
},
ModifyChildren {
coord: Vec<RespoCoord>,
dom_path: Vec<u32>,
operations: Vec<ChildDomOp<T>>,
},
ModifyAttrs {
coord: Vec<RespoCoord>,
dom_path: Vec<u32>,
set: StrDict,
unset: HashSet<String>,
},
ModifyStyle {
coord: Vec<RespoCoord>,
dom_path: Vec<u32>,
set: StrDict,
unset: HashSet<String>,
},
ModifyEvent {
coord: Vec<RespoCoord>,
dom_path: Vec<u32>,
add: HashSet<String>,
remove: HashSet<String>,
},
/// this is only part of effects.
/// effects that collected while diffing children are nested inside
Effect {
coord: Vec<RespoCoord>,
dom_path: Vec<u32>,
effect_type: RespoEffectType,
// when args not changed in update, that effects are not re-run
skip_indexes: HashSet<u32>,
},
}

impl<T> DomChange<T>
where
T: Debug + Clone,
{
pub fn get_coord(&self) -> Vec<RespoCoord> {
match self {
DomChange::ReplaceElement { coord, .. } => coord.clone(),
DomChange::ModifyChildren { coord, .. } => coord.clone(),
DomChange::ModifyAttrs { coord, .. } => coord.clone(),
DomChange::ModifyStyle { coord, .. } => coord.clone(),
DomChange::ModifyEvent { coord, .. } => coord.clone(),
DomChange::Effect { coord, .. } => coord.clone(),
}
}
pub fn get_dom_path(&self) -> Vec<u32> {
match self {
DomChange::ReplaceElement { dom_path, .. } => dom_path.clone(),
DomChange::ModifyChildren { dom_path, .. } => dom_path.clone(),
DomChange::ModifyAttrs { dom_path, .. } => dom_path.clone(),
DomChange::ModifyStyle { dom_path, .. } => dom_path.clone(),
DomChange::ModifyEvent { dom_path, .. } => dom_path.clone(),
DomChange::Effect { dom_path, .. } => dom_path.clone(),
impl From<RespoEffectType> for Cirru {
fn from(effect_type: RespoEffectType) -> Self {
match effect_type {
RespoEffectType::Mounted => "::mounted".into(),
RespoEffectType::BeforeUpdate => "::before-update".into(),
RespoEffectType::Updated => "::updated".into(),
RespoEffectType::BeforeUnmount => "::before-unmount".into(),
}
}
}

/// used in list diffing, this is still part of `DomChange`
#[derive(Debug, Clone)]
pub enum ChildDomOp<T>
where
T: Debug + Clone,
{
InsertAfter(u32, RespoIndexKey, RespoNode<T>),
RemoveAt(u32),
Append(RespoIndexKey, RespoNode<T>),
Prepend(RespoIndexKey, RespoNode<T>),
/// order is required in operating children elements, so put effect inside
NestedEffect {
nested_coord: Vec<RespoCoord>,
nested_dom_path: Vec<u32>,
effect_type: RespoEffectType,
// when args not changed in update, that effects are not re-run
skip_indexes: HashSet<u32>,
},
}

/// dispatch function passed from root of renderer,
/// call it like `dispatch.run(op)`
#[derive(Clone)]
Expand Down
Loading

0 comments on commit cfa5fbb

Please sign in to comment.