Prechádzať zdrojové kódy

call_with_explicit_closure support closure in block (#4031)

* call_with_explicit_closure support closure in block

* fix test
Clouds 1 mesiac pred
rodič
commit
28a85ab614
2 zmenil súbory, kde vykonal 92 pridanie a 17 odobranie
  1. 81 17
      packages/rsx/src/attribute.rs
  2. 11 0
      packages/rsx/tests/parsing.rs

+ 81 - 17
packages/rsx/src/attribute.rs

@@ -25,7 +25,8 @@ use syn::{
     parse::{Parse, ParseStream},
     parse_quote,
     spanned::Spanned,
-    Block, Expr, ExprClosure, ExprIf, Ident, Lit, LitBool, LitFloat, LitInt, LitStr, Token,
+    Block, Expr, ExprBlock, ExprClosure, ExprIf, Ident, Lit, LitBool, LitFloat, LitInt, LitStr,
+    Stmt, Token,
 };
 
 /// A property value in the from of a `name: value` pair with an optional comma.
@@ -233,25 +234,55 @@ impl Attribute {
                         )
                     }
                 }
-                AttributeValue::EventTokens(tokens) => match &self.name {
-                    AttributeName::BuiltIn(name) => {
-                        let event_tokens_is_closure =
-                            syn::parse2::<ExprClosure>(tokens.to_token_stream()).is_ok();
-                        let function_name =
-                            quote_spanned! { tokens.span() => dioxus_elements::events::#name };
-                        let function = if event_tokens_is_closure {
-                            // If we see an explicit closure, we can call the `call_with_explicit_closure` version of the event for better type inference
-                            quote_spanned! { tokens.span() => #function_name::call_with_explicit_closure }
-                        } else {
-                            function_name
+                AttributeValue::EventTokens(_) | AttributeValue::AttrExpr(_) => {
+                    let (tokens, span) = match &self.value {
+                        AttributeValue::EventTokens(tokens) => {
+                            (tokens.to_token_stream(), tokens.span())
+                        }
+                        AttributeValue::AttrExpr(tokens) => {
+                            (tokens.to_token_stream(), tokens.span())
+                        }
+                        _ => unreachable!(),
+                    };
+
+                    fn check_tokens_is_closure(tokens: &TokenStream2) -> bool {
+                        if syn::parse2::<ExprClosure>(tokens.to_token_stream()).is_ok() {
+                            return true;
+                        }
+                        let Ok(block) = syn::parse2::<ExprBlock>(tokens.to_token_stream()) else {
+                            return false;
                         };
-                        quote_spanned! { tokens.span() =>
-                            #function(#tokens)
+                        let mut block = &block;
+                        loop {
+                            match block.block.stmts.last() {
+                                Some(Stmt::Expr(Expr::Closure(_), _)) => return true,
+                                Some(Stmt::Expr(Expr::Block(b), _)) => {
+                                    block = b;
+                                    continue;
+                                }
+                                _ => return false,
+                            }
+                        }
+                    }
+                    match &self.name {
+                        AttributeName::BuiltIn(name) => {
+                            let event_tokens_is_closure = check_tokens_is_closure(&tokens);
+                            let function_name =
+                                quote_spanned! { span => dioxus_elements::events::#name };
+                            let function = if event_tokens_is_closure {
+                                // If we see an explicit closure, we can call the `call_with_explicit_closure` version of the event for better type inference
+                                quote_spanned! { span => #function_name::call_with_explicit_closure }
+                            } else {
+                                function_name
+                            };
+                            quote_spanned! { span =>
+                                #function(#tokens)
+                            }
                         }
+                        AttributeName::Custom(_) => unreachable!("Handled elsewhere in the macro"),
+                        AttributeName::Spread(_) => unreachable!("Handled elsewhere in the macro"),
                     }
-                    AttributeName::Custom(_) => unreachable!("Handled elsewhere in the macro"),
-                    AttributeName::Spread(_) => unreachable!("Handled elsewhere in the macro"),
-                },
+                }
                 _ => {
                     quote_spanned! { value.span() => dioxus_elements::events::#name(#value) }
                 }
@@ -834,8 +865,41 @@ mod tests {
         let a: Attribute = parse2(quote! { onclick: |e| {} }).unwrap();
         let b: Attribute = parse2(quote! { onclick: |e| {} }).unwrap();
         let c: Attribute = parse2(quote! { onclick: move |e| {} }).unwrap();
+        let d: Attribute = parse2(quote! { onclick: { |e| {} } }).unwrap();
         assert_eq!(a, b);
         assert_ne!(a, c);
+        assert_ne!(a, d);
+    }
+
+    #[test]
+    fn call_with_explicit_closure() {
+        let mut a: Attribute = parse2(quote! { onclick: |e| {} }).unwrap();
+        a.el_name = Some(parse_quote!(button));
+        assert!(a
+            .rendered_as_dynamic_attr()
+            .to_string()
+            .contains("call_with_explicit_closure"));
+
+        let mut a: Attribute = parse2(quote! { onclick: { let a = 1; |e| {} } }).unwrap();
+        a.el_name = Some(parse_quote!(button));
+        assert!(a
+            .rendered_as_dynamic_attr()
+            .to_string()
+            .contains("call_with_explicit_closure"));
+
+        let mut a: Attribute = parse2(quote! { onclick: { let b = 2; { |e| { b } } } }).unwrap();
+        a.el_name = Some(parse_quote!(button));
+        assert!(a
+            .rendered_as_dynamic_attr()
+            .to_string()
+            .contains("call_with_explicit_closure"));
+
+        let mut a: Attribute = parse2(quote! { onclick: { let r = |e| { b }; r } }).unwrap();
+        a.el_name = Some(parse_quote!(button));
+        assert!(!a
+            .rendered_as_dynamic_attr()
+            .to_string()
+            .contains("call_with_explicit_closure"));
     }
 
     /// Make sure reserved keywords are parsed as attributes

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

@@ -110,6 +110,17 @@ fn complex_kitchen_sink() {
             }
         }
 
+        // No nesting
+        Component {
+            adsasd: "asd",
+            onclick: {
+                let a = 1;
+                move |_| {
+                    let blah = 120;
+                }
+            }
+        }
+
         // Component path
         my::thing::Component {
             adsasd: "asd",