@@ -2714,6 +2714,25 @@ abstract class StylesheetParser extends Parser {
2714
2714
..writeCharCode ($lparen);
2715
2715
break ;
2716
2716
2717
+ case "min" :
2718
+ case "max" :
2719
+ // min() and max() are parsed as the plain CSS mathematical functions if
2720
+ // possible, and otherwise are parsed as normal Sass functions.
2721
+ var beginningOfContents = scanner.state;
2722
+ if (! scanner.scanChar ($lparen)) return null ;
2723
+ whitespace ();
2724
+
2725
+ var buffer = InterpolationBuffer ()
2726
+ ..write (name)
2727
+ ..writeCharCode ($lparen);
2728
+
2729
+ if (! _tryMinMaxContents (buffer)) {
2730
+ scanner.state = beginningOfContents;
2731
+ return null ;
2732
+ }
2733
+
2734
+ return StringExpression (buffer.interpolation (scanner.spanFrom (start)));
2735
+
2717
2736
case "progid" :
2718
2737
if (! scanner.scanChar ($colon)) return null ;
2719
2738
buffer = InterpolationBuffer ()
@@ -2748,7 +2767,11 @@ abstract class StylesheetParser extends Parser {
2748
2767
///
2749
2768
/// Assumes the scanner is positioned immediately before the opening
2750
2769
/// parenthesis of the argument list.
2751
- CalculationExpression ? _tryCalculation (String name, LineScannerState start) {
2770
+ ///
2771
+ /// If [allowMinMax] is `true` , this parses `min()` and `max()` functions as
2772
+ /// calculations.
2773
+ CalculationExpression ? _tryCalculation (String name, LineScannerState start,
2774
+ {bool allowMinMax = false }) {
2752
2775
assert (scanner.peekChar () == $lparen);
2753
2776
switch (name) {
2754
2777
case "calc" :
@@ -2757,18 +2780,9 @@ abstract class StylesheetParser extends Parser {
2757
2780
2758
2781
case "min" :
2759
2782
case "max" :
2760
- // min() and max() are parsed as calculations if possible, and otherwise
2761
- // are parsed as normal Sass functions.
2762
- var beforeArguments = scanner.state;
2763
- List <Expression > arguments;
2764
- try {
2765
- arguments = _calculationArguments ();
2766
- } on FormatException catch (_) {
2767
- scanner.state = beforeArguments;
2768
- return null ;
2769
- }
2770
-
2771
- return CalculationExpression (name, arguments, scanner.spanFrom (start));
2783
+ if (! allowMinMax) return null ;
2784
+ return CalculationExpression (
2785
+ name, _calculationArguments (), scanner.spanFrom (start));
2772
2786
2773
2787
case "clamp" :
2774
2788
var arguments = _calculationArguments (3 );
@@ -2779,6 +2793,142 @@ abstract class StylesheetParser extends Parser {
2779
2793
}
2780
2794
}
2781
2795
2796
+ /// Consumes the contents of a plain-CSS `min()` or `max()` function into
2797
+ /// [buffer] if one is available.
2798
+ ///
2799
+ /// Returns whether this succeeded.
2800
+ ///
2801
+ /// If [allowComma] is `true` (the default), this allows `CalcValue`
2802
+ /// productions separated by commas.
2803
+ bool _tryMinMaxContents (InterpolationBuffer buffer,
2804
+ {bool allowComma = true }) {
2805
+ // The number of open parentheses that need to be closed.
2806
+ while (true ) {
2807
+ var next = scanner.peekChar ();
2808
+ switch (next) {
2809
+ case $minus:
2810
+ case $plus:
2811
+ case $0:
2812
+ case $1:
2813
+ case $2:
2814
+ case $3:
2815
+ case $4:
2816
+ case $5:
2817
+ case $6:
2818
+ case $7:
2819
+ case $8:
2820
+ case $9:
2821
+ case $dot:
2822
+ try {
2823
+ buffer.write (rawText (_number));
2824
+ } on FormatException catch (_) {
2825
+ return false ;
2826
+ }
2827
+ break ;
2828
+
2829
+ case $hash:
2830
+ if (scanner.peekChar (1 ) != $lbrace) return false ;
2831
+ buffer.add (singleInterpolation ());
2832
+ break ;
2833
+
2834
+ case $c:
2835
+ case $C :
2836
+ switch (scanner.peekChar (1 )) {
2837
+ case $a:
2838
+ case $A :
2839
+ if (! _tryMinMaxFunction (buffer, "calc" )) return false ;
2840
+ break ;
2841
+
2842
+ case $l:
2843
+ case $L :
2844
+ if (! _tryMinMaxFunction (buffer, "clamp" )) return false ;
2845
+ break ;
2846
+ }
2847
+ break ;
2848
+
2849
+ case $e:
2850
+ case $E :
2851
+ if (! _tryMinMaxFunction (buffer, "env" )) return false ;
2852
+ break ;
2853
+
2854
+ case $v:
2855
+ case $V :
2856
+ if (! _tryMinMaxFunction (buffer, "var" )) return false ;
2857
+ break ;
2858
+
2859
+ case $lparen:
2860
+ buffer.writeCharCode (scanner.readChar ());
2861
+ if (! _tryMinMaxContents (buffer, allowComma: false )) return false ;
2862
+ break ;
2863
+
2864
+ case $m:
2865
+ case $M :
2866
+ scanner.readChar ();
2867
+ if (scanIdentChar ($i)) {
2868
+ if (! scanIdentChar ($n)) return false ;
2869
+ buffer.write ("min(" );
2870
+ } else if (scanIdentChar ($a)) {
2871
+ if (! scanIdentChar ($x)) return false ;
2872
+ buffer.write ("max(" );
2873
+ } else {
2874
+ return false ;
2875
+ }
2876
+ if (! scanner.scanChar ($lparen)) return false ;
2877
+
2878
+ if (! _tryMinMaxContents (buffer)) return false ;
2879
+ break ;
2880
+
2881
+ default :
2882
+ return false ;
2883
+ }
2884
+
2885
+ whitespace ();
2886
+
2887
+ next = scanner.peekChar ();
2888
+ switch (next) {
2889
+ case $rparen:
2890
+ buffer.writeCharCode (scanner.readChar ());
2891
+ return true ;
2892
+
2893
+ case $plus:
2894
+ case $minus:
2895
+ case $asterisk:
2896
+ case $slash:
2897
+ buffer.writeCharCode ($space);
2898
+ buffer.writeCharCode (scanner.readChar ());
2899
+ buffer.writeCharCode ($space);
2900
+ break ;
2901
+
2902
+ case $comma:
2903
+ if (! allowComma) return false ;
2904
+ buffer.writeCharCode (scanner.readChar ());
2905
+ buffer.writeCharCode ($space);
2906
+ break ;
2907
+
2908
+ default :
2909
+ return false ;
2910
+ }
2911
+
2912
+ whitespace ();
2913
+ }
2914
+ }
2915
+
2916
+ /// Consumes a function named [name] containing an
2917
+ /// `InterpolatedDeclarationValue` if possible, and adds its text to [buffer] .
2918
+ ///
2919
+ /// Returns whether such a function could be consumed.
2920
+ bool _tryMinMaxFunction (InterpolationBuffer buffer, String name) {
2921
+ if (! scanIdentifier (name)) return false ;
2922
+ if (! scanner.scanChar ($lparen)) return false ;
2923
+ buffer
2924
+ ..write (name)
2925
+ ..writeCharCode ($lparen)
2926
+ ..addInterpolation (_interpolatedDeclarationValue (allowEmpty: true ))
2927
+ ..writeCharCode ($rparen);
2928
+ if (! scanner.scanChar ($rparen)) return false ;
2929
+ return true ;
2930
+ }
2931
+
2782
2932
/// Consumes and returns arguments for a calculation expression, including the
2783
2933
/// opening and closing parentheses.
2784
2934
///
@@ -2876,7 +3026,7 @@ abstract class StylesheetParser extends Parser {
2876
3026
if (scanner.peekChar () != $lparen) scanner.error ('Expected "(" or ".".' );
2877
3027
2878
3028
var lowerCase = ident.toLowerCase ();
2879
- var calculation = _tryCalculation (lowerCase, start);
3029
+ var calculation = _tryCalculation (lowerCase, start, allowMinMax : true );
2880
3030
if (calculation != null ) {
2881
3031
return calculation;
2882
3032
} else if (lowerCase == "if" ) {
0 commit comments