diff --git a/packages/autofmt/README.md b/packages/autofmt/README.md index bcacee982..eec3b590e 100644 --- a/packages/autofmt/README.md +++ b/packages/autofmt/README.md @@ -18,3 +18,71 @@ Sorted roughly in order of what's possible # Technique + + +div { + div {} + div {} +} + + +div + +possible line break +div +div + + + +string of possible items within a nesting +div { + attr_pair + expr + text + comment +} +a nesting is either a component or an element + +idea: +collect all items into a queue +q +```rust +section { + div { + h1 { p { "asdasd" } } + h1 { p { "asdasd" } } + } +} + +section {} +``` + + +// space +// space +// space + + +3 - section +3 - section div +3 - section div h1 +3 - section div h1 p +3 - section div h1 p text +3 - section +3 - section div +3 - section div h1 +3 - section div h1 p +3 - section div h1 p text + +block + +- when we hit the end of a trail, we can make a decision what needs to be hard breaked +- most nestings cannot be merged into a single one, so at some point we need to write the line break +- this is the scan section. we scan forward until it's obvious where to place a hard break +- when a line is finished, we can print it out by unloading our queued items +- never double nested + + +Terms +- break is a whitespace than can flex, dependent on the situation +- ‹⁠› diff --git a/packages/autofmt/src/buffer.rs b/packages/autofmt/src/buffer.rs index abf1ac623..efb4754c9 100644 --- a/packages/autofmt/src/buffer.rs +++ b/packages/autofmt/src/buffer.rs @@ -67,8 +67,8 @@ impl Buffer { match node { BodyNode::Element(el) => self.write_element(el), BodyNode::Component(component) => self.write_component(component), - BodyNode::Text(text) => self.write_text(text), - BodyNode::RawExpr(exp) => self.write_raw_expr(exp), + BodyNode::Text(text) => self.write_text(&text.text), + BodyNode::RawExpr(exp) => self.write_raw_expr(&exp.expr), } } @@ -129,7 +129,7 @@ impl Buffer { for (idx, child) in children.iter().enumerate() { match child { // check if the expr is a short - BodyNode::RawExpr(_) => { + BodyNode::RawExpr { .. } => { self.tabbed_line()?; self.write_ident(child)?; if idx != last_child - 1 { diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index e886bfca0..916545deb 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -276,7 +276,7 @@ impl Buffer { } match children { - [BodyNode::Text(ref text)] => Some(text.value().len()), + [BodyNode::Text(ref text)] => Some(text.text.value().len()), [BodyNode::Component(ref comp)] => { let attr_len = self.field_len(&comp.fields, &comp.manual_props); @@ -290,7 +290,7 @@ impl Buffer { } [BodyNode::RawExpr(ref expr)] => { // TODO: let rawexprs to be inlined - get_expr_length(expr) + get_expr_length(&expr.expr) } [BodyNode::Element(ref el)] => { let attr_len = self.is_short_attrs(&el.attributes); @@ -301,8 +301,10 @@ impl Buffer { if el.children.len() == 1 { if let BodyNode::Text(ref text) = el.children[0] { - if text.value().len() + el.name.to_string().len() + attr_len < 80 { - return Some(text.value().len() + el.name.to_string().len() + attr_len); + if text.text.value().len() + el.name.to_string().len() + attr_len < 80 { + return Some( + text.text.value().len() + el.name.to_string().len() + attr_len, + ); } } } @@ -316,8 +318,8 @@ impl Buffer { for item in items { match item { BodyNode::Component(_) | BodyNode::Element(_) => return None, - BodyNode::Text(text) => total_count += text.value().len(), - BodyNode::RawExpr(expr) => match get_expr_length(expr) { + BodyNode::Text(text) => total_count += text.text.value().len(), + BodyNode::RawExpr(expr) => match get_expr_length(&expr.expr) { Some(len) => total_count += len, None => return None, }, diff --git a/packages/autofmt/src/expr.rs b/packages/autofmt/src/expr.rs index 55fa5ea60..48ea6fcc7 100644 --- a/packages/autofmt/src/expr.rs +++ b/packages/autofmt/src/expr.rs @@ -18,6 +18,24 @@ impl Buffer { let end = placement.end(); // let num_spaces_desired = (self.indent * 4) as isize; + // print comments + // let mut queued_comments = vec![]; + // let mut offset = 2; + // loop { + // let line = &self.src[start.line - offset]; + // if line.trim_start().starts_with("//") { + // queued_comments.push(line); + // } else { + // break; + // } + + // offset += 1; + // } + // let had_comments = !queued_comments.is_empty(); + // for comment in queued_comments.into_iter().rev() { + // writeln!(self.buf, "{}", comment)?; + // } + // if the expr is on one line, just write it directly if start.line == end.line { write!( @@ -30,7 +48,6 @@ impl Buffer { // If the expr is multiline, we want to collect all of its lines together and write them out properly // This involves unshifting the first line if it's aligned - let first_line = &self.src[start.line - 1]; write!( self.buf, diff --git a/packages/autofmt/tests/block.rs b/packages/autofmt/tests/block.rs new file mode 100644 index 000000000..6b1298b57 --- /dev/null +++ b/packages/autofmt/tests/block.rs @@ -0,0 +1,38 @@ +use std::collections::VecDeque; + +use dioxus_rsx::{BodyNode, CallBody}; + +const LINE_WIDTH: usize = 80; + +pub struct Printer { + buf: String, + // queue: Vec, +} + +pub enum Break { + // Space flexes to line if need be + Space, + + // Line always forces a new line + // Comments are an example of this + Line, +} + +// enum Item { +// BreakBegin, +// BreakEnd, +// Text(Cow<'static, str>), +// } + +impl Printer { + fn doit(&mut self, body: CallBody) { + for node in body.roots {} + } + fn node(&mut self, node: BodyNode) {} +} + +#[test] +fn it_works() { + let src = r#"div {}"#; + let contents: CallBody = syn::parse_str(src).unwrap(); +} diff --git a/packages/autofmt/tests/samples.rs b/packages/autofmt/tests/samples.rs index 954fa8201..1f9c359c1 100644 --- a/packages/autofmt/tests/samples.rs +++ b/packages/autofmt/tests/samples.rs @@ -27,3 +27,6 @@ twoway! ("tinynoopt" => tinynoopt); twoway! ("long" => long); twoway! ("key" => key); + +// Disabled because we can't handle comments on exprs yet +twoway! ("multirsx" => multirsx); diff --git a/packages/autofmt/tests/samples/multirsx.rsx b/packages/autofmt/tests/samples/multirsx.rsx new file mode 100644 index 000000000..7c7afd646 --- /dev/null +++ b/packages/autofmt/tests/samples/multirsx.rsx @@ -0,0 +1,30 @@ +rsx! { + + // hi + div {} + + // hi + div { abcd, ball, s } + + // + // + // + div { abcd, ball, s } + + // + // + // + div { + // + abcd, + + // + ball, + + // + s, + + // + "asdasd" + } +} diff --git a/packages/rsx/README.md b/packages/rsx/README.md new file mode 100644 index 000000000..d1a21bba3 --- /dev/null +++ b/packages/rsx/README.md @@ -0,0 +1,3 @@ +# The actual RSX language implemented using syn parsers. + +Unlike many macros, actually respects whitespace. diff --git a/packages/rsx/src/component.rs b/packages/rsx/src/component.rs index 685ea0124..ae00926dd 100644 --- a/packages/rsx/src/component.rs +++ b/packages/rsx/src/component.rs @@ -238,11 +238,9 @@ impl Parse for ComponentField { let forked = input.fork(); let t: LitStr = forked.parse()?; // the string literal must either be the end of the input or a followed by a comma - if forked.is_empty() || forked.peek(Token![,]) { - if is_literal_foramtted(&t) { - let content = ContentField::Formatted(input.parse()?); - return Ok(Self { name, content }); - } + if (forked.is_empty() || forked.peek(Token![,])) && is_literal_foramtted(&t) { + let content = ContentField::Formatted(input.parse()?); + return Ok(Self { name, content }); } } diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index e0c502660..85638f3a7 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -18,6 +18,9 @@ mod component; mod element; mod ifmt; mod node; +mod whitespace; +mod raw_expr; +mod text; // Re-export the namespaces into each other pub use component::*; diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index e85329e6a..34d26e2a2 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -1,3 +1,5 @@ +use crate::{raw_expr::RawExprNode, text::TextNode}; + use super::*; use proc_macro2::{Span, TokenStream as TokenStream2}; @@ -5,7 +7,7 @@ use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned, - token, Expr, LitStr, Result, + token, LitStr, Result, }; /* @@ -16,17 +18,17 @@ Parse -> "text {with_args}" -> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr */ -#[derive(PartialEq, Eq, Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum BodyNode { Element(Element), Component(Component), - Text(LitStr), - RawExpr(Expr), + Text(TextNode), + RawExpr(RawExprNode), } impl BodyNode { pub fn is_litstr(&self) -> bool { - matches!(self, BodyNode::Text(_)) + matches!(self, BodyNode::Text { .. }) } pub fn span(&self) -> Span { @@ -83,12 +85,11 @@ impl Parse for BodyNode { // crate::Input:: {} if body_stream.peek(token::Brace) { Component::validate_component_path(&path)?; - return Ok(BodyNode::Component(stream.parse()?)); } } - Ok(BodyNode::RawExpr(stream.parse::()?)) + Ok(BodyNode::RawExpr(stream.parse()?)) } } @@ -97,8 +98,8 @@ impl ToTokens for BodyNode { match &self { BodyNode::Element(el) => el.to_tokens(tokens), BodyNode::Component(comp) => comp.to_tokens(tokens), - BodyNode::Text(txt) => tokens.append_all(quote! { - __cx.text(format_args_f!(#txt)) + BodyNode::Text(text) => tokens.append_all(quote! { + __cx.text(format_args_f!(#text)) }), BodyNode::RawExpr(exp) => tokens.append_all(quote! { __cx.fragment_from_iter(#exp) diff --git a/packages/rsx/src/raw_expr.rs b/packages/rsx/src/raw_expr.rs new file mode 100644 index 000000000..1a0b48ce6 --- /dev/null +++ b/packages/rsx/src/raw_expr.rs @@ -0,0 +1,41 @@ +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{ToTokens, TokenStreamExt}; +use syn::{parse::Parse, spanned::Spanned, Expr}; + +use crate::whitespace::Whitespace; + +#[derive(Debug, PartialEq, Eq)] +pub struct RawExprNode { + pub ws: Whitespace, + pub expr: Expr, +} + +impl RawExprNode { + pub fn span(&self) -> Span { + self.expr.span() + } +} + +impl Parse for RawExprNode { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let before_cursor = input.cursor(); + + let expr = input.parse()?; + + let after = input.cursor(); + + dbg!(before_cursor.span().start(), after.span().end()); + + Ok(Self { + ws: Whitespace::default(), + expr, + }) + } +} + +impl ToTokens for RawExprNode { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let expr = &self.expr; + tokens.append_all(quote::quote! { #expr }); + } +} diff --git a/packages/rsx/src/text.rs b/packages/rsx/src/text.rs new file mode 100644 index 000000000..2be647394 --- /dev/null +++ b/packages/rsx/src/text.rs @@ -0,0 +1,33 @@ +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::{parse::Parse, LitStr}; + +use crate::whitespace::Whitespace; + +#[derive(Debug, PartialEq, Eq)] +pub struct TextNode { + pub ws: Whitespace, + pub text: LitStr, +} + +impl TextNode { + pub fn span(&self) -> Span { + self.text.span() + } +} + +impl Parse for TextNode { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + Ok(Self { + ws: Whitespace::default(), + text: input.parse()?, + }) + } +} + +impl ToTokens for TextNode { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let text = &self.text; + tokens.append_all(quote! { #text }); + } +} diff --git a/packages/rsx/src/whitespace.rs b/packages/rsx/src/whitespace.rs new file mode 100644 index 000000000..c409c8880 --- /dev/null +++ b/packages/rsx/src/whitespace.rs @@ -0,0 +1,17 @@ +#![allow(dead_code)] + +use proc_macro2::Span; + +#[derive(Debug, Default)] +pub struct Whitespace { + span_start: Option, + span_end: Option, +} + +// right now we dont care if whitespace is different, sorry +impl Eq for Whitespace {} +impl PartialEq for Whitespace { + fn eq(&self, _other: &Self) -> bool { + true + } +}