diff --git a/crates/hdx_ast/src/css/rules/supports.rs b/crates/hdx_ast/src/css/rules/supports.rs index 1447db6..d63e308 100644 --- a/crates/hdx_ast/src/css/rules/supports.rs +++ b/crates/hdx_ast/src/css/rules/supports.rs @@ -1,8 +1,16 @@ -use crate::{css::stylesheet::Rule, syntax::SimpleBlock}; +use std::ops::Deref; + +use crate::{ + css::{properties::Property, selector::ComplexSelector, stylesheet::Rule}, + syntax::ComponentValues, +}; use hdx_atom::atom; use hdx_lexer::Span; -use hdx_parser::{diagnostics, AtRule, Parse, Parser, Result as ParserResult, RuleList, Spanned, Token, Vec}; -use hdx_writer::{CssWriter, OutputOption, Result as WriterResult, WriteCss}; +use hdx_parser::{ + diagnostics, AtRule, ConditionalAtRule, Parse, Parser, Result as ParserResult, RuleList, Spanned, Token, Vec, +}; +use hdx_writer::{write_css, CssWriter, OutputOption, Result as WriterResult, WriteCss}; +use smallvec::SmallVec; mod kw { use hdx_parser::custom_keyword; @@ -93,81 +101,33 @@ impl<'a> WriteCss<'a> for SupportsRules<'a> { #[cfg_attr(feature = "serde", derive(serde::Serialize), serde(tag = "type", content = "value"))] pub enum SupportsCondition<'a> { Is(SupportsFeature<'a>), - Not(SupportsFeature<'a>), - And(Vec<'a, SupportsFeature<'a>>), - Or(Vec<'a, SupportsFeature<'a>>), + Not(Box>), + And(SmallVec<[SupportsFeature<'a>; 2]>), + Or(SmallVec<[SupportsFeature<'a>; 2]>), +} + +impl<'a> ConditionalAtRule<'a> for SupportsCondition<'a> { + type Feature = SupportsFeature<'a>; + fn create_is(feature: SupportsFeature<'a>) -> Self { + Self::Is(feature) + } + fn create_not(feature: SupportsCondition<'a>) -> Self { + Self::Not(Box::new(feature)) + } + fn create_and(feature: SmallVec<[SupportsFeature<'a>; 2]>) -> Self { + Self::And(feature) + } + fn create_or(feature: SmallVec<[SupportsFeature<'a>; 2]>) -> Self { + Self::Or(feature) + } } impl<'a> Parse<'a> for SupportsCondition<'a> { fn parse(parser: &mut Parser<'a>) -> ParserResult { - if parser.peek::().is_some() { - let mut features = parser.new_vec(); - loop { - parser.parse::()?; - features.push(parser.parse::()?); - if parser.peek::().is_some() { - continue; - } else { - return Ok(Self::And(features)); - } - } - } else if parser.peek::().is_some() { - let mut features = parser.new_vec(); - loop { - parser.parse::()?; - features.push(parser.parse::()?); - if parser.peek::().is_some() { - continue; - } else { - return Ok(Self::And(features)); - } - } - } else if let Some(token) = parser.peek::() { - parser.hop(token); - return parser.parse::().map(Self::Not); - } - - // handle double parens - let mut wrapped = true; - let checkpoint = parser.checkpoint(); - parser.parse::()?; - if parser.peek::().is_none() { - wrapped = false; - parser.rewind(checkpoint); - } - let feature = parser.parse::()?; - if parser.peek::().is_some() { - let mut features = parser.new_vec(); - features.push(feature); - loop { - parser.parse::()?; - features.push(parser.parse::()?); - if parser.peek::().is_none() { - if wrapped { - parser.parse::()?; - } - return Ok(Self::And(features)); - } - } - } else if parser.peek::().is_some() { - let mut features = parser.new_vec(); - features.push(feature); - loop { - parser.parse::()?; - features.push(parser.parse::()?); - if parser.peek::().is_none() { - if wrapped { - parser.parse::()?; - } - return Ok(Self::Or(features)); - } - } + if parser.peek::().is_some() { + return Ok(Self::Is(parser.parse::()?)); } - if wrapped { - parser.parse::()?; - } - parser.parse::()?; - Ok(Self::Is(feature)) + Self::parse_condition(parser) } } @@ -175,10 +135,14 @@ impl<'a> WriteCss<'a> for SupportsCondition<'a> { fn write_css(&self, sink: &mut W) -> WriterResult { match self { Self::Is(feature) => feature.write_css(sink), - Self::Not(feature) => { - atom!("not").write_css(sink)?; - sink.write_whitespace()?; - feature.write_css(sink) + Self::Not(condition) => { + match condition.deref() { + SupportsCondition::Is(_) => { + write_css!(sink, atom!("not"), (), condition) + } + _ => write_css!(sink, atom!("not"), (), '(', condition, ')'), + } + Ok(()) } Self::And(features) => { let mut first = true; @@ -220,21 +184,60 @@ impl<'a> WriteCss<'a> for SupportsCondition<'a> { #[derive(PartialEq, Debug, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize), serde())] -pub struct SupportsFeature<'a>(pub SimpleBlock<'a>); +pub enum SupportsFeature<'a> { + FontTech(ComponentValues<'a>), + FontFormat(ComponentValues<'a>), + Selector(ComplexSelector<'a>), + Property(Property<'a>), +} impl<'a> Parse<'a> for SupportsFeature<'a> { fn parse(parser: &mut Parser<'a>) -> ParserResult { - if parser.peek::().is_none() { - let token = parser.peek::().unwrap(); - Err(diagnostics::ExpectedOpenCurly(token, token.span()))? + dbg!(parser.peek::()); + let parens = parser.parse_if_peek::()?.is_some(); + dbg!(parser.peek::()); + if let Some(token) = parser.peek::() { + match parser.parse_atom_lower(token) { + atom!("selector") => { + parser.hop(token); + dbg!("SELECTOR"); + let selector = parser.parse::()?; + // End function + parser.parse::()?; + if parens { + parser.parse::()?; + } + Ok(Self::Selector(selector)) + } + atom!("font-tech") => { + todo!(); + } + atom!("font-format") => { + todo!(); + } + atom => Err(diagnostics::UnexpectedFunction(atom, token.span()))?, + } + } else { + if !parens { + let token = parser.peek::().unwrap(); + Err(diagnostics::Unexpected(token, token.span()))?; + } + let property = parser.parse::()?; + parser.parse::()?; + Ok(Self::Property(property)) } - Ok(Self(SimpleBlock::parse(parser)?)) } } impl<'a> WriteCss<'a> for SupportsFeature<'a> { fn write_css(&self, sink: &mut W) -> WriterResult { - self.0.write_css(sink) + match self { + Self::FontTech(_) => todo!(), + Self::FontFormat(_) => todo!(), + Self::Selector(selector) => write_css!(sink, atom!("selector"), '(', selector, ')'), + Self::Property(property) => write_css!(sink, '(', property, ')'), + } + Ok(()) } } @@ -245,8 +248,8 @@ mod tests { #[test] fn size_test() { - assert_size!(Supports, 96); - assert_size!(SupportsCondition, 48); + assert_size!(Supports, 344); + assert_size!(SupportsCondition, 296); assert_size!(SupportsRules, 32); } @@ -259,7 +262,16 @@ mod tests { assert_parse!(Supports, "@supports (width: 1--foo) and (width: 1foo) {\n\n}"); assert_parse!(Supports, "@supports (width: 100vw) {\n\tbody {\n\t\twidth: 100vw;\n\t}\n}"); assert_parse!(Supports, "@supports not ((text-align-last: justify) or (-moz-text-align-last: justify)) {\n\n}"); - assert_parse!(Supports, "@supports ((position: -webkit-sticky) or (position: sticky)) {}"); + assert_parse!( + Supports, + "@supports ((position: -webkit-sticky) or (position: sticky)) {}", + "@supports (position: -webkit-sticky) or (position: sticky) {\n\n}" + ); + + assert_parse!(Supports, "@supports selector(h2 > p) {\n\n}"); + assert_parse!(Supports, "@supports (selector(h2 > p)) {}", "@supports selector(h2 > p) {\n\n}"); + assert_parse!(Supports, "@supports not selector(h2 > p) {\n\n}"); + assert_parse!(Supports, "@supports not (selector(h2 > p)) {}", "@supports not selector(h2 > p) {\n\n}"); } #[test] @@ -267,12 +279,12 @@ mod tests { assert_minify!( Supports, "@supports (width: 1px) { body { width:1px; } }", - "@supports(width: 1px){body{width:1px}}" + "@supports(width:1px){body{width:1px}}" ); assert_minify!( Supports, "@supports not (width: 1--foo) { a { width:1px } }", - "@supports not(width: 1--foo){a{width:1px}}" + "@supports not(width:1--foo){a{width:1px}}" ); assert_minify!(Supports, "@supports (color: black) {}", ""); } diff --git a/crates/hdx_ast/src/css/selector/mod.rs b/crates/hdx_ast/src/css/selector/mod.rs index 5bf20b5..96eed28 100644 --- a/crates/hdx_ast/src/css/selector/mod.rs +++ b/crates/hdx_ast/src/css/selector/mod.rs @@ -59,6 +59,7 @@ impl<'a> WriteCss<'a> for SelectorList<'a> { } } +pub type ComplexSelector<'a> = SelectorList<'a>; pub type ForgivingSelector<'a> = SelectorList<'a>; pub type RelativeSelector<'a> = SelectorList<'a>; @@ -257,6 +258,7 @@ mod tests { #[test] fn size_test() { assert_size!(SelectorList, 32); + assert_size!(ComplexSelector, 32); assert_size!(ForgivingSelector, 32); assert_size!(RelativeSelector, 32); assert_size!(SelectorComponent, 48); diff --git a/crates/hdx_ast/src/css/stylesheet.rs b/crates/hdx_ast/src/css/stylesheet.rs index 25cdeb4..ea853f3 100644 --- a/crates/hdx_ast/src/css/stylesheet.rs +++ b/crates/hdx_ast/src/css/stylesheet.rs @@ -154,7 +154,7 @@ mod tests { #[test] fn size_test() { assert_size!(StyleSheet, 32); - assert_size!(Rule, 152); + assert_size!(Rule, 344); assert_size!(AtRuleId, 1); }