mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-17 06:08:26 +00:00
update interperter to use CapuredContext
This commit is contained in:
parent
871f51f91b
commit
62cca95905
5 changed files with 145 additions and 48 deletions
|
@ -1,15 +1,24 @@
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::desktop::launch_with_props(with_hot_reload, app, |b| b);
|
dioxus::desktop::launch_with_props(with_hot_reload, app, |b| b);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
let mut count = use_state(&cx, || 0);
|
let count = use_state(&cx, || 0);
|
||||||
|
|
||||||
|
use_future(&cx, (), move |_| {
|
||||||
|
let mut count = count.clone();
|
||||||
|
async move {
|
||||||
|
loop {
|
||||||
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 { "High-Five counter: {count}" }
|
h1 { "High-Five counter: {count}" }
|
||||||
button { onclick: move |_| count += 1, "Up high!" }
|
|
||||||
button { onclick: move |_| count -= 1, "Down low!" }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,6 +180,8 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
|
||||||
#[proc_macro_error::proc_macro_error]
|
#[proc_macro_error::proc_macro_error]
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn rsx(s: TokenStream) -> TokenStream {
|
pub fn rsx(s: TokenStream) -> TokenStream {
|
||||||
|
#[cfg(feature = "hot_reload")]
|
||||||
|
let rsx_text = s.to_string();
|
||||||
match syn::parse::<rsx::CallBody>(s) {
|
match syn::parse::<rsx::CallBody>(s) {
|
||||||
Err(err) => err.to_compile_error().into(),
|
Err(err) => err.to_compile_error().into(),
|
||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
|
@ -191,10 +193,16 @@ pub fn rsx(s: TokenStream) -> TokenStream {
|
||||||
quote! {
|
quote! {
|
||||||
{
|
{
|
||||||
let line_num = get_line_num();
|
let line_num = get_line_num();
|
||||||
|
let rsx_text_index: RsxTextIndex = cx.consume_context().unwrap();
|
||||||
|
use_state(&cx, || {
|
||||||
|
rsx_text_index.insert(
|
||||||
|
line_num.clone(),
|
||||||
|
#rsx_text.to_string(),
|
||||||
|
);
|
||||||
|
});
|
||||||
LazyNodes::new(move |factory|{
|
LazyNodes::new(move |factory|{
|
||||||
let rsx_text_index: RsxTextIndex = cx.consume_context().unwrap();
|
|
||||||
let read = rsx_text_index.read();
|
let read = rsx_text_index.read();
|
||||||
if let Some(text) = read.get(line_num){
|
if let Some(text) = read.get(&line_num){
|
||||||
interpert_rsx(
|
interpert_rsx(
|
||||||
factory,
|
factory,
|
||||||
&text,
|
&text,
|
||||||
|
@ -202,7 +210,7 @@ pub fn rsx(s: TokenStream) -> TokenStream {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
println!("rsx: line number {} not found", line_num);
|
println!("rsx: line number {:?} not found", line_num);
|
||||||
factory.static_text("")
|
factory.static_text("")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -38,13 +38,13 @@ impl CapturedContextBuilder {
|
||||||
(name.to_string(), value.to_token_stream())
|
(name.to_string(), value.to_token_stream())
|
||||||
}
|
}
|
||||||
ElementAttr::AttrExpression { name, value } => {
|
ElementAttr::AttrExpression { name, value } => {
|
||||||
(name.to_string(), value.to_token_stream())
|
todo!()
|
||||||
}
|
}
|
||||||
ElementAttr::CustomAttrText { name, value } => {
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
(name.value(), value.to_token_stream())
|
(name.value(), value.to_token_stream())
|
||||||
}
|
}
|
||||||
ElementAttr::CustomAttrExpression { name, value } => {
|
ElementAttr::CustomAttrExpression { name, value } => {
|
||||||
(name.value(), value.to_token_stream())
|
todo!()
|
||||||
}
|
}
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
@ -56,7 +56,6 @@ impl CapturedContextBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BodyNode::Component(comp) => {
|
BodyNode::Component(comp) => {
|
||||||
let fn_name = comp.name.segments.last().unwrap().ident.to_string();
|
|
||||||
captured.components.push(comp);
|
captured.components.push(comp);
|
||||||
}
|
}
|
||||||
BodyNode::Text(t) => {
|
BodyNode::Text(t) => {
|
||||||
|
@ -105,8 +104,10 @@ struct CapturedComponentBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CapturedContext<'a> {
|
pub struct CapturedContext<'a> {
|
||||||
// map of the attribute name to the formated value
|
// map of the variable name to the formated value
|
||||||
pub captured: IfmtArgs,
|
pub captured: IfmtArgs,
|
||||||
|
// // map of the attribute name and element path to the formated value
|
||||||
|
// pub captured_attribute_values: IfmtArgs,
|
||||||
// the only thing we can update in component is the children
|
// the only thing we can update in component is the children
|
||||||
pub components: Vec<VNode<'a>>,
|
pub components: Vec<VNode<'a>>,
|
||||||
// we can't reasonably interpert iterators, so they are staticly inserted
|
// we can't reasonably interpert iterators, so they are staticly inserted
|
||||||
|
@ -117,22 +118,3 @@ pub struct IfmtArgs {
|
||||||
// live reload only supports named arguments
|
// live reload only supports named arguments
|
||||||
pub named_args: Vec<(&'static str, String)>,
|
pub named_args: Vec<(&'static str, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum IfmtSegment<'a> {
|
|
||||||
Static(&'a str),
|
|
||||||
Dynamic(&'a str),
|
|
||||||
}
|
|
||||||
|
|
||||||
enum RsxNode<'a> {
|
|
||||||
Element {
|
|
||||||
name: String,
|
|
||||||
attributes: Vec<(String, IfmtSegment<'a>)>,
|
|
||||||
children: Vec<RsxNode<'a>>,
|
|
||||||
},
|
|
||||||
Text {
|
|
||||||
text: Vec<IfmtSegment<'a>>,
|
|
||||||
},
|
|
||||||
Component {
|
|
||||||
children: Vec<RsxNode<'a>>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,44 +1,128 @@
|
||||||
use dioxus_core::{Attribute, NodeFactory, VNode};
|
use dioxus_core::{Attribute, NodeFactory, VNode};
|
||||||
use dioxus_rsx::{BodyNode, CallBody, ElementAttr, IfmtInput};
|
use dioxus_rsx::{BodyNode, CallBody, ElementAttr, IfmtInput};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
|
use std::str::FromStr;
|
||||||
use syn::parse2;
|
use syn::parse2;
|
||||||
|
|
||||||
use crate::attributes::attrbute_to_static_str;
|
use crate::attributes::attrbute_to_static_str;
|
||||||
|
use crate::captuered_context::{CapturedContext, IfmtArgs};
|
||||||
use crate::elements::element_to_static_str;
|
use crate::elements::element_to_static_str;
|
||||||
|
|
||||||
pub fn build<'a>(rsx: CallBody, factory: &NodeFactory<'a>) -> VNode<'a> {
|
#[derive(Debug)]
|
||||||
|
enum Segment {
|
||||||
|
Ident(String),
|
||||||
|
Literal(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InterperedIfmt {
|
||||||
|
segments: Vec<Segment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterperedIfmt {
|
||||||
|
fn resolve(&self, captured: &IfmtArgs) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
for seg in &self.segments {
|
||||||
|
match seg {
|
||||||
|
Segment::Ident(name) => {
|
||||||
|
let (_, value) = captured
|
||||||
|
.named_args
|
||||||
|
.iter()
|
||||||
|
.find(|(n, _)| *n == name)
|
||||||
|
.expect(format!("could not resolve {}", name).as_str());
|
||||||
|
result.push_str(value);
|
||||||
|
}
|
||||||
|
Segment::Literal(lit) => result.push_str(lit),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for InterperedIfmt {
|
||||||
|
type Err = ();
|
||||||
|
fn from_str(input: &str) -> Result<Self, ()> {
|
||||||
|
let mut segments = Vec::new();
|
||||||
|
let mut segment = String::new();
|
||||||
|
let mut chars = input.chars().peekable();
|
||||||
|
while let Some(c) = chars.next() {
|
||||||
|
if c == '{' {
|
||||||
|
if chars.peek().copied() != Some('{') {
|
||||||
|
let old;
|
||||||
|
(old, segment) = (segment, String::new());
|
||||||
|
if !old.is_empty() {
|
||||||
|
segments.push(Segment::Literal(old));
|
||||||
|
}
|
||||||
|
while let Some(c) = chars.next() {
|
||||||
|
if c == '}' {
|
||||||
|
let old;
|
||||||
|
(old, segment) = (segment, String::new());
|
||||||
|
if !old.is_empty() {
|
||||||
|
segments.push(Segment::Ident(old));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if c == ':' {
|
||||||
|
while Some('}') != chars.next() {}
|
||||||
|
let old;
|
||||||
|
(old, segment) = (segment, String::new());
|
||||||
|
if !old.is_empty() {
|
||||||
|
segments.push(Segment::Ident(old));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
segment.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
segment.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !segment.is_empty() {
|
||||||
|
segments.push(Segment::Literal(segment));
|
||||||
|
}
|
||||||
|
Ok(Self { segments })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build<'a>(rsx: CallBody, ctx: CapturedContext, factory: &NodeFactory<'a>) -> VNode<'a> {
|
||||||
let children_built = factory.bump().alloc(Vec::new());
|
let children_built = factory.bump().alloc(Vec::new());
|
||||||
for (i, child) in rsx.roots.into_iter().enumerate() {
|
for (i, child) in rsx.roots.into_iter().enumerate() {
|
||||||
children_built.push(build_node(child, factory, i.to_string().as_str()));
|
children_built.push(build_node(child, &ctx, factory, i.to_string().as_str()));
|
||||||
}
|
}
|
||||||
factory.fragment_from_iter(children_built.iter())
|
factory.fragment_from_iter(children_built.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_node<'a>(node: BodyNode, factory: &NodeFactory<'a>, key: &str) -> Option<VNode<'a>> {
|
fn build_node<'a>(
|
||||||
|
node: BodyNode,
|
||||||
|
ctx: &CapturedContext,
|
||||||
|
factory: &NodeFactory<'a>,
|
||||||
|
key: &str,
|
||||||
|
) -> Option<VNode<'a>> {
|
||||||
let bump = factory.bump();
|
let bump = factory.bump();
|
||||||
match node {
|
match node {
|
||||||
BodyNode::Text(text) => {
|
BodyNode::Text(text) => {
|
||||||
let ifmt_input: IfmtInput = parse2(text.into_token_stream()).unwrap();
|
let ifmt: InterperedIfmt = text.value().parse().unwrap();
|
||||||
Some(factory.text(format_args!("{}", ifmt_input.format_literal.value())))
|
let text = bump.alloc(ifmt.resolve(&ctx.captured));
|
||||||
|
Some(factory.text(format_args!("{}", text)))
|
||||||
}
|
}
|
||||||
BodyNode::Element(el) => {
|
BodyNode::Element(el) => {
|
||||||
let attributes: &mut Vec<Attribute> = bump.alloc(Vec::new());
|
let attributes: &mut Vec<Attribute> = bump.alloc(Vec::new());
|
||||||
for attr in el.attributes {
|
for attr in el.attributes {
|
||||||
let result: Option<(String, IfmtInput)> = match attr.attr {
|
let result: Option<(String, InterperedIfmt)> = match attr.attr {
|
||||||
ElementAttr::AttrText { name, value } => {
|
ElementAttr::AttrText { name, value } => {
|
||||||
Some((name.to_string(), parse2(value.into_token_stream()).unwrap()))
|
Some((name.to_string(), value.value().parse().unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementAttr::AttrExpression { name, value } => {
|
ElementAttr::AttrExpression { name, value } => {
|
||||||
Some((name.to_string(), parse2(value.into_token_stream()).unwrap()))
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementAttr::CustomAttrText { name, value } => {
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
Some((name.value(), parse2(value.into_token_stream()).unwrap()))
|
Some((name.value(), value.value().parse().unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementAttr::CustomAttrExpression { name, value } => {
|
ElementAttr::CustomAttrExpression { name, value } => {
|
||||||
Some((name.value(), parse2(value.into_token_stream()).unwrap()))
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementAttr::EventTokens { .. } => None,
|
ElementAttr::EventTokens { .. } => None,
|
||||||
|
@ -47,7 +131,7 @@ fn build_node<'a>(node: BodyNode, factory: &NodeFactory<'a>, key: &str) -> Optio
|
||||||
};
|
};
|
||||||
if let Some((name, value)) = result {
|
if let Some((name, value)) = result {
|
||||||
if let Some((name, namespace)) = attrbute_to_static_str(&name) {
|
if let Some((name, namespace)) = attrbute_to_static_str(&name) {
|
||||||
let value = bump.alloc(value.format_literal.value());
|
let value = bump.alloc(value.resolve(&ctx.captured));
|
||||||
attributes.push(Attribute {
|
attributes.push(Attribute {
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
|
@ -62,7 +146,7 @@ fn build_node<'a>(node: BodyNode, factory: &NodeFactory<'a>, key: &str) -> Optio
|
||||||
}
|
}
|
||||||
let children = bump.alloc(Vec::new());
|
let children = bump.alloc(Vec::new());
|
||||||
for (i, child) in el.children.into_iter().enumerate() {
|
for (i, child) in el.children.into_iter().enumerate() {
|
||||||
let node = build_node(child, factory, i.to_string().as_str());
|
let node = build_node(child, ctx, factory, i.to_string().as_str());
|
||||||
if let Some(node) = node {
|
if let Some(node) = node {
|
||||||
children.push(node);
|
children.push(node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use dioxus_core::{Component, Element, LazyNodes, Scope, VNode};
|
use dioxus_core::{Component, Element, LazyNodes, Scope, VNode};
|
||||||
use dioxus_hooks::*;
|
use dioxus_hooks::*;
|
||||||
|
use interperter::build;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::panic::Location;
|
use std::panic::Location;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -11,10 +12,18 @@ pub mod captuered_context;
|
||||||
mod elements;
|
mod elements;
|
||||||
mod interperter;
|
mod interperter;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct CodeLocation {
|
||||||
|
pub file: String,
|
||||||
|
pub line: u32,
|
||||||
|
pub column: u32,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_hot_reload(cx: Scope<Component>) -> Element {
|
pub fn with_hot_reload(cx: Scope<Component>) -> Element {
|
||||||
use_state(&cx, || {
|
use_state(&cx, || {
|
||||||
if cx.consume_context::<RsxTextIndex>().is_none() {
|
if cx.consume_context::<RsxTextIndex>().is_none() {
|
||||||
cx.provide_context(RsxTextIndex::default());
|
let index = RsxTextIndex::default();
|
||||||
|
cx.provide_context(index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cx.render(LazyNodes::new(|node_factory| {
|
cx.render(LazyNodes::new(|node_factory| {
|
||||||
|
@ -27,25 +36,30 @@ pub fn interpert_rsx<'a, 'b>(
|
||||||
text: &str,
|
text: &str,
|
||||||
context: captuered_context::CapturedContext,
|
context: captuered_context::CapturedContext,
|
||||||
) -> VNode<'a> {
|
) -> VNode<'a> {
|
||||||
panic!()
|
build(parse_str(text).unwrap(), context, &factory)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn get_line_num() -> &'static Location<'static> {
|
pub fn get_line_num() -> CodeLocation {
|
||||||
Location::caller()
|
let location = Location::caller();
|
||||||
|
CodeLocation {
|
||||||
|
file: location.file().to_string(),
|
||||||
|
line: location.line(),
|
||||||
|
column: location.column(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct RsxTextIndex {
|
pub struct RsxTextIndex {
|
||||||
hm: Rc<RwLock<HashMap<&'static Location<'static>, String>>>,
|
hm: Rc<RwLock<HashMap<CodeLocation, String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RsxTextIndex {
|
impl RsxTextIndex {
|
||||||
pub fn insert(&self, loc: &'static Location<'static>, text: String) {
|
pub fn insert(&self, loc: CodeLocation, text: String) {
|
||||||
self.hm.write().unwrap().insert(loc, text);
|
self.hm.write().unwrap().insert(loc, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self) -> RwLockReadGuard<HashMap<&'static Location<'static>, String>> {
|
pub fn read(&self) -> RwLockReadGuard<HashMap<CodeLocation, String>> {
|
||||||
self.hm.read().unwrap()
|
self.hm.read().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue