Skip to content

Commit

Permalink
a bunch more fixes that I can't be bothered to separate into individu…
Browse files Browse the repository at this point in the history
…al commits
  • Loading branch information
keithamus committed Feb 24, 2024
1 parent 1828b68 commit aae7c27
Show file tree
Hide file tree
Showing 15 changed files with 327 additions and 216 deletions.
34 changes: 34 additions & 0 deletions crates/hdx_ast/src/css/selector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,40 @@ mod pseudo_class;
use attribute::Attribute;
use pseudo_class::PseudoClass;

#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub struct Selectors<'a>(pub Vec<'a, Spanned<Selector<'a>>>);

impl<'a> Parse<'a> for Selectors<'a> {
fn parse(parser: &mut Parser<'a>) -> ParserResult<Spanned<Self>> {
let span = parser.span();
let mut selectors = parser.new_vec();
loop {
selectors.push(Selector::parse(parser)?);
discard!(parser, Token::Whitespace);
match parser.cur() {
Token::Comma => { parser.advance(); },
_ => break,
}
}
Ok(Selectors( selectors ).spanned(span.end(parser.pos())))
}
}

impl<'a> WriteCss<'a> for Selectors<'a> {
fn write_css<W: CssWriter>(&self, sink: &mut W) -> WriterResult {
let mut iter = self.0.iter().peekable();
while let Some(selector) = iter.next() {
selector.write_css(sink)?;
if iter.peek().is_some() {
sink.write_char(',')?;
}
sink.write_trivia_char(' ')?;
}
Ok(())
}
}

// This encapsulates both `simple-selector` and `compound-selector`.
// As `simple-selector` is a `compound-selector` but with only one `Component`.
// Having `Selector` be both ` simple-selector` and `compound-selector` makes
Expand Down
11 changes: 5 additions & 6 deletions crates/hdx_ast/src/css/stylerule.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
use hdx_lexer::Token;
use hdx_parser::{expect, unexpected, Parse, Parser, QualifiedRule, Result as ParserResult, Block};
use hdx_parser::{Block, Parse, Parser, QualifiedRule, Result as ParserResult};
use hdx_writer::{CssWriter, Result as WriterResult, WriteCss};
#[cfg(feature = "serde")]
use serde::Serialize;

use crate::{
css::{properties::StyleProperty, selector::Selector},
syntax::Declaration,
css::{properties::StyleProperty, selector::Selectors},
Box, Spanned, Vec,
};

// https://drafts.csswg.org/cssom-1/#the-cssstylerule-interface
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct StyleRule<'a> {
pub selectors: Box<'a, Spanned<Selector<'a>>>,
pub selectors: Box<'a, Spanned<Selectors<'a>>>,
pub style: Box<'a, Spanned<StyleDeclaration<'a>>>,
}

Expand All @@ -28,7 +26,7 @@ impl<'a> Parse<'a> for StyleRule<'a> {

impl<'a> QualifiedRule<'a> for StyleRule<'a> {
type Block = StyleDeclaration<'a>;
type Prelude = Selector<'a>;
type Prelude = Selectors<'a>;
}

impl<'a> WriteCss<'a> for StyleRule<'a> {
Expand Down Expand Up @@ -101,6 +99,7 @@ mod test {
fn test_writes() {
let allocator = Allocator::default();
test_write::<StyleRule>(&allocator, "body {}", "body{}");
test_write::<StyleRule>(&allocator, "body, body {}", "body,body{}");
test_write::<StyleRule>(&allocator, "body { width:1px }", "body{width:1px}");
}
}
36 changes: 18 additions & 18 deletions crates/hdx_ast/src/css/stylesheet.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use hdx_atom::atom;
use hdx_derive::Atomizable;
use hdx_lexer::Token;
use hdx_parser::{diagnostics, discard, StyleSheet as StyleSheetTrait, Parse, Parser, Result as ParserResult};
use hdx_parser::{diagnostics, Parse, Parser, Result as ParserResult, StyleSheet as StyleSheetTrait};
use hdx_writer::{CssWriter, Result as WriterResult, WriteCss};
#[cfg(feature = "serde")]
use serde::Serialize;
Expand Down Expand Up @@ -62,23 +62,21 @@ impl<'a> Parse<'a> for Rule<'a> {
fn parse(parser: &mut Parser<'a>) -> ParserResult<Spanned<Self>> {
let span = parser.span();
Ok(match parser.cur() {
Token::AtKeyword(atom) => {
match atom.to_ascii_lowercase() {
atom!("charset") => {
let rule = CharsetRule::parse(parser)?;
Rule::Charset(parser.boxup(rule))
}
atom!("page") => {
let rule = PageRule::parse(parser)?;
Rule::Page(parser.boxup(rule))
}
_ => {
let rule = AtRule::parse(parser)?;
parser.warn(diagnostics::UnknownRule(rule.span).into());
Rule::UnknownAt(parser.boxup(rule))
}
Token::AtKeyword(atom) => match atom.to_ascii_lowercase() {
atom!("charset") => {
let rule = CharsetRule::parse(parser)?;
Rule::Charset(parser.boxup(rule))
}
}
atom!("page") => {
let rule = PageRule::parse(parser)?;
Rule::Page(parser.boxup(rule))
}
_ => {
let rule = AtRule::parse(parser)?;
parser.warn(diagnostics::UnknownRule(rule.span).into());
Rule::UnknownAt(parser.boxup(rule))
}
},
// "Consume a qualified rule from input. If anything is returned, append it to rules."
_ => {
let checkpoint = parser.checkpoint();
Expand All @@ -92,7 +90,8 @@ impl<'a> Parse<'a> for Rule<'a> {
}
}
}
}.spanned(span.end(parser.pos())))
}
.spanned(span.end(parser.pos())))
}
}

Expand Down Expand Up @@ -134,6 +133,7 @@ mod tests {
fn test_writes() {
let allocator = Allocator::default();
test_write::<StyleSheet>(&allocator, "body {}", "body{}");
test_write::<StyleSheet>(&allocator, "body, body {}", "body,body{}");
test_write::<StyleSheet>(&allocator, "body { width: 1px }", "body{width:1px}");
}
}
2 changes: 1 addition & 1 deletion crates/hdx_ast/src/css/values/sizing/max_height.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pub type MaxHeight = super::super::Todo;
pub type MaxHeight = super::MaxWidth;
51 changes: 50 additions & 1 deletion crates/hdx_ast/src/css/values/sizing/max_width.rs
Original file line number Diff line number Diff line change
@@ -1 +1,50 @@
pub type MaxWidth = super::super::Todo;
#[cfg(feature = "serde")]
use serde::Serialize;

use super::super::units::LengthPercentage;
use crate::{Parsable, Writable};

// https://drafts.csswg.org/css-sizing-4/#sizing-values
#[derive(Parsable, Writable, Default, Debug, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "kebab-case"))]
pub enum MaxWidth {
#[default]
None, // atom!("none")
MinContent, // atom!("min-content")
MaxContent, // atom!("max-content") TODO: `intrinsic` non standard
// https://drafts.csswg.org/css-sizing-4/#sizing-values
Stretch, // atom!("stretch") TODO: -webkit-fill-available, -moz-available
FitContent, // atom!("fit-content")
Contain, // atom!("contain")

#[parsable(DimensionOrZero, FromToken, Check::Positive)]
LengthPercentage(LengthPercentage),
#[parsable(Function, FromToken, Check::Positive, atom = "fit-content")]
#[writable(as_function = "fit-content")]
FitContentFunction(LengthPercentage),
}

#[cfg(test)]
mod tests {
use oxc_allocator::Allocator;

use super::*;
use crate::test_helpers::test_write;

#[test]
fn size_test() {
use std::mem::size_of;
assert_eq!(size_of::<MaxWidth>(), 12);
}

#[test]
fn test_writes() {
let allocator = Allocator::default();
test_write::<MaxWidth>(&allocator, "0", "0");
test_write::<MaxWidth>(&allocator, "1px", "1px");
test_write::<MaxWidth>(&allocator, "none", "none");
test_write::<MaxWidth>(&allocator, "fit-content", "fit-content");
test_write::<MaxWidth>(&allocator, "fit-content(20rem)", "fit-content(20rem)");
test_write::<MaxWidth>(&allocator, "fit-content(0)", "fit-content(0)");
}
}
2 changes: 1 addition & 1 deletion crates/hdx_ast/src/css/values/sizing/min_height.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pub type MinHeight = super::super::Todo;
pub type MinHeight = super::MaxWidth;
2 changes: 1 addition & 1 deletion crates/hdx_ast/src/css/values/sizing/min_width.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pub type MinWidth = super::super::Todo;
pub type MinWidth = super::MaxWidth;
82 changes: 4 additions & 78 deletions crates/hdx_ast/src/css/values/sizing/width.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
use hdx_atom::atom;
use hdx_lexer::Token;
use hdx_parser::{diagnostics, expect, unexpected, Parse, Parser, Result as ParserResult, Spanned};
#[cfg(feature = "serde")]
use serde::Serialize;

use super::super::units::LengthPercentage;
use crate::Writable;
use crate::{Parsable, Writable};

// https://drafts.csswg.org/css-sizing-4/#sizing-values
#[derive(Writable, Default, Debug, PartialEq, Hash)]
#[derive(Parsable, Writable, Default, Debug, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "kebab-case"))]
pub enum Width {
#[default]
Expand All @@ -20,84 +17,13 @@ pub enum Width {
FitContent, // atom!("fit-content")
Contain, // atom!("contain")

#[parsable(DimensionOrZero, FromToken, Check::Positive)]
LengthPercentage(LengthPercentage),
#[parsable(Function, FromToken, Check::Positive, atom = "fit-content")]
#[writable(as_function = "fit-content")]
FitContentFunction(LengthPercentage),
}

impl<'a> Parse<'a> for Width {
fn parse(parser: &mut Parser<'a>) -> ParserResult<Spanned<Self>> {
let span = parser.span();
match parser.cur() {
Token::Ident(atom!("auto")) => {
parser.advance();
Ok(Self::Auto.spanned(span))
}
Token::Ident(atom!("min-content")) => {
parser.advance();
Ok(Self::MinContent.spanned(span))
}
Token::Ident(atom!("max-content")) => {
parser.advance();
Ok(Self::MaxContent.spanned(span))
}
Token::Ident(atom!("stretch")) => {
parser.advance();
Ok(Self::Stretch.spanned(span))
}
Token::Ident(atom!("fit-content")) => {
parser.advance();
Ok(Self::FitContent.spanned(span))
}
Token::Ident(atom!("contain")) => {
parser.advance();
Ok(Self::Contain.spanned(span))
}
Token::Dimension(val, unit, _) => {
if val < 0.0 {
Err(diagnostics::NumberNotNegative(val, span))?
}
if let Some(val) = LengthPercentage::new(val.into(), unit.clone()) {
parser.advance();
Ok(Self::LengthPercentage(val).spanned(span))
} else {
Err(diagnostics::UnexpectedDimension(unit, span))?
}
}
Token::Number(val, _) if val == 0.0 => {
parser.advance();
Ok(Self::LengthPercentage(LengthPercentage::Zero).spanned(span))
}
Token::Function(atom!("fit-content")) => {
parser.advance();
match parser.cur() {
Token::Dimension(val, unit, _) => {
if val < 0.0 {
Err(diagnostics::NumberNotNegative(val, span))?
}
if let Some(val) = LengthPercentage::new(val.into(), unit.clone()) {
parser.advance();
expect!(parser, Token::RightParen);
parser.advance();
Ok(Self::FitContentFunction(val).spanned(span))
} else {
Err(diagnostics::UnexpectedDimension(unit, span))?
}
}
Token::Number(val, _) if val == 0.0 => {
parser.advance();
expect!(parser, Token::RightParen);
parser.advance();
Ok(Self::FitContentFunction(LengthPercentage::Zero).spanned(span))
}
token => unexpected!(parser, token),
}
}
token => unexpected!(parser, token),
}
}
}

#[cfg(test)]
mod tests {
use oxc_allocator::Allocator;
Expand Down
50 changes: 14 additions & 36 deletions crates/hdx_ast/src/css/values/units/length.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use hdx_atom::{atom, Atom};
use hdx_lexer::Token;
use hdx_parser::{diagnostics::UnexpectedDimension, unexpected, Parse, Parser, Result as ParserResult, Spanned};
use hdx_parser::FromToken;
#[cfg(feature = "serde")]
use serde::Serialize;

Expand All @@ -23,7 +23,7 @@ macro_rules! length {
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum Length {
#[writable(rename = "0")]
Zero, // TODO: atom!("zero") <- shouldn't need to be an atom but something weird is happening
Zero,
$(
#[writable(suffix = $atom)]
$name(CSSFloat),
Expand All @@ -48,23 +48,12 @@ macro_rules! length {
}
}

impl<'a> Parse<'a> for Length {
fn parse(parser: &mut Parser) -> ParserResult<Spanned<Self>> {
let span = parser.span();
match parser.cur() {
Token::Number(n, _) if n == 0.0 => {
parser.advance();
Ok(Self::Zero.spanned(span))
}
Token::Dimension(n, unit, _) => {
if let Some(length) = Self::new(n.into(), unit.clone()) {
parser.advance();
Ok(length.spanned(span))
} else {
Err(UnexpectedDimension(unit, span))?
}
}
token => unexpected!(parser, token),
impl FromToken for Length {
fn from_token(token: Token) -> Option<Self> {
match token {
Token::Number(n, _) if n == 0.0 => Some(Self::Zero),
Token::Dimension(n, unit, _) => Self::new(n.into(), unit),
_ => None,
}
}
}
Expand Down Expand Up @@ -101,23 +90,12 @@ macro_rules! length {
}
}

impl<'a> Parse<'a> for LengthPercentage {
fn parse(parser: &mut Parser) -> ParserResult<Spanned<Self>> {
let span = parser.span();
match parser.cur() {
Token::Number(n, _) if n == 0.0 => {
parser.advance();
Ok(Self::Zero.spanned(span))
},
Token::Dimension(n, unit, _) => {
parser.advance();
if let Some(length) = Self::new(n.into(), unit.clone()) {
Ok(length.spanned(span))
} else {
Err(UnexpectedDimension(unit, span))?
}
}
token => unexpected!(parser, token),
impl FromToken for LengthPercentage {
fn from_token(token: Token) -> Option<Self> {
match token {
Token::Number(n, _) if n == 0.0 => Some(Self::Zero),
Token::Dimension(n, unit, _) => Self::new(n.into(), unit),
_ => None,
}
}
}
Expand Down
Loading

0 comments on commit aae7c27

Please sign in to comment.