diff --git a/Cargo.toml b/Cargo.toml index 44d161884..9092e99e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ members = [ # TODO @Jon, share the validation code # "packages/web", # "packages/hooks", - "packages/cli", + # "packages/cli", # "examples", # "packages/html-macro", # "packages/html-macro-2", diff --git a/notes/CHANGELOG.md b/notes/CHANGELOG.md index eaa255047..242208ab0 100644 --- a/notes/CHANGELOG.md +++ b/notes/CHANGELOG.md @@ -28,12 +28,12 @@ # Project: Web_sys renderer (TBD) - [x] WebSys edit interpreter -- [ ] Event system using async channels - +- [x] Event system using async channels +- [ ] Implement conversion of all event types into synthetic events # Project: String Render (TBD) > Implement a light-weight string renderer with basic caching -- [ ] (SSR) Implement stateful 3rd party string renderer - [x] (Macro) Make VText nodes automatically capture and format IE allow "Text is {blah}" +- [ ] (SSR) Implement stateful 3rd party string renderer # Project: Hooks + Context + Subscriptions (TBD) > Implement the foundations for state management @@ -41,12 +41,12 @@ - [x] Implement use_state (rewrite to use the use_reducer api like rei) - [x] Implement use_ref - [x] Implement use_context (only the API, not the state management solution) -- [ ] Implement use_reducer +- [ ] Implement use_reducer (WIP) # Project: QOL > Make it easier to write components -- [x] (Macro) Tweak event syntax to not be dependent on wasm32 target (just return regular closures which get boxed) -- [ ] (Macro) Tweak component syntax to accept a new custom element +- [x] (Macro) Tweak event syntax to not be dependent on wasm32 target (just return regular closures which get boxed/alloced) +- [x] (Macro) Tweak component syntax to accept a new custom element - [ ] (Macro) Allow components to specify their props as function args # Project: Initial VDOM support (TBD) @@ -58,6 +58,6 @@ - [x] (Core) Introduce the VDOM and patch API for 3rd party renderers - [x] (Core) Implement lifecycle - [x] (Core) Implement an event system -- [ ] (Core) Implement child nodes, scope creation +- [x] (Core) Implement child nodes, scope creation - [ ] (Core) Implement dirty tagging and compression diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 429f15eb7..e92f5d2fc 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -36,8 +36,5 @@ log = "0.4.14" serde = { version = "1.0.123", features = ["derive"], optional=true } -# todo, remove -uuid = {version = "0.8.2", features=["serde", "v4"]} - [features] default = [] diff --git a/packages/core/examples/step.rs b/packages/core/examples/step.rs index eb26db781..f4e7a4f1f 100644 --- a/packages/core/examples/step.rs +++ b/packages/core/examples/step.rs @@ -1,10 +1,10 @@ -use dioxus_core::virtual_dom::VirtualDom; +use dioxus_core::debug_renderer::DebugRenderer; use dioxus_core::{component::Properties, prelude::*}; fn main() -> Result<(), ()> { let p1 = SomeProps { name: "bob".into() }; - let _vdom = VirtualDom::new_with_props(Example, p1); + let _vdom = DebugRenderer::new_with_props(Example, p1); Ok(()) } @@ -18,9 +18,6 @@ static Example: FC = |ctx, _props| { ctx.render(html! {

"hello world!"

-

"hello world!"

-

"hello world!"

-

"hello world!"

}) }; diff --git a/packages/core/src/debug_renderer.rs b/packages/core/src/debug_renderer.rs index 68470bbcc..6445b64cb 100644 --- a/packages/core/src/debug_renderer.rs +++ b/packages/core/src/debug_renderer.rs @@ -3,7 +3,7 @@ //! //! Renderers don't actually need to own the virtual dom (it's up to the implementer). -use crate::virtual_dom::VirtualDom; +use crate::{events::EventTrigger, virtual_dom::VirtualDom}; use crate::{innerlude::Result, prelude::*}; pub struct DebugRenderer { @@ -33,6 +33,10 @@ impl DebugRenderer { Self { internal_dom: dom } } + pub fn handle_event(&mut self, trigger: EventTrigger) -> Result<()> { + Ok(()) + } + pub fn step(&mut self, machine: &mut DiffMachine) -> Result<()> { Ok(()) } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 616c4bc6c..9699fe837 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -36,13 +36,13 @@ use crate::{innerlude::*, scope::Scope}; use bumpalo::Bump; use fxhash::{FxHashMap, FxHashSet}; use generational_arena::Arena; -use uuid::Uuid; use std::{ cell::{RefCell, RefMut}, cmp::Ordering, collections::VecDeque, rc::{Rc, Weak}, + sync::atomic::AtomicU32, }; /// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while @@ -66,13 +66,33 @@ pub struct DiffMachine<'a> { pub enum LifeCycleEvent<'a> { Mount { caller: Weak Fn(Context<'r>) -> DomTree + 'a>, - id: Uuid, + scope: Weak, + id: u32, + }, + PropsChanged { + caller: Weak Fn(Context<'r>) -> DomTree + 'a>, + scope: Weak, + id: u32, + }, + SameProps { + caller: Weak Fn(Context<'r>) -> DomTree + 'a>, + scope: Weak, + id: u32, + }, + Replace { + caller: Weak Fn(Context<'r>) -> DomTree + 'a>, + old_scope: Weak, + new_scope: Weak, + id: u32, }, - PropsChanged, - SameProps, Remove, } +static COUNTER: AtomicU32 = AtomicU32::new(1); +fn get_id() -> u32 { + COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed) +} + impl<'a> DiffMachine<'a> { pub fn new() -> Self { Self { @@ -128,12 +148,27 @@ impl<'a> DiffMachine<'a> { } (VNode::Component(cold), VNode::Component(cnew)) => { - todo!("should not happen") - // if cold.caller_ref != cnew.caller_ref { - // // todo: create a stable addr - // self.lifecycle_events.push_back(LifeCycleEvent::Mount); - // return; - // } + // todo!("should not happen") + if cold.user_fc == cnew.user_fc { + // todo: create a stable addr + let caller = Rc::downgrade(&cnew.caller); + let id = cold.stable_addr.borrow().unwrap(); + let scope = Rc::downgrade(&cold.ass_scope); + self.lifecycle_events + .push_back(LifeCycleEvent::PropsChanged { caller, id, scope }); + } else { + let caller = Rc::downgrade(&cnew.caller); + let id = cold.stable_addr.borrow().unwrap(); + let old_scope = Rc::downgrade(&cold.ass_scope); + let new_scope = Rc::downgrade(&cnew.ass_scope); + + self.lifecycle_events.push_back(LifeCycleEvent::Replace { + caller, + id, + old_scope, + new_scope, + }); + } // let comparator = &cnew.comparator.0; // let old_props = cold.raw_props.as_ref(); @@ -151,23 +186,7 @@ impl<'a> DiffMachine<'a> { // todo: knock out any listeners (_, VNode::Component(_new)) => { - // self.lifecycle_events.push_back(LifeCycleEvent::Mount); - // we have no stable reference to work from - // push the lifecycle event onto the queue - // self.lifecycle_events - // .borrow_mut() - // .push_back(LifecycleEvent { - // event_type: LifecycleType::Mount { - // props: new.props, - // to: self.cur_idx, - // }, - // }); - // we need to associaote this new component with a scope... - - // self.change_list.save_known_root(id) self.change_list.commit_traversal(); - - // push the current } (VNode::Component(_old), _) => { @@ -242,36 +261,18 @@ impl<'a> DiffMachine<'a> { /* todo: integrate re-entrace */ - // NodeKind::Cached(ref c) => { - // cached_roots.insert(c.id); - // let (node, template) = cached_set.get(c.id); - // if let Some(template) = template { - // create_with_template( - // cached_set, - // self.change_list, - // registry, - // template, - // node, - // cached_roots, - // ); - // } else { - // create(cached_set, change_list, registry, node, cached_roots); - // } - // } VNode::Component(component) => { self.change_list .create_text_node("placeholder for vcomponent"); - let id = uuid::Uuid::new_v4(); + + let id = get_id(); *component.stable_addr.as_ref().borrow_mut() = Some(id); self.change_list.save_known_root(id); - - let caller = Rc::downgrade(&component.caller); - // let broken_caller: Weak DomTree + 'static> = - // unsafe { std::mem::transmute(caller) }; + let scope = Rc::downgrade(&component.ass_scope); self.lifecycle_events.push_back(LifeCycleEvent::Mount { - caller, - // caller: broken_caller, + caller: Rc::downgrade(&component.caller), id, + scope }); } VNode::Suspended => { diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 6bd71bd4b..bee22d390 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -3,7 +3,7 @@ //! 3rd party renderers are responsible for forming this virtual events from events. //! The goal here is to provide a consistent event interface across all renderer types. //! -//! also... websys is kinda bad for rust-analyzer so we coerce for you automatically :) +//! also... websys integerates poorly with rust analyzer, so we handle that for you automatically. use crate::innerlude::ScopeIdx; diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 0d0a88b85..479fd27a3 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -88,8 +88,8 @@ impl<'a> VNode<'a> { VNode::Suspended => { todo!() } - VNode::Component(_) => { - todo!() + VNode::Component(c) => { + c.key } } } @@ -107,27 +107,6 @@ pub struct VElement<'a> { pub attributes: &'a [Attribute<'a>], pub children: &'a [VNode<'a>], pub namespace: Option<&'a str>, - // The HTML tag, such as "div" - // pub tag: &'a str, - - // pub tag_name: &'a str, - // pub attributes: &'a [Attribute<'a>], - // todo: hook up listeners - // pub listeners: &'a [Listener<'a>], - // / HTML attributes such as id, class, style, etc - // pub attrs: HashMap, - // TODO: @JON Get this to not heap allocate, but rather borrow - // pub attrs: HashMap<&'static str, &'static str>, - - // TODO @Jon, re-enable "events" - // - // /// Events that will get added to your real DOM element via `.addEventListener` - // pub events: Events, - // pub events: HashMap, - - // /// The children of this `VNode`. So a
structure would - // /// have a parent div and one child, em. - // pub children: Vec, } impl<'a> VElement<'a> { @@ -264,10 +243,14 @@ impl<'a> VText<'a> { /// Virtual Components for custom user-defined components /// Only supports the functional syntax -pub type StableScopeAddres = Rc>>; +pub type StableScopeAddres = RefCell>; +pub type VCompAssociatedScope = RefCell>; pub struct VComponent<'src> { - pub stable_addr: StableScopeAddres, + pub key: NodeKey, + + pub stable_addr: Rc, + pub ass_scope: Rc, pub comparator: Rc bool + 'src>, pub caller: Rc Fn(Context<'r>) -> DomTree + 'src>, @@ -276,28 +259,15 @@ pub struct VComponent<'src> { raw_props: *const (), // a pointer to the raw fn typ - caller_ref: *const (), + pub user_fc: *const (), _p: PhantomData<&'src ()>, } impl std::fmt::Debug for VComponent<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - todo!() + Ok(()) } } -// pub struct Comparator(pub Rc bool>); -// impl std::fmt::Debug for Comparator { -// fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// Ok(()) -// } -// } - -// pub struct Caller(pub Rc DomTree>); -// impl std::fmt::Debug for Caller { -// fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// Ok(()) -// } -// } impl<'a> VComponent<'a> { // use the type parameter on props creation and move it into a portable context @@ -318,7 +288,7 @@ impl<'a> VComponent<'a> { // Therefore, if the render functions are identical (by address), then so will be // props type paramter (because it is the same render function). Therefore, we can be // sure - if caller_ref == other.caller_ref { + if caller_ref == other.user_fc { let real_other = unsafe { &*(other.raw_props as *const _ as *const P) }; real_other == props } else { @@ -333,7 +303,9 @@ impl<'a> VComponent<'a> { }; Self { - caller_ref, + key: NodeKey::NONE, + ass_scope: Rc::new(RefCell::new(None)), + user_fc: caller_ref, raw_props: props as *const P as *const _, _p: PhantomData, caller: Rc::new(caller), diff --git a/packages/core/src/patch.rs b/packages/core/src/patch.rs index 05b653ce9..caeb9ea9f 100644 --- a/packages/core/src/patch.rs +++ b/packages/core/src/patch.rs @@ -94,12 +94,12 @@ pub enum Edit<'src_bump> { // ======================================================== // push a known node on to the stack TraverseToKnown { - node: uuid::Uuid, + node: u32, // node: ScopeIdx, }, // Add the current top of the stack to the known nodes MakeKnown { - node: uuid::Uuid, + node: u32, // node: ScopeIdx, }, // Remove the current top of the stack from the known nodes @@ -383,32 +383,16 @@ impl<'a> EditMachine<'a> { // debug!("emit: remove_event_listener({:?})", event); } - pub fn save_known_root(&mut self, id: uuid::Uuid) { - // pub fn save_known_root(&mut self, id: ScopeIdx) { - // pub fn save_known_root(&mut self, id: ScopeIdx) { + pub fn save_known_root(&mut self, id: u32) { log::debug!("emit: save_known_root({:?})", id); self.emitter.push(Edit::MakeKnown { node: id }) } - pub fn load_known_root(&mut self, id: uuid::Uuid) { + pub fn load_known_root(&mut self, id: u32) { log::debug!("emit: TraverseToKnown({:?})", id); self.emitter.push(Edit::TraverseToKnown { node: id }) } - // pub fn save_template(&mut self, id: CacheId) { - // debug_assert!(self.traversal_is_committed()); - // debug_assert!(!self.has_template(id)); - // // debug!("emit: save_template({:?})", id); - // self.templates.insert(id); - // self.emitter.save_template(id.into()); - // } - - // pub fn push_template(&mut self, id: CacheId) { - // debug_assert!(self.traversal_is_committed()); - // debug_assert!(self.has_template(id)); - // // debug!("emit: push_template({:?})", id); - // self.emitter.push_template(id.into()); - // } } // Keeps track of where we are moving in a DOM tree, and shortens traversal diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 31f38416e..8eed046f5 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -96,10 +96,11 @@ impl Scope { scope: self.myidx, listeners: &self.listeners, }; + let caller = self.caller.upgrade().expect("Failed to get caller"); - todo!() + // todo!() // Note that the actual modification of the vnode head element occurs during this call - // let _: DomTree = (self.caller.0.as_ref())(ctx); + let _: DomTree = (caller.as_ref())(ctx); // let _: DomTree = (self.raw_caller)(ctx, &self.props); /* @@ -114,11 +115,13 @@ impl Scope { - Public API cannot drop or destructure VNode */ - // frame.head_node = node_slot - // .deref() - // .borrow_mut() - // .take() - // .expect("Viewing did not happen"); + frame.head_node = node_slot.as_ref() + // .deref() + .borrow_mut() + .take() + .expect("Viewing did not happen"); + + Ok(()) } // A safe wrapper around calling listeners diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index e7154ce68..927767695 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -3,10 +3,7 @@ use crate::{error::Error, innerlude::*}; use crate::{patch::Edit, scope::Scope}; use generational_arena::Arena; -use std::{ - any::TypeId, - rc::{Rc, Weak}, -}; +use std::{any::TypeId, borrow::Borrow, rc::{Rc, Weak}}; use thiserror::private::AsDynError; // We actually allocate the properties for components in their parent's properties @@ -82,19 +79,19 @@ impl VirtualDom { .ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?; component.run_scope()?; - let component = &*component; + // let component = &*component; - // // get raw pointer to the arena - // let very_unsafe_components = &mut self.components as *mut generational_arena::Arena; + // get raw pointer to the arena + let very_unsafe_components = &mut self.components as *mut generational_arena::Arena; - // { - // let component = self - // .components - // .get(self.base_scope) - // .ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))? + { + let component = self + .components + .get(self.base_scope) + .ok_or_else(|| Error::FatalInternal("Acquring base component should never fail"))?; - // diff_machine.diff_node(component.old_frame(), component.new_frame()); - // } + diff_machine.diff_node(component.old_frame(), component.new_frame()); + } // let p = &mut self.components; // chew down the the lifecycle events until all dirty nodes are computed while let Some(event) = diff_machine.lifecycle_events.pop_front() { @@ -102,31 +99,48 @@ impl VirtualDom { // A new component has been computed from the diffing algorithm // create a new component in the arena, run it, move the diffing machine to this new spot, and then diff it // this will flood the lifecycle queue with new updates - LifeCycleEvent::Mount { caller, id } => { + LifeCycleEvent::Mount { caller, id, scope } => { log::debug!("Mounting a new component"); - diff_machine.change_list.load_known_root(id); // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas. // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime. unsafe { - // let p = &mut *(very_unsafe_components); - // let p = &mut self.components; + let p = &mut *(very_unsafe_components); // todo, hook up the parent/child indexes properly - let idx = self.components.insert_with(|f| Scope::new(caller, f, None)); - let c = self.components.get_mut(idx).unwrap(); - // let idx = p.insert_with(|f| Scope::new(caller, f, None)); - // let c = p.get_mut(idx).unwrap(); - c.run_scope(); - // diff_machine.diff_node(c.old_frame(), c.new_frame()); + let idx = p.insert_with(|f| Scope::new(caller, f, None)); + let c = p.get_mut(idx).unwrap(); + + let real_scope = scope.upgrade().unwrap(); + *real_scope.as_ref().borrow_mut() = Some(idx); + c.run_scope()?; + + diff_machine.change_list.load_known_root(id); + + diff_machine.diff_node(c.old_frame(), c.new_frame()); } } - LifeCycleEvent::PropsChanged => { - // + LifeCycleEvent::PropsChanged { caller, id, scope } => { + let idx = scope.upgrade().unwrap().as_ref().borrow().unwrap(); + + // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children + // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas. + // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime. + unsafe { + let p = &mut *(very_unsafe_components); + // todo, hook up the parent/child indexes properly + // let idx = p.insert_with(|f| Scope::new(caller, f, None)); + let c = p.get_mut(idx).unwrap(); + c.run_scope()?; + + diff_machine.change_list.load_known_root(id); + + diff_machine.diff_node(c.old_frame(), c.new_frame()); + } // break 'render; } - LifeCycleEvent::SameProps => { + LifeCycleEvent::SameProps { caller, id, scope } => { // // break 'render; } @@ -134,6 +148,7 @@ impl VirtualDom { // // break 'render; } + LifeCycleEvent::Replace { caller, id, .. } => {} } // } else { @@ -176,13 +191,14 @@ impl VirtualDom { .expect("Borrowing should not fail"); component.call_listener(event); - component.run_scope(); + component.run_scope()?; let mut diff_machine = DiffMachine::new(); // let mut diff_machine = DiffMachine::new(&self.diff_bump); - diff_machine.diff_node(component.old_frame(), component.new_frame()); + // diff_machine.diff_node(component.old_frame(), component.new_frame()); - Ok(diff_machine.consume()) + // Ok(diff_machine.consume()) + self.rebuild() } } diff --git a/packages/html-macro-test/Cargo.toml b/packages/html-macro-test/Cargo.toml deleted file mode 100644 index 318551fff..000000000 --- a/packages/html-macro-test/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "html-macro-test" -version = "0.1.0" -authors = ["Chinedu Francis Nwafili "] -edition = "2018" - -[dependencies] -html-macro = {path = "../html-macro"} -virtual-dom-rs = { path = "../virtual-dom-rs" } -virtual-node = {path = "../virtual-node"} -trybuild = "1.0" diff --git a/packages/html-macro-test/README.md b/packages/html-macro-test/README.md deleted file mode 100644 index f153294c8..000000000 --- a/packages/html-macro-test/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# html-macro-test - -Unit tests for the `html!` macro diff --git a/packages/html-macro-test/src/lib.rs b/packages/html-macro-test/src/lib.rs deleted file mode 100644 index 45369ca9b..000000000 --- a/packages/html-macro-test/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Tests for our html! procedural macro -//! -//! To run all tests in this library: -//! -//! cargo test --color=always --package html-macro-test --lib "" -- --nocapture - -// #![feature(proc_macro_hygiene)] - -// TODO: Deny warnings to ensure that the macro isn't creating any warnings. -// #![deny(warnings)] - -#[cfg(test)] -mod tests; diff --git a/packages/html-macro-test/src/tests.rs b/packages/html-macro-test/src/tests.rs deleted file mode 100644 index bc0747c59..000000000 --- a/packages/html-macro-test/src/tests.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod all_tests; -mod text; -mod ui; diff --git a/packages/html-macro-test/src/tests/all_tests.rs b/packages/html-macro-test/src/tests/all_tests.rs deleted file mode 100644 index 21d2104ce..000000000 --- a/packages/html-macro-test/src/tests/all_tests.rs +++ /dev/null @@ -1,377 +0,0 @@ -//! This is a catch-all module to place new tests as we go. -//! -//! Over time we'll pull tests out of here and organize them. -//! -//! For example - there is a `text_tests.rs` module where all of our text node related -//! tests live. - -use html_macro::html; -use std::collections::HashMap; -use virtual_node::{IterableNodes, VElement, VText, View, VirtualNode}; - -struct HtmlMacroTest { - generated: VirtualNode, - expected: VirtualNode, -} - -impl HtmlMacroTest { - /// Ensure that the generated and the expected virtual node are equal. - fn test(self) { - assert_eq!(self.expected, self.generated); - } -} - -#[test] -fn empty_div() { - HtmlMacroTest { - generated: html! {
}, - expected: VirtualNode::element("div"), - } - .test(); -} - -#[test] -fn one_attr() { - let mut attrs = HashMap::new(); - attrs.insert("id".to_string(), "hello-world".to_string()); - let mut expected = VElement::new("div"); - expected.attrs = attrs; - - HtmlMacroTest { - generated: html! {
}, - expected: expected.into(), - } - .test(); -} - -/// Events are ignored in non wasm-32 targets -#[test] -fn ignore_events_on_non_wasm32_targets() { - HtmlMacroTest { - generated: html! { -
- }, - expected: html! {
}, - } - .test(); -} - -#[test] -fn child_node() { - let mut expected = VElement::new("div"); - expected.children = vec![VirtualNode::element("span")]; - - HtmlMacroTest { - generated: html! {
}, - expected: expected.into(), - } - .test(); -} - -#[test] -fn sibling_child_nodes() { - let mut expected = VElement::new("div"); - expected.children = vec![VirtualNode::element("span"), VirtualNode::element("b")]; - - HtmlMacroTest { - generated: html! {
}, - expected: expected.into(), - } - .test(); -} - -/// Nested 3 nodes deep -#[test] -fn three_nodes_deep() { - let mut child = VElement::new("span"); - child.children = vec![VirtualNode::element("b")]; - - let mut expected = VElement::new("div"); - expected.children = vec![child.into()]; - - HtmlMacroTest { - generated: html! {
}, - expected: expected.into(), - } - .test() -} - -#[test] -fn sibling_text_nodes() { - let mut expected = VElement::new("div"); - expected.children = vec![VirtualNode::text("This is a text node")]; - - HtmlMacroTest { - generated: html! {
This is a text node
}, - expected: expected.into(), - } - .test(); -} - -#[test] -fn nested_macro() { - let child_2 = html! { }; - - let mut expected = VElement::new("div"); - expected.children = vec![VirtualNode::element("span"), VirtualNode::element("b")]; - - HtmlMacroTest { - generated: html! { -
- { html! { } } - { child_2 } -
- }, - expected: expected.into(), - } - .test(); -} - -/// If the first thing we see is a block then we grab whatever is inside it. -#[test] -fn block_root() { - let em = html! { }; - - let expected = VirtualNode::element("em"); - - HtmlMacroTest { - generated: html! { - { em } - }, - expected, - } - .test(); -} - -/// Text followed by a block -#[test] -fn text_next_to_block() { - let child = html! {
    }; - - let mut expected = VElement::new("div"); - expected.children = vec![ - VirtualNode::text(" A bit of text "), - VirtualNode::element("ul"), - ]; - - HtmlMacroTest { - generated: html! { -
    - A bit of text - { child } -
    - }, - expected: expected.into(), - } - .test(); -} - -/// Ensure that we maintain the correct spacing around punctuation tokens, since -/// they resolve into a separate TokenStream during parsing. -#[test] -fn punctuation_token() { - let text = "Hello, World"; - - HtmlMacroTest { - generated: html! { Hello, World }, - expected: VirtualNode::text(text), - } - .test() -} - -#[test] -fn vec_of_nodes() { - let children = vec![html! {
    }, html! { }]; - - let mut expected = VElement::new("div"); - expected.children = vec![VirtualNode::element("div"), VirtualNode::element("strong")]; - - HtmlMacroTest { - generated: html! {
    { children }
    }, - expected: expected.into(), - } - .test(); -} - -/// Just make sure that this compiles since async, for, loop, and type are keywords -#[test] -fn keyword_attribute() { - html! {