Span hygene and editor UX (#2702)

* add span for closing tag to macro expansion

* decouple name resolution from locations

* commit to new macro errors
This commit is contained in:
WorldSEnder 2022-05-25 13:01:10 +02:00 committed by GitHub
parent 9d00e0ede7
commit e68060afa7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 76 additions and 48 deletions

View File

@ -1,7 +1,7 @@
use yew::prelude::*;
#[function_component(App)]
fn app() -> Html {
#[function_component]
fn App() -> Html {
let state = use_state(|| 0);
let incr_counter = {

View File

@ -18,6 +18,7 @@ pub struct HtmlComponent {
ty: Type,
props: ComponentProps,
children: HtmlChildrenTree,
close: Option<HtmlComponentClose>,
}
impl PeekValue<()> for HtmlComponent {
@ -47,6 +48,7 @@ impl Parse for HtmlComponent {
ty: open.ty,
props: open.props,
children: HtmlChildrenTree::new(),
close: None,
});
}
@ -67,7 +69,7 @@ impl Parse for HtmlComponent {
children.parse_child(input)?;
}
input.parse::<HtmlComponentClose>()?;
let close = input.parse::<HtmlComponentClose>()?;
if !children.is_empty() {
if let Some(children_prop) = open.props.children() {
@ -82,6 +84,7 @@ impl Parse for HtmlComponent {
ty: open.ty,
props: open.props,
children,
close: Some(close),
})
}
}
@ -92,9 +95,11 @@ impl ToTokens for HtmlComponent {
ty,
props,
children,
close,
} = self;
let props_ty = quote_spanned!(ty.span()=> <#ty as ::yew::html::BaseComponent>::Properties);
let ty_span = ty.span().resolved_at(Span::call_site());
let props_ty = quote_spanned!(ty_span=> <#ty as ::yew::html::BaseComponent>::Properties);
let children_renderer = if children.is_empty() {
None
} else {
@ -105,23 +110,32 @@ impl ToTokens for HtmlComponent {
let special_props = props.special();
let node_ref = if let Some(node_ref) = &special_props.node_ref {
let value = &node_ref.value;
quote_spanned! {value.span()=> #value }
quote! { #value }
} else {
quote! { <::yew::html::NodeRef as ::std::default::Default>::default() }
};
let key = if let Some(key) = &special_props.key {
let value = &key.value;
quote_spanned! {value.span()=>
quote_spanned! {value.span().resolved_at(Span::call_site())=>
#[allow(clippy::useless_conversion)]
Some(::std::convert::Into::<::yew::virtual_dom::Key>::into(#value))
}
} else {
quote! { ::std::option::Option::None }
};
let use_close_tag = if let Some(close) = close {
let close_ty = &close.ty;
quote_spanned! {close_ty.span()=>
let _ = |_:#close_ty| {};
}
} else {
Default::default()
};
tokens.extend(quote_spanned! {ty.span()=>
tokens.extend(quote_spanned! {ty_span=>
{
#use_close_tag
let __yew_props = #build_props;
::yew::virtual_dom::VChild::<#ty>::new(__yew_props, #node_ref, #key)
}
@ -268,7 +282,7 @@ impl Parse for HtmlComponentOpen {
struct HtmlComponentClose {
tag: TagTokens,
_ty: Type,
ty: Type,
}
impl HtmlComponentClose {
fn to_spanned(&self) -> impl ToTokens {
@ -296,7 +310,7 @@ impl Parse for HtmlComponentClose {
fn parse(input: ParseStream) -> syn::Result<Self> {
TagTokens::parse_end_content(input, |input, tag| {
let ty = input.parse()?;
Ok(Self { tag, _ty: ty })
Ok(Self { tag, ty })
})
}
}

View File

@ -1,5 +1,5 @@
use boolinator::Boolinator;
use proc_macro2::{Delimiter, TokenStream};
use proc_macro2::{Delimiter, Span, TokenStream};
use proc_macro_error::emit_warning;
use quote::{quote, quote_spanned, ToTokens};
use syn::buffer::Cursor;
@ -123,7 +123,7 @@ impl ToTokens for HtmlElement {
.as_ref()
.map(|attr| {
let value = &attr.value;
quote_spanned! {value.span()=>
quote_spanned! {value.span().resolved_at(Span::call_site())=>
::yew::html::IntoPropValue::<::yew::html::NodeRef>
::into_prop_value(#value)
}
@ -133,7 +133,7 @@ impl ToTokens for HtmlElement {
.as_ref()
.map(|attr| {
let value = attr.value.optimize_literals();
quote_spanned! {value.span()=>
quote_spanned! {value.span().resolved_at(Span::call_site())=>
::std::option::Option::Some(
::std::convert::Into::<::yew::virtual_dom::Key>::into(#value)
)
@ -174,15 +174,17 @@ impl ToTokens for HtmlElement {
#key
}}),
},
expr => Value::Dynamic(quote_spanned! {expr.span()=>
if #expr {
::std::option::Option::Some(
::yew::virtual_dom::AttrValue::Static(#key)
)
} else {
::std::option::Option::None
}
}),
expr => Value::Dynamic(
quote_spanned! {expr.span().resolved_at(Span::call_site())=>
if #expr {
::std::option::Option::Some(
::yew::virtual_dom::AttrValue::Static(#key)
)
} else {
::std::option::Option::None
}
},
),
},
))
});

View File

@ -1,5 +1,5 @@
use proc_macro2::TokenStream;
use quote::{quote_spanned, ToTokens};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::buffer::Cursor;
use syn::parse::{Parse, ParseStream, Result};
use syn::spanned::Spanned;
@ -49,7 +49,7 @@ impl ToTokens for HtmlNode {
let sr = lit.stringify();
quote_spanned! {lit.span()=> ::yew::virtual_dom::VText::new(#sr) }
}
HtmlNode::Expression(expr) => quote_spanned! {expr.span()=> #expr},
HtmlNode::Expression(expr) => quote! {#expr},
});
}
}
@ -60,9 +60,9 @@ impl ToNodeIterator for HtmlNode {
HtmlNode::Literal(_) => None,
HtmlNode::Expression(expr) => {
// NodeSeq turns both Into<T> and Vec<Into<T>> into IntoIterator<Item = T>
Some(
quote_spanned! {expr.span()=> ::std::convert::Into::<::yew::utils::NodeSeq<_, _>>::into(#expr)},
)
Some(quote_spanned! {expr.span().resolved_at(Span::call_site())=>
::std::convert::Into::<::yew::utils::NodeSeq<_, _>>::into(#expr)
})
}
}
}

View File

@ -181,10 +181,12 @@ impl Parse for HtmlRootVNode {
impl ToTokens for HtmlRootVNode {
fn to_tokens(&self, tokens: &mut TokenStream) {
let new_tokens = self.0.to_token_stream();
tokens.extend(quote! {{
#[allow(clippy::useless_conversion)]
<::yew::virtual_dom::VNode as ::std::convert::From<_>>::from(#new_tokens)
}});
tokens.extend(
quote_spanned! {self.0.span().resolved_at(Span::mixed_site())=> {
#[allow(clippy::useless_conversion)]
<::yew::virtual_dom::VNode as ::std::convert::From<_>>::from(#new_tokens)
}},
);
}
}

View File

@ -1,6 +1,6 @@
use std::convert::TryFrom;
use proc_macro2::{Ident, TokenStream};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
@ -50,8 +50,9 @@ impl ComponentProps {
}
fn prop_validation_tokens(&self, props_ty: impl ToTokens, has_children: bool) -> TokenStream {
let props_ident = Ident::new("__yew_props", props_ty.span());
let check_children = if has_children {
Some(quote_spanned! {props_ty.span()=> __yew_props.children; })
Some(quote_spanned! {props_ty.span()=> #props_ident.children; })
} else {
None
};
@ -59,7 +60,11 @@ impl ComponentProps {
let check_props: TokenStream = self
.props
.iter()
.map(|Prop { label, .. }| quote_spanned! ( label.span()=> __yew_props.#label; ))
.map(|Prop { label, .. }| {
quote_spanned! {
Span::call_site().located_at(label.span())=> #props_ident.#label;
}
})
.chain(self.base_expr.iter().map(|expr| {
quote_spanned! {props_ty.span()=>
let _: #props_ty = #expr;
@ -70,7 +75,7 @@ impl ComponentProps {
quote_spanned! {props_ty.span()=>
#[allow(clippy::no_effect)]
if false {
let _ = |__yew_props: #props_ty| {
let _ = |#props_ident: #props_ty| {
#check_children
#check_props
};
@ -110,7 +115,7 @@ impl ComponentProps {
Some(expr) => {
let ident = Ident::new("__yew_props", props_ty.span());
let set_props = self.props.iter().map(|Prop { label, value, .. }| {
quote_spanned! {value.span()=>
quote_spanned! {value.span().resolved_at(Span::call_site())=>
#ident.#label = ::yew::html::IntoPropValue::into_prop_value(#value);
}
});
@ -121,7 +126,7 @@ impl ComponentProps {
});
quote! {
let mut #ident = #expr;
let mut #ident: #props_ty = #expr;
#(#set_props)*
#set_children
#ident

View File

@ -1,11 +1,11 @@
use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use quote::{quote_spanned, ToTokens};
use syn::spanned::Spanned;
use syn::{Expr, Lit, LitStr};
/// Stringify a value at runtime.
fn stringify_at_runtime(src: impl ToTokens) -> TokenStream {
quote_spanned! {src.span()=>
quote_spanned! {src.span().resolved_at(Span::call_site())=>
::std::convert::Into::<::yew::virtual_dom::AttrValue>::into(#src)
}
}

View File

@ -18,6 +18,7 @@ error[E0599]: no method named `build` found for struct `PropsBuilder<PropsBuilde
|
= note: the method was found for
- `PropsBuilder<PropsBuilderStepPropsBuilder>`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `Comp<MissingTypeBounds>: yew::BaseComponent` is not satisfied
--> tests/function_component_attr/generic-props-fail.rs:27:14
@ -27,6 +28,7 @@ error[E0277]: the trait bound `Comp<MissingTypeBounds>: yew::BaseComponent` is n
|
= help: the following implementations were found:
<Comp<P> as yew::BaseComponent>
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0599]: the function or associated item `new` exists for struct `VChild<Comp<MissingTypeBounds>>`, but its trait bounds were not satisfied
--> tests/function_component_attr/generic-props-fail.rs:27:14
@ -39,6 +41,7 @@ error[E0599]: the function or associated item `new` exists for struct `VChild<Co
|
= note: the following trait bounds were not satisfied:
`Comp<MissingTypeBounds>: yew::BaseComponent`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `MissingTypeBounds: yew::Properties` is not satisfied
--> tests/function_component_attr/generic-props-fail.rs:27:14
@ -54,6 +57,7 @@ note: required by a bound in `Comp`
...
11 | P: Properties + PartialEq,
| ^^^^^^^^^^ required by this bound in `Comp`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0107]: missing generics for struct `Comp`
--> tests/function_component_attr/generic-props-fail.rs:30:14

View File

@ -12,6 +12,7 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= note: 2 redundant requirements hidden
= note: required because of the requirements on the impl of `Into<NodeSeq<(), VNode>>` for `()`
note: required by `into`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: `()` doesn't implement `std::fmt::Display`
--> tests/html_macro/block-fail.rs:12:16
@ -27,6 +28,7 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= note: 2 redundant requirements hidden
= note: required because of the requirements on the impl of `Into<NodeSeq<(), VNode>>` for `()`
note: required by `into`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: `()` doesn't implement `std::fmt::Display`
--> tests/html_macro/block-fail.rs:15:17

View File

@ -290,15 +290,6 @@ error[E0308]: mismatched types
= note: expected struct `ChildProperties`
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
--> tests/html_macro/component-fail.rs:53:14
|
53 | html! { <Child ..p1 ..p2 /> };
| ^^^^^ expected struct `ChildProperties`, found struct `std::ops::Range`
|
= note: expected struct `ChildProperties`
found struct `std::ops::Range<_>`
error[E0609]: no field `value` on type `ChildProperties`
--> tests/html_macro/component-fail.rs:69:20
|
@ -404,6 +395,7 @@ error[E0609]: no field `children` on type `ChildProperties`
| ^^^^^ unknown field
|
= note: available fields are: `string`, `int`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0599]: no method named `children` found for struct `ChildPropertiesBuilder` in the current scope
--> tests/html_macro/component-fail.rs:87:14
@ -413,6 +405,8 @@ error[E0599]: no method named `children` found for struct `ChildPropertiesBuilde
...
87 | html! { <Child>{ "Not allowed" }</Child> };
| ^^^^^ method not found in `ChildPropertiesBuilder<ChildPropertiesBuilderStep_missing_required_prop_int>`
|
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0609]: no field `children` on type `ChildProperties`
--> tests/html_macro/component-fail.rs:94:10
@ -421,6 +415,7 @@ error[E0609]: no field `children` on type `ChildProperties`
| ^^^^^ unknown field
|
= note: available fields are: `string`, `int`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0599]: no method named `build` found for struct `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>` in the current scope
--> tests/html_macro/component-fail.rs:99:14
@ -433,6 +428,7 @@ error[E0599]: no method named `build` found for struct `ChildContainerProperties
|
= note: the method was found for
- `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStepPropsBuilder>`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0599]: no method named `build` found for struct `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStep_missing_required_prop_children>` in the current scope
--> tests/html_macro/component-fail.rs:100:14
@ -445,6 +441,7 @@ error[E0599]: no method named `build` found for struct `ChildContainerProperties
|
= note: the method was found for
- `ChildContainerPropertiesBuilder<ChildContainerPropertiesBuilderStepPropsBuilder>`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `VChild<Child>: From<yew::virtual_dom::VText>` is not satisfied
--> tests/html_macro/component-fail.rs:101:31

View File

@ -5,6 +5,7 @@ error[E0277]: the trait bound `Unimplemented: yew::Component` is not satisfied
| ^^^^^^^^^^^^^ the trait `yew::Component` is not implemented for `Unimplemented`
|
= note: required because of the requirements on the impl of `BaseComponent` for `Unimplemented`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0599]: the function or associated item `new` exists for struct `VChild<Unimplemented>`, but its trait bounds were not satisfied
--> tests/html_macro/component-unimplemented-fail.rs:6:14
@ -17,3 +18,4 @@ error[E0599]: the function or associated item `new` exists for struct `VChild<Un
|
= note: the following trait bounds were not satisfied:
`Unimplemented: BaseComponent`
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)