Skip to content

Commit

Permalink
implement list-style shorthand
Browse files Browse the repository at this point in the history
  • Loading branch information
keithamus committed Aug 4, 2023
1 parent a5d0980 commit d7515ce
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 9 deletions.
8 changes: 4 additions & 4 deletions crates/hdx_ast/src/css/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,10 +563,10 @@ properties! {
atom!("counter-increment") => CounterIncrement<Expr<'a, Todo>>,
atom!("counter-reset") => CounterReset<Expr<'a, Todo>>,
atom!("counter-set") => CounterSet<Expr<'a, Todo>>,
atom!("list-style") => ListStyle<Expr<'a, Todo>> shorthand=true,
atom!("list-style-image") => ListStyleImage<Expr<'a, Todo>> inherits=true,
atom!("list-style-position") => ListStylePosition<Expr<'a, Todo>> inherits=true,
atom!("list-style-type") => ListStyleType<Expr<'a, Todo>> inherits=true,
atom!("list-style") => ListStyle<ListStyleShorthand<'a>> shorthand=true,
atom!("list-style-image") => ListStyleImage<Expr<'a, ListStyleImageValue<'a>>> inherits=true,
atom!("list-style-position") => ListStylePosition<Expr<'a, ListStylePositionValue>> inherits=true,
atom!("list-style-type") => ListStyleType<Expr<'a, ListStyleTypeValue<'a>>> inherits=true,
atom!("marker-side") => MarkerSide<Expr<'a, Todo>> inherits=true,

// https://drafts.csswg.org/css-logical-1/#property-index
Expand Down
56 changes: 53 additions & 3 deletions crates/hdx_ast/src/css/values/counter_styles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,63 @@
use serde::Serialize;

use super::Image;
use crate::{atom, Atom, Atomizable, Box, Vec};
use crate::{atom, Atom, Atomizable, Box, Spanned, Vec};

#[derive(Debug, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum CounterStyle<'a> {
Named(Atom),
Symbols(Symbols<'a>),
Symbols(Spanned<Symbols<'a>>),
Predefined(PredefinedCounterStyle),
}

// https://drafts.csswg.org/css-counter-styles-3/#predefined-counters
#[derive(Atomizable, Default, Debug, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum PredefinedCounterStyle {
Decimal, // atom!("decimal"),
DecimalLeadingZero, // atom!("decimal-leading-zero"),
ArabicIndic, // atom!("arabic-indic"),
Armenian, // atom!("armenian"),
UpperArmenian, // atom!("upper-armenian"),
LowerArmenian, // atom!("lower-armenian"),
Bengali, // atom!("bengali"),
Cambodian, // atom!("cambodian"),
Khmer, // atom!("khmer"),
CjkDecimal, // atom!("cjk-decimal"),
Devanagari, // atom!("devanagari"),
Georgian, // atom!("georgian"),
Gujarati, // atom!("gujarati"),
Gurmukhi, // atom!("gurmukhi"),
Hebrew, // atom!("hebrew"),
Kannada, // atom!("kannada"),
Lao, // atom!("lao"),
Malayalam, // atom!("malayalam"),
Mongolian, // atom!("mongolian"),
Myanmar, // atom!("myanmar"),
Oriya, // atom!("oriya"),
Persian, // atom!("persian"),
LowerRoman, // atom!("lower-roman"),
UpperRoman, // atom!("upper-roman"),
Tamil, // atom!("tamil"),
Telugu, // atom!("telugu"),
Thai, // atom!("thai"),
Tibetan, // atom!("tibetan"),
LowerAlpha, // atom!("lower-alpha"),
UpperAlpha, // atom!("upper-alpha"),
UpperLatin, // atom!("upper-latin"),
LowerGreek, // atom!("lower-greek"),
Hiragana, // atom!("hiragana"),
HiraganaIroha, // atom!("hiragana-iroha"),
Katakana, // atom!("katakana"),
KatakanaIroha, // atom!("katakana-iroha"),
#[default]
Disc, // atom!("disc"),
Square, // atom!("square"),
DisclousureOpen, // atom!("disclousure-open"),
DisclousureClosed, // atom!("disclousure-closed"),
CjkEarthlyBranch, // atom!("cjk-earthly-branch"),
CjkHeavenlyStem, // atom!("cjk-heavenly-stem"),
}

// https://drafts.csswg.org/css-counter-styles-3/#funcdef-symbols
Expand Down Expand Up @@ -47,7 +97,7 @@ mod tests {
#[test]
fn size_test() {
use std::mem::size_of;
assert_eq!(size_of::<CounterStyle>(), 16);
assert_eq!(size_of::<CounterStyle>(), 24);
assert_eq!(size_of::<Symbols>(), 16);
assert_eq!(size_of::<Symbol>(), 16);
assert_eq!(size_of::<SymbolsType>(), 1);
Expand Down
49 changes: 47 additions & 2 deletions crates/hdx_ast/src/css/values/lists.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#[cfg(feature = "serde")]
use serde::Serialize;

use super::CounterStyle;
use crate::{Atom, Box};
use super::{CounterStyle, Expr, Image, PredefinedCounterStyle, Shorthand};
use crate::{atom, Atom, Atomizable, Box, Spanned};

// https://drafts.csswg.org/css-lists-3/#counter-functions
#[derive(Debug, PartialEq, Hash)]
Expand All @@ -29,6 +29,48 @@ pub struct Counters<'a> {
pub style: Box<'a, CounterStyle<'a>>,
}

// https://drafts.csswg.org/css-lists-3/#funcdef-counters
#[derive(Atomizable, Default, Debug, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum ListStylePositionValue {
#[default]
Outside, // atom!("outside")
Inside, // atom!("inside")
}

// https://drafts.csswg.org/css-lists-3/#funcdef-counters
#[derive(Debug, Default, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum ListStyleImageValue<'a> {
#[default]
None,
Image(Spanned<Image<'a>>),
}
//
// https://drafts.csswg.org/css-lists-3/#funcdef-counters
#[derive(Debug, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum ListStyleTypeValue<'a> {
CounterStyle(Spanned<CounterStyle<'a>>),
String(Atom),
None,
}

impl<'a> Default for ListStyleTypeValue<'a> {
fn default() -> Self {
Self::CounterStyle(Spanned::dummy(CounterStyle::Predefined(PredefinedCounterStyle::Disc)))
}
}

// https://drafts.csswg.org/css-lists/#propdef-list-style
#[derive(Debug, Default, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub struct ListStyleShorthand<'a> {
pub position: Shorthand<'a, Expr<'a, ListStylePositionValue>>,
pub image: Shorthand<'a, Expr<'a, ListStyleImageValue<'a>>>,
pub marker: Shorthand<'a, Expr<'a, ListStyleTypeValue<'a>>>,
}

#[cfg(test)]
mod tests {

Expand All @@ -40,5 +82,8 @@ mod tests {
assert_eq!(size_of::<CounterOrCounters>(), 24);
assert_eq!(size_of::<Counter>(), 16);
assert_eq!(size_of::<Counters>(), 24);
assert_eq!(size_of::<ListStyleTypeValue>(), 32);
assert_eq!(size_of::<ListStyleImageValue>(), 24);
assert_eq!(size_of::<ListStylePositionValue>(), 1);
}
}
35 changes: 35 additions & 0 deletions crates/hdx_parser/src/css/values/counter_styles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use hdx_ast::css::values::{CounterStyle, PredefinedCounterStyle, Symbols};

use crate::{atom, diagnostics, Atomizable, Kind, Parse, Parser, Result, Spanned};

impl<'a> Parse<'a> for CounterStyle<'a> {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
let span = parser.cur().span;
match parser.cur().kind {
Kind::Ident => {
let ident = parser.expect_ident()?;
if let Some(node) = PredefinedCounterStyle::from_atom(ident.clone()) {
Ok(Self::Predefined(node).spanned(span))
} else {
Ok(Self::Named(ident).spanned(span))
}
}
Kind::Function => {
let ident = parser.expect_ident()?;
if ident == atom!("symbols") {
let node = Symbols::parse(parser)?;
Ok(Self::Symbols(node).spanned(span.up_to(&parser.cur().span)))
} else {
Err(diagnostics::ExpectedFunction(atom!("symbols"), ident, parser.cur().span))?
}
}
k => Err(diagnostics::Unexpected(k, parser.cur().span))?,
}
}
}

impl<'a> Parse<'a> for Symbols<'a> {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
Err(diagnostics::Unimplemented(parser.cur().span))?
}
}
112 changes: 112 additions & 0 deletions crates/hdx_parser/src/css/values/lists.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use hdx_ast::css::values::{
CounterStyle, Expr, Image, ListStyleImageValue, ListStylePositionValue, ListStyleShorthand,
ListStyleTypeValue, Shorthand,
};

use crate::{atom, diagnostics, Kind, Parse, Parser, Result, Spanned};

impl<'a> Parse<'a> for ListStyleShorthand<'a> {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
let span = parser.cur().span;
let mut position = Shorthand::Implicit;
let mut image = Shorthand::Implicit;
let mut marker = Shorthand::Implicit;
loop {
match parser.cur().kind {
Kind::Semicolon | Kind::Comma | Kind::Eof => {
break;
}
Kind::Ident => {
let ident = parser.cur_atom().unwrap();
if position.is_implicit() && matches!(ident, atom!("inside") | atom!("outside"))
{
let node = Expr::<ListStylePositionValue>::parse(parser)?;
position = Shorthand::Explicit(parser.boxup(node));
} else if image.is_implicit() && matches!(ident, atom!("none")) {
let node = Expr::<ListStyleImageValue>::parse(parser)?;
image = Shorthand::Explicit(parser.boxup(node));
} else if marker.is_implicit() {
let node = Expr::<ListStyleTypeValue>::parse(parser)?;
marker = Shorthand::Explicit(parser.boxup(node));
} else {
Err(diagnostics::UnexpectedIdent(ident.clone(), parser.cur().span))?
}
}
k => {
let checkpoint = parser.checkpoint();
if image.is_implicit() {
let node = Expr::<ListStyleImageValue>::parse(parser);
match node {
Ok(node) => {
image = Shorthand::Explicit(parser.boxup(node));
continue;
}
Err(_) => parser.rewind(checkpoint),
}
}
let checkpoint = parser.checkpoint();
if marker.is_implicit() {
let node = Expr::<ListStyleTypeValue>::parse(parser);
match node {
Ok(node) => {
marker = Shorthand::Explicit(parser.boxup(node));
continue;
}
Err(_) => parser.rewind(checkpoint),
}
}
Err(diagnostics::Unexpected(k, parser.cur().span))?
}
}
if position.is_explicit() && image.is_explicit() && marker.is_explicit() {
break;
}
}
Ok(Self { position, image, marker }.spanned(span.up_to(&parser.cur().span)))
}
}

impl<'a> Parse<'a> for ListStyleTypeValue<'a> {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
let span = parser.cur().span;
match parser.cur().kind {
Kind::Ident => {
let ident = parser.cur_atom().unwrap();
if ident == atom!("none") {
parser.advance();
Ok(Self::None.spanned(span))
} else {
let node = CounterStyle::parse(parser)?;
Ok(Self::CounterStyle(node).spanned(span.up_to(&parser.cur().span)))
}
}
Kind::String => Ok(Self::String(parser.expect_string()?).spanned(span)),
_ => {
let node = CounterStyle::parse(parser)?;
Ok(Self::CounterStyle(node).spanned(span.up_to(&parser.cur().span)))
}
}
}
}

impl<'a> Parse<'a> for ListStyleImageValue<'a> {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
let span = parser.cur().span;
match parser.cur().kind {
Kind::Ident => {
let ident = parser.cur_atom().unwrap();
if ident == atom!("none") {
parser.advance();
Ok(Self::None.spanned(span))
} else {
let node = Image::parse(parser)?;
Ok(Self::Image(node).spanned(span.up_to(&parser.cur().span)))
}
}
_ => {
let node = Image::parse(parser)?;
Ok(Self::Image(node).spanned(span.up_to(&parser.cur().span)))
}
}
}
}
10 changes: 10 additions & 0 deletions crates/hdx_parser/src/css/values/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ pub mod backgrounds;
pub mod r#box;
pub mod color;
pub mod content;
pub mod counter_styles;
pub mod display;
pub mod expr;
pub mod fonts;
pub mod inline;
pub mod length;
pub mod lists;
pub mod non_standard;
pub mod page_floats;
pub mod shorthand;
Expand Down Expand Up @@ -51,6 +53,7 @@ parse_for_enums! {
FloatReferenceValue,
InlineSizingValue,
LineStyle,
ListStylePositionValue,
MinIntrinsicSizingValue,
OverflowKeyword,
PositionValue,
Expand All @@ -63,6 +66,13 @@ parse_for_enums! {
VisibilityValue,
}

// TODO:
impl<'a> Parse<'a> for Image<'a> {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
Err(diagnostics::Unimplemented(parser.cur().span))?
}
}

// TODO:
impl<'a> Parse<'a> for RatioOrAuto {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
Expand Down
Loading

0 comments on commit d7515ce

Please sign in to comment.