@@ -44,100 +44,26 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
4444 // ### RGB
4545
4646 new BuiltInCallable .overloaded ("rgb" , {
47- r"$red, $green, $blue" : (arguments) {
48- if (arguments[0 ].isSpecialNumber ||
49- arguments[1 ].isSpecialNumber ||
50- arguments[2 ].isSpecialNumber) {
51- return _functionString ('rgb' , arguments);
52- }
53-
54- var red = arguments[0 ].assertNumber ("red" );
55- var green = arguments[1 ].assertNumber ("green" );
56- var blue = arguments[2 ].assertNumber ("blue" );
57-
58- return new SassColor .rgb (
59- fuzzyRound (_percentageOrUnitless (red, 255 , "red" )),
60- fuzzyRound (_percentageOrUnitless (green, 255 , "green" )),
61- fuzzyRound (_percentageOrUnitless (blue, 255 , "blue" )));
62- },
63- r"$red, $green" : (arguments) {
64- // rgb(123, var(--foo)) is valid CSS because --foo might be `456, 789` and
65- // functions are parsed after variable substitution.
66- if (arguments[0 ].isVar || arguments[1 ].isVar) {
67- return _functionString ('rgb' , arguments);
68- } else {
69- throw new SassScriptException (r"Missing argument $blue." );
70- }
71- },
72- r"$red" : (arguments) {
73- if (arguments.first.isVar) {
74- return _functionString ('rgb' , arguments);
75- } else {
76- throw new SassScriptException (r"Missing argument $green." );
77- }
47+ r"$red, $green, $blue, $alpha" : (arguments) => _rgb ("rgb" , arguments),
48+ r"$red, $green, $blue" : (arguments) => _rgb ("rgb" , arguments),
49+ r"$color, $alpha" : (arguments) => _rgbTwoArg ("rgb" , arguments),
50+ r"$channels" : (arguments) {
51+ var parsed = _parseChannels (
52+ "rgb" , [r"$red" , r"$green" , r"$blue" ], arguments.first);
53+ return parsed is SassString ? parsed : _rgb ("rgb" , parsed as List <Value >);
7854 }
7955 }),
8056
8157 new BuiltInCallable .overloaded ("rgba" , {
82- r"$red, $green, $blue, $alpha" : (arguments) {
83- if (arguments[0 ].isSpecialNumber ||
84- arguments[1 ].isSpecialNumber ||
85- arguments[2 ].isSpecialNumber ||
86- arguments[3 ].isSpecialNumber) {
87- return _functionString ('rgba' , arguments);
88- }
89-
90- var red = arguments[0 ].assertNumber ("red" );
91- var green = arguments[1 ].assertNumber ("green" );
92- var blue = arguments[2 ].assertNumber ("blue" );
93- var alpha = arguments[3 ].assertNumber ("alpha" );
94-
95- return new SassColor .rgb (
96- fuzzyRound (_percentageOrUnitless (red, 255 , "red" )),
97- fuzzyRound (_percentageOrUnitless (green, 255 , "green" )),
98- fuzzyRound (_percentageOrUnitless (blue, 255 , "blue" )),
99- _percentageOrUnitless (alpha, 1 , "alpha" ));
100- },
101- r"$color, $alpha" : (arguments) {
102- // rgba(var(--foo), 0.5) is valid CSS because --foo might be `123, 456,
103- // 789` and functions are parsed after variable substitution.
104- if (arguments[0 ].isVar) {
105- return _functionString ('rgba' , arguments);
106- } else if (arguments[1 ].isVar) {
107- var first = arguments[0 ];
108- if (first is SassColor ) {
109- return new SassString (
110- "rgba(${first .red }, ${first .green }, ${first .blue }, "
111- "${arguments [1 ].toCssString ()})" ,
112- quotes: false );
113- } else {
114- return _functionString ('rgba' , arguments);
115- }
116- } else if (arguments[1 ].isSpecialNumber) {
117- var color = arguments[0 ].assertColor ("color" );
118- return new SassString (
119- "rgba(${color .red }, ${color .green }, ${color .blue }, "
120- "${arguments [1 ].toCssString ()})" ,
121- quotes: false );
122- }
123-
124- var color = arguments[0 ].assertColor ("color" );
125- var alpha = arguments[1 ].assertNumber ("alpha" );
126- return color.changeAlpha (_percentageOrUnitless (alpha, 1 , "alpha" ));
127- },
128- r"$red, $green, $blue" : (arguments) {
129- if (arguments[0 ].isVar || arguments[1 ].isVar || arguments[2 ].isVar) {
130- return _functionString ('rgba' , arguments);
131- } else {
132- throw new SassScriptException (r"Missing argument $alpha." );
133- }
134- },
135- r"$red" : (arguments) {
136- if (arguments.first.isVar) {
137- return _functionString ('rgba' , arguments);
138- } else {
139- throw new SassScriptException (r"Missing argument $green." );
140- }
58+ r"$red, $green, $blue, $alpha" : (arguments) => _rgb ("rgba" , arguments),
59+ r"$red, $green, $blue" : (arguments) => _rgb ("rgba" , arguments),
60+ r"$color, $alpha" : (arguments) => _rgbTwoArg ("rgba" , arguments),
61+ r"$channels" : (arguments) {
62+ var parsed = _parseChannels (
63+ "rgba" , [r"$red" , r"$green" , r"$blue" ], arguments.first);
64+ return parsed is SassString
65+ ? parsed
66+ : _rgb ("rgba" , parsed as List <Value >);
14167 }
14268 }),
14369
@@ -163,20 +89,9 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
16389 // ### HSL
16490
16591 new BuiltInCallable .overloaded ("hsl" , {
166- r"$hue, $saturation, $lightness" : (arguments) {
167- if (arguments[0 ].isSpecialNumber ||
168- arguments[1 ].isSpecialNumber ||
169- arguments[2 ].isSpecialNumber) {
170- return _functionString ("hsl" , arguments);
171- }
172-
173- var hue = arguments[0 ].assertNumber ("hue" );
174- var saturation = arguments[1 ].assertNumber ("saturation" );
175- var lightness = arguments[2 ].assertNumber ("lightness" );
176-
177- return new SassColor .hsl (hue.value, saturation.value.clamp (0 , 100 ),
178- lightness.value.clamp (0 , 100 ));
179- },
92+ r"$hue, $saturation, $lightness, $alpha" : (arguments) =>
93+ _hsl ("hsl" , arguments),
94+ r"$hue, $saturation, $lightness" : (arguments) => _hsl ("hsl" , arguments),
18095 r"$hue, $saturation" : (arguments) {
18196 // hsl(123, var(--foo)) is valid CSS because --foo might be `10%, 20%` and
18297 // functions are parsed after variable substitution.
@@ -186,57 +101,30 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
186101 throw new SassScriptException (r"Missing argument $lightness." );
187102 }
188103 },
189- r"$hue" : (arguments) {
190- if (arguments.first.isVar) {
191- return _functionString ('hsl' , arguments);
192- } else {
193- throw new SassScriptException (r"Missing argument $saturation." );
194- }
104+ r"$channels" : (arguments) {
105+ var parsed = _parseChannels (
106+ "hsl" , [r"$hue" , r"$saturation" , r"$lightness" ], arguments.first);
107+ return parsed is SassString ? parsed : _hsl ("hsl" , parsed as List <Value >);
195108 }
196109 }),
197110
198111 new BuiltInCallable .overloaded ("hsla" , {
199- r"$hue, $saturation, $lightness, $alpha" : (arguments) {
200- if (arguments[0 ].isSpecialNumber ||
201- arguments[1 ].isSpecialNumber ||
202- arguments[2 ].isSpecialNumber ||
203- arguments[3 ].isSpecialNumber) {
204- return _functionString ("hsla" , arguments);
205- }
206-
207- var hue = arguments[0 ].assertNumber ("hue" );
208- var saturation = arguments[1 ].assertNumber ("saturation" );
209- var lightness = arguments[2 ].assertNumber ("lightness" );
210- var alpha = arguments[3 ].assertNumber ("alpha" );
211-
212- return new SassColor .hsl (
213- hue.value,
214- saturation.value.clamp (0 , 100 ),
215- lightness.value.clamp (0 , 100 ),
216- _percentageOrUnitless (alpha, 1 , "alpha" ));
217- },
218- r"$hue, $saturation, $lightness" : (arguments) {
219- // hsla(123, var(--foo)) is valid CSS because --foo might be `10%, 20%,
220- // 0.5` and functions are parsed after variable substitution.
221- if (arguments[0 ].isVar || arguments[1 ].isVar || arguments[2 ].isVar) {
222- return _functionString ('hsla' , arguments);
223- } else {
224- throw new SassScriptException (r"Missing argument $alpha." );
225- }
226- },
112+ r"$hue, $saturation, $lightness, $alpha" : (arguments) =>
113+ _hsl ("hsla" , arguments),
114+ r"$hue, $saturation, $lightness" : (arguments) => _hsl ("hsla" , arguments),
227115 r"$hue, $saturation" : (arguments) {
228116 if (arguments[0 ].isVar || arguments[1 ].isVar) {
229117 return _functionString ('hsla' , arguments);
230118 } else {
231119 throw new SassScriptException (r"Missing argument $lightness." );
232120 }
233121 },
234- r"$hue " : (arguments) {
235- if (arguments.first.isVar) {
236- return _functionString ( ' hsla' , arguments);
237- } else {
238- throw new SassScriptException ( r"Missing argument $saturation." );
239- }
122+ r"$channels " : (arguments) {
123+ var parsed = _parseChannels (
124+ " hsla" , [ r"$hue" , r"$saturation" , r"$lightness" ], arguments.first );
125+ return parsed is SassString
126+ ? parsed
127+ : _hsl ( "hsla" , parsed as List < Value >);
240128 }
241129 }),
242130
@@ -1009,6 +897,135 @@ SassString _functionString(String name, Iterable<Value> arguments) =>
1009897 ")" ,
1010898 quotes: false );
1011899
900+ Value _rgb (String name, List <Value > arguments) {
901+ var alpha = arguments.length > 3 ? arguments[3 ] : null ;
902+ if (arguments[0 ].isSpecialNumber ||
903+ arguments[1 ].isSpecialNumber ||
904+ arguments[2 ].isSpecialNumber ||
905+ (alpha? .isSpecialNumber ?? false )) {
906+ return _functionString (name, arguments);
907+ }
908+
909+ var red = arguments[0 ].assertNumber ("red" );
910+ var green = arguments[1 ].assertNumber ("green" );
911+ var blue = arguments[2 ].assertNumber ("blue" );
912+
913+ return new SassColor .rgb (
914+ fuzzyRound (_percentageOrUnitless (red, 255 , "red" )),
915+ fuzzyRound (_percentageOrUnitless (green, 255 , "green" )),
916+ fuzzyRound (_percentageOrUnitless (blue, 255 , "blue" )),
917+ alpha == null
918+ ? null
919+ : _percentageOrUnitless (alpha.assertNumber ("alpha" ), 1 , "alpha" ));
920+ }
921+
922+ Value _rgbTwoArg (String name, List <Value > arguments) {
923+ // rgba(var(--foo), 0.5) is valid CSS because --foo might be `123, 456, 789`
924+ // and functions are parsed after variable substitution.
925+ if (arguments[0 ].isVar) {
926+ return _functionString (name, arguments);
927+ } else if (arguments[1 ].isVar) {
928+ var first = arguments[0 ];
929+ if (first is SassColor ) {
930+ return new SassString (
931+ "$name (${first .red }, ${first .green }, ${first .blue }, "
932+ "${arguments [1 ].toCssString ()})" ,
933+ quotes: false );
934+ } else {
935+ return _functionString (name, arguments);
936+ }
937+ } else if (arguments[1 ].isSpecialNumber) {
938+ var color = arguments[0 ].assertColor ("color" );
939+ return new SassString (
940+ "$name (${color .red }, ${color .green }, ${color .blue }, "
941+ "${arguments [1 ].toCssString ()})" ,
942+ quotes: false );
943+ }
944+
945+ var color = arguments[0 ].assertColor ("color" );
946+ var alpha = arguments[1 ].assertNumber ("alpha" );
947+ return color.changeAlpha (_percentageOrUnitless (alpha, 1 , "alpha" ));
948+ }
949+
950+ Value _hsl (String name, List <Value > arguments) {
951+ var alpha = arguments.length > 3 ? arguments[3 ] : null ;
952+ if (arguments[0 ].isSpecialNumber ||
953+ arguments[1 ].isSpecialNumber ||
954+ arguments[2 ].isSpecialNumber ||
955+ (alpha? .isSpecialNumber ?? false )) {
956+ return _functionString (name, arguments);
957+ }
958+
959+ var hue = arguments[0 ].assertNumber ("hue" );
960+ var saturation = arguments[1 ].assertNumber ("saturation" );
961+ var lightness = arguments[2 ].assertNumber ("lightness" );
962+
963+ return new SassColor .hsl (
964+ hue.value,
965+ saturation.value.clamp (0 , 100 ),
966+ lightness.value.clamp (0 , 100 ),
967+ alpha == null
968+ ? null
969+ : _percentageOrUnitless (alpha.assertNumber ("alpha" ), 1 , "alpha" ));
970+ }
971+
972+ /* SassString | List<Value> */ _parseChannels (
973+ String name, List <String > argumentNames, Value channels) {
974+ if (channels.isVar) return _functionString (name, [channels]);
975+
976+ var isCommaSeparated = channels.separator == ListSeparator .comma;
977+ var isBracketed = channels.hasBrackets;
978+ if (isCommaSeparated || isBracketed) {
979+ var buffer = new StringBuffer (r"$channels must be" );
980+ if (isBracketed) buffer.write (" an unbracketed" );
981+ if (isCommaSeparated) {
982+ buffer.write (isBracketed ? "," : " a" );
983+ buffer.write (" space-separated" );
984+ }
985+ buffer.write (" list." );
986+ throw new SassScriptException (buffer.toString ());
987+ }
988+
989+ var list = channels.asList;
990+ if (list.length > 3 ) {
991+ throw new SassScriptException (
992+ "Only 3 elements allowed, but ${list .length } were passed." );
993+ } else if (list.length < 3 ) {
994+ if (list.any ((value) => value.isVar) ||
995+ (list.isNotEmpty && _isVarSlash (list.last))) {
996+ return _functionString (name, [channels]);
997+ } else {
998+ var argument = argumentNames[list.length];
999+ throw new SassScriptException ("Missing element $argument ." );
1000+ }
1001+ }
1002+
1003+ var maybeSlashSeparated = list[2 ];
1004+ if (maybeSlashSeparated is SassNumber &&
1005+ maybeSlashSeparated.asSlash != null ) {
1006+ return [
1007+ list[0 ],
1008+ list[1 ],
1009+ maybeSlashSeparated.asSlash.item1,
1010+ maybeSlashSeparated.asSlash.item2
1011+ ];
1012+ } else if (maybeSlashSeparated is SassString &&
1013+ ! maybeSlashSeparated.hasQuotes &&
1014+ maybeSlashSeparated.text.contains ("/" )) {
1015+ return _functionString (name, [channels]);
1016+ } else {
1017+ return list;
1018+ }
1019+ }
1020+
1021+ /// Returns whether [value] is an unquoted string that start with `var(` and
1022+ /// contains `/` .
1023+ bool _isVarSlash (Value value) =>
1024+ value is SassString &&
1025+ value.hasQuotes &&
1026+ startsWithIgnoreCase (value.text, "var(" ) &&
1027+ value.text.contains ("/" );
1028+
10121029/// Asserts that [number] is a percentage or has no units, and normalizes the
10131030/// value.
10141031///
0 commit comments