Skip to content

Commit

Permalink
Implement container feature ids
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Apr 17, 2023
1 parent a84fbdb commit 8df86e4
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 79 deletions.
5 changes: 3 additions & 2 deletions derive/src/into_owned.rs
Expand Up @@ -96,7 +96,7 @@ pub(crate) fn derive_into_owned(input: TokenStream) -> TokenStream {
quote! {
match self {
#variants
_ => unsafe { std::mem::transmute(self) }
_ => unsafe { std::mem::transmute_copy(&self) }
}
}
}
Expand All @@ -110,10 +110,11 @@ pub(crate) fn derive_into_owned(input: TokenStream) -> TokenStream {
let into_owned = if generics.lifetimes().next().is_none() {
panic!("can't derive IntoOwned on a type without any lifetimes")
} else {
let params = generics.type_params();
quote! {
impl #impl_generics #self_name #ty_generics #where_clause {
/// Consumes the value and returns an owned clone.
pub fn into_owned<'x>(self) -> #self_name<'x> {
pub fn into_owned<'x>(self) -> #self_name<'x, #(#params),*> {
#res
}
}
Expand Down
111 changes: 82 additions & 29 deletions derive/src/lib.rs
@@ -1,11 +1,11 @@
use std::collections::HashSet;

use proc_macro::{self, TokenStream};
use proc_macro2::Span;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{
parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields,
GenericParam, Ident, Member, Token, Type,
GenericParam, Generics, Ident, Member, Token, Type,
};

mod into_owned;
Expand All @@ -25,7 +25,60 @@ pub fn derive_visit_children(input: TokenStream) -> TokenStream {
..
} = parse_macro_input!(input);

let options: Vec<VisitOptions> = attrs
.iter()
.filter_map(|attr| {
if attr.path.is_ident("visit") {
let opts: VisitOptions = attr.parse_args().unwrap();
Some(opts)
} else {
None
}
})
.collect();

let visit_types = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit_types")) {
let types: VisitTypes = attr.parse_args().unwrap();
let types = types.types;
Some(quote! { crate::visit_types!(#(#types)|*) })
} else {
None
};

if options.is_empty() {
derive(&ident, &data, &generics, None, visit_types)
} else {
options
.into_iter()
.map(|options| derive(&ident, &data, &generics, Some(options), visit_types.clone()))
.collect()
}
}

fn derive(
ident: &Ident,
data: &Data,
generics: &Generics,
options: Option<VisitOptions>,
visit_types: Option<TokenStream2>,
) -> TokenStream {
let mut impl_generics = generics.clone();
let mut type_defs = quote! {};
let generics = if let Some(VisitOptions {
generic: Some(generic), ..
}) = &options
{
let mappings = generics
.type_params()
.zip(generic.type_params())
.map(|(a, b)| quote! { type #a = #b; });
type_defs = quote! { #(#mappings)* };
impl_generics.params.clear();
generic
} else {
&generics
};

if impl_generics.lifetimes().next().is_none() {
impl_generics.params.insert(0, parse_quote! { 'i })
}
Expand All @@ -37,7 +90,9 @@ pub fn derive_visit_children(input: TokenStream) -> TokenStream {
GenericParam::Type(t.ident.clone().into())
} else {
let t: GenericParam = parse_quote! { __T };
impl_generics.params.push(parse_quote! { #t: Visit<#lifetime, __T, #v> });
impl_generics
.params
.push(parse_quote! { #t: crate::visitor::Visit<#lifetime, __T, #v> });
t
};

Expand All @@ -52,21 +107,6 @@ pub fn derive_visit_children(input: TokenStream) -> TokenStream {
})
}

let options = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit")) {
let opts: VisitOptions = attr.parse_args().unwrap();
Some(opts)
} else {
None
};

let visit_types = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit_types")) {
let types: VisitTypes = attr.parse_args().unwrap();
let types = types.types;
Some(quote! { crate::visit_types!(#(#types)|*) })
} else {
None
};

let mut seen_types = HashSet::new();
let mut child_types = Vec::new();
let mut visit = Vec::new();
Expand Down Expand Up @@ -157,7 +197,15 @@ pub fn derive_visit_children(input: TokenStream) -> TokenStream {
child_types.push(quote! { crate::visitor::VisitTypes::empty().bits() });
}

let self_visit = if let Some(VisitOptions { visit, kind }) = options {
let (_, ty_generics, _) = generics.split_for_impl();
let (impl_generics, _, where_clause) = impl_generics.split_for_impl();

let self_visit = if let Some(VisitOptions {
visit: Some(visit),
kind: Some(kind),
..
}) = &options
{
child_types.push(quote! { crate::visitor::VisitTypes::#kind.bits() });

quote! {
Expand All @@ -173,12 +221,9 @@ pub fn derive_visit_children(input: TokenStream) -> TokenStream {
quote! {}
};

let (_, ty_generics, _) = generics.split_for_impl();
let (impl_generics, _, where_clause) = impl_generics.split_for_impl();

let child_types = visit_types.unwrap_or_else(|| {
quote! {
unsafe { crate::visitor::VisitTypes::from_bits_unchecked(#(#child_types)|*) }
unsafe { #type_defs crate::visitor::VisitTypes::from_bits_unchecked(#(#child_types)|*) }
}
});

Expand Down Expand Up @@ -208,16 +253,24 @@ fn skip_type(attrs: &Vec<Attribute>) -> bool {
}

struct VisitOptions {
visit: Ident,
kind: Ident,
visit: Option<Ident>,
kind: Option<Ident>,
generic: Option<Generics>,
}

impl Parse for VisitOptions {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let visit: Ident = input.parse()?;
let _: Token![,] = input.parse()?;
let kind: Ident = input.parse()?;
Ok(Self { visit, kind })
let (visit, kind, comma) = if input.peek(Ident) {
let visit: Ident = input.parse()?;
let _: Token![,] = input.parse()?;
let kind: Ident = input.parse()?;
let comma: Result<Token![,], _> = input.parse();
(Some(visit), Some(kind), comma.is_ok())
} else {
(None, None, true)
};
let generic: Option<Generics> = if comma { Some(input.parse()?) } else { None };
Ok(Self { visit, kind, generic })
}
}

Expand Down
86 changes: 77 additions & 9 deletions node/ast.d.ts
Expand Up @@ -98,7 +98,7 @@ export type Rule<D = Declaration> =
export type MediaCondition =
| {
type: "feature";
value: MediaFeature;
value: QueryFeatureFor_MediaFeatureId;
}
| {
type: "not";
Expand All @@ -116,14 +116,14 @@ export type MediaCondition =
type: "operation";
};
/**
* A [media feature](https://drafts.csswg.org/mediaqueries/#typedef-media-feature)
* A generic media feature or container feature.
*/
export type MediaFeature =
export type QueryFeatureFor_MediaFeatureId =
| {
/**
* The name of the feature.
*/
name: MediaFeatureName;
name: MediaFeatureNameFor_MediaFeatureId;
type: "plain";
/**
* The feature value.
Expand All @@ -134,14 +134,14 @@ export type MediaFeature =
/**
* The name of the feature.
*/
name: MediaFeatureName;
name: MediaFeatureNameFor_MediaFeatureId;
type: "boolean";
}
| {
/**
* The name of the feature.
*/
name: MediaFeatureName;
name: MediaFeatureNameFor_MediaFeatureId;
/**
* A comparator.
*/
Expand All @@ -164,7 +164,7 @@ export type MediaFeature =
/**
* The name of the feature.
*/
name: MediaFeatureName;
name: MediaFeatureNameFor_MediaFeatureId;
/**
* A start value.
*/
Expand All @@ -178,7 +178,7 @@ export type MediaFeature =
/**
* A media feature name.
*/
export type MediaFeatureName = MediaFeatureId | String | String;
export type MediaFeatureNameFor_MediaFeatureId = MediaFeatureId | String | String;
/**
* A media query feature identifier.
*/
Expand Down Expand Up @@ -6894,7 +6894,7 @@ export type SyntaxComponentKind =
export type ContainerCondition =
| {
type: "feature";
value: MediaFeature;
value: QueryFeatureFor_ContainerSizeFeatureId;
}
| {
type: "not";
Expand All @@ -6915,6 +6915,74 @@ export type ContainerCondition =
type: "style";
value: StyleQuery;
};
/**
* A generic media feature or container feature.
*/
export type QueryFeatureFor_ContainerSizeFeatureId =
| {
/**
* The name of the feature.
*/
name: MediaFeatureNameFor_ContainerSizeFeatureId;
type: "plain";
/**
* The feature value.
*/
value: MediaFeatureValue;
}
| {
/**
* The name of the feature.
*/
name: MediaFeatureNameFor_ContainerSizeFeatureId;
type: "boolean";
}
| {
/**
* The name of the feature.
*/
name: MediaFeatureNameFor_ContainerSizeFeatureId;
/**
* A comparator.
*/
operator: MediaFeatureComparison;
type: "range";
/**
* The feature value.
*/
value: MediaFeatureValue;
}
| {
/**
* The end value.
*/
end: MediaFeatureValue;
/**
* A comparator for the end value.
*/
endOperator: MediaFeatureComparison;
/**
* The name of the feature.
*/
name: MediaFeatureNameFor_ContainerSizeFeatureId;
/**
* A start value.
*/
start: MediaFeatureValue;
/**
* A comparator for the start value.
*/
startOperator: MediaFeatureComparison;
type: "interval";
};
/**
* A media feature name.
*/
export type MediaFeatureNameFor_ContainerSizeFeatureId = ContainerSizeFeatureId | String | String;
/**
* A container query size feature identifier.
*/
export type ContainerSizeFeatureId = "width" | "height" | "inline-size" | "block-size" | "aspect-ratio" | "orientation";
/**
* Represents a style query within a container condition.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Expand Up @@ -23921,6 +23921,9 @@ mod tests {
ParserError::UnexpectedToken(crate::properties::custom::Token::Ident("bar".into())),
);

error_test("@container (inline-size <= foo) {}", ParserError::InvalidMediaQuery);
error_test("@container (orientation <= 10px) {}", ParserError::InvalidMediaQuery);

error_test("@container style(width) {}", ParserError::EndOfInput);
error_test(
"@container style(style(--foo: bar)) {}",
Expand Down

0 comments on commit 8df86e4

Please sign in to comment.