mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
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:
parent
9d00e0ede7
commit
e68060afa7
@ -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 = {
|
||||
|
||||
@ -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 })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
},
|
||||
),
|
||||
},
|
||||
))
|
||||
});
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user