Remove support for element listener magical closures (#782)

This commit is contained in:
Justin Starry 2019-12-07 17:48:56 -08:00 committed by GitHub
parent f48bc90edb
commit 0e8ffcc54c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 143 deletions

View File

@ -149,7 +149,10 @@ impl ToTokens for HtmlTag {
#vtag.node_ref = #node_ref; #vtag.node_ref = #node_ref;
} }
}); });
let listeners = listeners.iter().map(|(name, callback)| { let listeners = listeners.iter().map(|listener| {
let name = &listener.label.name;
let callback = &listener.value;
quote_spanned! {name.span()=> { quote_spanned! {name.span()=> {
::yew::html::#name::Wrapper::new( ::yew::html::#name::Wrapper::new(
<::yew::virtual_dom::vtag::VTag<_> as ::yew::virtual_dom::Transformer<_, _, _>>::transform( <::yew::virtual_dom::vtag::VTag<_> as ::yew::virtual_dom::Transformer<_, _, _>>::transform(

View File

@ -1,15 +1,14 @@
use crate::html_tree::HtmlProp as TagAttribute; use crate::html_tree::HtmlProp as TagAttribute;
use crate::PeekValue; use crate::PeekValue;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use proc_macro2::TokenStream; use std::collections::HashSet;
use quote::{quote, quote_spanned, ToTokens}; use std::iter::FromIterator;
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream, Result as ParseResult}; use syn::parse::{Parse, ParseStream, Result as ParseResult};
use syn::{Expr, ExprClosure, ExprTuple, Ident, Pat}; use syn::{Expr, ExprTuple};
pub struct TagAttributes { pub struct TagAttributes {
pub attributes: Vec<TagAttribute>, pub attributes: Vec<TagAttribute>,
pub listeners: Vec<(Ident, TokenStream)>, pub listeners: Vec<TagAttribute>,
pub classes: Option<ClassesForm>, pub classes: Option<ClassesForm>,
pub value: Option<Expr>, pub value: Option<Expr>,
pub kind: Option<Expr>, pub kind: Option<Expr>,
@ -25,75 +24,67 @@ pub enum ClassesForm {
Single(Expr), Single(Expr),
} }
pub struct TagListener {
name: Ident,
handler: Expr,
event_name: String,
}
lazy_static! { lazy_static! {
static ref LISTENER_MAP: HashMap<&'static str, &'static str> = { static ref LISTENER_SET: HashSet<&'static str> = {
let mut m = HashMap::new(); HashSet::from_iter(
m.insert("onclick", "ClickEvent"); vec![
m.insert("ondoubleclick", "DoubleClickEvent"); "onclick",
m.insert("onkeypress", "KeyPressEvent"); "ondoubleclick",
m.insert("onkeydown", "KeyDownEvent"); "onkeypress",
m.insert("onkeyup", "KeyUpEvent"); "onkeydown",
m.insert("onmousedown", "MouseDownEvent"); "onkeyup",
m.insert("onmousemove", "MouseMoveEvent"); "onmousedown",
m.insert("onmouseout", "MouseOutEvent"); "onmousemove",
m.insert("onmouseenter", "MouseEnterEvent"); "onmouseout",
m.insert("onmouseleave", "MouseLeaveEvent"); "onmouseenter",
m.insert("onmousewheel", "MouseWheelEvent"); "onmouseleave",
m.insert("onmouseover", "MouseOverEvent"); "onmousewheel",
m.insert("onmouseup", "MouseUpEvent"); "onmouseover",
m.insert("ontouchcancel", "TouchCancel"); "onmouseup",
m.insert("ontouchend", "TouchEnd"); "ontouchcancel",
m.insert("ontouchenter", "TouchEnter"); "ontouchend",
m.insert("ontouchmove", "TouchMove"); "ontouchenter",
m.insert("ontouchstart", "TouchStart"); "ontouchmove",
m.insert("ongotpointercapture", "GotPointerCaptureEvent"); "ontouchstart",
m.insert("onlostpointercapture", "LostPointerCaptureEvent"); "ongotpointercapture",
m.insert("onpointercancel", "PointerCancelEvent"); "onlostpointercapture",
m.insert("onpointerdown", "PointerDownEvent"); "onpointercancel",
m.insert("onpointerenter", "PointerEnterEvent"); "onpointerdown",
m.insert("onpointerleave", "PointerLeaveEvent"); "onpointerenter",
m.insert("onpointermove", "PointerMoveEvent"); "onpointerleave",
m.insert("onpointerout", "PointerOutEvent"); "onpointermove",
m.insert("onpointerover", "PointerOverEvent"); "onpointerout",
m.insert("onpointerup", "PointerUpEvent"); "onpointerover",
m.insert("onscroll", "ScrollEvent"); "onpointerup",
m.insert("onblur", "BlurEvent"); "onscroll",
m.insert("onfocus", "FocusEvent"); "onblur",
m.insert("onsubmit", "SubmitEvent"); "onfocus",
m.insert("oninput", "InputData"); "onsubmit",
m.insert("onchange", "ChangeData"); "oninput",
m.insert("ondrag", "DragEvent"); "onchange",
m.insert("ondragstart", "DragStartEvent"); "ondrag",
m.insert("ondragend", "DragEndEvent"); "ondragstart",
m.insert("ondragenter", "DragEnterEvent"); "ondragend",
m.insert("ondragleave", "DragLeaveEvent"); "ondragenter",
m.insert("ondragover", "DragOverEvent"); "ondragleave",
m.insert("ondragexit", "DragExitEvent"); "ondragover",
m.insert("ondrop", "DragDropEvent"); "ondragexit",
m.insert("oncontextmenu", "ContextMenuEvent"); "ondrop",
m "oncontextmenu",
]
.into_iter(),
)
}; };
} }
impl TagAttributes { impl TagAttributes {
fn drain_listeners(attrs: &mut Vec<TagAttribute>) -> Vec<TagListener> { fn drain_listeners(attrs: &mut Vec<TagAttribute>) -> Vec<TagAttribute> {
let mut i = 0; let mut i = 0;
let mut drained = Vec::new(); let mut drained = Vec::new();
while i < attrs.len() { while i < attrs.len() {
let name_str = attrs[i].label.to_string(); let name_str = attrs[i].label.to_string();
if let Some(event_type) = LISTENER_MAP.get(&name_str.as_str()) { if LISTENER_SET.contains(&name_str.as_str()) {
let TagAttribute { label, value } = attrs.remove(i); drained.push(attrs.remove(i));
drained.push(TagListener {
name: label.name,
handler: value,
event_name: event_type.to_owned().to_string(),
});
} else { } else {
i += 1; i += 1;
} }
@ -119,54 +110,6 @@ impl TagAttributes {
expr => ClassesForm::Single(expr), expr => ClassesForm::Single(expr),
} }
} }
fn map_listener(listener: TagListener) -> ParseResult<(Ident, TokenStream)> {
let TagListener {
name,
event_name,
handler,
} = listener;
let callback: TokenStream = match handler {
Expr::Closure(closure) => {
let ExprClosure {
inputs,
body,
or1_token,
or2_token,
..
} = closure;
let or_span = quote! {#or1_token#or2_token};
if inputs.len() != 1 {
return Err(syn::Error::new_spanned(
or_span,
"there must be one closure argument",
));
}
let var = match inputs.first().unwrap() {
Pat::Ident(pat) => Ok(pat.into_token_stream()),
Pat::Wild(pat) => Ok(pat.into_token_stream()),
_ => Err(syn::Error::new_spanned(or_span, "invalid closure argument")),
}?;
let callback =
Ident::new(&format!("__yew_{}_callback", name.to_string()), name.span());
let segment = syn::PathSegment {
ident: Ident::new(&event_name, name.span()),
arguments: syn::PathArguments::None,
};
quote_spanned! {name.span()=> {
let #callback = move | #var: ::yew::events::#segment | #body;
#callback
}}
}
callback => callback.into_token_stream(),
};
Ok((name, callback))
}
} }
impl Parse for TagAttributes { impl Parse for TagAttributes {
@ -178,7 +121,7 @@ impl Parse for TagAttributes {
let mut listeners = Vec::new(); let mut listeners = Vec::new();
for listener in TagAttributes::drain_listeners(&mut attributes) { for listener in TagAttributes::drain_listeners(&mut attributes) {
listeners.push(TagAttributes::map_listener(listener)?); listeners.push(listener);
} }
// Multiple listener attributes are allowed, but no others // Multiple listener attributes are allowed, but no others

View File

@ -30,9 +30,7 @@ fn compile_fail() {
html! { <a href=() /> }; html! { <a href=() /> };
html! { <input onclick=1 /> }; html! { <input onclick=1 /> };
html! { <input onclick=|| () /> }; html! { <input onclick=Callback::from(|a: String| ()) /> };
html! { <input onclick=|a, b| () /> };
html! { <input onclick=|a: String| () /> };
html! { <input string=NotToString /> }; html! { <input string=NotToString /> };

View File

@ -102,28 +102,10 @@ error: only one `class` attribute allowed
23 | html! { <div class="first" class="second" /> }; 23 | html! { <div class="first" class="second" /> };
| ^^^^^ | ^^^^^
error: there must be one closure argument
--> $DIR/html-tag-fail.rs:33:28
|
33 | html! { <input onclick=|| () /> };
| ^^
error: there must be one closure argument
--> $DIR/html-tag-fail.rs:34:28
|
34 | html! { <input onclick=|a, b| () /> };
| ^^^^^^
error: invalid closure argument
--> $DIR/html-tag-fail.rs:35:28
|
35 | html! { <input onclick=|a: String| () /> };
| ^^^^^^^^^^^
error: only one `ref` attribute allowed error: only one `ref` attribute allowed
--> $DIR/html-tag-fail.rs:40:27 --> $DIR/html-tag-fail.rs:38:27
| |
40 | html! { <input ref=() ref=() /> }; 38 | html! { <input ref=() ref=() /> };
| ^^^ | ^^^
error[E0308]: mismatched types error[E0308]: mismatched types
@ -193,13 +175,22 @@ error[E0308]: mismatched types
= note: expected type `yew::callback::Callback<stdweb::webapi::events::mouse::ClickEvent>` = note: expected type `yew::callback::Callback<stdweb::webapi::events::mouse::ClickEvent>`
found type `{integer}` found type `{integer}`
error[E0308]: mismatched types
--> $DIR/html-tag-fail.rs:33:20
|
33 | html! { <input onclick=Callback::from(|a: String| ()) /> };
| ^^^^^^^ expected struct `stdweb::webapi::events::mouse::ClickEvent`, found struct `std::string::String`
|
= note: expected type `yew::callback::Callback<stdweb::webapi::events::mouse::ClickEvent>`
found type `yew::callback::Callback<std::string::String>`
error[E0599]: no method named `to_string` found for type `NotToString` in the current scope error[E0599]: no method named `to_string` found for type `NotToString` in the current scope
--> $DIR/html-tag-fail.rs:37:27 --> $DIR/html-tag-fail.rs:35:27
| |
3 | struct NotToString; 3 | struct NotToString;
| ------------------- method `to_string` not found for this | ------------------- method `to_string` not found for this
... ...
37 | html! { <input string=NotToString /> }; 35 | html! { <input string=NotToString /> };
| ^^^^^^^^^^^ method not found in `NotToString` | ^^^^^^^^^^^ method not found in `NotToString`
| |
= note: the method `to_string` exists but the following trait bounds were not satisfied: = note: the method `to_string` exists but the following trait bounds were not satisfied:
@ -209,9 +200,9 @@ error[E0599]: no method named `to_string` found for type `NotToString` in the cu
candidate #1: `std::string::ToString` candidate #1: `std::string::ToString`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/html-tag-fail.rs:39:24 --> $DIR/html-tag-fail.rs:37:24
| |
39 | html! { <input ref=() /> }; 37 | html! { <input ref=() /> };
| ^^ expected struct `yew::html::NodeRef`, found () | ^^ expected struct `yew::html::NodeRef`, found ()
| |
= note: expected type `yew::html::NodeRef` = note: expected type `yew::html::NodeRef`

View File

@ -37,7 +37,6 @@ pass_helper! {
</svg> </svg>
<img class=("avatar", "hidden") src="http://pic.com" /> <img class=("avatar", "hidden") src="http://pic.com" />
<img class="avatar hidden", /> <img class="avatar hidden", />
<button onclick=|e| panic!(e) />
<button onclick=&onclick /> <button onclick=&onclick />
<button onclick=onclick /> <button onclick=onclick />
<a href="http://google.com" /> <a href="http://google.com" />