Skip to content

Commit 608fc10

Browse files
committed
Fix calc in @media
Fixes parcel-bundler#195. Adds a new AddInternal trait used for performing addition within a calc(). This doesn't wrap sums in calc() functions, because that's not necessary for nested sums within math functions. The result is wrapped in calc() if necessary during parsing. Later, if the calc() needs to be modified, we need to unwrap this calc, perform the addition, and re-wrap if necessary. That's now done by the public std::ops::Add implementation.
1 parent e9277fd commit 608fc10

File tree

8 files changed

+164
-20
lines changed

8 files changed

+164
-20
lines changed

src/lib.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5881,6 +5881,10 @@ mod tests {
58815881
".foo { left: calc(50% - 100px + clamp(0px, calc(50vw - 50px), 100px)) }",
58825882
".foo{left:calc(50% - 100px + clamp(0px,50vw - 50px,100px))}",
58835883
);
5884+
minify_test(
5885+
".foo { left: calc(10px + min(10px, 1rem) + max(2px, 1vw)) }",
5886+
".foo{left:calc(10px + min(10px,1rem) + max(2px,1vw))}",
5887+
);
58845888
}
58855889

58865890
#[test]
@@ -6337,6 +6341,43 @@ mod tests {
63376341
"#,
63386342
"\n",
63396343
);
6344+
6345+
prefix_test(
6346+
r#"
6347+
@media (width > calc(1px + 1rem)) {
6348+
.foo { color: yellow; }
6349+
}
6350+
"#,
6351+
indoc! { r#"
6352+
@media (min-width: calc(1.001px + 1rem)) {
6353+
.foo {
6354+
color: #ff0;
6355+
}
6356+
}
6357+
"#},
6358+
Browsers {
6359+
chrome: Some(85 << 16),
6360+
..Browsers::default()
6361+
},
6362+
);
6363+
prefix_test(
6364+
r#"
6365+
@media (width > max(10px, 1rem)) {
6366+
.foo { color: yellow; }
6367+
}
6368+
"#,
6369+
indoc! { r#"
6370+
@media (min-width: calc(max(10px, 1rem) + .001px)) {
6371+
.foo {
6372+
color: #ff0;
6373+
}
6374+
}
6375+
"#},
6376+
Browsers {
6377+
chrome: Some(85 << 16),
6378+
..Browsers::default()
6379+
},
6380+
);
63406381
}
63416382

63426383
#[test]

src/traits.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ pub(crate) mod private {
8989
pub trait TryAdd<T> {
9090
fn try_add(&self, other: &T) -> Option<T>;
9191
}
92+
93+
pub trait AddInternal {
94+
fn add(self, other: Self) -> Self;
95+
}
9296
}
9397

9498
pub(crate) trait FromStandard<T>: Sized {

src/values/angle.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use super::number::CSSNumber;
66
use super::percentage::DimensionPercentage;
77
use crate::error::{ParserError, PrinterError};
88
use crate::printer::Printer;
9-
use crate::traits::{private::TryAdd, Parse, ToCss};
9+
use crate::traits::{
10+
private::{AddInternal, TryAdd},
11+
Parse, ToCss,
12+
};
1013
use cssparser::*;
1114
use std::f32::consts::PI;
1215

@@ -150,6 +153,12 @@ impl std::ops::Add<Angle> for Angle {
150153
}
151154
}
152155

156+
impl AddInternal for Angle {
157+
fn add(self, other: Self) -> Self {
158+
self + other
159+
}
160+
}
161+
153162
impl TryAdd<Angle> for Angle {
154163
fn try_add(&self, other: &Angle) -> Option<Angle> {
155164
Some(Angle::Deg(self.to_degrees() + other.to_degrees()))

src/values/calc.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use crate::compat::Feature;
44
use crate::error::{ParserError, PrinterError};
55
use crate::printer::Printer;
6+
use crate::traits::private::AddInternal;
67
use crate::traits::{Parse, ToCss};
78
use cssparser::*;
89

@@ -123,7 +124,7 @@ impl<
123124
'i,
124125
V: Parse<'i>
125126
+ std::ops::Mul<f32, Output = V>
126-
+ std::ops::Add<V, Output = V>
127+
+ AddInternal
127128
+ std::cmp::PartialOrd<V>
128129
+ std::convert::Into<Calc<V>>
129130
+ std::convert::From<Calc<V>>
@@ -221,7 +222,7 @@ impl<
221222
'i,
222223
V: Parse<'i>
223224
+ std::ops::Mul<f32, Output = V>
224-
+ std::ops::Add<V, Output = V>
225+
+ AddInternal
225226
+ std::cmp::PartialOrd<V>
226227
+ std::convert::Into<Calc<V>>
227228
+ std::convert::From<Calc<V>>
@@ -240,12 +241,12 @@ impl<
240241
match *input.next()? {
241242
Token::Delim('+') => {
242243
let next = Calc::parse_product(input)?;
243-
cur = cur + next;
244+
cur = cur.add(next);
244245
}
245246
Token::Delim('-') => {
246247
let mut rhs = Calc::parse_product(input)?;
247248
rhs = rhs * -1.0;
248-
cur = cur + rhs;
249+
cur = cur.add(rhs);
249250
}
250251
ref t => {
251252
let t = t.clone();
@@ -392,21 +393,18 @@ impl<V: std::ops::Mul<f32, Output = V>> std::ops::Mul<f32> for Calc<V> {
392393
}
393394
}
394395

395-
impl<
396-
V: std::ops::Add<V, Output = V> + std::convert::Into<Calc<V>> + std::convert::From<Calc<V>> + std::fmt::Debug,
397-
> std::ops::Add<Calc<V>> for Calc<V>
396+
impl<V: AddInternal + std::convert::Into<Calc<V>> + std::convert::From<Calc<V>> + std::fmt::Debug> AddInternal
397+
for Calc<V>
398398
{
399-
type Output = Self;
400-
401399
fn add(self, other: Calc<V>) -> Calc<V> {
402400
match (self, other) {
403-
(Calc::Value(a), Calc::Value(b)) => (*a + *b).into(),
401+
(Calc::Value(a), Calc::Value(b)) => (a.add(*b)).into(),
404402
(Calc::Number(a), Calc::Number(b)) => Calc::Number(a + b),
405-
(Calc::Value(a), b) => (*a + V::from(b)).into(),
406-
(a, Calc::Value(b)) => (V::from(a) + *b).into(),
403+
(Calc::Value(a), b) => (a.add(V::from(b))).into(),
404+
(a, Calc::Value(b)) => (V::from(a).add(*b)).into(),
407405
(Calc::Function(a), b) => Calc::Sum(Box::new(Calc::Function(a)), Box::new(b)),
408406
(a, Calc::Function(b)) => Calc::Sum(Box::new(a), Box::new(Calc::Function(b))),
409-
(a, b) => (V::from(a) + V::from(b)).into(),
407+
(a, b) => V::from(a).add(V::from(b)).into(),
410408
}
411409
}
412410
}

src/values/length.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
//! CSS length values.
22
3-
use super::calc::Calc;
3+
use super::calc::{Calc, MathFunction};
44
use super::number::CSSNumber;
55
use super::percentage::DimensionPercentage;
66
use crate::error::{ParserError, PrinterError};
77
use crate::printer::Printer;
8-
use crate::traits::{private::TryAdd, Parse, ToCss};
8+
use crate::traits::{
9+
private::{AddInternal, TryAdd},
10+
Parse, ToCss,
11+
};
912
use const_str;
1013
use cssparser::*;
1114

@@ -444,6 +447,37 @@ impl std::ops::Add<Length> for Length {
444447
type Output = Self;
445448

446449
fn add(self, other: Length) -> Length {
450+
// Unwrap calc(...) functions so we can add inside.
451+
// Then wrap the result in a calc(...) again if necessary.
452+
let a = unwrap_calc(self);
453+
let b = unwrap_calc(other);
454+
let res = AddInternal::add(a, b);
455+
match res {
456+
Length::Calc(c) => match *c {
457+
Calc::Value(l) => *l,
458+
Calc::Function(f) if !matches!(*f, MathFunction::Calc(_)) => Length::Calc(Box::new(Calc::Function(f))),
459+
c => Length::Calc(Box::new(Calc::Function(Box::new(MathFunction::Calc(c))))),
460+
},
461+
_ => res,
462+
}
463+
}
464+
}
465+
466+
fn unwrap_calc(length: Length) -> Length {
467+
match length {
468+
Length::Calc(c) => match *c {
469+
Calc::Function(f) => match *f {
470+
MathFunction::Calc(c) => Length::Calc(Box::new(c)),
471+
c => Length::Calc(Box::new(Calc::Function(Box::new(c)))),
472+
},
473+
_ => Length::Calc(c),
474+
},
475+
_ => length,
476+
}
477+
}
478+
479+
impl AddInternal for Length {
480+
fn add(self, other: Self) -> Self {
447481
match self.try_add(&other) {
448482
Some(r) => r,
449483
None => self.add(other),
@@ -488,7 +522,7 @@ impl Length {
488522
}
489523

490524
match (a, b) {
491-
(Length::Calc(a), Length::Calc(b)) => return Length::Calc(Box::new(*a + *b)),
525+
(Length::Calc(a), Length::Calc(b)) => return Length::Calc(Box::new(a.add(*b))),
492526
(Length::Calc(calc), b) => {
493527
if let Calc::Value(a) = *calc {
494528
a.add(b)

src/values/number.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use super::calc::Calc;
44
use crate::error::{ParserError, PrinterError};
55
use crate::printer::Printer;
6+
use crate::traits::private::AddInternal;
67
use crate::traits::{Parse, ToCss};
78
use cssparser::*;
89

@@ -64,6 +65,12 @@ impl std::convert::From<Calc<CSSNumber>> for CSSNumber {
6465
}
6566
}
6667

68+
impl AddInternal for CSSNumber {
69+
fn add(self, other: Self) -> Self {
70+
self + other
71+
}
72+
}
73+
6774
/// A CSS [`<integer>`](https://www.w3.org/TR/css-values-4/#integers) value.
6875
pub type CSSInteger = i32;
6976

src/values/percentage.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//! CSS percentage values.
22
3-
use super::calc::Calc;
3+
use super::calc::{Calc, MathFunction};
44
use super::number::CSSNumber;
55
use crate::error::{ParserError, PrinterError};
66
use crate::printer::Printer;
7+
use crate::traits::private::AddInternal;
78
use crate::traits::{private::TryAdd, Parse, ToCss};
89
use cssparser::*;
910

@@ -92,6 +93,12 @@ impl std::ops::Add<Percentage> for Percentage {
9293
}
9394
}
9495

96+
impl AddInternal for Percentage {
97+
fn add(self, other: Self) -> Self {
98+
self + other
99+
}
100+
}
101+
95102
impl std::cmp::PartialEq<CSSNumber> for Percentage {
96103
fn eq(&self, other: &CSSNumber) -> bool {
97104
self.0 == *other
@@ -233,9 +240,44 @@ impl<D: std::ops::Mul<CSSNumber, Output = D>> std::ops::Mul<CSSNumber> for Dimen
233240
impl<D: TryAdd<D> + Clone + std::cmp::PartialEq<CSSNumber> + std::cmp::PartialOrd<CSSNumber> + std::fmt::Debug>
234241
std::ops::Add<DimensionPercentage<D>> for DimensionPercentage<D>
235242
{
236-
type Output = Self;
243+
type Output = DimensionPercentage<D>;
237244

238245
fn add(self, other: DimensionPercentage<D>) -> DimensionPercentage<D> {
246+
// Unwrap calc(...) functions so we can add inside.
247+
// Then wrap the result in a calc(...) again if necessary.
248+
let a = unwrap_calc(self);
249+
let b = unwrap_calc(other);
250+
let res = AddInternal::add(a, b);
251+
match res {
252+
DimensionPercentage::Calc(c) => match *c {
253+
Calc::Value(l) => *l,
254+
Calc::Function(f) if !matches!(*f, MathFunction::Calc(_)) => {
255+
DimensionPercentage::Calc(Box::new(Calc::Function(f)))
256+
}
257+
c => DimensionPercentage::Calc(Box::new(Calc::Function(Box::new(MathFunction::Calc(c))))),
258+
},
259+
_ => res,
260+
}
261+
}
262+
}
263+
264+
fn unwrap_calc<D>(v: DimensionPercentage<D>) -> DimensionPercentage<D> {
265+
match v {
266+
DimensionPercentage::Calc(c) => match *c {
267+
Calc::Function(f) => match *f {
268+
MathFunction::Calc(c) => DimensionPercentage::Calc(Box::new(c)),
269+
c => DimensionPercentage::Calc(Box::new(Calc::Function(Box::new(c)))),
270+
},
271+
_ => DimensionPercentage::Calc(c),
272+
},
273+
_ => v,
274+
}
275+
}
276+
277+
impl<D: TryAdd<D> + Clone + std::cmp::PartialEq<CSSNumber> + std::cmp::PartialOrd<CSSNumber> + std::fmt::Debug>
278+
AddInternal for DimensionPercentage<D>
279+
{
280+
fn add(self, other: Self) -> Self {
239281
match self.add_recursive(&other) {
240282
Some(r) => r,
241283
None => self.add(other),
@@ -309,7 +351,9 @@ impl<D: TryAdd<D> + Clone + std::cmp::PartialEq<CSSNumber> + std::cmp::PartialOr
309351
}
310352

311353
match (a, b) {
312-
(DimensionPercentage::Calc(a), DimensionPercentage::Calc(b)) => DimensionPercentage::Calc(Box::new(*a + *b)),
354+
(DimensionPercentage::Calc(a), DimensionPercentage::Calc(b)) => {
355+
DimensionPercentage::Calc(Box::new(a.add(*b)))
356+
}
313357
(DimensionPercentage::Calc(calc), b) => {
314358
if let Calc::Value(a) = *calc {
315359
a.add(b)

src/values/time.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::calc::Calc;
44
use super::number::CSSNumber;
55
use crate::error::{ParserError, PrinterError};
66
use crate::printer::Printer;
7+
use crate::traits::private::AddInternal;
78
use crate::traits::{Parse, ToCss};
89
use cssparser::*;
910

@@ -127,6 +128,12 @@ impl std::ops::Add<Time> for Time {
127128
}
128129
}
129130

131+
impl AddInternal for Time {
132+
fn add(self, other: Self) -> Self {
133+
self + other
134+
}
135+
}
136+
130137
impl std::cmp::PartialEq<f32> for Time {
131138
fn eq(&self, other: &f32) -> bool {
132139
match self {

0 commit comments

Comments
 (0)