Skip to content

Commit

Permalink
Properly handle some CSS identifier edge cases (#816)
Browse files Browse the repository at this point in the history
See sass/sass#2738
Closes #815
  • Loading branch information
nex3 authored Sep 6, 2019
1 parent aea990d commit d901bcb
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 19 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
## 1.22.12

* **Potentially breaking bug fix:** character sequences consisting of two or
more hyphens followed by a number (such as `--123`), or two or more hyphens on
their own (such as `--`), are now parsed as identifiers [in accordance with
the CSS spec][ident-token-diagram].

[ident-token-diagram]: https://drafts.csswg.org/css-syntax-3/#ident-token-diagram

The sequence `--` was previously parsed as multiple applications of the `-`
operator. Since this is unlikely to be used intentionally in practice, we
consider this bug fix safe.

### Command-Line Interface

* Fix a bug where changes in `.css` files would be ignored in `--watch` mode.
Expand Down
18 changes: 10 additions & 8 deletions lib/src/parse/parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,18 @@ class Parser {
@protected
String identifier({bool normalize = false, bool unit = false}) {
// NOTE: this logic is largely duplicated in
// StylesheetParser._interpolatedIdentifier and isIdentifier in utils.dart.
// Most changes here should be mirrored there.
// StylesheetParser.interpolatedIdentifier. Most changes here should be
// mirrored there.

var text = StringBuffer();
while (scanner.scanChar($dash)) {
if (scanner.scanChar($dash)) {
text.writeCharCode($dash);

if (scanner.scanChar($dash)) {
text.writeCharCode($dash);
_identifierBody(text, normalize: normalize, unit: unit);
return text.toString();
}
}

var first = scanner.peekChar();
Expand Down Expand Up @@ -580,11 +586,7 @@ class Parser {

var second = scanner.peekChar(forward + 1);
if (second == null) return false;
if (isNameStart(second) || second == $backslash) return true;
if (second != $dash) return false;

var third = scanner.peekChar(forward + 2);
return third != null && isNameStart(third);
return isNameStart(second) || second == $backslash || second == $dash;
}

/// Returns whether the scanner is immediately before a sequence of characters
Expand Down
26 changes: 15 additions & 11 deletions lib/src/parse/stylesheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2979,8 +2979,14 @@ relase. For details, see http://bit.ly/moz-document.
var start = scanner.state;
var buffer = InterpolationBuffer();

while (scanner.scanChar($dash)) {
if (scanner.scanChar($dash)) {
buffer.writeCharCode($dash);

if (scanner.scanChar($dash)) {
buffer.writeCharCode($dash);
_interpolatedIdentifierBody(buffer);
return buffer.interpolation(scanner.spanFrom(start));
}
}

var first = scanner.peekChar();
Expand All @@ -2996,6 +3002,13 @@ relase. For details, see http://bit.ly/moz-document.
scanner.error("Expected identifier.");
}

_interpolatedIdentifierBody(buffer);
return buffer.interpolation(scanner.spanFrom(start));
}

/// Consumes a chunk of a possibly-interpolated CSS identifier after the name
/// start, and adds the contents to the [buffer] buffer.
void _interpolatedIdentifierBody(InterpolationBuffer buffer) {
while (true) {
var next = scanner.peekChar();
if (next == null) {
Expand All @@ -3013,8 +3026,6 @@ relase. For details, see http://bit.ly/moz-document.
break;
}
}

return buffer.interpolation(scanner.spanFrom(start));
}

/// Consumes interpolation.
Expand Down Expand Up @@ -3261,15 +3272,8 @@ relase. For details, see http://bit.ly/moz-document.
if (first != $dash) return false;
var second = scanner.peekChar(1);
if (second == null) return false;
if (isNameStart(second) || second == $backslash) return true;

if (second == $hash) return scanner.peekChar(2) == $lbrace;
if (second != $dash) return false;

var third = scanner.peekChar(2);
if (third == null) return false;
if (third == $hash) return scanner.peekChar(3) == $lbrace;
return isNameStart(third);
return isNameStart(second) || second == $backslash || second == $dash;
}

/// Returns whether the scanner is immediately before a sequence of characters
Expand Down

0 comments on commit d901bcb

Please sign in to comment.