Skip to content

Commit

Permalink
add bordershorthand & linewidth
Browse files Browse the repository at this point in the history
  • Loading branch information
keithamus committed Aug 4, 2023
1 parent 01b1962 commit a5d0980
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 58 deletions.
2 changes: 1 addition & 1 deletion crates/hdx_ast/src/css/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ properties! {
// ! background-position redefined in css-backgrounds-4
atom!("background-repeat") => BackgroundRepeat<Expr<'a, Todo>>,
atom!("background-size") => BackgroundSize<Expr<'a, Todo>>,
atom!("border") => Border<Expr<'a, Todo>> shorthand=true,
atom!("border") => Border<BorderShorthand<'a>> shorthand=true,
atom!("border-bottom") => BorderBottom<Expr<'a, Todo>> shorthand=true,
atom!("border-bottom-color") => BorderBottomColor<Expr<'a, ColorValue<'a>>> initial=Expr::Literal(Spanned::dummy(ColorValue::CurrentColor)),
atom!("border-bottom-left-radius") => BorderBottomLeftRadius<Expr<'a, Todo>>,
Expand Down
85 changes: 35 additions & 50 deletions crates/hdx_ast/src/css/values/backgrounds.rs
Original file line number Diff line number Diff line change
@@ -1,71 +1,56 @@
#[cfg(feature = "serde")]
use serde::Serialize;

use super::Length;
use crate::{atom, Atom, Atomizable};
use super::{ColorValue, Expr, Length, MathExpr, Shorthand};
use crate::{atom, Atom, Atomizable, Spanned};

// https://drafts.csswg.org/css-position-3/#inset-shorthands
#[derive(Debug, Default, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub struct BorderShorthand<'a> {
pub line_width: Shorthand<'a, MathExpr<'a, LineWidth>>,
pub line_style: Shorthand<'a, Expr<'a, LineStyle>>,
pub color: Shorthand<'a, MathExpr<'a, ColorValue<'a>>>,
}

// https://drafts.csswg.org/css-backgrounds-3/#typedef-line-width
#[derive(Default, Debug, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum LineWidth {
Thin,
#[default]
Medium,
Thick,
Length(Length),
Thin, // atom!("thin")
#[default]
Medium, // atom!("medium")
Thick, // atom!("thick")
Length(Spanned<Length>),
}

// impl Atomizable for LineWidth {
// fn from_atom(atom: Atom) -> Option<Self> {
// match atom {
// atom!("thin") => Some(Self::Thin),
// atom!("medium") => Some(Self::Medium),
// atom!("thick") => Some(Self::Thick),
// _ => {
// if let Some(length) = Length::from_atom(atom) {
// return Some(Self::Length(length));
// }
// None
// }
// }
// }
//
// fn to_atom(&self) -> Atom {
// match self {
// Self::Thin => atom!("thin"),
// Self::Medium => atom!("medium"),
// Self::Thick => atom!("thick"),
// Self::Length(l) => l.unit.to_atom(),
// }
// }
// }

// https://drafts.csswg.org/css-backgrounds-3/#typedef-line-style
#[derive(Atomizable, Debug, Default, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum LineStyle {
#[default]
None, // atom!("none")
Hidden, // atom!("hidden")
Dotted, // atom!("dotted")
Dashed, // atom!("dashed")
Solid, // atom!("solid")
Double, // atom!("double")
Groove, // atom!("groove")
Ridge, // atom!("ridge")
Inset, // atom!("inset")
Outset, // atom!("outset")
#[default]
None, // atom!("none")
Hidden, // atom!("hidden")
Dotted, // atom!("dotted")
Dashed, // atom!("dashed")
Solid, // atom!("solid")
Double, // atom!("double")
Groove, // atom!("groove")
Ridge, // atom!("ridge")
Inset, // atom!("inset")
Outset, // atom!("outset")
}

#[cfg(test)]
mod tests {

use super::*;
use super::*;

#[test]
fn size_test() {
use std::mem::size_of;
assert_eq!(size_of::<LineWidth>(), 8);
assert_eq!(size_of::<LineStyle>(), 1);
}
#[test]
fn size_test() {
use std::mem::size_of;
assert_eq!(size_of::<BorderShorthand>(), 24);
assert_eq!(size_of::<LineWidth>(), 16);
assert_eq!(size_of::<LineStyle>(), 1);
}
}
157 changes: 157 additions & 0 deletions crates/hdx_parser/src/css/values/backgrounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use hdx_ast::css::values::{
BorderShorthand, ColorValue, Expr, Length, LineStyle, LineWidth, MathExpr, Shorthand,
};

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

impl<'a> Parse<'a> for BorderShorthand<'a> {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
let span = parser.cur().span;
let mut line_width = Shorthand::Implicit;
let mut line_style = Shorthand::Implicit;
let mut color = Shorthand::Implicit;
loop {
match parser.cur().kind {
Kind::Ident => {
let ident = parser.cur_atom().unwrap();
if line_style.is_implicit()
&& matches!(
ident,
atom!("none")
| atom!("hidden") | atom!("dotted")
| atom!("dashed") | atom!("solid")
| atom!("double") | atom!("groove")
| atom!("ridge") | atom!("inset")
| atom!("outset")
) {
let node = Expr::<LineStyle>::parse(parser)?;
line_style = Shorthand::Explicit(parser.boxup(node));
} else if line_width.is_implicit()
&& matches!(ident, atom!("thin") | atom!("medium") | atom!("thick"))
{
let node = MathExpr::<LineWidth>::parse(parser)?;
line_width = Shorthand::Explicit(parser.boxup(node));
} else if color.is_implicit() {
let node = MathExpr::<ColorValue>::parse(parser)?;
color = Shorthand::Explicit(parser.boxup(node));
} else {
Err(diagnostics::UnexpectedIdent(ident.clone(), parser.cur().span))?
}
}
Kind::Semicolon | Kind::Comma | Kind::Eof => {
break;
}
Kind::Dimension => {
if line_width.is_implicit() {
let node = MathExpr::<LineWidth>::parse(parser)?;
line_width = Shorthand::Explicit(parser.boxup(node));
} else {
Err(diagnostics::Unexpected(Kind::Dimension, parser.cur().span))?
}
}
k => {
let checkpoint = parser.checkpoint();
if line_width.is_implicit() {
let node = MathExpr::<LineWidth>::parse(parser);
match node {
Ok(node) => {
line_width = Shorthand::Explicit(parser.boxup(node));
continue;
}
Err(_) => parser.rewind(checkpoint),
}
}
let checkpoint = parser.checkpoint();
if color.is_implicit() {
let node = MathExpr::<ColorValue>::parse(parser);
match node {
Ok(node) => {
color = Shorthand::Explicit(parser.boxup(node));
continue;
}
Err(_) => parser.rewind(checkpoint),
}
}
Err(diagnostics::Unexpected(k, parser.cur().span))?
}
}
if color.is_explicit() && line_style.is_explicit() && line_width.is_explicit() {
break;
}
}
Ok(Self { color, line_style, line_width }.spanned(span.up_to(&parser.cur().span)))
}
}

impl<'a> Parse<'a> for LineWidth {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
let span = parser.cur().span;
match parser.cur().kind {
Kind::Number | Kind::Dimension => {
Ok(Self::Length(Length::parse(parser)?).spanned(span.up_to(&parser.cur().span)))
}
Kind::Ident => {
let ident = parser.cur_atom().unwrap();
match ident {
atom!("thin") => Ok(Self::Thin.spanned(span)),
atom!("medium") => Ok(Self::Medium.spanned(span)),
atom!("thick") => Ok(Self::Thick.spanned(span)),
_ => Err(diagnostics::UnexpectedIdent(ident, span))?,
}
}
k => Err(diagnostics::Unexpected(k, span))?,
}
}
}

#[cfg(test)]
mod test {

use hdx_ast::css::values::{
BorderShorthand, ColorValue, Expr, Length, LineStyle, LineWidth, MathExpr, NamedColor, Px,
Shorthand,
};

use crate::{Allocator, Parser, ParserOptions, Span, Spanned};

#[test]
fn parses_border_1px_solid_red() {
let allocator = Allocator::default();
let parser = Parser::new(&allocator, "1px solid red", ParserOptions::default());
let expected = Spanned {
span: Span::new(0, 13),
node: BorderShorthand {
line_width: Shorthand::Explicit(parser.boxup(Spanned {
span: Span::new(0, 4),
node: MathExpr::Literal(Spanned {
span: Span::new(0, 4),
node: LineWidth::Length(Spanned {
span: Span::new(0, 4),
node: Length::Px(Px(1.0)),
}),
}),
})),
line_style: Shorthand::Explicit(parser.boxup(Spanned {
span: Span::new(4, 10),
node: Expr::Literal(Spanned { span: Span::new(4, 10), node: LineStyle::Solid }),
})),
color: Shorthand::Explicit(parser.boxup(Spanned {
span: Span::new(10, 13),
node: MathExpr::Literal(Spanned {
span: Span::new(10, 13),
node: ColorValue::Named(NamedColor::Red),
}),
})),
},
};
let parser_return = parser.parse_entirely_with::<BorderShorthand>();
if !parser_return.errors.is_empty() {
panic!("{:?}", parser_return.errors[0]);
}
if !parser_return.warnings.is_empty() {
panic!("{:?}", parser_return.warnings[0]);
}
let ast = parser_return.output.unwrap();
assert_eq!(ast, expected);
}
}
7 changes: 0 additions & 7 deletions crates/hdx_parser/src/css/values/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,6 @@ impl<'a> Parse<'a> for TimeOrAuto {
}
}

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

impl<'a> Parse<'a> for NoNonGlobalValuesAllowed {
fn parse(parser: &mut Parser<'a>) -> Result<Spanned<Self>> {
Err(diagnostics::Unexpected(parser.cur().kind, parser.cur().span))?
Expand Down
21 changes: 21 additions & 0 deletions crates/hdx_writer/src/css/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,3 +700,24 @@ impl<'a> WriteCss<'a> for VerticalAlignShorthand<'a> {
Ok(())
}
}

impl<'a> WriteCss<'a> for BorderShorthand<'a> {
fn write_css<W: CssWriter>(&self, sink: &mut W) -> Result {
if let Shorthand::Explicit(line_width) = &self.line_width {
line_width.write_css(sink)?;
if self.line_style.is_explicit() {
sink.write_char(' ')?;
}
}
if let Shorthand::Explicit(line_style) = &self.line_style {
line_style.write_css(sink)?;
if self.color.is_explicit() {
sink.write_char(' ')?;
}
}
if let Shorthand::Explicit(color) = &self.color {
color.write_css(sink)?;
}
Ok(())
}
}

0 comments on commit a5d0980

Please sign in to comment.