Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Span hygene and editor UX #2702

Merged
merged 3 commits into from May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/counter_functional/src/main.rs
@@ -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 = {
Expand Down
28 changes: 21 additions & 7 deletions packages/yew-macro/src/html_tree/html_component.rs
Expand Up @@ -18,6 +18,7 @@ pub struct HtmlComponent {
ty: Type,
props: ComponentProps,
children: HtmlChildrenTree,
close: Option<HtmlComponentClose>,
}

impl PeekValue<()> for HtmlComponent {
Expand Down Expand Up @@ -47,6 +48,7 @@ impl Parse for HtmlComponent {
ty: open.ty,
props: open.props,
children: HtmlChildrenTree::new(),
close: None,
});
}

Expand All @@ -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() {
Expand All @@ -82,6 +84,7 @@ impl Parse for HtmlComponent {
ty: open.ty,
props: open.props,
children,
close: Some(close),
})
}
}
Expand All @@ -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 {
Expand All @@ -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)
}
Expand Down Expand Up @@ -268,7 +282,7 @@ impl Parse for HtmlComponentOpen {

struct HtmlComponentClose {
tag: TagTokens,
_ty: Type,
ty: Type,
}
impl HtmlComponentClose {
fn to_spanned(&self) -> impl ToTokens {
Expand Down Expand Up @@ -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 })
})
}
}
26 changes: 14 additions & 12 deletions packages/yew-macro/src/html_tree/html_element.rs
@@ -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;
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
)
Expand Down Expand Up @@ -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
}
},
),
},
))
});
Expand Down
12 changes: 6 additions & 6 deletions packages/yew-macro/src/html_tree/html_node.rs
@@ -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;
Expand Down Expand Up @@ -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},
});
}
}
Expand All @@ -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)
})
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions packages/yew-macro/src/html_tree/mod.rs
Expand Up @@ -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)
}},
);
}
}

Expand Down
17 changes: 11 additions & 6 deletions packages/yew-macro/src/props/component.rs
@@ -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;
Expand Down Expand Up @@ -50,16 +50,21 @@ 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
};

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;
Expand All @@ -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
};
Expand Down Expand Up @@ -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);
}
});
Expand All @@ -121,7 +126,7 @@ impl ComponentProps {
});

quote! {
let mut #ident = #expr;
let mut #ident: #props_ty = #expr;
#(#set_props)*
#set_children
#ident
Expand Down
4 changes: 2 additions & 2 deletions packages/yew-macro/src/stringify.rs
@@ -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)
}
}
Expand Down
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions packages/yew-macro/tests/html_macro/block-fail.stderr
Expand Up @@ -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
Expand All @@ -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
Expand Down
15 changes: 6 additions & 9 deletions packages/yew-macro/tests/html_macro/component-fail.stderr
Expand Up @@ -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
|
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
Expand Up @@ -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
Expand All @@ -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)