@@ -114,7 +114,10 @@ String serializeSelector(Selector selector, {bool inspect = false}) {
114114final class _SerializeVisitor
115115 implements CssVisitor <void >, ValueVisitor <void >, SelectorVisitor <void > {
116116 /// A buffer that contains the CSS produced so far.
117- final SourceMapBuffer _buffer;
117+ ///
118+ /// This can be temporarily replaced to capture a particular chunk of
119+ /// serialization to a string.
120+ SourceMapBuffer _buffer;
118121
119122 /// The current indentation of the CSS output.
120123 var _indentation = 0 ;
@@ -763,42 +766,17 @@ final class _SerializeVisitor
763766 // In compressed mode, emit colors in the shortest representation possible.
764767 if (_isCompressed) {
765768 var rgb = color.toSpace (ColorSpace .rgb);
766- if (opaque && _tryIntegerRgb (rgb)) return ;
767-
768- var red = _writeNumberToString (rgb.channel0);
769- var green = _writeNumberToString (rgb.channel1);
770- var blue = _writeNumberToString (rgb.channel2);
769+ if (opaque && _tryHexOrNamedRgb (rgb)) return ;
771770
772- var hsl = color.toSpace (ColorSpace .hsl);
773- var hue = _writeNumberToString (hsl.channel0);
774- var saturation = _writeNumberToString (hsl.channel1);
775- var lightness = _writeNumberToString (hsl.channel2);
771+ var rgbString = _capture (() => _writeRgb (rgb));
772+ var hslString = _capture (() => _writeHsl (rgb.toSpace (ColorSpace .hsl)));
776773
777774 // Add two characters for HSL for the %s on saturation and lightness.
778- if (red.length + green.length + blue.length <=
779- hue.length + saturation.length + lightness.length + 2 ) {
780- _buffer
781- ..write (opaque ? 'rgb(' : 'rgba(' )
782- ..write (red)
783- ..writeCharCode ($comma)
784- ..write (green)
785- ..writeCharCode ($comma)
786- ..write (blue);
775+ if (rgbString.length <= hslString.length + 2 ) {
776+ _buffer.write (rgbString);
787777 } else {
788- _buffer
789- ..write (opaque ? 'hsl(' : 'hsla(' )
790- ..write (hue)
791- ..writeCharCode ($comma)
792- ..write (saturation)
793- ..write ('%,' )
794- ..write (lightness)
795- ..writeCharCode ($percent);
796- }
797- if (! opaque) {
798- _buffer.writeCharCode ($comma);
799- _writeNumber (color.alpha);
778+ _buffer.write (hslString);
800779 }
801- _buffer.writeCharCode ($rparen);
802780 return ;
803781 }
804782
@@ -852,7 +830,7 @@ final class _SerializeVisitor
852830 ///
853831 /// Otherwise, writes nothing and returns `false` . Assumes [value] is in the
854832 /// RGB space.
855- bool _tryIntegerRgb (SassColor rgb) {
833+ bool _tryHexOrNamedRgb (SassColor rgb) {
856834 assert (rgb.space == ColorSpace .rgb);
857835 if (! _canUseHex (rgb)) return false ;
858836
@@ -898,11 +876,14 @@ final class _SerializeVisitor
898876 var opaque = fuzzyEquals (color.alpha, 1 );
899877 var rgb = color.toSpace (ColorSpace .rgb);
900878 _buffer.write (opaque ? "rgb(" : "rgba(" );
901- _writeNumber (rgb.channel ('red' ));
902- _buffer.write (_commaSeparator);
903- _writeNumber (rgb.channel ('green' ));
904- _buffer.write (_commaSeparator);
905- _writeNumber (rgb.channel ('blue' ));
879+
880+ if (! _tryIntegerRgbChannels (rgb)) {
881+ _writeChannel (color.channel0 * 100 / 255 , '%' );
882+ _buffer.write (_commaSeparator);
883+ _writeChannel (color.channel1 * 100 / 255 , '%' );
884+ _buffer.write (_commaSeparator);
885+ _writeChannel (color.channel2 * 100 / 255 , '%' );
886+ }
906887
907888 if (! opaque) {
908889 _buffer.write (_commaSeparator);
@@ -912,6 +893,32 @@ final class _SerializeVisitor
912893 _buffer.writeCharCode ($rparen);
913894 }
914895
896+ /// If [value] 's channels are all integers, writes them as such and returns
897+ /// `true` .
898+ ///
899+ /// Otherwise, writes nothing and returns `false` . Assumes [value] is in the
900+ /// RGB space.
901+ bool _tryIntegerRgbChannels (SassColor rgb) {
902+ assert (rgb.space == ColorSpace .rgb);
903+
904+ var red = _asInt (rgb.channel0);
905+ if (red == null ) return false ;
906+
907+ var green = _asInt (rgb.channel1);
908+ if (green == null ) return false ;
909+
910+ var blue = _asInt (rgb.channel2);
911+ if (blue == null ) return false ;
912+
913+ _buffer.write (_removeExponent (red.toString ()));
914+ _buffer.write (_commaSeparator);
915+ _buffer.write (_removeExponent (green.toString ()));
916+ _buffer.write (_commaSeparator);
917+ _buffer.write (_removeExponent (blue.toString ()));
918+
919+ return true ;
920+ }
921+
915922 /// Writes [value] as an `hsl()` or `hsla()` function.
916923 void _writeHsl (SassColor color) {
917924 var opaque = fuzzyEquals (color.alpha, 1 );
@@ -1143,38 +1150,31 @@ final class _SerializeVisitor
11431150 }
11441151 }
11451152
1146- /// Like [_writeNumber] , but returns a string rather than writing to
1147- /// [_buffer] .
1148- String _writeNumberToString (double number) {
1149- var buffer = NoSourceMapBuffer ();
1150- _writeNumber (number, buffer);
1151- return buffer.toString ();
1152- }
1153-
11541153 /// Writes [number] without exponent notation and with at most
11551154 /// [SassNumber.precision] digits after the decimal point.
11561155 ///
11571156 /// The number is written to [buffer] , which defaults to [_buffer] .
1158- void _writeNumber (double number, [SourceMapBuffer ? buffer]) {
1159- buffer ?? = _buffer;
1157+ void _writeNumber (double number) {
1158+ if (! number.isFinite) {
1159+ visitCalculation (
1160+ SassCalculation .unsimplified ('calc' , [SassNumber (number)]));
1161+ return ;
1162+ }
11601163
11611164 // Dart always converts integers to strings in the obvious way, so all we
11621165 // have to do is clamp doubles that are close to being integers.
1163- if (fuzzyAsInt (number) case var integer?
1164- // In inspect mode, we want to show the full precision of every number,
1165- // so we only write them as integers when they're precisely equal.
1166- when ! _inspect || number == integer) {
1166+ if (_asInt (number) case var integer? ) {
11671167 // JS still uses exponential notation for integers, so we have to handle
11681168 // it here.
1169- buffer .write (_removeExponent (integer.toString ()));
1169+ _buffer .write (_removeExponent (integer.toString ()));
11701170 return ;
11711171 }
11721172
11731173 var text = _removeExponent (number.toString ());
11741174
11751175 // Write the number at full precision in inspect mode.
11761176 if (_inspect) {
1177- buffer .write (text);
1177+ _buffer .write (text);
11781178 return ;
11791179 }
11801180
@@ -1185,11 +1185,22 @@ final class _SerializeVisitor
11851185
11861186 if (canWriteDirectly) {
11871187 if (_isCompressed && text.codeUnitAt (0 ) == $0) text = text.substring (1 );
1188- buffer .write (text);
1188+ _buffer .write (text);
11891189 return ;
11901190 }
11911191
1192- _writeRounded (text, buffer);
1192+ _writeRounded (text, _buffer);
1193+ }
1194+
1195+ /// If [number] is close enough to an integer, returns it as one.
1196+ ///
1197+ /// Normally, "close enough" includes fuzzy matching, but in inspect mode we
1198+ /// want to show the full precision of every number so "close enough" requires
1199+ /// literally being an integer.
1200+ int ? _asInt (double number) {
1201+ if (_inspect) return fuzzyAsInt (number);
1202+ var rounded = number.round ();
1203+ return rounded == number ? rounded : null ;
11931204 }
11941205
11951206 /// If [text] is written in exponent notation, returns a string representation
@@ -1854,6 +1865,19 @@ final class _SerializeVisitor
18541865 bool _isInvisible (CssNode node) =>
18551866 ! _inspect &&
18561867 (_isCompressed ? node.isInvisibleHidingComments : node.isInvisible);
1868+
1869+ /// Runs [callback] without adding to [_buffer] and returns the text it would
1870+ /// have emitted.
1871+ String _capture (void Function () callback) {
1872+ var oldBuffer = _buffer;
1873+ _buffer = NoSourceMapBuffer ();
1874+ try {
1875+ callback ();
1876+ return _buffer.toString ();
1877+ } finally {
1878+ _buffer = oldBuffer;
1879+ }
1880+ }
18571881}
18581882
18591883/// An enum of generated CSS styles.
0 commit comments