Sfoglia il codice sorgente

Merge pull request #272 from DioxusLabs/jk/documet-everything

docs: add `deny(missing_docs)` is many more places
Jonathan Kelley 3 anni fa
parent
commit
2181ccd80f

+ 2 - 2
packages/core-macro/src/rsx/mod.rs

@@ -79,7 +79,7 @@ impl ToTokens for CallBody {
         match &self.custom_context {
             // The `in cx` pattern allows directly rendering
             Some(ident) => out_tokens.append_all(quote! {
-                #ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
+                #ident.render(LazyNodes::new(move |__cx: NodeFactory| -> VNode {
                     use dioxus_elements::{GlobalAttributes, SvgAttributes};
                     #inner
                 }))
@@ -87,7 +87,7 @@ impl ToTokens for CallBody {
 
             // Otherwise we just build the LazyNode wrapper
             None => out_tokens.append_all(quote! {
-                LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
+                LazyNodes::new(move |__cx: NodeFactory| -> VNode {
                     use dioxus_elements::{GlobalAttributes, SvgAttributes};
                     #inner
                 })

+ 1 - 9
packages/core/src/diff.rs

@@ -257,11 +257,10 @@ impl<'b> DiffState<'b> {
         vcomponent.scope.set(Some(new_idx));
 
         log::trace!(
-            "created component \"{}\", id: {:?} parent {:?} orig: {:?}",
+            "created component \"{}\", id: {:?} parent {:?}",
             vcomponent.fn_name,
             new_idx,
             parent_idx,
-            vcomponent.originator
         );
 
         // if vcomponent.can_memoize {
@@ -957,13 +956,6 @@ impl<'b> DiffState<'b> {
                     let props = scope.props.take().unwrap();
                     c.props.borrow_mut().replace(props);
                     self.scopes.try_remove(scope_id).unwrap();
-
-                    // // we can only remove components if they are actively being diffed
-                    // if self.scope_stack.contains(&c.originator) {
-                    //     log::trace!("Removing component {:?}", old);
-
-                    //     self.scopes.try_remove(scope_id).unwrap();
-                    // }
                 }
                 self.leave_scope();
             }

+ 22 - 5
packages/core/src/events.rs

@@ -119,24 +119,41 @@ pub enum EventPriority {
     Low = 0,
 }
 
+/// The internal Dioxus type that carries any event data to the relevant handler.
+///
+///
+///
+///
+///
 pub struct AnyEvent {
     pub(crate) bubble_state: Rc<BubbleState>,
     pub(crate) data: Arc<dyn Any + Send + Sync>,
 }
 
 impl AnyEvent {
+    /// Convert this AnyEvent into a specific UiEvent with EventData.
+    ///
+    /// ```rust, ignore
+    /// let evt: FormEvent = evvt.downcast().unwrap();
+    /// ```
     pub fn downcast<T: Send + Sync + 'static>(self) -> Option<UiEvent<T>> {
         let AnyEvent { data, bubble_state } = self;
 
-        if let Ok(data) = data.downcast::<T>() {
-            Some(UiEvent { bubble_state, data })
-        } else {
-            None
-        }
+        data.downcast::<T>()
+            .ok()
+            .map(|data| UiEvent { bubble_state, data })
     }
 }
 
+/// A UiEvent is a type that wraps various EventData.
+///
+/// You should prefer to use the name of the event directly, rather than
+/// the UiEvent<T> generic type.
+///
+/// For the HTML crate, this would include [`MouseEvent`], [`FormEvent`] etc.
 pub struct UiEvent<T> {
+    /// The internal data of the event
+    /// This is wrapped in an Arc so that it can be sent across threads
     pub data: Arc<T>,
 
     #[allow(unused)]

+ 34 - 28
packages/core/src/lazynodes.rs

@@ -35,32 +35,36 @@ enum StackNodeStorage<'a, 'b> {
 }
 
 impl<'a, 'b> LazyNodes<'a, 'b> {
-    pub fn new_some<F>(_val: F) -> Self
-    where
-        F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
-    {
-        Self::new(_val)
-    }
-
-    /// force this call onto the stack
-    pub fn new_boxed<F>(_val: F) -> Self
-    where
-        F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
-    {
+    /// Create a new LazyNodes closure, optimistically placing it onto the stack.
+    ///
+    /// If the closure cannot fit into the stack allocation (16 bytes), then it
+    /// is placed on the heap. Most closures will fit into the stack, and is
+    /// the most optimal way to use the creation function.
+    pub fn new(val: impl FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b) -> Self {
         // there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
-        let mut slot = Some(_val);
+        let mut slot = Some(val);
 
         let val = move |fac: Option<NodeFactory<'a>>| {
             let inner = slot.take().unwrap();
             fac.map(inner)
         };
 
-        Self {
-            inner: StackNodeStorage::Heap(Box::new(val)),
+        // miri does not know how to work with mucking directly into bytes
+        // just use a heap allocated type when miri is running
+        if cfg!(miri) {
+            Self {
+                inner: StackNodeStorage::Heap(Box::new(val)),
+            }
+        } else {
+            unsafe { LazyNodes::new_inner(val) }
         }
     }
 
-    pub fn new(_val: impl FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b) -> Self {
+    /// Create a new LazyNodes closure, but force it onto the heap.
+    pub fn new_boxed<F>(_val: F) -> Self
+    where
+        F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
+    {
         // there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
         let mut slot = Some(_val);
 
@@ -69,13 +73,8 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
             fac.map(inner)
         };
 
-        // miri does not know how to work with mucking directly into bytes
-        if cfg!(miri) {
-            Self {
-                inner: StackNodeStorage::Heap(Box::new(val)),
-            }
-        } else {
-            unsafe { LazyNodes::new_inner(val) }
+        Self {
+            inner: StackNodeStorage::Heap(Box::new(val)),
         }
     }
 
@@ -153,6 +152,15 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
         }
     }
 
+    /// Call the closure with the given factory to produce real VNodes.
+    ///
+    /// ```rust, ignore
+    /// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
+    ///
+    /// let fac = NodeFactory::new(&cx);
+    ///
+    /// let node = f.call(cac);
+    /// ```
     pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
         match self.inner {
             StackNodeStorage::Heap(mut lazy) => lazy(Some(f)).unwrap(),
@@ -245,9 +253,7 @@ mod tests {
     #[test]
     fn it_works() {
         fn app(cx: Scope<()>) -> Element {
-            cx.render(LazyNodes::new_some(|f| {
-                f.text(format_args!("hello world!"))
-            }))
+            cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!"))))
         }
 
         let mut dom = VirtualDom::new(app);
@@ -280,7 +286,7 @@ mod tests {
                     .map(|i| {
                         let val = cx.props.inner.clone();
 
-                        LazyNodes::new_some(move |f| {
+                        LazyNodes::new(move |f| {
                             log::debug!("hell closure");
                             let inner = DropInner { id: i };
                             f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
@@ -288,7 +294,7 @@ mod tests {
                     })
                     .collect::<Vec<_>>();
 
-                LazyNodes::new_some(|f| {
+                LazyNodes::new(|f| {
                     log::debug!("main closure");
                     f.fragment_from_iter(it)
                 })

+ 4 - 0
packages/core/src/lib.rs

@@ -1,5 +1,6 @@
 #![allow(non_snake_case)]
 #![doc = include_str!("../README.md")]
+#![deny(missing_docs)]
 
 pub(crate) mod diff;
 pub(crate) mod events;
@@ -76,6 +77,9 @@ pub use crate::innerlude::{
     VElement, VFragment, VNode, VPlaceholder, VText, VirtualDom,
 };
 
+/// The purpose of this module is to alleviate imports of many common types
+///
+/// This includes types like [`Scope`], [`Element`], and [`Component`].
 pub mod prelude {
     pub use crate::innerlude::{
         fc_to_builder, Attributes, Component, DioxusElement, Element, EventHandler, Fragment,

+ 86 - 10
packages/core/src/mutations.rs

@@ -16,8 +16,13 @@ use std::{any::Any, fmt::Debug};
 ///
 /// Mutations are the only link between the RealDOM and the VirtualDOM.
 pub struct Mutations<'a> {
+    /// The list of edits that need to be applied for the RealDOM to match the VirtualDOM.
     pub edits: Vec<DomEdit<'a>>,
+
+    /// The list of Scopes that were diffed, created, and removed during the Diff process.
     pub dirty_scopes: FxHashSet<ScopeId>,
+
+    /// The list of nodes to connect to the RealDOM.
     pub refs: Vec<NodeRefMutation<'a>>,
 }
 
@@ -39,74 +44,145 @@ impl Debug for Mutations<'_> {
     serde(tag = "type")
 )]
 pub enum DomEdit<'bump> {
+    /// Push the given root node onto our stack.
     PushRoot {
+        /// The ID of the root node to push.
         root: u64,
     },
 
+    /// Pop the topmost node from our stack and append them to the node
+    /// at the top of the stack.
     AppendChildren {
+        /// How many nodes should be popped from the stack.
+        /// The node remaining on the stack will be the target for the append.
         many: u32,
     },
 
-    // // save a possibly-fragment node as a template
-    // SaveAsTemplate {
-    //     many: u32,
-    // },
-
-    // "Root" refers to the item directly
-    // it's a waste of an instruction to push the root directly
+    /// Replace a given (single) node with a handful of nodes currently on the stack.
     ReplaceWith {
+        /// The ID of the node to be replaced.
         root: u64,
+
+        /// How many nodes should be popped from the stack to replace the target node.
         m: u32,
     },
+
+    /// Insert a number of nodes after a given node.
     InsertAfter {
+        /// The ID of the node to insert after.
         root: u64,
+
+        /// How many nodes should be popped from the stack to insert after the target node.
         n: u32,
     },
+
+    /// Insert a number of nodes before a given node.
     InsertBefore {
+        /// The ID of the node to insert before.
         root: u64,
+
+        /// How many nodes should be popped from the stack to insert before the target node.
         n: u32,
     },
+
+    /// Remove a particular node from the DOM
     Remove {
+        /// The ID of the node to remove.
         root: u64,
     },
+
+    /// Create a new purely-text node
     CreateTextNode {
-        text: &'bump str,
+        /// The ID the new node should have.
         root: u64,
+
+        /// The textcontent of the node
+        text: &'bump str,
     },
+
+    /// Create a new purely-element node
     CreateElement {
-        tag: &'bump str,
+        /// The ID the new node should have.
         root: u64,
+
+        /// The tagname of the node
+        tag: &'bump str,
     },
+
+    /// Create a new purely-comment node with a given namespace
     CreateElementNs {
-        tag: &'bump str,
+        /// The ID the new node should have.
         root: u64,
+
+        /// The namespace of the node
+        tag: &'bump str,
+
+        /// The namespace of the node (like `SVG`)
         ns: &'static str,
     },
+
+    /// Create a new placeholder node.
+    /// In most implementations, this will either be a hidden div or a comment node.
     CreatePlaceholder {
+        /// The ID the new node should have.
         root: u64,
     },
+
+    /// Create a new Event Listener.
     NewEventListener {
+        /// The name of the event to listen for.
         event_name: &'static str,
+
+        /// The ID of the node to attach the listener to.
         scope: ScopeId,
+
+        /// The ID of the node to attach the listener to.
         root: u64,
     },
+
+    /// Remove an existing Event Listener.
     RemoveEventListener {
+        /// The ID of the node to remove.
         root: u64,
+
+        /// The name of the event to remove.
         event: &'static str,
     },
+
+    /// Set the textcontent of a node.
     SetText {
+        /// The ID of the node to set the textcontent of.
         root: u64,
+
+        /// The textcontent of the node
         text: &'bump str,
     },
+
+    /// Set the value of a node's attribute.
     SetAttribute {
+        /// The ID of the node to set the attribute of.
         root: u64,
+
+        /// The name of the attribute to set.
         field: &'static str,
+
+        /// The value of the attribute.
         value: &'bump str,
+
+        /// The (optional) namespace of the attribute.
+        /// For instance, "style" is in the "style" namespace.
         ns: Option<&'bump str>,
     },
+
+    /// Remove an attribute from a node.
     RemoveAttribute {
+        /// The ID of the node to remove.
         root: u64,
+
+        /// The name of the attribute to remove.
         name: &'static str,
+
+        /// The namespace of the attribute.
         ns: Option<&'bump str>,
     },
 }

+ 80 - 13
packages/core/src/nodes.rs

@@ -148,7 +148,7 @@ impl<'src> VNode<'src> {
     }
 
     // Create an "owned" version of the vnode.
-    pub fn decouple(&self) -> VNode<'src> {
+    pub(crate) fn decouple(&self) -> VNode<'src> {
         match *self {
             VNode::Text(t) => VNode::Text(t),
             VNode::Element(e) => VNode::Element(e),
@@ -181,7 +181,6 @@ impl Debug for VNode<'_> {
                 .field("fnptr", &comp.user_fc)
                 .field("key", &comp.key)
                 .field("scope", &comp.scope)
-                .field("originator", &comp.originator)
                 .finish(),
         }
     }
@@ -200,6 +199,7 @@ impl std::fmt::Display for ElementId {
 }
 
 impl ElementId {
+    /// Convertt the ElementId to a `u64`.
     pub fn as_u64(self) -> u64 {
         self.0 as u64
     }
@@ -211,18 +211,26 @@ fn empty_cell() -> Cell<Option<ElementId>> {
 
 /// A placeholder node only generated when Fragments don't have any children.
 pub struct VPlaceholder {
+    /// The [`ElementId`] of the placeholder.
     pub id: Cell<Option<ElementId>>,
 }
 
 /// A bump-allocated string slice and metadata.
 pub struct VText<'src> {
-    pub text: &'src str,
+    /// The [`ElementId`] of the VText.
     pub id: Cell<Option<ElementId>>,
+
+    /// The text of the VText.
+    pub text: &'src str,
+
+    /// An indiciation if this VText can be ignored during diffing
+    /// Is usually only when there are no strings to be formatted (so the text is &'static str)
     pub is_static: bool,
 }
 
 /// A list of VNodes with no single root.
 pub struct VFragment<'src> {
+    /// The key of the fragment to be used during keyed diffing.
     pub key: Option<&'src str>,
 
     /// Fragments can never have zero children. Enforced by NodeFactory.
@@ -233,13 +241,34 @@ pub struct VFragment<'src> {
 
 /// An element like a "div" with children, listeners, and attributes.
 pub struct VElement<'a> {
+    /// The [`ElementId`] of the VText.
+    pub id: Cell<Option<ElementId>>,
+
+    /// The key of the element to be used during keyed diffing.
+    pub key: Option<&'a str>,
+
+    /// The tag name of the element.
+    ///
+    /// IE "div"
     pub tag: &'static str,
+
+    /// The namespace of the VElement
+    ///
+    /// IE "svg"
     pub namespace: Option<&'static str>,
-    pub key: Option<&'a str>,
-    pub id: Cell<Option<ElementId>>,
+
+    /// The parent of the Element (if any).
+    ///
+    /// Used when bubbling events
     pub parent: Cell<Option<ElementId>>,
+
+    /// The Listeners of the VElement.
     pub listeners: &'a [Listener<'a>],
+
+    /// The attributes of the VElement.
     pub attributes: &'a [Attribute<'a>],
+
+    /// The children of the VElement.
     pub children: &'a [VNode<'a>],
 }
 
@@ -275,12 +304,19 @@ impl Debug for VElement<'_> {
 /// };
 /// ```
 pub trait DioxusElement {
+    /// The tag name of the element.
     const TAG_NAME: &'static str;
+
+    /// The namespace of the element.
     const NAME_SPACE: Option<&'static str>;
+
+    /// The tag name of the element.
     #[inline]
     fn tag_name(&self) -> &'static str {
         Self::TAG_NAME
     }
+
+    /// The namespace of the element.
     #[inline]
     fn namespace(&self) -> Option<&'static str> {
         Self::NAME_SPACE
@@ -291,16 +327,25 @@ pub trait DioxusElement {
 /// `href="https://example.com"`.
 #[derive(Clone, Debug)]
 pub struct Attribute<'a> {
+    /// The name of the attribute.
     pub name: &'static str,
 
+    /// The value of the attribute.
     pub value: &'a str,
 
+    /// An indication if this attribute can be ignored during diffing
+    ///
+    /// Usually only when there are no strings to be formatted (so the value is &'static str)
     pub is_static: bool,
 
+    /// An indication of we should always try and set the attribute.
+    /// Used in controlled components to ensure changes are propagated.
     pub is_volatile: bool,
 
-    // Doesn't exist in the html spec.
-    // Used in Dioxus to denote "style" tags.
+    /// The namespace of the attribute.
+    ///
+    /// Doesn't exist in the html spec.
+    /// Used in Dioxus to denote "style" tags and other attribute groups.
     pub namespace: Option<&'static str>,
 }
 
@@ -353,6 +398,8 @@ type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
 ///
 /// ```
 pub struct EventHandler<'bump, T = ()> {
+    /// The (optional) callback that the user specified
+    /// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
     pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
 }
 
@@ -381,12 +428,23 @@ impl<T> EventHandler<'_, T> {
 /// Virtual Components for custom user-defined components
 /// Only supports the functional syntax
 pub struct VComponent<'src> {
+    /// The key of the component to be used during keyed diffing.
     pub key: Option<&'src str>,
-    pub originator: ScopeId,
+
+    /// The ID of the component.
+    /// Will not be assigned until after the component has been initialized.
     pub scope: Cell<Option<ScopeId>>,
+
+    /// An indication if the component is static (can be memozied)
     pub can_memoize: bool,
+
+    /// The function pointer to the component's render function.
     pub user_fc: ComponentPtr,
+
+    /// The actual name of the component.
     pub fn_name: &'static str,
+
+    /// The props of the component.
     pub props: RefCell<Option<Box<dyn AnyProps + 'src>>>,
 }
 
@@ -434,6 +492,7 @@ pub struct NodeFactory<'a> {
 }
 
 impl<'a> NodeFactory<'a> {
+    /// Create a new [`NodeFactory`] from a [`Scope`] or [`ScopeState`]
     pub fn new(scope: &'a ScopeState) -> NodeFactory<'a> {
         NodeFactory {
             scope,
@@ -441,6 +500,7 @@ impl<'a> NodeFactory<'a> {
         }
     }
 
+    /// Get the custom alloactor for this component
     #[inline]
     pub fn bump(&self) -> &'a bumpalo::Bump {
         self.bump
@@ -482,6 +542,7 @@ impl<'a> NodeFactory<'a> {
         }))
     }
 
+    /// Create a new [`VNode::VElement`]
     pub fn element(
         &self,
         el: impl DioxusElement,
@@ -500,6 +561,9 @@ impl<'a> NodeFactory<'a> {
         )
     }
 
+    /// Create a new [`VNode::VElement`] without the trait bound
+    ///
+    /// IE pass in "div" instead of `div`
     pub fn raw_element(
         &self,
         tag_name: &'static str,
@@ -529,6 +593,7 @@ impl<'a> NodeFactory<'a> {
         }))
     }
 
+    /// Create a new [`Attribute`]
     pub fn attr(
         &self,
         name: &'static str,
@@ -546,6 +611,7 @@ impl<'a> NodeFactory<'a> {
         }
     }
 
+    /// Create a new [`VNode::VComponent`]
     pub fn component<P>(
         &self,
         component: fn(Scope<'a, P>) -> Element,
@@ -561,11 +627,8 @@ impl<'a> NodeFactory<'a> {
             scope: Default::default(),
             can_memoize: P::IS_STATIC,
             user_fc: component as ComponentPtr,
-            originator: self.scope.scope_id(),
             fn_name,
             props: RefCell::new(Some(Box::new(VComponentProps {
-                // local_props: RefCell::new(Some(props)),
-                // heap_props: RefCell::new(None),
                 props,
                 memo: P::memoize, // smuggle the memoization function across borders
 
@@ -586,6 +649,7 @@ impl<'a> NodeFactory<'a> {
         VNode::Component(vcomp)
     }
 
+    /// Create a new [`Listener`]
     pub fn listener(self, event: &'static str, callback: InternalHandler<'a>) -> Listener<'a> {
         Listener {
             event,
@@ -594,6 +658,7 @@ impl<'a> NodeFactory<'a> {
         }
     }
 
+    /// Create a new [`VNode::VFragment`] from a root of the rsx! call
     pub fn fragment_root<'b, 'c>(
         self,
         node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
@@ -614,6 +679,7 @@ impl<'a> NodeFactory<'a> {
         }
     }
 
+    /// Create a new [`VNode::VFragment`] from any iterator
     pub fn fragment_from_iter<'b, 'c>(
         self,
         node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
@@ -653,8 +719,7 @@ 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
+    /// Create a new [`VNode`] from any iterator of children
     pub fn create_children(
         self,
         node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
@@ -679,6 +744,7 @@ impl<'a> NodeFactory<'a> {
         }
     }
 
+    /// Create a new [`EventHandler`] from an [`FnMut`]
     pub fn event_handler<T>(self, f: impl FnMut(T) + 'a) -> EventHandler<'a, T> {
         let handler: &mut dyn FnMut(T) = self.bump.alloc(f);
         let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
@@ -709,6 +775,7 @@ impl Debug for NodeFactory<'_> {
 /// As such, all node creation must go through the factory, which is only available in the component context.
 /// These strict requirements make it possible to manage lifetimes and state.
 pub trait IntoVNode<'a> {
+    /// Convert this into a [`VNode`], using the [`NodeFactory`] as a source of allocation
     fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
 }
 

+ 6 - 0
packages/core/src/properties.rs

@@ -128,8 +128,14 @@ pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
 /// }
 /// ```
 pub trait Properties: Sized {
+    /// The type of the builder for this component.
+    /// Used to create "in-progress" versions of the props.
     type Builder;
+
+    /// An indication if these props are can be memoized automatically.
     const IS_STATIC: bool;
+
+    /// Create a builder for this component.
     fn builder() -> Self::Builder;
 
     /// Memoization can only happen if the props are valid for the 'static lifetime

+ 5 - 1
packages/core/src/scopes.rs

@@ -396,7 +396,10 @@ impl ScopeArena {
 /// }
 /// ```
 pub struct Scope<'a, P = ()> {
+    /// The internal ScopeState for this component
     pub scope: &'a ScopeState,
+
+    /// The props for this component
     pub props: &'a P,
 }
 
@@ -750,7 +753,8 @@ impl ScopeState {
         self.push_future(fut);
     }
 
-    // todo: attach some state to the future to know if we should poll it
+    /// Informs the scheduler that this task is no longer needed and should be removed
+    /// on next poll.
     pub fn remove_future(&self, id: TaskId) {
         self.tasks.remove_fut(id);
     }

+ 5 - 0
packages/core/src/util.rs

@@ -1,5 +1,7 @@
 use crate::innerlude::*;
 
+/// An iterator that only yields "real" [`Element`]s. IE only Elements that are
+/// not [`VNode::VComponent`] or [`VNode::VFragment`], .
 pub struct ElementIdIterator<'a> {
     vdom: &'a VirtualDom,
 
@@ -9,6 +11,9 @@ pub struct ElementIdIterator<'a> {
 }
 
 impl<'a> ElementIdIterator<'a> {
+    /// Create a new iterator from the given [`VirtualDom`] and [`VNode`]
+    ///
+    /// This will allow you to iterate through all the real childrne of the [`VNode`].
     pub fn new(vdom: &'a VirtualDom, node: &'a VNode<'a>) -> Self {
         Self {
             vdom,

+ 9 - 3
packages/core/src/virtual_dom.rs

@@ -114,15 +114,18 @@ pub struct VirtualDom {
     ),
 }
 
+/// The type of message that can be sent to the scheduler.
+///
+/// These messages control how the scheduler will process updates to the UI.
 #[derive(Debug)]
 pub enum SchedulerMsg {
-    // events from the host
+    /// Events from the Renderer
     Event(UserEvent),
 
-    // setstate
+    /// Immediate updates from Components that mark them as dirty
     Immediate(ScopeId),
 
-    // an async task pushed from an event handler (or just spawned)
+    /// New tasks from components that should be polled when the next poll is ready
     NewTask(ScopeId),
 }
 
@@ -388,6 +391,9 @@ impl VirtualDom {
         }
     }
 
+    /// Handle an individual message for the scheduler.
+    ///
+    /// This will either call an event listener or mark a component as dirty.
     pub fn process_message(&mut self, msg: SchedulerMsg) {
         match msg {
             SchedulerMsg::NewTask(_id) => {

+ 1 - 2
packages/desktop/src/controller.rs

@@ -1,5 +1,4 @@
-use crate::desktop_context::DesktopContext;
-use crate::user_window_events::UserWindowEvent;
+use crate::desktop_context::{DesktopContext, UserWindowEvent};
 use dioxus_core::*;
 use std::{
     collections::{HashMap, VecDeque},

+ 87 - 12
packages/desktop/src/desktop_context.rs

@@ -3,14 +3,23 @@ use std::rc::Rc;
 use dioxus_core::ScopeState;
 use wry::application::event_loop::EventLoopProxy;
 
-use crate::user_window_events::UserWindowEvent;
 use UserWindowEvent::*;
 
-type ProxyType = EventLoopProxy<UserWindowEvent>;
+pub type ProxyType = EventLoopProxy<UserWindowEvent>;
 
-/// Desktop-Window handle api context
+/// Get an imperative handle to the current window
+pub fn use_window(cx: &ScopeState) -> &Rc<DesktopContext> {
+    cx.use_hook(|_| cx.consume_context::<DesktopContext>())
+        .as_ref()
+        .unwrap()
+}
+
+/// An imperative interface to the current window.
 ///
-/// you can use this context control some window event
+/// To get a handle to the current window, use the [`use_window`] hook.
+///
+///
+/// # Example
 ///
 /// you can use `cx.consume_context::<DesktopContext>` to get this context
 ///
@@ -19,7 +28,8 @@ type ProxyType = EventLoopProxy<UserWindowEvent>;
 /// ```
 #[derive(Clone)]
 pub struct DesktopContext {
-    proxy: ProxyType,
+    /// The wry/tao proxy to the current window
+    pub proxy: ProxyType,
 }
 
 impl DesktopContext {
@@ -84,12 +94,12 @@ impl DesktopContext {
         let _ = self.proxy.send_event(AlwaysOnTop(top));
     }
 
-    // set cursor visible or not
+    /// set cursor visible or not
     pub fn set_cursor_visible(&self, visible: bool) {
         let _ = self.proxy.send_event(CursorVisible(visible));
     }
 
-    // set cursor grab
+    /// set cursor grab
     pub fn set_cursor_grab(&self, grab: bool) {
         let _ = self.proxy.send_event(CursorGrab(grab));
     }
@@ -110,9 +120,74 @@ impl DesktopContext {
     }
 }
 
-/// use this function can get the `DesktopContext` context.
-pub fn use_window(cx: &ScopeState) -> &Rc<DesktopContext> {
-    cx.use_hook(|_| cx.consume_context::<DesktopContext>())
-        .as_ref()
-        .unwrap()
+use wry::application::event_loop::ControlFlow;
+use wry::application::window::Fullscreen as WryFullscreen;
+
+use crate::controller::DesktopController;
+
+#[derive(Debug)]
+pub enum UserWindowEvent {
+    Update,
+
+    CloseWindow,
+    DragWindow,
+    FocusWindow,
+
+    Visible(bool),
+    Minimize(bool),
+    Maximize(bool),
+    MaximizeToggle,
+    Resizable(bool),
+    AlwaysOnTop(bool),
+    Fullscreen(bool),
+
+    CursorVisible(bool),
+    CursorGrab(bool),
+
+    SetTitle(String),
+    SetDecorations(bool),
+
+    DevTool,
+}
+
+pub(super) fn handler(
+    user_event: UserWindowEvent,
+    desktop: &mut DesktopController,
+    control_flow: &mut ControlFlow,
+) {
+    // currently dioxus-desktop supports a single window only,
+    // so we can grab the only webview from the map;
+    let webview = desktop.webviews.values().next().unwrap();
+    let window = webview.window();
+
+    match user_event {
+        Update => desktop.try_load_ready_webviews(),
+        CloseWindow => *control_flow = ControlFlow::Exit,
+        DragWindow => {
+            // if the drag_window has any errors, we don't do anything
+            window.fullscreen().is_none().then(|| window.drag_window());
+        }
+        Visible(state) => window.set_visible(state),
+        Minimize(state) => window.set_minimized(state),
+        Maximize(state) => window.set_maximized(state),
+        MaximizeToggle => window.set_maximized(!window.is_maximized()),
+        Fullscreen(state) => {
+            if let Some(handle) = window.current_monitor() {
+                window.set_fullscreen(state.then(|| WryFullscreen::Borderless(Some(handle))));
+            }
+        }
+        FocusWindow => window.set_focus(),
+        Resizable(state) => window.set_resizable(state),
+        AlwaysOnTop(state) => window.set_always_on_top(state),
+
+        CursorVisible(state) => window.set_cursor_visible(state),
+        CursorGrab(state) => {
+            let _ = window.set_cursor_grab(state);
+        }
+
+        SetTitle(content) => window.set_title(&content),
+        SetDecorations(state) => window.set_decorations(state),
+
+        DevTool => webview.devtool(),
+    }
 }

+ 13 - 13
packages/desktop/src/lib.rs

@@ -1,18 +1,23 @@
 #![doc = include_str!("readme.md")]
 #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
 #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
+#![deny(missing_docs)]
 
-pub mod cfg;
+mod cfg;
 mod controller;
-pub mod desktop_context;
-pub mod escape;
-pub mod events;
+mod desktop_context;
+mod escape;
+mod events;
 mod protocol;
-mod user_window_events;
 
+use desktop_context::UserWindowEvent;
+pub use desktop_context::{use_window, DesktopContext};
+pub use wry;
+pub use wry::application as tao;
+
+use crate::events::trigger_from_serialized;
 use cfg::DesktopConfig;
 use controller::DesktopController;
-pub use desktop_context::use_window;
 use dioxus_core::*;
 use events::parse_ipc_message;
 use tao::{
@@ -20,12 +25,8 @@ use tao::{
     event_loop::{ControlFlow, EventLoop},
     window::Window,
 };
-pub use wry;
-pub use wry::application as tao;
 use wry::webview::WebViewBuilder;
 
-use crate::events::trigger_from_serialized;
-
 /// Launch the WebView and run the event loop.
 ///
 /// This function will start a multithreaded Tokio runtime as well the WebView event loop.
@@ -142,8 +143,7 @@ pub fn launch_with_props<P: 'static + Send>(
                                 }
                                 "initialize" => {
                                     is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
-                                    let _ = proxy
-                                        .send_event(user_window_events::UserWindowEvent::Update);
+                                    let _ = proxy.send_event(UserWindowEvent::Update);
                                 }
                                 "browser_open" => {
                                     let data = message.params();
@@ -218,7 +218,7 @@ pub fn launch_with_props<P: 'static + Send>(
             },
 
             Event::UserEvent(user_event) => {
-                user_window_events::handler(user_event, &mut desktop, control_flow)
+                desktop_context::handler(user_event, &mut desktop, control_flow)
             }
             Event::MainEventsCleared => {}
             Event::Resumed => {}

+ 0 - 73
packages/desktop/src/user_window_events.rs

@@ -1,73 +0,0 @@
-use wry::application::event_loop::ControlFlow;
-use wry::application::window::Fullscreen as WryFullscreen;
-
-use crate::controller::DesktopController;
-
-#[derive(Debug)]
-pub(crate) enum UserWindowEvent {
-    Update,
-
-    CloseWindow,
-    DragWindow,
-    FocusWindow,
-
-    Visible(bool),
-    Minimize(bool),
-    Maximize(bool),
-    MaximizeToggle,
-    Resizable(bool),
-    AlwaysOnTop(bool),
-    Fullscreen(bool),
-
-    CursorVisible(bool),
-    CursorGrab(bool),
-
-    SetTitle(String),
-    SetDecorations(bool),
-
-    DevTool,
-}
-
-use UserWindowEvent::*;
-
-pub(super) fn handler(
-    user_event: UserWindowEvent,
-    desktop: &mut DesktopController,
-    control_flow: &mut ControlFlow,
-) {
-    // currently dioxus-desktop supports a single window only,
-    // so we can grab the only webview from the map;
-    let webview = desktop.webviews.values().next().unwrap();
-    let window = webview.window();
-
-    match user_event {
-        Update => desktop.try_load_ready_webviews(),
-        CloseWindow => *control_flow = ControlFlow::Exit,
-        DragWindow => {
-            // if the drag_window has any errors, we don't do anything
-            window.fullscreen().is_none().then(|| window.drag_window());
-        }
-        Visible(state) => window.set_visible(state),
-        Minimize(state) => window.set_minimized(state),
-        Maximize(state) => window.set_maximized(state),
-        MaximizeToggle => window.set_maximized(!window.is_maximized()),
-        Fullscreen(state) => {
-            if let Some(handle) = window.current_monitor() {
-                window.set_fullscreen(state.then(|| WryFullscreen::Borderless(Some(handle))));
-            }
-        }
-        FocusWindow => window.set_focus(),
-        Resizable(state) => window.set_resizable(state),
-        AlwaysOnTop(state) => window.set_always_on_top(state),
-
-        CursorVisible(state) => window.set_cursor_visible(state),
-        CursorGrab(state) => {
-            let _ = window.set_cursor_grab(state);
-        }
-
-        SetTitle(content) => window.set_title(&content),
-        SetDecorations(state) => window.set_decorations(state),
-
-        DevTool => webview.devtool(),
-    }
-}

+ 3 - 0
packages/hooks/src/lib.rs

@@ -1,3 +1,6 @@
+// #![deny(missing_docs)]
+//! Useful foundational hooks for Dioxus
+
 mod usestate;
 pub use usestate::{use_state, UseState};
 

+ 33 - 9
packages/hooks/src/usefuture.rs

@@ -1,19 +1,30 @@
+#![allow(missing_docs)]
 use dioxus_core::{ScopeState, TaskId};
 use std::{cell::Cell, future::Future, rc::Rc, sync::Arc};
 
+/// A future that resolves to a value.
+///
+/// This runs the future only once - though the future may be regenerated
+/// through the [`UseFuture::restart`] method.
+///
+/// This is commonly used for components that cannot be rendered until some
+/// asynchronous operation has completed.
+///
+///
+///
+///
+///
 pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
     cx: &'a ScopeState,
     new_fut: impl FnOnce() -> F,
 ) -> &'a UseFuture<T> {
-    let state = cx.use_hook(move |_| {
-        //
-        UseFuture {
-            update: cx.schedule_update(),
-            needs_regen: Cell::new(true),
-            slot: Rc::new(Cell::new(None)),
-            value: None,
-            task: None,
-        }
+    let state = cx.use_hook(move |_| UseFuture {
+        update: cx.schedule_update(),
+        needs_regen: Cell::new(true),
+        slot: Rc::new(Cell::new(None)),
+        value: None,
+        task: None,
+        pending: true,
     });
 
     if let Some(value) = state.slot.take() {
@@ -24,6 +35,7 @@ pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
     if state.needs_regen.get() {
         // We don't need regen anymore
         state.needs_regen.set(false);
+        state.pending = false;
 
         // Create the new future
         let fut = new_fut();
@@ -42,10 +54,17 @@ pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
     state
 }
 
+pub enum FutureState<'a, T> {
+    Pending,
+    Complete(&'a T),
+    Regenerating(&'a T), // the old value
+}
+
 pub struct UseFuture<T> {
     update: Arc<dyn Fn()>,
     needs_regen: Cell<bool>,
     value: Option<T>,
+    pending: bool,
     slot: Rc<Cell<Option<T>>>,
     task: Option<TaskId>,
 }
@@ -72,4 +91,9 @@ impl<T> UseFuture<T> {
     pub fn value(&self) -> Option<&T> {
         self.value.as_ref()
     }
+
+    pub fn state(&self) -> FutureState<T> {
+        // self.value.as_ref()
+        FutureState::Pending
+    }
 }

+ 2 - 2
packages/rsx/src/lib.rs

@@ -79,7 +79,7 @@ impl ToTokens for CallBody {
         match &self.custom_context {
             // The `in cx` pattern allows directly rendering
             Some(ident) => out_tokens.append_all(quote! {
-                #ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
+                #ident.render(LazyNodes::new(move |__cx: NodeFactory| -> VNode {
                     use dioxus_elements::{GlobalAttributes, SvgAttributes};
                     #inner
                 }))
@@ -87,7 +87,7 @@ impl ToTokens for CallBody {
 
             // Otherwise we just build the LazyNode wrapper
             None => out_tokens.append_all(quote! {
-                LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
+                LazyNodes::new(move |__cx: NodeFactory| -> VNode {
                     use dioxus_elements::{GlobalAttributes, SvgAttributes};
                     #inner
                 })

+ 2 - 0
packages/web/src/lib.rs

@@ -1,3 +1,5 @@
+#![deny(missing_docs)]
+
 //! Dioxus WebSys
 //!
 //! ## Overview