add hot reloading context trait

This commit is contained in:
Evan Almloff committed 2022-12-10 14:09:59 -06:00
1 parent f16e3be2c2
commit 2131e5658b
4 files changed
+116 -32

No files matched your search

+4 -1
View File
@@ -11,6 +11,8 @@
//! - [ ] Keys //! - [ ] Keys
//! - [ ] Properties spreading with with `..` syntax //! - [ ] Properties spreading with with `..` syntax
use std::marker::PhantomData;
use super::*; use super::*;
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
@@ -165,8 +167,9 @@ impl ToTokens for Component {
} }
if !self.children.is_empty() { if !self.children.is_empty() {
let renderer = TemplateRenderer { let renderer: TemplateRenderer = TemplateRenderer {
roots: &self.children, roots: &self.children,
phantom: PhantomData,
}; };
toks.append_all(quote! { toks.append_all(quote! {
+19
View File
@@ -0,0 +1,19 @@
pub trait HotReloadingContext {
fn map_attribute(
element_name_rust: &str,
attribute_name_rust: &str,
) -> Option<(&'static str, Option<&'static str>)>;
fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)>;
}
pub struct Empty;
impl HotReloadingContext for Empty {
fn map_attribute(_: &str, _: &str) -> Option<(&'static str, Option<&'static str>)> {
None
}
fn map_element(_: &str) -> Option<(&'static str, Option<&'static str>)> {
None
}
}
+87 -30
View File
@@ -15,6 +15,7 @@
mod errors; mod errors;
mod component; mod component;
mod element; mod element;
mod hot_reloading_context;
mod ifmt; mod ifmt;
mod node; mod node;
@@ -22,6 +23,7 @@ mod node;
pub use component::*; pub use component::*;
use dioxus_core::{Template, TemplateAttribute, TemplateNode}; use dioxus_core::{Template, TemplateAttribute, TemplateNode};
pub use element::*; pub use element::*;
use hot_reloading_context::{Empty, HotReloadingContext};
pub use ifmt::*; pub use ifmt::*;
pub use node::*; pub use node::*;
@@ -35,24 +37,29 @@ use syn::{
/// Fundametnally, every CallBody is a template /// Fundametnally, every CallBody is a template
#[derive(Default)] #[derive(Default)]
pub struct CallBody { pub struct CallBody<Ctx: HotReloadingContext = Empty> {
pub roots: Vec<BodyNode>, pub roots: Vec<BodyNode>,
// set this after // set this after
pub inline_cx: bool, pub inline_cx: bool,
phantom: std::marker::PhantomData<Ctx>,
} }
impl CallBody { impl<Ctx: HotReloadingContext> CallBody<Ctx> {
/// This function intentionally leaks memory to create a static template. /// This function intentionally leaks memory to create a static template.
/// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue. /// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue.
/// the previous_location is the location of the previous template at the time the template was originally compiled. /// the previous_location is the location of the previous template at the time the template was originally compiled.
pub fn leak_template(&self, previous_location: &'static str) -> Template { pub fn leak_template(&self, previous_location: &'static str) -> Template {
let mut renderer = TemplateRenderer { roots: &self.roots }; let mut renderer: TemplateRenderer<Ctx> = TemplateRenderer {
roots: &self.roots,
phantom: std::marker::PhantomData,
};
renderer.leak_template(previous_location) renderer.leak_template(previous_location)
} }
} }
impl Parse for CallBody { impl<Ctx: HotReloadingContext> Parse for CallBody<Ctx> {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let mut roots = Vec::new(); let mut roots = Vec::new();
@@ -69,14 +76,18 @@ impl Parse for CallBody {
Ok(Self { Ok(Self {
roots, roots,
inline_cx: false, inline_cx: false,
phantom: std::marker::PhantomData,
}) })
} }
} }
/// Serialize the same way, regardless of flavor /// Serialize the same way, regardless of flavor
impl ToTokens for CallBody { impl<Ctx: HotReloadingContext> ToTokens for CallBody<Ctx> {
fn to_tokens(&self, out_tokens: &mut TokenStream2) { fn to_tokens(&self, out_tokens: &mut TokenStream2) {
let body = TemplateRenderer { roots: &self.roots }; let body: TemplateRenderer<Ctx> = TemplateRenderer {
roots: &self.roots,
phantom: std::marker::PhantomData,
};
if self.inline_cx { if self.inline_cx {
out_tokens.append_all(quote! { out_tokens.append_all(quote! {
@@ -95,13 +106,14 @@ impl ToTokens for CallBody {
} }
} }
pub struct TemplateRenderer<'a> { pub struct TemplateRenderer<'a, Ctx: HotReloadingContext = Empty> {
pub roots: &'a [BodyNode], pub roots: &'a [BodyNode],
phantom: std::marker::PhantomData<Ctx>,
} }
impl<'a> TemplateRenderer<'a> { impl<'a, Ctx: HotReloadingContext> TemplateRenderer<'a, Ctx> {
fn leak_template(&mut self, previous_location: &'static str) -> Template<'static> { fn leak_template(&mut self, previous_location: &'static str) -> Template<'static> {
let mut context = DynamicContext::default(); let mut context: DynamicContext<Ctx> = DynamicContext::default();
let roots: Vec<_> = self let roots: Vec<_> = self
.roots .roots
@@ -138,9 +150,9 @@ impl<'a> TemplateRenderer<'a> {
} }
} }
impl<'a> ToTokens for TemplateRenderer<'a> { impl<'a, Ctx: HotReloadingContext> ToTokens for TemplateRenderer<'a, Ctx> {
fn to_tokens(&self, out_tokens: &mut TokenStream2) { fn to_tokens(&self, out_tokens: &mut TokenStream2) {
let mut context = DynamicContext::default(); let mut context: DynamicContext<Ctx> = DynamicContext::default();
let key = match self.roots.get(0) { let key = match self.roots.get(0) {
Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(), Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(),
@@ -199,17 +211,31 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
} }
// As we print out the dynamic nodes, we want to keep track of them in a linear fashion // As we print out the dynamic nodes, we want to keep track of them in a linear fashion
// We'll use the size of the vecs to determine the index of the dynamic node in the final // We'll use the size of the vecs to determine the index of the dynamic node in the final
#[derive(Default)] pub struct DynamicContext<'a, Ctx: HotReloadingContext> {
pub struct DynamicContext<'a> {
dynamic_nodes: Vec<&'a BodyNode>, dynamic_nodes: Vec<&'a BodyNode>,
dynamic_attributes: Vec<&'a ElementAttrNamed>, dynamic_attributes: Vec<&'a ElementAttrNamed>,
current_path: Vec<u8>, current_path: Vec<u8>,
node_paths: Vec<Vec<u8>>, node_paths: Vec<Vec<u8>>,
attr_paths: Vec<Vec<u8>>, attr_paths: Vec<Vec<u8>>,
phantom: std::marker::PhantomData<Ctx>,
} }
impl<'a> DynamicContext<'a> { impl<'a, Ctx: HotReloadingContext> Default for DynamicContext<'a, Ctx> {
fn default() -> Self {
Self {
dynamic_nodes: Vec::new(),
dynamic_attributes: Vec::new(),
current_path: Vec::new(),
node_paths: Vec::new(),
attr_paths: Vec::new(),
phantom: std::marker::PhantomData,
}
}
}
impl<'a, Ctx: HotReloadingContext> DynamicContext<'a, Ctx> {
fn leak_node(&mut self, root: &'a BodyNode) -> TemplateNode<'static> { fn leak_node(&mut self, root: &'a BodyNode) -> TemplateNode<'static> {
match root { match root {
BodyNode::Element(el) => { BodyNode::Element(el) => {
@@ -222,15 +248,24 @@ impl<'a> DynamicContext<'a> {
// [0, 2] // [0, 2]
// [0, 2, 1] // [0, 2, 1]
let element_name_rust = el.name.to_string();
let static_attrs: Vec<_> = el let static_attrs: Vec<_> = el
.attributes .attributes
.iter() .iter()
.map(|attr| match &attr.attr { .map(|attr| match &attr.attr {
ElementAttr::AttrText { name: _, value } if value.is_static() => { ElementAttr::AttrText { name, value } if value.is_static() => {
let value = value.source.as_ref().unwrap(); let value = value.source.as_ref().unwrap();
let attribute_name_rust = name.to_string();
let (name, namespace) =
Ctx::map_attribute(&element_name_rust, &attribute_name_rust)
.unwrap_or((
Box::leak(attribute_name_rust.into_boxed_str()),
None,
));
TemplateAttribute::Static { TemplateAttribute::Static {
name: "todo", name,
namespace: None, namespace,
value: Box::leak(value.value().into_boxed_str()), value: Box::leak(value.value().into_boxed_str()),
// name: dioxus_elements::#el_name::#name.0, // name: dioxus_elements::#el_name::#name.0,
// namespace: dioxus_elements::#el_name::#name.1, // namespace: dioxus_elements::#el_name::#name.1,
@@ -277,15 +312,11 @@ impl<'a> DynamicContext<'a> {
}) })
.collect(); .collect();
// TemplateNode::Element { let (tag, namespace) = Ctx::map_element(&element_name_rust)
// tag: dioxus_elements::#el_name::TAG_NAME, .unwrap_or((Box::leak(element_name_rust.into_boxed_str()), None));
// namespace: dioxus_elements::#el_name::NAME_SPACE,
// attrs: &[ #attrs ],
// children: &[ #children ],
// }
TemplateNode::Element { TemplateNode::Element {
tag: "todo", tag,
namespace: None, namespace,
attrs: Box::leak(static_attrs.into_boxed_slice()), attrs: Box::leak(static_attrs.into_boxed_slice()),
children: Box::leak(children.into_boxed_slice()), children: Box::leak(children.into_boxed_slice()),
} }
@@ -420,7 +451,7 @@ impl<'a> DynamicContext<'a> {
#[test] #[test]
fn template() { fn template() {
let input = quote! { let input = quote! {
div { svg {
width: 100, width: 100,
height: "100px", height: "100px",
"width2": 100, "width2": 100,
@@ -431,7 +462,33 @@ fn template() {
(0..10).map(|i| rsx!{"{i}"}) (0..10).map(|i| rsx!{"{i}"})
} }
}; };
let call_body: CallBody = syn::parse2(input).unwrap();
struct Mock;
impl HotReloadingContext for Mock {
fn map_attribute(
element_name_rust: &str,
attribute_name_rust: &str,
) -> Option<(&'static str, Option<&'static str>)> {
match element_name_rust {
"svg" => match attribute_name_rust {
"width" => Some(("width", Some("style"))),
"height" => Some(("height", Some("style"))),
_ => None,
},
_ => None,
}
}
fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)> {
match element_name_rust {
"svg" => Some(("svg", Some("svg"))),
_ => None,
}
}
}
let call_body: CallBody<Mock> = syn::parse2(input).unwrap();
let template = call_body.leak_template("testing"); let template = call_body.leak_template("testing");
@@ -442,13 +499,13 @@ fn template() {
Template { Template {
name: "testing", name: "testing",
roots: &[TemplateNode::Element { roots: &[TemplateNode::Element {
tag: "div", tag: "svg",
namespace: None, namespace: Some("svg"),
attrs: &[ attrs: &[
TemplateAttribute::Dynamic { id: 0 }, TemplateAttribute::Dynamic { id: 0 },
TemplateAttribute::Static { TemplateAttribute::Static {
name: "height", name: "height",
namespace: None, namespace: Some("style"),
value: "100px", value: "100px",
}, },
TemplateAttribute::Dynamic { id: 1 }, TemplateAttribute::Dynamic { id: 1 },
+6 -1
View File
@@ -1,3 +1,5 @@
use std::marker::PhantomData;
use super::*; use super::*;
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
@@ -141,7 +143,10 @@ impl ToTokens for BodyNode {
pat, expr, body, .. pat, expr, body, ..
} = exp; } = exp;
let renderer = TemplateRenderer { roots: body }; let renderer: TemplateRenderer = TemplateRenderer {
roots: body,
phantom: PhantomData,
};
tokens.append_all(quote! { tokens.append_all(quote! {
__cx.make_node( __cx.make_node(