diff --git a/CHANGELOG.md b/CHANGELOG.md index e561e8de..92210567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.0-alpha.3 + +* Emit colors using their original representation if possible. + +* Emit colors without an original representation as names if possible. + ## 1.0.0-alpha.2 * Fix a bug where variables, functions, and mixins were broken in imported diff --git a/lib/src/color_names.dart b/lib/src/color_names.dart index fe9940df..3698783d 100644 --- a/lib/src/color_names.dart +++ b/lib/src/color_names.dart @@ -2,6 +2,8 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'package:collection/collection.dart'; + import 'utils.dart'; import 'value.dart'; @@ -157,3 +159,9 @@ final colorsByName = normalizedMap({ 'yellow': new SassColor.rgb(0xFF, 0xFF, 0x00), 'yellowgreen': new SassColor.rgb(0x9A, 0xCD, 0x32), }); + +/// A map from Sass colors to (lowercase) color names. +final namesByColor = mapMap/**/( + colorsByName, + key: (_, color) => color, + value: (name, _) => name); diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 142d4cef..2aacd6c8 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -15,6 +15,7 @@ import '../interpolation_buffer.dart'; import '../util/character.dart'; import '../utils.dart'; import '../value.dart'; +import '../value/color.dart'; import 'parser.dart'; /// The base class for both the SCSS and indented syntax parsers. @@ -1492,14 +1493,20 @@ abstract class StylesheetParser extends Parser { var first = scanner.peekChar(); if (first != null && isDigit(first)) { - return new ColorExpression(_hexColorContents(), scanner.spanFrom(start)); + var color = _hexColorContents(); + var span = scanner.spanFrom(start); + setOriginalSpan(color, span); + return new ColorExpression(color, span); } var afterHash = scanner.state; var identifier = _interpolatedIdentifier(); if (_isHexColor(identifier)) { scanner.state = afterHash; - return new ColorExpression(_hexColorContents(), scanner.spanFrom(start)); + var color = _hexColorContents(); + var span = scanner.spanFrom(start); + setOriginalSpan(color, span); + return new ColorExpression(color, span); } var buffer = new InterpolationBuffer(); @@ -1740,7 +1747,13 @@ abstract class StylesheetParser extends Parser { } var color = colorsByName[lower]; - if (color != null) return new ColorExpression(color, identifier.span); + if (color != null) { + // TODO(nweiz): Avoid copying the color in compressed mode. + color = new SassColor.rgb( + color.red, color.green, color.blue, color.alpha); + setOriginalSpan(color, identifier.span); + return new ColorExpression(color, identifier.span); + } } var specialFunction = _trySpecialFunction(lower, start); diff --git a/lib/src/value.dart b/lib/src/value.dart index 85ea04b4..3db33706 100644 --- a/lib/src/value.dart +++ b/lib/src/value.dart @@ -16,7 +16,7 @@ import 'visitor/serialize.dart'; export 'value/argument_list.dart'; export 'value/boolean.dart'; -export 'value/color.dart'; +export 'value/color.dart' hide setOriginalSpan; export 'value/function.dart'; export 'value/list.dart'; export 'value/map.dart'; diff --git a/lib/src/value/color.dart b/lib/src/value/color.dart index 687445c9..9a9ec680 100644 --- a/lib/src/value/color.dart +++ b/lib/src/value/color.dart @@ -4,11 +4,20 @@ import 'dart:math' as math; +import 'package:source_span/source_span.dart'; + import '../exception.dart'; import '../util/number.dart'; import '../value.dart'; import '../visitor/interface/value.dart'; +/// Sets the span for [SassColor.original] to [span]. +/// +/// This is a separate function so it can be hidden in most exports. +void setOriginalSpan(SassColor color, SourceSpan span) { + color._originalSpan = span; +} + // TODO(nweiz): track original representation. /// A SassScript color. class SassColor extends Value { @@ -63,6 +72,15 @@ class SassColor extends Value { /// This color's alpha channel, between `0` and `1`. final num alpha; + /// The original string representation of this color, or `null` if one is + /// unavailable. + String get original => _originalSpan?.text; + + /// The span tracking the location in which this color was originally defined. + /// + /// This is tracked as a span to avoid extra substring allocations. + SourceSpan _originalSpan; + /// Creates an RGB color. /// /// Throws a [RangeError] if [red], [green], and [blue] aren't between `0` and diff --git a/lib/src/visitor/serialize.dart b/lib/src/visitor/serialize.dart index 9f4c481f..a283ec2d 100644 --- a/lib/src/visitor/serialize.dart +++ b/lib/src/visitor/serialize.dart @@ -10,6 +10,7 @@ import 'package:string_scanner/string_scanner.dart'; import '../ast/css.dart'; import '../ast/selector.dart'; +import '../color_names.dart'; import '../exception.dart'; import '../util/character.dart'; import '../util/number.dart'; @@ -280,8 +281,11 @@ class _SerializeCssVisitor void visitBoolean(SassBoolean value) => _buffer.write(value.value.toString()); void visitColor(SassColor value) { - // TODO(nweiz): Use color names for named colors. - if (value.alpha == 1) { + if (value.original != null) { + _buffer.write(value.original); + } else if (namesByColor.containsKey(value)) { + _buffer.write(namesByColor[value]); + } else if (value.alpha == 1) { _buffer.writeCharCode($hash); _writeHexComponent(value.red); _writeHexComponent(value.green); diff --git a/pubspec.yaml b/pubspec.yaml index 76d5e970..7b36bc5b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.0.0-alpha.2 +version: 1.0.0-dev description: A Sass implementation in Dart. author: Dart Team homepage: https://github.com/sass/dart-sass