Parcourir la source

wip: add whitespace parsing to rsx

Jonathan Kelley il y a 2 ans
Parent
commit
181a51a63b

+ 68 - 0
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
+- ‹⁠›

+ 3 - 3
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 {

+ 8 - 6
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,
                         },

+ 18 - 1
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,

+ 38 - 0
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<Item>,
+}
+
+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();
+}

+ 3 - 0
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);

+ 30 - 0
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"
+    }
+}

+ 3 - 0
packages/rsx/README.md

@@ -0,0 +1,3 @@
+# The actual RSX language implemented using syn parsers.
+
+Unlike many macros, actually respects whitespace.

+ 3 - 5
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 });
             }
         }
 

+ 3 - 0
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::*;

+ 10 - 9
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::<InputProps<'_, i32> {}
             if body_stream.peek(token::Brace) {
                 Component::validate_component_path(&path)?;
-
                 return Ok(BodyNode::Component(stream.parse()?));
             }
         }
 
-        Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
+        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)

+ 41 - 0
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<Self> {
+        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 });
+    }
+}

+ 33 - 0
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<Self> {
+        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 });
+    }
+}

+ 17 - 0
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>,
+    span_end: Option<Span>,
+}
+
+// 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
+    }
+}