Skip to content

Commit

Permalink
Add deprecation warnings for hex alpha colors
Browse files Browse the repository at this point in the history
Also support unambiguous hex alpha colors.

Closes #360
See sass/sass#2179
  • Loading branch information
nex3 committed Jun 22, 2018
1 parent d6dc8c9 commit 4ea9a67
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 21 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
## 1.6.3
## 1.7.0

* Emit deprecation warnings for tokens such as `#abcd` that are ambiguous
between ID strings and hex colors with alpha channels. These will be
interpreted as colors in a release on or after 19 September 2018.

* Parse unambiguous hex colors with alpha channels as colors.

* Fix a bug where relative imports from files on the load path could look in the
incorrect location.
Expand Down
65 changes: 50 additions & 15 deletions lib/src/parse/stylesheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1742,7 +1742,20 @@ abstract class StylesheetParser extends Parser {
var identifier = _interpolatedIdentifier();
if (_isHexColor(identifier)) {
scanner.state = afterHash;
return new ColorExpression(_hexColorContents(start));
var color = _hexColorContents(start);

var plain = identifier.asPlain;
if (plain.length == 4 || plain.length == 8) {
logger.warn('''
The value "$color" is currently parsed as a string, but it will be parsed as a
color in a release on or after 19 September 2018.
To continue parsing it as a string, use "unquote('$color')".
To parse it as a color, use "${color.toStringAsRgb()}".
''', span: color.originalSpan, deprecation: true);
} else {
return new ColorExpression(color);
}
}

var buffer = new InterpolationBuffer();
Expand All @@ -1753,30 +1766,52 @@ abstract class StylesheetParser extends Parser {

/// Consumes the contents of a hex color, after the `#`.
SassColor _hexColorContents(LineScannerState start) {
var red = _hexDigit();
var green = _hexDigit();
var blue = _hexDigit();

var next = scanner.peekChar();
if (next != null && isHex(next)) {
red = (red << 4) + green;
green = (blue << 4) + _hexDigit();
blue = (_hexDigit() << 4) + _hexDigit();
var digit1 = _hexDigit();
var digit2 = _hexDigit();
var digit3 = _hexDigit();

int red;
int green;
int blue;
num alpha = 1;
if (!isHex(scanner.peekChar())) {
// #abc
red = (digit1 << 4) + digit1;
green = (digit2 << 4) + digit2;
blue = (digit3 << 4) + digit3;
} else {
red = (red << 4) + red;
green = (green << 4) + green;
blue = (blue << 4) + blue;
var digit4 = _hexDigit();
if (!isHex(scanner.peekChar())) {
// #abcd
red = (digit1 << 4) + digit1;
green = (digit2 << 4) + digit2;
blue = (digit3 << 4) + digit3;
alpha = ((digit4 << 4) + digit4) / 0xff;
} else {
red = (digit1 << 4) + digit2;
green = (digit3 << 4) + digit4;
blue = (_hexDigit() << 4) + _hexDigit();

if (isHex(scanner.peekChar())) {
alpha = ((_hexDigit() << 4) + _hexDigit()) / 0xff;
}
}
}

return new SassColor.rgb(red, green, blue, 1, scanner.spanFrom(start));
return new SassColor.rgb(red, green, blue, alpha, scanner.spanFrom(start));
}

/// Returns whether [interpolation] is a plain string that can be parsed as a
/// hex color.
bool _isHexColor(Interpolation interpolation) {
var plain = interpolation.asPlain;
if (plain == null) return false;
if (plain.length != 3 && plain.length != 6) return false;
if (plain.length != 3 &&
plain.length != 4 &&
plain.length != 6 &&
plain.length != 8) {
return false;
}
return plain.codeUnits.every(isHex);
}

Expand Down
11 changes: 7 additions & 4 deletions lib/src/util/character.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,13 @@ bool isName(int character) =>
isNameStart(character) || isDigit(character) || character == $minus;

/// Returns whether [character] is a hexadeicmal digit.
bool isHex(int character) =>
isDigit(character) ||
(character >= $a && character <= $f) ||
(character >= $A && character <= $F);
bool isHex(int character) {
if (character == null) return false;
if (isDigit(character)) return true;
if (character >= $a && character <= $f) return true;
if (character >= $A && character <= $F) return true;
return false;
}

/// Returns whether [character] is the beginning of a UTF-16 surrogate pair.
bool isHighSurrogate(int character) =>
Expand Down
16 changes: 16 additions & 0 deletions lib/src/value/color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,20 @@ class SassColor extends Value implements ext.SassColor {

return fuzzyRound(result * 255);
}

/// Returns an `rgb()` or `rgba()` function call that will evaluate to this
/// color.
String toStringAsRgb() {
var isOpaque = fuzzyEquals(alpha, 1);
var buffer = new StringBuffer(isOpaque ? "rgb" : "rgba")
..write("($red, $green, $blue");

if (!isOpaque) {
// Write the alpha as a SassNumber to ensure it's valid CSS.
buffer.write(", ${new SassNumber(alpha)}");
}

buffer.write(")");
return buffer.toString();
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: sass
version: 1.6.3-dev
version: 1.7.0
description: A Sass implementation in Dart.
author: Dart Team <misc@dartlang.org>
homepage: https://github.com/sass/dart-sass
Expand Down

0 comments on commit 4ea9a67

Please sign in to comment.