From 36c8bbd8576e5d593dc65ff6fd4ca5d81074cec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E4=B8=9D?= Date: Mon, 19 Sep 2022 22:49:07 +0800 Subject: [PATCH 1/7] feat: support `column-width` and `column-count` properties --- src/lib.rs | 23 ++++++++++ src/properties/border.rs | 18 ++++++++ src/properties/columns.rs | 24 ++++++++++ src/properties/mod.rs | 7 +++ src/values/length.rs | 96 ++++++++++++++++++++++++++++++++++++++- src/values/number.rs | 2 +- 6 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 src/properties/columns.rs diff --git a/src/lib.rs b/src/lib.rs index 53e7f7e6..6344a1d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5982,6 +5982,28 @@ mod tests { ); } + #[test] + fn test_columns() { + minify_test(".foo { column-width: auto; }", ".foo{column-width:auto}"); + minify_test(".foo { column-width: 0px; }", ".foo{column-width:0}"); + minify_test(".foo { column-width: calc(20px * 2); }", ".foo{column-width:40px}"); + minify_test(".foo { column-width: calc(100% - 30px); }", ".foo{column-width:calc(100% - 30px)}"); + minify_test(".foo { column-width: clamp(1em, 2px, 4vh); }", ".foo{column-width:clamp(1em,2px,4vh)}"); + + minify_test(".foo { column-count: auto; }", ".foo{column-count:auto}"); + minify_test(".foo { column-count: 1; }", ".foo{column-count:1}"); + + // TODO: Supprot calc + // minify_test(".foo { column-count: min(10, 3); }", ".foo{column-count:3}"); + // minify_test(".foo { column-count: calc(10 / 5); }", ".foo{column-count:2}"); + // minify_test(".foo { column-count: calc(2 * 3); }", ".foo{column-count:6}"); + + // Invalid values + // minify_test(".foo { column-count: 1.4; }", ".foo{column-count:1.4}"); + // minify_test(".foo { column-count: 1.5; }", ".foo{column-count:1.5}"); + + } + #[test] fn test_calc() { minify_test(".foo { width: calc(20px * 2) }", ".foo{width:40px}"); @@ -8381,6 +8403,7 @@ mod tests { minify_test(".foo { animation: none, none }", ".foo{animation:none,none}"); minify_test(".foo { animation: \"none\" none }", ".foo{animation:\"none\"}"); minify_test(".foo { animation: none none }", ".foo{animation:none}"); + minify_test(".foo { animation: none none .3s }", ".foo{animation:none none .3s}"); // Test animation-name + animation-fill-mode minify_test( diff --git a/src/properties/border.rs b/src/properties/border.rs index b17200fc..56a7d234 100644 --- a/src/properties/border.rs +++ b/src/properties/border.rs @@ -19,6 +19,24 @@ use crate::values::rect::Rect; use crate::values::size::Size2D; use cssparser::*; + +enum_property! { + /// A [``](https://www.w3.org/TR/CSS22/tables.html#propdef-border-collapse) + /// property. + pub enum BorderCollapse { + /// The `collapse` value. + Collapse, + /// The `separate` value. + Separate, + } +} + +impl Default for BorderCollapse { + fn default() -> BorderCollapse { + BorderCollapse::Separate + } +} + /// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr( diff --git a/src/properties/columns.rs b/src/properties/columns.rs new file mode 100644 index 00000000..f124d5a1 --- /dev/null +++ b/src/properties/columns.rs @@ -0,0 +1,24 @@ +//! CSS properties related to Multi-column Layout. +//! https://www.w3.org/TR/css-multicol-1/ + +use super::{Property, PropertyId}; +use crate::compat::Feature; +use crate::context::PropertyHandlerContext; +use crate::declaration::{DeclarationBlock, DeclarationList}; +use crate::error::{ParserError, PrinterError}; +use crate::macros::{define_shorthand, enum_property}; +use crate::printer::Printer; +use crate::targets::Browsers; +use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; +use cssparser::*; + +define_shorthand! { + /// A value for the [columns](https://www.w3.org/TR/css-multicol-1/#columns) shorthand property. + /// columns = <'column-width'> || <'column-count'> + pub struct Columns { + /// column-width + a: ColumnWidth(), + /// column-count + b: ColumnCount(), + } +} diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 2a0216d5..713c4a37 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -789,6 +789,12 @@ define_properties! { "overflow": Overflow(Overflow) shorthand: true, "overflow-x": OverflowX(OverflowKeyword), "overflow-y": OverflowY(OverflowKeyword), + + // TODO: `columns` shorthand + // "columns": Columns(Columns) shorthand: true, + "column-width": ColumnWidth(LengthOrAuto), // "auto | " + "column-count": ColumnCount(IntegerOrAuto), // "auto | " + "text-overflow": TextOverflow(TextOverflow, VendorPrefix) / O, // https://www.w3.org/TR/2020/WD-css-position-3-20200519 @@ -806,6 +812,7 @@ define_properties! { "inset": Inset(Inset) shorthand: true, "border-spacing": BorderSpacing(Size2D), + // "border-collapse": BorderCollapse(BorderCollapse), "border-top-color": BorderTopColor(CssColor) [logical_group: BorderColor, category: Physical], "border-bottom-color": BorderBottomColor(CssColor) [logical_group: BorderColor, category: Physical], diff --git a/src/values/length.rs b/src/values/length.rs index 0f6ebfac..f8ba484a 100644 --- a/src/values/length.rs +++ b/src/values/length.rs @@ -2,7 +2,7 @@ use super::angle::impl_try_from_angle; use super::calc::{Calc, MathFunction}; -use super::number::CSSNumber; +use super::number::{CSSInteger, CSSNumber}; use super::percentage::DimensionPercentage; use crate::error::{ParserError, PrinterError}; use crate::printer::Printer; @@ -35,6 +35,100 @@ impl LengthPercentage { } } +/// Either a [``](https://www.w3.org/TR/css-values-4/#integers), or the `auto` keyword. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(tag = "type", content = "value", rename_all = "kebab-case") +)] +pub enum IntegerOrAuto { + /// The `auto` keyword. + Auto, + /// A `` value. + Integer(CSSInteger), +} + +impl Default for IntegerOrAuto { + fn default() -> Self { + IntegerOrAuto::Auto + } +} + +impl<'i> Parse<'i> for IntegerOrAuto { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result>> { + if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { + return Ok(IntegerOrAuto::Auto); + } + + if let Ok(integer) = input.try_parse(CSSInteger::parse) { + return Ok(IntegerOrAuto::Integer(integer)); + } + + Err(input.new_error_for_next_token()) + } +} + +impl ToCss for IntegerOrAuto { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + use IntegerOrAuto::*; + match self { + Auto => dest.write_str("auto"), + Integer(integer) => integer.to_css(dest), + } + } +} + +/// Either a [``](https://www.w3.org/TR/css-values-4/#lengths), or the `auto` keyword. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(tag = "type", content = "value", rename_all = "kebab-case") +)] +pub enum LengthOrAuto { + /// The `auto` keyword. + Auto, + /// A [``](https://www.w3.org/TR/css-values-4/#typedef-length-percentage) value. + Length(Length), +} + +impl Default for LengthOrAuto { + fn default() -> Self { + LengthOrAuto::Auto + } +} + +impl<'i> Parse<'i> for LengthOrAuto { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result>> { + if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { + return Ok(LengthOrAuto::Auto); + } + + if let Ok(percent) = input.try_parse(|input| Length::parse(input)) { + return Ok(LengthOrAuto::Length(percent)); + } + + Err(input.new_error_for_next_token()) + } +} + +impl ToCss for LengthOrAuto { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + use LengthOrAuto::*; + match self { + Auto => dest.write_str("auto"), + Length(l) => l.to_css(dest), + } + } +} + /// Either a [``](https://www.w3.org/TR/css-values-4/#typedef-length-percentage), or the `auto` keyword. #[derive(Debug, Clone, PartialEq)] #[cfg_attr( diff --git a/src/values/number.rs b/src/values/number.rs index bac4d23d..8c2a8843 100644 --- a/src/values/number.rs +++ b/src/values/number.rs @@ -115,7 +115,7 @@ pub type CSSInteger = i32; impl<'i> Parse<'i> for CSSInteger { fn parse<'t>(input: &mut Parser<'i, 't>) -> Result>> { - // TODO: calc?? + // TODO: Support calc?? let integer = input.expect_integer()?; Ok(integer) } From 3fff94c102799cf00126045b7a165968009a910c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E4=B8=9D?= Date: Wed, 21 Sep 2022 22:09:31 +0800 Subject: [PATCH 2/7] test: add more case --- src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 6344a1d3..20aeca01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5993,10 +5993,16 @@ mod tests { minify_test(".foo { column-count: auto; }", ".foo{column-count:auto}"); minify_test(".foo { column-count: 1; }", ".foo{column-count:1}"); + // Test minimum and maximum values: Chrome/Safari is 65535, Firefox is 1000. + minify_test(".foo { column-count: 123456789000; }", ".foo{column-count:2147483647}"); + minify_test(".foo { column-count: -123456789000; }", ".foo{column-count:-2147483648}"); + // minify_test("@supports ( column-width: 0px ) { .bar { column-width: 0px; } }", "@supports (column-width: 0px){.bar{column-width:0}}"); + // TODO: Supprot calc // minify_test(".foo { column-count: min(10, 3); }", ".foo{column-count:3}"); // minify_test(".foo { column-count: calc(10 / 5); }", ".foo{column-count:2}"); // minify_test(".foo { column-count: calc(2 * 3); }", ".foo{column-count:6}"); + // minify_test(".foo { column-count: calc(infinity * 1); }", ".foo{column-count:2147483647}"); // Invalid values // minify_test(".foo { column-count: 1.4; }", ".foo{column-count:1.4}"); @@ -21949,6 +21955,9 @@ mod tests { minify_test(".foo { z-index: 999999 }", ".foo{z-index:999999}"); minify_test(".foo { z-index: 9999999 }", ".foo{z-index:9999999}"); minify_test(".foo { z-index: -9999999 }", ".foo{z-index:-9999999}"); + minify_test(".foo { z-index: calc(10 * 2) }", ".foo{z-index:calc(10*2)}"); + minify_test(".foo { z-index: 2147483647 }", ".foo{z-index:2147483647}"); + minify_test(".foo { z-index: auto }", ".foo{z-index:auto}"); } #[test] From 6ba65fd8c125ae48a05bc44e435a0059115f8472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E4=B8=9D?= Date: Sat, 24 Sep 2022 00:00:07 +0800 Subject: [PATCH 3/7] feat: implement `columns` shorthand --- src/declaration.rs | 5 ++++ src/lib.rs | 11 ++++++--- src/properties/border.rs | 18 -------------- src/properties/columns.rs | 49 ++++++++++++++++++++++++++++++++++----- src/properties/mod.rs | 10 ++++---- 5 files changed, 61 insertions(+), 32 deletions(-) diff --git a/src/declaration.rs b/src/declaration.rs index 4f66a12e..df1a73b1 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -15,6 +15,7 @@ use crate::properties::{ background::BackgroundHandler, border::BorderHandler, contain::ContainerHandler, + columns::ColumnsHandler, display::DisplayHandler, flex::FlexHandler, font::FontHandler, @@ -463,6 +464,7 @@ pub(crate) struct DeclarationHandler<'i> { box_shadow: BoxShadowHandler, mask: MaskHandler<'i>, container: ContainerHandler<'i>, + columns: ColumnsHandler, fallback: FallbackHandler, prefix: PrefixHandler, decls: DeclarationList<'i>, @@ -495,6 +497,7 @@ impl<'i> DeclarationHandler<'i> { box_shadow: BoxShadowHandler::new(targets), mask: MaskHandler::default(), container: ContainerHandler::default(), + columns: ColumnsHandler::default(), fallback: FallbackHandler::new(targets), prefix: PrefixHandler::new(targets), decls: DeclarationList::new(), @@ -536,6 +539,7 @@ impl<'i> DeclarationHandler<'i> { || self.box_shadow.handle_property(property, &mut self.decls, context) || self.mask.handle_property(property, &mut self.decls, context) || self.container.handle_property(property, &mut self.decls, context) + || self.columns.handle_property(property, &mut self.decls, context) || self.fallback.handle_property(property, &mut self.decls, context) || self.prefix.handle_property(property, &mut self.decls, context) } @@ -565,6 +569,7 @@ impl<'i> DeclarationHandler<'i> { self.box_shadow.finalize(&mut self.decls, context); self.mask.finalize(&mut self.decls, context); self.container.finalize(&mut self.decls, context); + self.columns.finalize(&mut self.decls, context); self.fallback.finalize(&mut self.decls, context); self.prefix.finalize(&mut self.decls, context); } diff --git a/src/lib.rs b/src/lib.rs index 20aeca01..025992ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5984,9 +5984,16 @@ mod tests { #[test] fn test_columns() { + // columns shorthand + minify_test(".foo { column-width: 200px; column-count: 2 }", ".foo{columns:200px 2}"); + minify_test(".foo { column-width: auto; column-count: 3 }", ".foo{columns:3}"); + minify_test(".foo { column-width: 10em; column-count: auto }", ".foo{columns:10em}"); + minify_test(".foo { column-width: calc(80px * 2); column-count: auto }", ".foo{columns:160px}"); + minify_test(".foo { column-count: auto; column-width: 20vw; }", ".foo{columns:20vw}"); + minify_test(".foo { column-width: auto; }", ".foo{column-width:auto}"); minify_test(".foo { column-width: 0px; }", ".foo{column-width:0}"); - minify_test(".foo { column-width: calc(20px * 2); }", ".foo{column-width:40px}"); + minify_test(".foo { column-width: calc(80px * 2); }", ".foo{column-width:160px}"); minify_test(".foo { column-width: calc(100% - 30px); }", ".foo{column-width:calc(100% - 30px)}"); minify_test(".foo { column-width: clamp(1em, 2px, 4vh); }", ".foo{column-width:clamp(1em,2px,4vh)}"); @@ -5996,7 +6003,6 @@ mod tests { // Test minimum and maximum values: Chrome/Safari is 65535, Firefox is 1000. minify_test(".foo { column-count: 123456789000; }", ".foo{column-count:2147483647}"); minify_test(".foo { column-count: -123456789000; }", ".foo{column-count:-2147483648}"); - // minify_test("@supports ( column-width: 0px ) { .bar { column-width: 0px; } }", "@supports (column-width: 0px){.bar{column-width:0}}"); // TODO: Supprot calc // minify_test(".foo { column-count: min(10, 3); }", ".foo{column-count:3}"); @@ -8409,7 +8415,6 @@ mod tests { minify_test(".foo { animation: none, none }", ".foo{animation:none,none}"); minify_test(".foo { animation: \"none\" none }", ".foo{animation:\"none\"}"); minify_test(".foo { animation: none none }", ".foo{animation:none}"); - minify_test(".foo { animation: none none .3s }", ".foo{animation:none none .3s}"); // Test animation-name + animation-fill-mode minify_test( diff --git a/src/properties/border.rs b/src/properties/border.rs index 56a7d234..b17200fc 100644 --- a/src/properties/border.rs +++ b/src/properties/border.rs @@ -19,24 +19,6 @@ use crate::values::rect::Rect; use crate::values::size::Size2D; use cssparser::*; - -enum_property! { - /// A [``](https://www.w3.org/TR/CSS22/tables.html#propdef-border-collapse) - /// property. - pub enum BorderCollapse { - /// The `collapse` value. - Collapse, - /// The `separate` value. - Separate, - } -} - -impl Default for BorderCollapse { - fn default() -> BorderCollapse { - BorderCollapse::Separate - } -} - /// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property. #[derive(Debug, Clone, PartialEq)] #[cfg_attr( diff --git a/src/properties/columns.rs b/src/properties/columns.rs index f124d5a1..9ed022d4 100644 --- a/src/properties/columns.rs +++ b/src/properties/columns.rs @@ -2,23 +2,60 @@ //! https://www.w3.org/TR/css-multicol-1/ use super::{Property, PropertyId}; -use crate::compat::Feature; use crate::context::PropertyHandlerContext; use crate::declaration::{DeclarationBlock, DeclarationList}; use crate::error::{ParserError, PrinterError}; -use crate::macros::{define_shorthand, enum_property}; +use crate::macros::{define_shorthand, shorthand_handler}; use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; +use crate::values::length::{LengthOrAuto, IntegerOrAuto}; use cssparser::*; define_shorthand! { /// A value for the [columns](https://www.w3.org/TR/css-multicol-1/#columns) shorthand property. /// columns = <'column-width'> || <'column-count'> pub struct Columns { - /// column-width - a: ColumnWidth(), - /// column-count - b: ColumnCount(), + /// The column-width property. + width: ColumnWidth(LengthOrAuto), + /// The column-count property. + count: ColumnCount(IntegerOrAuto), } } + +impl<'i> Parse<'i> for Columns { + fn parse<'t>(input: &mut Parser<'i, 't>) -> Result>> { + let width = LengthOrAuto::parse(input)?; + let count = if input.try_parse(|input| IntegerOrAuto::parse(input)).is_ok() { + IntegerOrAuto::parse(input)? + } else { + IntegerOrAuto::default() + }; + Ok(Columns { width, count }) + } +} + +impl<'i> ToCss for Columns { + fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> + where + W: std::fmt::Write, + { + if self.width != LengthOrAuto::default() { + self.width.to_css(dest)?; + } + + if self.width != LengthOrAuto::default() && self.count != IntegerOrAuto::default() { + dest.write_str(" ")?; + } + + if self.count != IntegerOrAuto::default() { + self.count.to_css(dest)?; + } + Ok(()) + } +} + +shorthand_handler!(ColumnsHandler -> Columns { + width: ColumnWidth(LengthOrAuto), + count: ColumnCount(IntegerOrAuto), +}); diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 713c4a37..02b432d6 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -110,6 +110,7 @@ pub mod list; pub(crate) mod margin_padding; pub mod masking; pub mod outline; +pub mod columns; pub mod overflow; pub mod position; pub(crate) mod prefix_handler; @@ -157,6 +158,7 @@ use list::*; use margin_padding::*; use masking::*; use outline::*; +use columns::*; use overflow::*; use size::*; use smallvec::{smallvec, SmallVec}; @@ -790,10 +792,9 @@ define_properties! { "overflow-x": OverflowX(OverflowKeyword), "overflow-y": OverflowY(OverflowKeyword), - // TODO: `columns` shorthand - // "columns": Columns(Columns) shorthand: true, - "column-width": ColumnWidth(LengthOrAuto), // "auto | " - "column-count": ColumnCount(IntegerOrAuto), // "auto | " + "columns": Columns(Columns) shorthand: true, // columns = <'column-width'> || <'column-count'> + "column-width": ColumnWidth(LengthOrAuto), // auto | + "column-count": ColumnCount(IntegerOrAuto), // auto | "text-overflow": TextOverflow(TextOverflow, VendorPrefix) / O, @@ -812,7 +813,6 @@ define_properties! { "inset": Inset(Inset) shorthand: true, "border-spacing": BorderSpacing(Size2D), - // "border-collapse": BorderCollapse(BorderCollapse), "border-top-color": BorderTopColor(CssColor) [logical_group: BorderColor, category: Physical], "border-bottom-color": BorderBottomColor(CssColor) [logical_group: BorderColor, category: Physical], From 80749a2f3b88a234ff3a9b3ff082d1935d3c6c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E4=B8=9D?= Date: Sat, 24 Sep 2022 12:03:33 +0800 Subject: [PATCH 4/7] test: add more shorthand case --- src/lib.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 025992ae..cc9f8053 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5985,11 +5985,19 @@ mod tests { #[test] fn test_columns() { // columns shorthand - minify_test(".foo { column-width: 200px; column-count: 2 }", ".foo{columns:200px 2}"); - minify_test(".foo { column-width: auto; column-count: 3 }", ".foo{columns:3}"); - minify_test(".foo { column-width: 10em; column-count: auto }", ".foo{columns:10em}"); - minify_test(".foo { column-width: calc(80px * 2); column-count: auto }", ".foo{columns:160px}"); + minify_test(".foo { column-width: 200px; column-count: 2; }", ".foo{columns:200px 2}"); + minify_test(".foo { column-width: auto; column-count: 3; }", ".foo{columns:3}"); + minify_test(".foo { column-width: 10em; column-count: auto; }", ".foo{columns:10em}"); + minify_test(".foo { column-width: calc(80px * 2); column-count: auto; }", ".foo{columns:160px}"); minify_test(".foo { column-count: auto; column-width: 20vw; }", ".foo{columns:20vw}"); + minify_test(".foo { columns: 200px; column-count: 2; }", ".foo{columns:200px 2}"); + minify_test(".foo { columns: 200px; column-count: 99999988888; }", ".foo{columns:200px 2147483647}"); + minify_test(".foo { columns: auto; column-count: 3; }", ".foo{columns:3}"); + minify_test(".foo { column-count: auto; columns: 200px; }", ".foo{columns:200px}"); + minify_test(".foo { column-count: auto; columns: 3; }", ".foo{columns:3}"); + minify_test(".foo { column-count: auto; columns: 100px 3; }", ".foo{columns:100px 3}"); + minify_test(".foo { column-width: 300px; columns: 2 auto; }", ".foo{columns:2}"); + minify_test(".foo { column-width: 300px; columns: 2; }", ".foo{columns:2}"); minify_test(".foo { column-width: auto; }", ".foo{column-width:auto}"); minify_test(".foo { column-width: 0px; }", ".foo{column-width:0}"); From 1503b635bf5cf3707a3cf73da8f828e8e4d095ad Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 24 Sep 2022 15:14:12 +0800 Subject: [PATCH 5/7] fix: rewrite columns parser Co-authored-by: Jason --- .gitignore | 3 +- selectors/parser.rs | 9 +++-- src/declaration.rs | 2 +- src/lib.rs | 42 ++++++++++++++++------ src/properties/columns.rs | 73 +++++++++++++++++++++++++++++++++++---- src/properties/mod.rs | 4 +-- src/values/length.rs | 17 ++++++++- 7 files changed, 126 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index e82b6873..3f780399 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ dist/ .parcel-cache node/*.flow artifacts -npm \ No newline at end of file +npm +.idea \ No newline at end of file diff --git a/selectors/parser.rs b/selectors/parser.rs index 6f64a31f..4d6d2e2c 100644 --- a/selectors/parser.rs +++ b/selectors/parser.rs @@ -1107,7 +1107,7 @@ pub enum Component<'i, Impl: SelectorImpl<'i>> { Scope, NthChild(i32, i32), NthLastChild(i32, i32), - NthCol(i32, i32), // https://www.w3.org/TR/selectors-4/#the-nth-col-pseudo + NthCol(i32, i32), // https://www.w3.org/TR/selectors-4/#the-nth-col-pseudo NthLastCol(i32, i32), // https://www.w3.org/TR/selectors-4/#the-nth-last-col-pseudo NthOfType(i32, i32), NthLastOfType(i32, i32), @@ -1606,7 +1606,12 @@ impl<'i, Impl: SelectorImpl<'i>> ToCss for Component<'i, Impl> { FirstOfType => dest.write_str(":first-of-type"), LastOfType => dest.write_str(":last-of-type"), OnlyOfType => dest.write_str(":only-of-type"), - NthChild(a, b) | NthLastChild(a, b) | NthOfType(a, b) | NthLastOfType(a, b) | NthCol(a, b) | NthLastCol(a, b) => { + NthChild(a, b) + | NthLastChild(a, b) + | NthOfType(a, b) + | NthLastOfType(a, b) + | NthCol(a, b) + | NthLastCol(a, b) => { match *self { NthChild(_, _) => dest.write_str(":nth-child(")?, NthLastChild(_, _) => dest.write_str(":nth-last-child(")?, diff --git a/src/declaration.rs b/src/declaration.rs index df1a73b1..bff35177 100644 --- a/src/declaration.rs +++ b/src/declaration.rs @@ -14,8 +14,8 @@ use crate::properties::{ animation::AnimationHandler, background::BackgroundHandler, border::BorderHandler, - contain::ContainerHandler, columns::ColumnsHandler, + contain::ContainerHandler, display::DisplayHandler, flex::FlexHandler, font::FontHandler, diff --git a/src/lib.rs b/src/lib.rs index cc9f8053..74d115d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5985,32 +5985,53 @@ mod tests { #[test] fn test_columns() { // columns shorthand - minify_test(".foo { column-width: 200px; column-count: 2; }", ".foo{columns:200px 2}"); + minify_test( + ".foo { column-width: 200px; column-count: 2; }", + ".foo{columns:200px 2}", + ); minify_test(".foo { column-width: auto; column-count: 3; }", ".foo{columns:3}"); minify_test(".foo { column-width: 10em; column-count: auto; }", ".foo{columns:10em}"); - minify_test(".foo { column-width: calc(80px * 2); column-count: auto; }", ".foo{columns:160px}"); - minify_test(".foo { column-count: auto; column-width: 20vw; }", ".foo{columns:20vw}"); + minify_test( + ".foo { column-width: calc(80px * 2); column-count: auto; }", + ".foo{columns:160px}", + ); + minify_test( + ".foo { column-count: auto; column-width: 20vw; }", + ".foo{columns:20vw}", + ); minify_test(".foo { columns: 200px; column-count: 2; }", ".foo{columns:200px 2}"); - minify_test(".foo { columns: 200px; column-count: 99999988888; }", ".foo{columns:200px 2147483647}"); + minify_test( + ".foo { columns: 200px; column-count: 99999988888; }", + ".foo{columns:200px 2147483647}", + ); minify_test(".foo { columns: auto; column-count: 3; }", ".foo{columns:3}"); minify_test(".foo { column-count: auto; columns: 200px; }", ".foo{columns:200px}"); minify_test(".foo { column-count: auto; columns: 3; }", ".foo{columns:3}"); - minify_test(".foo { column-count: auto; columns: 100px 3; }", ".foo{columns:100px 3}"); - minify_test(".foo { column-width: 300px; columns: 2 auto; }", ".foo{columns:2}"); + // minify_test(".foo { column-count: auto; columns: 100px 3; }", ".foo{columns:100px 3}"); + // minify_test(".foo { column-width: 300px; columns: 2 auto; }", ".foo{columns:2}"); minify_test(".foo { column-width: 300px; columns: 2; }", ".foo{columns:2}"); - + minify_test(".foo { column-width: auto; }", ".foo{column-width:auto}"); minify_test(".foo { column-width: 0px; }", ".foo{column-width:0}"); minify_test(".foo { column-width: calc(80px * 2); }", ".foo{column-width:160px}"); - minify_test(".foo { column-width: calc(100% - 30px); }", ".foo{column-width:calc(100% - 30px)}"); - minify_test(".foo { column-width: clamp(1em, 2px, 4vh); }", ".foo{column-width:clamp(1em,2px,4vh)}"); + minify_test( + ".foo { column-width: calc(100% - 30px); }", + ".foo{column-width:calc(100% - 30px)}", + ); + minify_test( + ".foo { column-width: clamp(1em, 2px, 4vh); }", + ".foo{column-width:clamp(1em,2px,4vh)}", + ); minify_test(".foo { column-count: auto; }", ".foo{column-count:auto}"); minify_test(".foo { column-count: 1; }", ".foo{column-count:1}"); // Test minimum and maximum values: Chrome/Safari is 65535, Firefox is 1000. minify_test(".foo { column-count: 123456789000; }", ".foo{column-count:2147483647}"); - minify_test(".foo { column-count: -123456789000; }", ".foo{column-count:-2147483648}"); + minify_test( + ".foo { column-count: -123456789000; }", + ".foo{column-count:-2147483648}", + ); // TODO: Supprot calc // minify_test(".foo { column-count: min(10, 3); }", ".foo{column-count:3}"); @@ -6021,7 +6042,6 @@ mod tests { // Invalid values // minify_test(".foo { column-count: 1.4; }", ".foo{column-count:1.4}"); // minify_test(".foo { column-count: 1.5; }", ".foo{column-count:1.5}"); - } #[test] diff --git a/src/properties/columns.rs b/src/properties/columns.rs index 9ed022d4..a51030eb 100644 --- a/src/properties/columns.rs +++ b/src/properties/columns.rs @@ -9,8 +9,9 @@ use crate::macros::{define_shorthand, shorthand_handler}; use crate::printer::Printer; use crate::targets::Browsers; use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss}; -use crate::values::length::{LengthOrAuto, IntegerOrAuto}; +use crate::values::length::{IntegerOrAuto, Length, LengthOrAuto, LengthValue}; use cssparser::*; +use std::ops::Not; define_shorthand! { /// A value for the [columns](https://www.w3.org/TR/css-multicol-1/#columns) shorthand property. @@ -25,13 +26,73 @@ define_shorthand! { impl<'i> Parse<'i> for Columns { fn parse<'t>(input: &mut Parser<'i, 't>) -> Result>> { - let width = LengthOrAuto::parse(input)?; - let count = if input.try_parse(|input| IntegerOrAuto::parse(input)).is_ok() { - IntegerOrAuto::parse(input)? + let state = input.state(); + let with_or_count = LengthOrAuto::parse(input)?; + + if let LengthOrAuto::Length(Length::Value(LengthValue::Unitless(..))) = with_or_count { + // first is unitless, so it must be column-count + // reparse with column-count + input.reset(&state); + let count = IntegerOrAuto::parse(input)?; + + let width = if input.try_parse(|input| LengthOrAuto::parse(input)).is_ok() { + LengthOrAuto::parse(input)? + } else { + LengthOrAuto::Auto + }; + + return Ok(Columns { width, count }); + } else if matches!(with_or_count, LengthOrAuto::Auto).not() + && matches!( + with_or_count, + LengthOrAuto::Length(Length::Value(LengthValue::Unitless(..))) + ) + .not() + { + // first is neither unitless nor auto, so it must be column-width + let count = if input.try_parse(|input| IntegerOrAuto::parse(input)).is_ok() { + IntegerOrAuto::parse(input)? + } else { + IntegerOrAuto::default() + }; + + return Ok(Columns { + width: with_or_count, + count, + }); + } + + // first is auto, check second + let state = input.state(); + let count = if input.try_parse(|input| LengthOrAuto::parse(input)).is_ok() { + LengthOrAuto::parse(input)? } else { - IntegerOrAuto::default() + LengthOrAuto::default() }; - Ok(Columns { width, count }) + + if let LengthOrAuto::Auto = count { + // second is auto, so first must be column-width + Ok(Columns { + width: LengthOrAuto::Auto, + count: IntegerOrAuto::Auto, + }) + } else if matches!(count, LengthOrAuto::Length(Length::Value(LengthValue::Unitless(..)))).not() { + // second is not unitless, so first must be column-count + // reparse with column-with + Ok(Columns { + width: count, + count: IntegerOrAuto::Auto, + }) + } else { + // second is unitless, so first must be column-width + input.reset(&state); + let count = IntegerOrAuto::parse(input)?; + + Ok(Columns { + width: with_or_count, + count, + }) + } } } diff --git a/src/properties/mod.rs b/src/properties/mod.rs index 02b432d6..a1242d91 100644 --- a/src/properties/mod.rs +++ b/src/properties/mod.rs @@ -97,6 +97,7 @@ pub mod border; pub mod border_image; pub mod border_radius; pub mod box_shadow; +pub mod columns; pub mod contain; pub mod css_modules; pub mod custom; @@ -110,7 +111,6 @@ pub mod list; pub(crate) mod margin_padding; pub mod masking; pub mod outline; -pub mod columns; pub mod overflow; pub mod position; pub(crate) mod prefix_handler; @@ -144,6 +144,7 @@ use border::*; use border_image::*; use border_radius::*; use box_shadow::*; +use columns::*; use contain::*; use css_modules::*; use cssparser::*; @@ -158,7 +159,6 @@ use list::*; use margin_padding::*; use masking::*; use outline::*; -use columns::*; use overflow::*; use size::*; use smallvec::{smallvec, SmallVec}; diff --git a/src/values/length.rs b/src/values/length.rs index f8ba484a..9bf8dad4 100644 --- a/src/values/length.rs +++ b/src/values/length.rs @@ -193,6 +193,10 @@ macro_rules! define_length_units { $(#[$meta])* $name(CSSNumber), )+ + + /// A [``](https://www.w3.org/TR/css-values-4/#lengths) value + /// without a unit specified. + Unitless(CSSNumber), } impl<'i> Parse<'i> for LengthValue { @@ -210,7 +214,7 @@ macro_rules! define_length_units { }, Token::Number { value, .. } => { // TODO: quirks mode only? - Ok(LengthValue::Px(value)) + Ok(LengthValue::Unitless(value)) } ref token => return Err(location.new_unexpected_token_error(token.clone())), } @@ -224,6 +228,9 @@ macro_rules! define_length_units { $( LengthValue::$name(value) => (*value, const_str::convert_ascii_case!(lower, stringify!($name))), )+ + + // TODO: only works in quirks mode? + LengthValue::Unitless(value) => (*value, "px"), } } } @@ -255,6 +262,8 @@ macro_rules! define_length_units { $( $name(value) => $name(value * other), )+ + + Unitless(value) => Unitless(value * other), } } } @@ -318,6 +327,8 @@ macro_rules! define_length_units { $( $name(value) => $name(op(*value)), )+ + + Unitless(value) => Unitless(op(*value)), } } } @@ -329,6 +340,8 @@ macro_rules! define_length_units { $( $name(value) => value.sign(), )+ + + Unitless(value) => value.sign(), } } } @@ -344,6 +357,8 @@ macro_rules! define_length_units { $( $name(value) => value.is_zero(), )+ + + Unitless(value) => value.is_zero(), } } } From 1a5061d5de56aeb41b9ddab268130df5d1e0acd6 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 24 Sep 2022 19:14:21 +0800 Subject: [PATCH 6/7] fix: rewrite columns parser again, all tests passed Co-authored-by: Jason --- src/lib.rs | 7 +++++-- src/properties/columns.rs | 19 +++---------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 74d115d2..ff57f88d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6007,8 +6007,11 @@ mod tests { minify_test(".foo { columns: auto; column-count: 3; }", ".foo{columns:3}"); minify_test(".foo { column-count: auto; columns: 200px; }", ".foo{columns:200px}"); minify_test(".foo { column-count: auto; columns: 3; }", ".foo{columns:3}"); - // minify_test(".foo { column-count: auto; columns: 100px 3; }", ".foo{columns:100px 3}"); - // minify_test(".foo { column-width: 300px; columns: 2 auto; }", ".foo{columns:2}"); + minify_test( + ".foo { column-count: auto; columns: 100px 3; }", + ".foo{columns:100px 3}", + ); + minify_test(".foo { column-width: 300px; columns: 2 auto; }", ".foo{columns:2}"); minify_test(".foo { column-width: 300px; columns: 2; }", ".foo{columns:2}"); minify_test(".foo { column-width: auto; }", ".foo{column-width:auto}"); diff --git a/src/properties/columns.rs b/src/properties/columns.rs index a51030eb..ad0245de 100644 --- a/src/properties/columns.rs +++ b/src/properties/columns.rs @@ -34,12 +34,7 @@ impl<'i> Parse<'i> for Columns { // reparse with column-count input.reset(&state); let count = IntegerOrAuto::parse(input)?; - - let width = if input.try_parse(|input| LengthOrAuto::parse(input)).is_ok() { - LengthOrAuto::parse(input)? - } else { - LengthOrAuto::Auto - }; + let width = input.try_parse(|input| LengthOrAuto::parse(input)).unwrap_or_default(); return Ok(Columns { width, count }); } else if matches!(with_or_count, LengthOrAuto::Auto).not() @@ -50,11 +45,7 @@ impl<'i> Parse<'i> for Columns { .not() { // first is neither unitless nor auto, so it must be column-width - let count = if input.try_parse(|input| IntegerOrAuto::parse(input)).is_ok() { - IntegerOrAuto::parse(input)? - } else { - IntegerOrAuto::default() - }; + let count = input.try_parse(|input| IntegerOrAuto::parse(input)).unwrap_or_default(); return Ok(Columns { width: with_or_count, @@ -64,11 +55,7 @@ impl<'i> Parse<'i> for Columns { // first is auto, check second let state = input.state(); - let count = if input.try_parse(|input| LengthOrAuto::parse(input)).is_ok() { - LengthOrAuto::parse(input)? - } else { - LengthOrAuto::default() - }; + let count = input.try_parse(|input| LengthOrAuto::parse(input)).unwrap_or_default(); if let LengthOrAuto::Auto = count { // second is auto, so first must be column-width From a1bbd77b2648cf58c66d877b1fd2e48d3373dbcc Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 24 Sep 2022 20:00:58 +0800 Subject: [PATCH 7/7] fix: all tests passed include tests other than columns test Co-authored-by: Jason --- src/properties/text.rs | 4 ++-- src/values/length.rs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/properties/text.rs b/src/properties/text.rs index 0710d6c6..4735b1db 100644 --- a/src/properties/text.rs +++ b/src/properties/text.rs @@ -1300,11 +1300,11 @@ impl ToCss for TextShadow { dest.write_char(' ')?; self.y_offset.to_css(dest)?; - if self.blur != Length::zero() || self.spread != Length::zero() { + if !self.blur.is_zero() || !self.spread.is_zero() { dest.write_char(' ')?; self.blur.to_css(dest)?; - if self.spread != Length::zero() { + if !self.spread.is_zero() { dest.write_char(' ')?; self.spread.to_css(dest)?; } diff --git a/src/values/length.rs b/src/values/length.rs index 9bf8dad4..09ab83c6 100644 --- a/src/values/length.rs +++ b/src/values/length.rs @@ -506,12 +506,15 @@ impl ToCss for LengthValue { } impl LengthValue { + // TODO(CGQAQ): This should be removed eventually. + // cause we already have IntegerValue pub(crate) fn to_css_unitless(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write, { match self { LengthValue::Px(value) => value.to_css(dest), + LengthValue::Unitless(value) => value.to_css(dest), _ => self.to_css(dest), } }