Răsfoiți Sursa

Fix parsing of braced expressions followed by a method (#4035)

* Fix parsing of braced expressions followed by a method

* Improves the logic for parsing braced expressions

* Fix hot-reload issue

* Added cases for braced expressions followed by identifiers or string literals
Juan Vásquez 1 lună în urmă
părinte
comite
71d85e6dd5
2 a modificat fișierele cu 53 adăugiri și 3 ștergeri
  1. 28 3
      packages/rsx/src/raw_expr.rs
  2. 25 0
      packages/rsx/tests/parsing.rs

+ 28 - 3
packages/rsx/src/raw_expr.rs

@@ -1,4 +1,4 @@
-use proc_macro2::TokenStream as TokenStream2;
+use proc_macro2::{Delimiter, TokenStream as TokenStream2, TokenTree};
 use quote::ToTokens;
 use std::hash;
 use syn::{parse::Parse, spanned::Spanned, token::Brace, Expr};
@@ -15,8 +15,33 @@ pub struct PartialExpr {
 
 impl Parse for PartialExpr {
     fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        // Parse as an expression if there is no brace
-        if !input.peek(syn::token::Brace) {
+        // Input is a braced expression if it's a braced group
+        // followed by either of the following:
+        // - the end of the stream
+        // - a comma
+        // - another braced group
+        // - an identifier
+        // - a string literal
+        let mut is_braced = false;
+        if let Some((TokenTree::Group(group), next)) = input.fork().cursor().token_tree() {
+            let next_char_is_a_comma = next.punct().is_some_and(|(tt, _)| tt.as_char() == ',');
+            let next_is_a_braced_exp = next.group(Delimiter::Brace).is_some();
+            let next_is_an_ident = next.ident().is_some();
+            let next_is_a_string_literal = next.literal().is_some();
+
+            if group.delimiter() == Delimiter::Brace
+                && (next.eof()
+                    || next_char_is_a_comma
+                    || next_is_a_braced_exp
+                    || next_is_an_ident
+                    || next_is_a_string_literal)
+            {
+                is_braced = true
+            }
+        };
+
+        // Parse as an expression if it's not braced
+        if !is_braced {
             let expr = input.parse::<syn::Expr>()?;
             return Ok(Self {
                 brace: None,

+ 25 - 0
packages/rsx/tests/parsing.rs

@@ -71,6 +71,7 @@ fn complex_kitchen_sink() {
         // complex_carry
         button {
             class: "flex items-center pl-3 py-3 pr-2 text-gray-500 hover:bg-indigo-50 rounded",
+            width: {"100%"}.to_string(),
             onclick: move |evt| {
                 show_user_menu.set(!show_user_menu.get());
                 evt.cancel_bubble();
@@ -167,3 +168,27 @@ fn key_cannot_be_static() {
     println!("{:?}", parsed.body.diagnostics);
     assert!(!parsed.body.diagnostics.is_empty());
 }
+
+#[test]
+fn braced_expressions() {
+    let item = quote::quote! {
+        div {
+            width: {100} - 50,
+            width: {"100%"}.to_string(),
+            width: {|| "100%"}(),
+        }
+        // Partial expressions in braces rsx should be allowed and output as-is
+        // for autocomplete
+        {partial.}
+        div {}
+        {partial.}
+        // Comments should be ignored
+        div {}
+        {partial.}
+        "hello world"
+        {partial.}
+        if true {}
+    };
+
+    let _cb: CallBody = syn::parse2(item).unwrap();
+}