handle formatting options
This commit is contained in:
1 parent
2183ecf3fb
commit
94448ea4aa
10 files changed
+278
-386
No files matched your search
+26
-28
@@ -14,7 +14,7 @@ fn app(cx: Scope) -> Element {
|
|||||||
},
|
},
|
||||||
|
|
||||||
p {
|
p {
|
||||||
"High-Five counter: {count}",
|
"High-Five counter: {count.to_string():?}",
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
@@ -43,33 +43,31 @@ fn app(cx: Scope) -> Element {
|
|||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flex_direction: "row",
|
flex_direction: "row",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "50%",
|
height: "50%",
|
||||||
Editable{
|
Editable{
|
||||||
current_code: submitted_rsx_code.get().clone(),
|
current_code: submitted_rsx_code.get().clone(),
|
||||||
},
|
},
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
width: "90%",
|
width: "90%",
|
||||||
value:
|
value: rsx_code,
|
||||||
rsx_code
|
oninput: move |evt| {
|
||||||
,
|
rsx_code.set(evt.value.clone());
|
||||||
oninput: move |evt| {
|
},
|
||||||
rsx_code.set(evt.value.clone());
|
}
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
button {
|
||||||
height: "100%",
|
height: "100%",
|
||||||
width: "10%",
|
width: "10%",
|
||||||
onclick: move |_|{
|
onclick: move |_|{
|
||||||
submitted_rsx_code.set(Some(rsx_code.get().clone()));
|
submitted_rsx_code.set(Some(rsx_code.get().clone()));
|
||||||
},
|
},
|
||||||
"submit"
|
"submit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +84,7 @@ fn Editable(cx: Scope<EditableProps>) -> Element {
|
|||||||
rsx_index.insert(
|
rsx_index.insert(
|
||||||
CodeLocation {
|
CodeLocation {
|
||||||
file: r"examples\hot_reload.rs".to_string(),
|
file: r"examples\hot_reload.rs".to_string(),
|
||||||
line: 95,
|
line: 93,
|
||||||
column: 15,
|
column: 15,
|
||||||
},
|
},
|
||||||
code.clone(),
|
code.clone(),
|
||||||
@@ -101,7 +99,7 @@ fn Editable(cx: Scope<EditableProps>) -> Element {
|
|||||||
},
|
},
|
||||||
|
|
||||||
p {
|
p {
|
||||||
"High-Five counter: {count}",
|
"High-Five counter: {count.to_string():?}",
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
div {
|
||||||
|
|||||||
@@ -188,40 +188,44 @@ pub fn rsx(s: TokenStream) -> TokenStream {
|
|||||||
{
|
{
|
||||||
use dioxus_rsx_interperter::captuered_context::CapturedContextBuilder;
|
use dioxus_rsx_interperter::captuered_context::CapturedContextBuilder;
|
||||||
|
|
||||||
let captured = CapturedContextBuilder::from_call_body(body);
|
match CapturedContextBuilder::from_call_body(body) {
|
||||||
quote::quote! {
|
Ok(captured) => {
|
||||||
{
|
quote::quote! {
|
||||||
let __line_num = get_line_num();
|
{
|
||||||
let __rsx_text_index: RsxTextIndex = cx.consume_context().unwrap();
|
let __line_num = get_line_num();
|
||||||
// only the insert the rsx text once
|
let __rsx_text_index: RsxTextIndex = cx.consume_context().unwrap();
|
||||||
if !__rsx_text_index.read().contains_key(&__line_num){
|
// only the insert the rsx text once
|
||||||
__rsx_text_index.insert(
|
if !__rsx_text_index.read().contains_key(&__line_num){
|
||||||
__line_num.clone(),
|
__rsx_text_index.insert(
|
||||||
#rsx_text.to_string(),
|
__line_num.clone(),
|
||||||
);
|
#rsx_text.to_string(),
|
||||||
}
|
);
|
||||||
LazyNodes::new(move |__cx|{
|
|
||||||
if let Some(__text) = {
|
|
||||||
let read = __rsx_text_index.read();
|
|
||||||
// clone prevents deadlock on nested rsx calls
|
|
||||||
read.get(&__line_num).cloned()
|
|
||||||
} {
|
|
||||||
match interpert_rsx(
|
|
||||||
__cx,
|
|
||||||
&__text,
|
|
||||||
#captured
|
|
||||||
){
|
|
||||||
Ok(vnode) => vnode,
|
|
||||||
Err(err) => __cx.text(format_args!("{:?}", err))
|
|
||||||
}
|
}
|
||||||
|
LazyNodes::new(move |__cx|{
|
||||||
|
if let Some(__text) = {
|
||||||
|
let read = __rsx_text_index.read();
|
||||||
|
// clone prevents deadlock on nested rsx calls
|
||||||
|
read.get(&__line_num).cloned()
|
||||||
|
} {
|
||||||
|
match interpert_rsx(
|
||||||
|
__cx,
|
||||||
|
&__text,
|
||||||
|
#captured
|
||||||
|
){
|
||||||
|
Ok(vnode) => vnode,
|
||||||
|
Err(err) => __cx.text(format_args!("{:?}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("rsx: line number {:?} not found in rsx index", __line_num);
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
panic!("rsx: line number {:?} not found in rsx index", __line_num);
|
.into()
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
Err(err) => err.into_compile_error().into(),
|
||||||
}
|
}
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "hot_reload"))]
|
#[cfg(not(feature = "hot_reload"))]
|
||||||
body.to_token_stream().into()
|
body.to_token_stream().into()
|
||||||
|
|||||||
@@ -9,8 +9,4 @@ license = "MIT/Apache-2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = { version = "1.0" }
|
proc-macro2 = { version = "1.0" }
|
||||||
syn = { version = "1.0", features = ["full", "extra-traits"] }
|
syn = { version = "1.0", features = ["full", "extra-traits"] }
|
||||||
quote = { version = "1.0", optional = true }
|
quote = { version = "1.0" }
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["to_tokens"]
|
|
||||||
to_tokens = ["quote"]
|
|
||||||
@@ -14,7 +14,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
#[cfg(feature = "to_tokens")]
|
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use syn::{
|
use syn::{
|
||||||
ext::IdentExt,
|
ext::IdentExt,
|
||||||
@@ -73,7 +72,6 @@ impl Parse for Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "to_tokens")]
|
|
||||||
impl ToTokens for Component {
|
impl ToTokens for Component {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
let name = &self.name;
|
let name = &self.name;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
#[cfg(feature = "to_tokens")]
|
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseBuffer, ParseStream},
|
parse::{Parse, ParseBuffer, ParseStream},
|
||||||
@@ -159,7 +158,6 @@ impl Parse for Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "to_tokens")]
|
|
||||||
impl ToTokens for Element {
|
impl ToTokens for Element {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
let name = &self.name;
|
let name = &self.name;
|
||||||
@@ -218,7 +216,6 @@ pub struct ElementAttrNamed {
|
|||||||
pub attr: ElementAttr,
|
pub attr: ElementAttr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "to_tokens")]
|
|
||||||
impl ToTokens for ElementAttrNamed {
|
impl ToTokens for ElementAttrNamed {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
let ElementAttrNamed { el_name, attr } = self;
|
let ElementAttrNamed { el_name, attr } = self;
|
||||||
|
|||||||
+128
-209
@@ -1,24 +1,60 @@
|
|||||||
#[cfg(feature = "to_tokens")]
|
use proc_macro2::{Span, TokenStream};
|
||||||
use ::quote::{quote, ToTokens};
|
|
||||||
use ::std::ops::Not;
|
use quote::{quote, ToTokens};
|
||||||
use ::syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
punctuated::Punctuated,
|
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
|
|
||||||
#[cfg(feature = "to_tokens")]
|
|
||||||
pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
|
pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
|
||||||
let IfmtInput {
|
// build format_literal
|
||||||
format_literal,
|
let mut format_literal = String::new();
|
||||||
positional_args,
|
let mut expr_counter = 0;
|
||||||
named_args,
|
for segment in input.segments.iter() {
|
||||||
} = input;
|
match segment {
|
||||||
|
Segment::Literal(s) => format_literal += &s,
|
||||||
|
Segment::Formatted {
|
||||||
|
format_args,
|
||||||
|
segment,
|
||||||
|
} => {
|
||||||
|
format_literal += "{";
|
||||||
|
match segment {
|
||||||
|
FormattedSegment::Expr(_) => {
|
||||||
|
format_literal += &expr_counter.to_string();
|
||||||
|
expr_counter += 1;
|
||||||
|
}
|
||||||
|
FormattedSegment::Ident(ident) => {
|
||||||
|
format_literal += &ident.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format_literal += ":";
|
||||||
|
format_literal += format_args;
|
||||||
|
format_literal += "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let named_args = named_args.into_iter().map(|(ident, expr)| {
|
let positional_args = input.segments.iter().filter_map(|seg| {
|
||||||
quote! {
|
if let Segment::Formatted { segment, .. } = seg {
|
||||||
#ident = #expr
|
if let FormattedSegment::Expr(expr) = segment {
|
||||||
|
Some(expr)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let named_args = input.segments.iter().filter_map(|seg| {
|
||||||
|
if let Segment::Formatted { segment, .. } = seg {
|
||||||
|
if let FormattedSegment::Ident(ident) = segment {
|
||||||
|
Some(quote! {#ident = #ident})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -34,219 +70,102 @@ pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
|
|||||||
#[allow(dead_code)] // dumb compiler does not see the struct being used...
|
#[allow(dead_code)] // dumb compiler does not see the struct being used...
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IfmtInput {
|
pub struct IfmtInput {
|
||||||
pub format_literal: LitStr,
|
pub segments: Vec<Segment>,
|
||||||
pub positional_args: Vec<Expr>,
|
|
||||||
pub named_args: Vec<(Ident, Expr)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IfmtInput {
|
impl IfmtInput {
|
||||||
fn parse_segments(self) -> Result<Self> {
|
pub fn from_str(input: &str) -> Result<Self> {
|
||||||
let IfmtInput {
|
let mut chars = input.chars().peekable();
|
||||||
mut format_literal,
|
let mut segments = Vec::new();
|
||||||
mut positional_args,
|
let mut current_literal = String::new();
|
||||||
mut named_args,
|
while let Some(c) = chars.next() {
|
||||||
} = self;
|
if c == '{' {
|
||||||
|
if let Some(c) = chars.next_if(|c| *c == '{') {
|
||||||
let s = format_literal.value();
|
current_literal.push(c);
|
||||||
let out_format_literal = &mut String::with_capacity(s.len());
|
continue;
|
||||||
|
|
||||||
let mut iterator = s.char_indices().peekable();
|
|
||||||
while let Some((i, c)) = iterator.next() {
|
|
||||||
out_format_literal.push(c);
|
|
||||||
if c != '{' {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// encountered `{`, let's see if it was `{{`
|
|
||||||
if let Some(&(_, '{')) = iterator.peek() {
|
|
||||||
let _ = iterator.next();
|
|
||||||
out_format_literal.push('{');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let (end, colon_or_closing_brace) = iterator
|
|
||||||
.find(|&(_, c)| c == '}' || c == ':')
|
|
||||||
.expect(concat!(
|
|
||||||
"Invalid format string literal\n",
|
|
||||||
"note: if you intended to print `{`, ",
|
|
||||||
"you can escape it using `{{`",
|
|
||||||
));
|
|
||||||
// We use defer to ensure all the `continue`s append the closing char.
|
|
||||||
let mut out_format_literal = defer(&mut *out_format_literal, |it| {
|
|
||||||
it.push(colon_or_closing_brace)
|
|
||||||
});
|
|
||||||
let out_format_literal: &mut String = *out_format_literal;
|
|
||||||
let mut arg = s[i + 1..end].trim();
|
|
||||||
if let Some("=") = arg.get(arg.len().saturating_sub(1)..) {
|
|
||||||
assert_eq!(
|
|
||||||
out_format_literal.pop(), // Remove the opening brace
|
|
||||||
Some('{'),
|
|
||||||
);
|
|
||||||
arg = &arg[..arg.len() - 1];
|
|
||||||
out_format_literal.push_str(arg);
|
|
||||||
out_format_literal.push_str(" = {");
|
|
||||||
}
|
|
||||||
if arg.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Segment {
|
|
||||||
Ident(Ident),
|
|
||||||
LitInt(LitInt),
|
|
||||||
}
|
|
||||||
let segments: Vec<Segment> = {
|
|
||||||
impl Parse for Segment {
|
|
||||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
|
||||||
let lookahead = input.lookahead1();
|
|
||||||
if lookahead.peek(Ident) {
|
|
||||||
input.parse().map(Segment::Ident)
|
|
||||||
} else if lookahead.peek(LitInt) {
|
|
||||||
input.parse().map(Segment::LitInt)
|
|
||||||
} else {
|
|
||||||
Err(lookahead.error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
match ::syn::parse::Parser::parse_str(
|
segments.push(Segment::Literal(current_literal));
|
||||||
Punctuated::<Segment, Token![.]>::parse_separated_nonempty,
|
current_literal = String::new();
|
||||||
arg,
|
let mut current_captured = String::new();
|
||||||
) {
|
while let Some(c) = chars.next() {
|
||||||
Ok(segments) => segments.into_iter().collect(),
|
if c == ':' {
|
||||||
Err(err) => return Err(err),
|
let mut current_format_args = String::new();
|
||||||
}
|
while let Some(c) = chars.next() {
|
||||||
};
|
if c == '}' {
|
||||||
match segments.len() {
|
segments.push(Segment::Formatted {
|
||||||
0 => unreachable!("`parse_separated_nonempty` returned empty"),
|
format_args: current_format_args,
|
||||||
1 => {
|
segment: FormattedSegment::parse(¤t_captured)?,
|
||||||
out_format_literal.push_str(arg);
|
});
|
||||||
match { segments }.pop().unwrap() {
|
break;
|
||||||
Segment::LitInt(_) => {
|
|
||||||
// found something like `{0}`, let `format_args!`
|
|
||||||
// handle it.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Segment::Ident(ident) => {
|
|
||||||
// if `ident = ...` is not yet among the extra args
|
|
||||||
if named_args.iter().all(|(it, _)| *it != ident) {
|
|
||||||
named_args.push((
|
|
||||||
ident.clone(),
|
|
||||||
parse_quote!(#ident), // Expr
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
current_format_args.push(c);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if c == '}' {
|
||||||
|
segments.push(Segment::Formatted {
|
||||||
|
format_args: String::new(),
|
||||||
|
segment: FormattedSegment::parse(¤t_captured)?,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current_captured.push(c);
|
||||||
}
|
}
|
||||||
_ => {
|
} else {
|
||||||
::std::fmt::Write::write_fmt(
|
current_literal.push(c);
|
||||||
out_format_literal,
|
|
||||||
format_args!("{}", positional_args.len()),
|
|
||||||
)
|
|
||||||
.expect("`usize` or `char` Display impl cannot panic");
|
|
||||||
let segments: Punctuated<TokenStream, Token![.]> = segments
|
|
||||||
.into_iter()
|
|
||||||
.map(|it| match it {
|
|
||||||
Segment::Ident(ident) => ident.into_token_stream(),
|
|
||||||
Segment::LitInt(literal) => literal.into_token_stream(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
positional_args.push(parse_quote! {
|
|
||||||
#segments
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
format_literal = LitStr::new(out_format_literal, format_literal.span());
|
segments.push(Segment::Literal(current_literal));
|
||||||
|
Ok(Self { segments })
|
||||||
Ok(Self {
|
|
||||||
format_literal,
|
|
||||||
positional_args,
|
|
||||||
named_args,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_positional_args(input: ParseStream) -> Result<Self> {
|
#[derive(Debug)]
|
||||||
let format_literal = input.parse()?;
|
pub enum Segment {
|
||||||
let mut positional_args = vec![];
|
Literal(String),
|
||||||
loop {
|
Formatted {
|
||||||
if input.parse::<Option<Token![,]>>()?.is_none() {
|
format_args: String,
|
||||||
return Ok(Self {
|
segment: FormattedSegment,
|
||||||
format_literal,
|
},
|
||||||
positional_args,
|
}
|
||||||
named_args: vec![],
|
|
||||||
});
|
#[derive(Debug)]
|
||||||
|
pub enum FormattedSegment {
|
||||||
|
Expr(Expr),
|
||||||
|
Ident(Ident),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormattedSegment {
|
||||||
|
fn parse(input: &str) -> Result<Self> {
|
||||||
|
if let Ok(ident) = parse_str::<Ident>(input) {
|
||||||
|
if &ident.to_string() == input {
|
||||||
|
return Ok(Self::Ident(ident));
|
||||||
}
|
}
|
||||||
if input.peek(Ident) && input.peek2(Token![=]) && input.peek3(Token![=]).not() {
|
|
||||||
// Found a positional parameter
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
positional_args.push(input.parse()?);
|
|
||||||
}
|
}
|
||||||
let named_args = Punctuated::<_, Token![,]>::parse_terminated_with(input, |input| {
|
if let Ok(expr) = parse_str(&("{".to_string() + input + "}")) {
|
||||||
Ok({
|
Ok(Self::Expr(expr))
|
||||||
let name: Ident = input.parse()?;
|
} else {
|
||||||
let _: Token![=] = input.parse()?;
|
Err(Error::new(
|
||||||
let expr: Expr = input.parse()?;
|
Span::call_site(),
|
||||||
(name, expr)
|
"Expected Ident or Expression",
|
||||||
})
|
))
|
||||||
})?
|
}
|
||||||
.into_iter()
|
}
|
||||||
.collect();
|
}
|
||||||
|
|
||||||
Ok(Self {
|
impl ToTokens for FormattedSegment {
|
||||||
format_literal,
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
positional_args,
|
match self {
|
||||||
named_args,
|
Self::Expr(expr) => expr.to_tokens(tokens),
|
||||||
})
|
Self::Ident(ident) => ident.to_tokens(tokens),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for IfmtInput {
|
impl Parse for IfmtInput {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
Self::parse_positional_args(input).and_then(|new| new.parse_segments())
|
let input: LitStr = input.parse()?;
|
||||||
|
let input_str = input.value();
|
||||||
|
IfmtInput::from_str(&input_str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn defer<'a, T: 'a, Drop: 'a>(x: T, drop: Drop) -> impl ::core::ops::DerefMut<Target = T> + 'a
|
|
||||||
where
|
|
||||||
Drop: FnOnce(T),
|
|
||||||
{
|
|
||||||
use ::core::mem::ManuallyDrop;
|
|
||||||
struct Ret<T, Drop>(ManuallyDrop<T>, ManuallyDrop<Drop>)
|
|
||||||
where
|
|
||||||
Drop: FnOnce(T);
|
|
||||||
impl<T, Drop> ::core::ops::Drop for Ret<T, Drop>
|
|
||||||
where
|
|
||||||
Drop: FnOnce(T),
|
|
||||||
{
|
|
||||||
fn drop(&'_ mut self) {
|
|
||||||
use ::core::ptr;
|
|
||||||
unsafe {
|
|
||||||
// # Safety
|
|
||||||
//
|
|
||||||
// - This is the canonical example of using `ManuallyDrop`.
|
|
||||||
let value = ManuallyDrop::into_inner(ptr::read(&self.0));
|
|
||||||
let drop = ManuallyDrop::into_inner(ptr::read(&self.1));
|
|
||||||
drop(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T, Drop> ::core::ops::Deref for Ret<T, Drop>
|
|
||||||
where
|
|
||||||
Drop: FnOnce(T),
|
|
||||||
{
|
|
||||||
type Target = T;
|
|
||||||
#[inline]
|
|
||||||
fn deref(&'_ self) -> &'_ Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T, Drop> ::core::ops::DerefMut for Ret<T, Drop>
|
|
||||||
where
|
|
||||||
Drop: FnOnce(T),
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn deref_mut(&'_ mut self) -> &'_ mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ret(ManuallyDrop::new(x), ManuallyDrop::new(drop))
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
#[cfg(feature = "to_tokens")]
|
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
@@ -79,7 +78,6 @@ impl Parse for BodyNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "to_tokens")]
|
|
||||||
impl ToTokens for BodyNode {
|
impl ToTokens for BodyNode {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
match &self {
|
match &self {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use dioxus_core::{Listener, VNode};
|
use dioxus_core::{Listener, VNode};
|
||||||
use dioxus_rsx::{BodyNode, CallBody, Component, ElementAttr, ElementAttrNamed, IfmtInput};
|
use dioxus_rsx::{
|
||||||
|
BodyNode, CallBody, Component, ElementAttr, ElementAttrNamed, IfmtInput, Segment,
|
||||||
|
};
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use syn::Expr;
|
use syn::{Expr, Result};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CapturedContextBuilder {
|
pub struct CapturedContextBuilder {
|
||||||
@@ -24,15 +26,15 @@ impl CapturedContextBuilder {
|
|||||||
self.captured_expressions.extend(other.captured_expressions);
|
self.captured_expressions.extend(other.captured_expressions);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_call_body(body: CallBody) -> Self {
|
pub fn from_call_body(body: CallBody) -> Result<Self> {
|
||||||
let mut new = Self::default();
|
let mut new = Self::default();
|
||||||
for node in body.roots {
|
for node in body.roots {
|
||||||
new.extend(Self::find_captured(node));
|
new.extend(Self::find_captured(node)?);
|
||||||
}
|
}
|
||||||
new
|
Ok(new)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_captured(node: BodyNode) -> Self {
|
fn find_captured(node: BodyNode) -> Result<Self> {
|
||||||
let mut captured = CapturedContextBuilder::default();
|
let mut captured = CapturedContextBuilder::default();
|
||||||
match node {
|
match node {
|
||||||
BodyNode::Element(el) => {
|
BodyNode::Element(el) => {
|
||||||
@@ -40,7 +42,7 @@ impl CapturedContextBuilder {
|
|||||||
match attr.attr {
|
match attr.attr {
|
||||||
ElementAttr::AttrText { name, value } => {
|
ElementAttr::AttrText { name, value } => {
|
||||||
let (name, value_tokens) = (name.to_string(), value.to_token_stream());
|
let (name, value_tokens) = (name.to_string(), value.to_token_stream());
|
||||||
let formated: IfmtInput = syn::parse2(value_tokens).unwrap();
|
let formated: IfmtInput = syn::parse2(value_tokens)?;
|
||||||
captured.attributes.insert(name, formated);
|
captured.attributes.insert(name, formated);
|
||||||
}
|
}
|
||||||
ElementAttr::AttrExpression { name: _, value } => {
|
ElementAttr::AttrExpression { name: _, value } => {
|
||||||
@@ -48,7 +50,7 @@ impl CapturedContextBuilder {
|
|||||||
}
|
}
|
||||||
ElementAttr::CustomAttrText { name, value } => {
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
let (name, value_tokens) = (name.value(), value.to_token_stream());
|
let (name, value_tokens) = (name.value(), value.to_token_stream());
|
||||||
let formated: IfmtInput = syn::parse2(value_tokens).unwrap();
|
let formated: IfmtInput = syn::parse2(value_tokens)?;
|
||||||
captured.attributes.insert(name, formated);
|
captured.attributes.insert(name, formated);
|
||||||
}
|
}
|
||||||
ElementAttr::CustomAttrExpression { name: _, value } => {
|
ElementAttr::CustomAttrExpression { name: _, value } => {
|
||||||
@@ -58,7 +60,7 @@ impl CapturedContextBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for child in el.children {
|
for child in el.children {
|
||||||
captured.extend(Self::find_captured(child));
|
captured.extend(Self::find_captured(child)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BodyNode::Component(comp) => {
|
BodyNode::Component(comp) => {
|
||||||
@@ -71,7 +73,7 @@ impl CapturedContextBuilder {
|
|||||||
}
|
}
|
||||||
BodyNode::RawExpr(_) => captured.iterators.push(node),
|
BodyNode::RawExpr(_) => captured.iterators.push(node),
|
||||||
}
|
}
|
||||||
captured
|
Ok(captured)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,34 +97,41 @@ impl ToTokens for CapturedContextBuilder {
|
|||||||
BodyNode::RawExpr(expr) => expr.to_token_stream().to_string(),
|
BodyNode::RawExpr(expr) => expr.to_token_stream().to_string(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
});
|
});
|
||||||
let captured_named: Vec<_> = attributes
|
let captured: Vec<_> = attributes
|
||||||
.iter()
|
.values()
|
||||||
.map(|(_, fmt)| fmt.named_args.iter())
|
.chain(text.iter())
|
||||||
.chain(text.iter().map(|fmt| fmt.named_args.iter()))
|
.map(|input| input.segments.iter())
|
||||||
.flatten()
|
.flatten()
|
||||||
|
.filter_map(|seg| match seg {
|
||||||
|
Segment::Formatted {
|
||||||
|
format_args,
|
||||||
|
segment,
|
||||||
|
} => {
|
||||||
|
let expr = segment.to_token_stream();
|
||||||
|
let as_string = expr.to_string();
|
||||||
|
let format_expr = if format_args.is_empty() {
|
||||||
|
"{".to_string() + format_args + "}"
|
||||||
|
} else {
|
||||||
|
"{".to_string() + ":" + format_args + "}"
|
||||||
|
};
|
||||||
|
Some(quote! {
|
||||||
|
FormattedArg{
|
||||||
|
expr: #as_string,
|
||||||
|
format_args: #format_args,
|
||||||
|
result: format!(#format_expr, #expr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let captured_expr: Vec<_> = attributes
|
|
||||||
.iter()
|
|
||||||
.map(|(_, fmt)| fmt.positional_args.iter())
|
|
||||||
.chain(text.iter().map(|fmt| fmt.positional_args.iter()))
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
let captured_names = captured_named.iter().map(|(n, _)| n.to_string()).chain(
|
|
||||||
captured_expr
|
|
||||||
.iter()
|
|
||||||
.map(|expr| expr.to_token_stream().to_string()),
|
|
||||||
);
|
|
||||||
let captured_expr = captured_named
|
|
||||||
.iter()
|
|
||||||
.map(|(_, e)| e)
|
|
||||||
.chain(captured_expr.iter().map(|expr| *expr));
|
|
||||||
let captured_attr_expressions_text = captured_expressions
|
let captured_attr_expressions_text = captured_expressions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| format!("{}", e.to_token_stream()));
|
.map(|e| format!("{}", e.to_token_stream()));
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
CapturedContext {
|
CapturedContext {
|
||||||
captured: IfmtArgs{
|
captured: IfmtArgs{
|
||||||
named_args: vec![#((#captured_names, #captured_expr.to_string())),*]
|
named_args: vec![#(#captured),*]
|
||||||
},
|
},
|
||||||
components: vec![#((#compontents_str, #components)),*],
|
components: vec![#((#compontents_str, #components)),*],
|
||||||
iterators: vec![#((#iterators_str, #iterators)),*],
|
iterators: vec![#((#iterators_str, #iterators)),*],
|
||||||
@@ -149,6 +158,12 @@ pub struct CapturedContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct IfmtArgs {
|
pub struct IfmtArgs {
|
||||||
// live reload only supports named arguments
|
// map expressions to the value string they produced
|
||||||
pub named_args: Vec<(&'static str, String)>,
|
pub named_args: Vec<FormattedArg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FormattedArg {
|
||||||
|
pub expr: &'static str,
|
||||||
|
pub format_args: &'static str,
|
||||||
|
pub result: String,
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use dioxus_core::{Attribute, AttributeValue, NodeFactory, VNode};
|
use dioxus_core::{Attribute, AttributeValue, NodeFactory, VNode};
|
||||||
use dioxus_rsx::{BodyNode, CallBody, ElementAttr};
|
use dioxus_rsx::{BodyNode, CallBody, ElementAttr, IfmtInput, Segment};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use std::str::FromStr;
|
|
||||||
use syn::{parse2, parse_str, Expr};
|
use syn::{parse2, parse_str, Expr};
|
||||||
|
|
||||||
use crate::attributes::attrbute_to_static_str;
|
use crate::attributes::attrbute_to_static_str;
|
||||||
@@ -9,28 +8,31 @@ use crate::captuered_context::{CapturedContext, IfmtArgs};
|
|||||||
use crate::elements::element_to_static_str;
|
use crate::elements::element_to_static_str;
|
||||||
use crate::error::{Error, RecompileReason};
|
use crate::error::{Error, RecompileReason};
|
||||||
|
|
||||||
#[derive(Debug)]
|
struct InterpertedIfmt(IfmtInput);
|
||||||
enum Segment {
|
|
||||||
Ident(String),
|
|
||||||
Literal(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InterperedIfmt {
|
impl InterpertedIfmt {
|
||||||
segments: Vec<Segment>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InterperedIfmt {
|
|
||||||
fn resolve(&self, captured: &IfmtArgs) -> String {
|
fn resolve(&self, captured: &IfmtArgs) -> String {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
for seg in &self.segments {
|
for seg in &self.0.segments {
|
||||||
match seg {
|
match seg {
|
||||||
Segment::Ident(name) => {
|
Segment::Formatted {
|
||||||
let (_, value) = captured
|
segment,
|
||||||
|
format_args,
|
||||||
|
} => {
|
||||||
|
let expr = segment.to_token_stream();
|
||||||
|
let expr_str = expr.to_string();
|
||||||
|
let expr: Expr = parse2(expr).unwrap();
|
||||||
|
let formatted = captured
|
||||||
.named_args
|
.named_args
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(n, _)| *n == name)
|
.find(|fmted| {
|
||||||
.expect(format!("could not resolve {}", name).as_str());
|
parse_str::<Expr>(fmted.expr).unwrap() == expr
|
||||||
result.push_str(value);
|
&& fmted.format_args == format_args
|
||||||
|
})
|
||||||
|
.expect(
|
||||||
|
format!("could not resolve {{{}:{}}}", expr_str, format_args).as_str(),
|
||||||
|
);
|
||||||
|
result.push_str(&formatted.result);
|
||||||
}
|
}
|
||||||
Segment::Literal(lit) => result.push_str(lit),
|
Segment::Literal(lit) => result.push_str(lit),
|
||||||
}
|
}
|
||||||
@@ -39,52 +41,6 @@ impl InterperedIfmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>(
|
pub fn build<'a>(
|
||||||
rsx: CallBody,
|
rsx: CallBody,
|
||||||
mut ctx: CapturedContext<'a>,
|
mut ctx: CapturedContext<'a>,
|
||||||
@@ -105,7 +61,9 @@ fn build_node<'a>(
|
|||||||
let bump = factory.bump();
|
let bump = factory.bump();
|
||||||
match node {
|
match node {
|
||||||
BodyNode::Text(text) => {
|
BodyNode::Text(text) => {
|
||||||
let ifmt: InterperedIfmt = text.value().parse().unwrap();
|
let ifmt = InterpertedIfmt(
|
||||||
|
IfmtInput::from_str(&text.value()).map_err(|err| Error::ParseError(err))?,
|
||||||
|
);
|
||||||
let text = bump.alloc(ifmt.resolve(&ctx.captured));
|
let text = bump.alloc(ifmt.resolve(&ctx.captured));
|
||||||
Ok(factory.text(format_args!("{}", text)))
|
Ok(factory.text(format_args!("{}", text)))
|
||||||
}
|
}
|
||||||
@@ -114,13 +72,21 @@ fn build_node<'a>(
|
|||||||
for attr in &el.attributes {
|
for attr in &el.attributes {
|
||||||
match &attr.attr {
|
match &attr.attr {
|
||||||
ElementAttr::AttrText { .. } | ElementAttr::CustomAttrText { .. } => {
|
ElementAttr::AttrText { .. } | ElementAttr::CustomAttrText { .. } => {
|
||||||
let (name, value): (String, InterperedIfmt) = match &attr.attr {
|
let (name, value): (String, InterpertedIfmt) = match &attr.attr {
|
||||||
ElementAttr::AttrText { name, value } => {
|
ElementAttr::AttrText { name, value } => (
|
||||||
(name.to_string(), value.value().parse().unwrap())
|
name.to_string(),
|
||||||
}
|
InterpertedIfmt(
|
||||||
ElementAttr::CustomAttrText { name, value } => {
|
IfmtInput::from_str(&value.value())
|
||||||
(name.value(), value.value().parse().unwrap())
|
.map_err(|err| Error::ParseError(err))?,
|
||||||
}
|
),
|
||||||
|
),
|
||||||
|
ElementAttr::CustomAttrText { name, value } => (
|
||||||
|
name.value(),
|
||||||
|
InterpertedIfmt(
|
||||||
|
IfmtInput::from_str(&value.value())
|
||||||
|
.map_err(|err| Error::ParseError(err))?,
|
||||||
|
),
|
||||||
|
),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -145,7 +111,6 @@ fn build_node<'a>(
|
|||||||
ElementAttr::CustomAttrExpression { name, value } => {
|
ElementAttr::CustomAttrExpression { name, value } => {
|
||||||
(name.value(), value)
|
(name.value(), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if let Some((_, resulting_value)) = ctx
|
if let Some((_, resulting_value)) = ctx
|
||||||
@@ -219,7 +184,9 @@ fn build_node<'a>(
|
|||||||
None,
|
None,
|
||||||
)),
|
)),
|
||||||
Some(lit) => {
|
Some(lit) => {
|
||||||
let ifmt: InterperedIfmt = lit.value().parse().unwrap();
|
let ifmt: InterpertedIfmt = InterpertedIfmt(
|
||||||
|
parse_str(&lit.value()).map_err(|err| Error::ParseError(err))?,
|
||||||
|
);
|
||||||
let key = bump.alloc(ifmt.resolve(&ctx.captured));
|
let key = bump.alloc(ifmt.resolve(&ctx.captured));
|
||||||
|
|
||||||
Ok(factory.raw_element(
|
Ok(factory.raw_element(
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@ pub mod prelude {
|
|||||||
|
|
||||||
#[cfg(feature = "hot_reload")]
|
#[cfg(feature = "hot_reload")]
|
||||||
pub use dioxus_rsx_interperter::{
|
pub use dioxus_rsx_interperter::{
|
||||||
captuered_context::{CapturedContext, IfmtArgs},
|
captuered_context::{CapturedContext, FormattedArg, IfmtArgs},
|
||||||
get_line_num, interpert_rsx, with_hot_reload, CodeLocation, RsxTextIndex,
|
get_line_num, interpert_rsx, with_hot_reload, CodeLocation, RsxTextIndex,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user