mirror of
https://github.com/danog/dart-sass.git
synced 2024-11-30 04:39:03 +01:00
Nested properties.
This commit is contained in:
parent
dbf2c18ae6
commit
cf086471c6
@ -4,7 +4,6 @@
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../utils.dart';
|
||||
import '../../visitor/interface/statement.dart';
|
||||
import 'expression.dart';
|
||||
import 'expression/interpolation.dart';
|
||||
@ -15,9 +14,12 @@ class Declaration implements Statement {
|
||||
|
||||
final Expression value;
|
||||
|
||||
FileSpan get span => spanForList([name, value]);
|
||||
final List<Statement> children;
|
||||
|
||||
Declaration(this.name, this.value);
|
||||
final FileSpan span;
|
||||
|
||||
Declaration(this.name, {this.value, Iterable<Statement> children, this.span})
|
||||
: children = children == null ? null : new List.unmodifiable(children);
|
||||
|
||||
/*=T*/ accept/*<T>*/(StatementVisitor/*<T>*/ visitor) =>
|
||||
visitor.visitDeclaration(this);
|
||||
|
@ -182,7 +182,6 @@ class Parser {
|
||||
new InterpolationExpression([], span: _scanner.emptySpan));
|
||||
}
|
||||
|
||||
// TODO: parse static values specially?
|
||||
return _expression();
|
||||
}
|
||||
|
||||
@ -234,7 +233,7 @@ class Parser {
|
||||
/// attempted; or it can return a [Declaration], indicating that it
|
||||
/// successfully consumed a declaration.
|
||||
dynamic _declarationOrBuffer() {
|
||||
var nameStart = _scanner.state;
|
||||
var start = _scanner.state;
|
||||
var nameBuffer = new InterpolationBuffer();
|
||||
|
||||
// Allow the "*prop: val", ":prop: val", "#prop: val", and ".prop: val"
|
||||
@ -256,14 +255,14 @@ class Parser {
|
||||
midBuffer.writeCharCode($colon);
|
||||
|
||||
// Parse custom properties as declarations no matter what.
|
||||
var name = nameBuffer.interpolation(_scanner.spanFrom(nameStart));
|
||||
var name = nameBuffer.interpolation(_scanner.spanFrom(start));
|
||||
if (name.initialPlain.startsWith('--')) {
|
||||
var value = _declarationValue();
|
||||
var next = _scanner.peekChar();
|
||||
if (next != $semicolon && next != $rbrace) {
|
||||
_scanner.expectChar($semicolon);
|
||||
}
|
||||
return new Declaration(name, value);
|
||||
return new Declaration(name, value: value);
|
||||
}
|
||||
|
||||
if (_scanner.scanChar($colon)) {
|
||||
@ -271,6 +270,12 @@ class Parser {
|
||||
}
|
||||
|
||||
var postColonWhitespace = _commentText();
|
||||
if (_scanner.peekChar() == $lbrace) {
|
||||
return new Declaration(name,
|
||||
children: _declarationChildren(),
|
||||
span: _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
midBuffer.write(postColonWhitespace);
|
||||
var couldBeSelector =
|
||||
postColonWhitespace.isEmpty && _lookingAtInterpolatedIdentifier();
|
||||
@ -283,7 +288,7 @@ class Parser {
|
||||
// Properties that are ambiguous with selectors can't have additional
|
||||
// properties nested beneath them, so we force an error.
|
||||
if (couldBeSelector) _scanner.expectChar($semicolon);
|
||||
} else if (next != $semicolon && next != $rbrace) {
|
||||
} else if (next != $semicolon && next != $lbrace && next != $rbrace) {
|
||||
// Force an exception if there isn't a valid end-of-property character
|
||||
// but don't consume that character.
|
||||
_scanner.expectChar($semicolon);
|
||||
@ -301,9 +306,68 @@ class Parser {
|
||||
return nameBuffer;
|
||||
}
|
||||
|
||||
return new Declaration(
|
||||
name,
|
||||
value: value,
|
||||
children: _scanner.peekChar() == $lbrace
|
||||
? _declarationChildren()
|
||||
: null,
|
||||
span: _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
List<Statement> _declarationChildren() {
|
||||
_scanner.expectChar($lbrace);
|
||||
var children = <Statement>[];
|
||||
loop: while (true) {
|
||||
children.addAll(_comments());
|
||||
switch (_scanner.peekChar()) {
|
||||
case $dollar:
|
||||
children.add(_variableDeclaration());
|
||||
break;
|
||||
|
||||
case $at:
|
||||
children.add(_atRule());
|
||||
break;
|
||||
|
||||
case $semicolon:
|
||||
_scanner.readChar();
|
||||
break;
|
||||
|
||||
case $rbrace:
|
||||
break loop;
|
||||
|
||||
default:
|
||||
children.add(_declaration());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
children.addAll(_comments());
|
||||
_scanner.expectChar($rbrace);
|
||||
return children;
|
||||
}
|
||||
|
||||
Declaration _declaration() {
|
||||
var start = _scanner.state;
|
||||
var name = _interpolatedIdentifier();
|
||||
_ignoreComments();
|
||||
// TODO: nested properties
|
||||
return new Declaration(name, value);
|
||||
_scanner.expectChar($colon);
|
||||
_ignoreComments();
|
||||
|
||||
if (_scanner.peekChar() == $lbrace) {
|
||||
return new Declaration(name,
|
||||
children: _declarationChildren(),
|
||||
span: _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
var value = _declarationExpression();
|
||||
return new Declaration(
|
||||
name,
|
||||
value: value,
|
||||
children: _scanner.peekChar() == $lbrace
|
||||
? _declarationChildren()
|
||||
: null,
|
||||
span: _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
/// Consumes whitespace if available and returns any comments it contained.
|
||||
|
@ -6,10 +6,17 @@ import '../../ast/sass/statement.dart';
|
||||
|
||||
abstract class StatementVisitor<T> {
|
||||
T visitComment(Comment node) => null;
|
||||
T visitDeclaration(Declaration node) => null;
|
||||
T visitExtendRule(ExtendRule node) => null;
|
||||
T visitVariableDeclaration(VariableDeclaration node) => null;
|
||||
|
||||
T visitDeclaration(Declaration node) {
|
||||
if (node.children == null) return null;
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitAtRule(AtRule node) {
|
||||
if (node.children == null) return null;
|
||||
for (var child in node.children) {
|
||||
|
@ -29,6 +29,9 @@ class PerformVisitor extends StatementVisitor {
|
||||
/// The current parent node in the output CSS tree.
|
||||
CssParentNode _parent;
|
||||
|
||||
/// The name of the current declaration parent.
|
||||
String _declarationName;
|
||||
|
||||
final _extender = new Extender();
|
||||
|
||||
PerformVisitor() : this._(new Environment());
|
||||
@ -53,17 +56,24 @@ class PerformVisitor extends StatementVisitor {
|
||||
|
||||
void visitDeclaration(Declaration node) {
|
||||
var name = _performInterpolation(node.name);
|
||||
var cssValue = _performExpression(node.value);
|
||||
var value = cssValue.value;
|
||||
if (_declarationName != null) {
|
||||
name = new CssValue("$_declarationName-${name.value}", span: name.span);
|
||||
}
|
||||
var cssValue = node.value == null ? null : _performExpression(node.value);
|
||||
|
||||
// Don't abort for an empty list because converting it to CSS will throw an
|
||||
// error that we want to user to see.
|
||||
if (value.isBlank &&
|
||||
!(value is SassList && value.contents.isEmpty)) {
|
||||
return;
|
||||
// If the value is an empty list, preserve it, because converting it to CSS
|
||||
// will throw an error that we want the user to see.
|
||||
if (cssValue != null &&
|
||||
(!cssValue.value.isBlank || cssValue.value is SassList)) {
|
||||
_parent.addChild(new CssDeclaration(name, cssValue, span: node.span));
|
||||
}
|
||||
|
||||
_parent.addChild(new CssDeclaration(name, cssValue, span: node.span));
|
||||
if (node.children != null) {
|
||||
var oldDeclarationName = _declarationName;
|
||||
_declarationName = name.value;
|
||||
super.visitDeclaration(node);
|
||||
_declarationName = oldDeclarationName;
|
||||
}
|
||||
}
|
||||
|
||||
void visitExtendRule(ExtendRule node) {
|
||||
|
Loading…
Reference in New Issue
Block a user