mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-21 21:31:11 +01:00
Merge pull request #200 from sass/no-unecessary-scope
Don't create scopes when nothing is declared
This commit is contained in:
commit
6d83c13603
@ -7,22 +7,20 @@ import 'package:source_span/source_span.dart';
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../interpolation.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// An `@at-root` rule.
|
||||
///
|
||||
/// This moves it contents "up" the tree through parent nodes.
|
||||
class AtRootRule implements Statement {
|
||||
class AtRootRule extends ParentStatement {
|
||||
/// The query specifying which statements this should move its contents
|
||||
/// through.
|
||||
final Interpolation query;
|
||||
|
||||
/// The statements contained in [this].
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
AtRootRule(Iterable<Statement> children, this.span, {this.query})
|
||||
: children = new List.from(children);
|
||||
: super(new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitAtRootRule(this);
|
||||
|
||||
|
@ -8,9 +8,10 @@ import '../../../utils.dart';
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../interpolation.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// An unknown at-rule.
|
||||
class AtRule implements Statement {
|
||||
class AtRule extends ParentStatement {
|
||||
/// The name of this rule.
|
||||
final String name;
|
||||
|
||||
@ -20,18 +21,12 @@ class AtRule implements Statement {
|
||||
/// The value of this rule.
|
||||
final Interpolation value;
|
||||
|
||||
/// The children of this rule.
|
||||
///
|
||||
/// If [children] is empty, [this] was declared with empty brackets. If
|
||||
/// [children] is null, it was declared without brackets.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
AtRule(String name, this.span, {this.value, Iterable<Statement> children})
|
||||
: name = name,
|
||||
normalizedName = unvendor(name),
|
||||
children = children == null ? null : new List.unmodifiable(children);
|
||||
super(children == null ? null : new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitAtRule(this);
|
||||
|
||||
|
@ -8,24 +8,21 @@ import '../../../visitor/interface/statement.dart';
|
||||
import '../expression.dart';
|
||||
import '../interpolation.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// A declaration (that is, a `name: value` pair).
|
||||
class Declaration implements Statement {
|
||||
class Declaration extends ParentStatement {
|
||||
/// The name of this declaration.
|
||||
final Interpolation name;
|
||||
|
||||
/// The value of this declaration.
|
||||
final Expression value;
|
||||
|
||||
/// The children of this declaration.
|
||||
///
|
||||
/// This is `null` if the declaration has no children.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
Declaration(this.name, this.span, {this.value, Iterable<Statement> children})
|
||||
: children = children == null ? null : new List.unmodifiable(children);
|
||||
: super(children =
|
||||
children == null ? null : new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitDeclaration(this);
|
||||
|
||||
|
@ -7,26 +7,24 @@ import 'package:source_span/source_span.dart';
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../expression.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// An `@each` rule.
|
||||
///
|
||||
/// This iterates over values in a list or map.
|
||||
class EachRule implements Statement {
|
||||
class EachRule extends ParentStatement {
|
||||
/// The variables assigned for each iteration.
|
||||
final List<String> variables;
|
||||
|
||||
/// The expression whose value this iterates through.
|
||||
final Expression list;
|
||||
|
||||
/// The child statements executed for each iteration.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
EachRule(Iterable<String> variables, this.list, Iterable<Statement> children,
|
||||
this.span)
|
||||
: variables = new List.unmodifiable(variables),
|
||||
children = new List.unmodifiable(children);
|
||||
super(new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitEachRule(this);
|
||||
|
||||
|
@ -7,11 +7,12 @@ import 'package:source_span/source_span.dart';
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../expression.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// A `@for` rule.
|
||||
///
|
||||
/// This iterates a set number of times.
|
||||
class ForRule implements Statement {
|
||||
class ForRule extends ParentStatement {
|
||||
/// The name of the variable that will contain the index value.
|
||||
final String variable;
|
||||
|
||||
@ -24,16 +25,13 @@ class ForRule implements Statement {
|
||||
/// Whether [to] is exclusive.
|
||||
final bool isExclusive;
|
||||
|
||||
/// The child statements executed for each iteration.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
ForRule(this.variable, this.from, this.to, Iterable<Statement> children,
|
||||
this.span,
|
||||
{bool exclusive: true})
|
||||
: children = new List.unmodifiable(children),
|
||||
isExclusive = exclusive;
|
||||
: isExclusive = exclusive,
|
||||
super(new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitForRule(this);
|
||||
|
||||
|
@ -3,11 +3,13 @@
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../expression.dart';
|
||||
import '../statement.dart';
|
||||
import 'function_rule.dart';
|
||||
import 'mixin_rule.dart';
|
||||
import 'variable_declaration.dart';
|
||||
|
||||
/// An `@if` rule.
|
||||
///
|
||||
@ -18,30 +20,58 @@ class IfRule implements Statement {
|
||||
/// The first clause whose expression evaluates to `true` will have its
|
||||
/// statements executed. If no expression evaluates to `true`, `lastClause`
|
||||
/// will be executed if it's not `null`.
|
||||
final List<Tuple2<Expression, List<Statement>>> clauses;
|
||||
final List<IfClause> clauses;
|
||||
|
||||
/// The final, unconditional `@else` clause.
|
||||
///
|
||||
/// This is `null` if there is no unconditional `@else`.
|
||||
final List<Statement> lastClause;
|
||||
final IfClause lastClause;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
IfRule(Iterable<Tuple2<Expression, Iterable<Statement>>> clauses, this.span,
|
||||
{Iterable<Statement> lastClause})
|
||||
: clauses = new List.unmodifiable(clauses.map((pair) =>
|
||||
new Tuple2(pair.item1, new List.unmodifiable(pair.item2)))),
|
||||
lastClause =
|
||||
lastClause == null ? null : new List.unmodifiable(lastClause);
|
||||
IfRule(Iterable<IfClause> clauses, this.span, {this.lastClause})
|
||||
: clauses = new List.unmodifiable(clauses) {
|
||||
assert(clauses.every((clause) => clause.expression != null));
|
||||
assert(lastClause?.expression == null);
|
||||
}
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitIfRule(this);
|
||||
|
||||
String toString() {
|
||||
var first = true;
|
||||
return clauses.map((pair) {
|
||||
return clauses.map((clause) {
|
||||
var name = first ? 'if' : 'else';
|
||||
first = false;
|
||||
return '@$name ${pair.item1} {${pair.item2.join(" ")}}';
|
||||
return '@$name ${clause.expression} {${clause.children.join(" ")}}';
|
||||
}).join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
/// A single clause in an `@if` rule.
|
||||
class IfClause {
|
||||
/// The expression to evaluate to determine whether to run this rule, or
|
||||
/// `null` if this is the final unconditional `@else` clause.
|
||||
final Expression expression;
|
||||
|
||||
/// The statements to evaluate if this clause matches.
|
||||
final List<Statement> children;
|
||||
|
||||
/// Whether any of [children] is a variable, function, or mixin declaration.
|
||||
final bool hasDeclarations;
|
||||
|
||||
IfClause(Expression expression, Iterable<Statement> children)
|
||||
: this._(expression, new List.unmodifiable(children));
|
||||
|
||||
IfClause.last(Iterable<Statement> children)
|
||||
: this._(null, new List.unmodifiable(children));
|
||||
|
||||
IfClause._(this.expression, this.children)
|
||||
: hasDeclarations = children.any((child) =>
|
||||
child is VariableDeclaration ||
|
||||
child is FunctionRule ||
|
||||
child is MixinRule);
|
||||
|
||||
String toString() =>
|
||||
(expression == null ? "@else" : "@if $expression") +
|
||||
" {${children.join(' ')}}";
|
||||
}
|
||||
|
@ -7,21 +7,19 @@ import 'package:source_span/source_span.dart';
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../interpolation.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// A `@media` rule.
|
||||
class MediaRule implements Statement {
|
||||
class MediaRule extends ParentStatement {
|
||||
/// The query that determines on which platforms the styles will be in effect.
|
||||
///
|
||||
/// This is only parsed after the interpolation has been resolved.
|
||||
final Interpolation query;
|
||||
|
||||
/// The contents of this rule.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
MediaRule(this.query, Iterable<Statement> children, this.span)
|
||||
: children = new List.unmodifiable(children);
|
||||
: super(new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitMediaRule(this);
|
||||
|
||||
|
24
lib/src/ast/sass/statement/parent.dart
Normal file
24
lib/src/ast/sass/statement/parent.dart
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2017 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import '../statement.dart';
|
||||
import 'function_rule.dart';
|
||||
import 'mixin_rule.dart';
|
||||
import 'variable_declaration.dart';
|
||||
|
||||
/// A [Statement] that can have child statements.
|
||||
abstract class ParentStatement implements Statement {
|
||||
/// The child statements of this statement.
|
||||
final List<Statement> children;
|
||||
|
||||
/// Whether any of [children] is a variable, function, or mixin declaration.
|
||||
final bool hasDeclarations;
|
||||
|
||||
ParentStatement(this.children)
|
||||
: hasDeclarations = children?.any((child) =>
|
||||
child is VariableDeclaration ||
|
||||
child is FunctionRule ||
|
||||
child is MixinRule) ??
|
||||
false;
|
||||
}
|
@ -7,23 +7,21 @@ import 'package:source_span/source_span.dart';
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../interpolation.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// A style rule.
|
||||
///
|
||||
/// This applies style declarations to elements that match a given selector.
|
||||
class StyleRule implements Statement {
|
||||
class StyleRule extends ParentStatement {
|
||||
/// The selector to which the declaration will be applied.
|
||||
///
|
||||
/// This is only parsed after the interpolation has been resolved.
|
||||
final Interpolation selector;
|
||||
|
||||
/// The declarations associated with the selector.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
StyleRule(this.selector, Iterable<Statement> children, this.span)
|
||||
: children = new List.unmodifiable(children);
|
||||
: super(new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitStyleRule(this);
|
||||
|
||||
|
@ -8,18 +8,16 @@ import '../../../visitor/interface/statement.dart';
|
||||
import '../../../parse/sass.dart';
|
||||
import '../../../parse/scss.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// A Sass stylesheet.
|
||||
///
|
||||
/// This is the root Sass node. It contains top-level statements.
|
||||
class Stylesheet implements Statement {
|
||||
/// The top-level statements of this Sass stylesheet.
|
||||
final List<Statement> children;
|
||||
|
||||
class Stylesheet extends ParentStatement {
|
||||
final FileSpan span;
|
||||
|
||||
Stylesheet(Iterable<Statement> children, this.span)
|
||||
: children = new List.unmodifiable(children);
|
||||
: super(new List.unmodifiable(children));
|
||||
|
||||
/// Parses an indented-syntax stylesheet from [contents].
|
||||
///
|
||||
|
@ -7,19 +7,17 @@ import 'package:source_span/source_span.dart';
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../statement.dart';
|
||||
import '../supports_condition.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// A `@supports` rule.
|
||||
class SupportsRule implements Statement {
|
||||
class SupportsRule extends ParentStatement {
|
||||
/// The condition that selects what browsers this rule targets.
|
||||
final SupportsCondition condition;
|
||||
|
||||
/// The contents of this rule.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
SupportsRule(this.condition, Iterable<Statement> children, this.span)
|
||||
: children = new List.from(children);
|
||||
: super(new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitSupportsRule(this);
|
||||
|
||||
|
@ -7,22 +7,20 @@ import 'package:source_span/source_span.dart';
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../expression.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// A `@while` rule.
|
||||
///
|
||||
/// This repeatedly executes a block of code as long as a statement evaluates to
|
||||
/// `true`.
|
||||
class WhileRule implements Statement {
|
||||
class WhileRule extends ParentStatement {
|
||||
/// The condition that determines whether the block will be executed.
|
||||
final Expression condition;
|
||||
|
||||
/// The code to execute repeatedly.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
WhileRule(this.condition, Iterable<Statement> children, this.span)
|
||||
: children = new List.unmodifiable(children);
|
||||
: super(new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitWhileRule(this);
|
||||
|
||||
|
@ -82,11 +82,11 @@ class AsyncEnvironment {
|
||||
bool get inMixin => _inMixin;
|
||||
var _inMixin = false;
|
||||
|
||||
/// Whether the environment is currently in a semi-global scope.
|
||||
/// Whether the environment is currently in a global or semi-global scope.
|
||||
///
|
||||
/// A semi-global scope can assign to global variables, but it doesn't declare
|
||||
/// them by default.
|
||||
var _inSemiGlobalScope = false;
|
||||
var _inSemiGlobalScope = true;
|
||||
|
||||
AsyncEnvironment()
|
||||
: _variables = [normalizedMap()],
|
||||
@ -279,10 +279,32 @@ class AsyncEnvironment {
|
||||
/// Variables, functions, and mixins declared in a given scope are
|
||||
/// inaccessible outside of it. If [semiGlobal] is passed, this scope can
|
||||
/// assign to global variables without a `!global` declaration.
|
||||
Future<T> scope<T>(Future<T> callback(), {bool semiGlobal: false}) async {
|
||||
semiGlobal = semiGlobal && (_inSemiGlobalScope || _variables.length == 1);
|
||||
///
|
||||
/// If [when] is false, this doesn't create a new scope and instead just
|
||||
/// executes [callback] and returns its result.
|
||||
Future<T> scope<T>(Future<T> callback(),
|
||||
{bool semiGlobal: false, bool when: true}) async {
|
||||
if (!when) {
|
||||
// We still have to track semi-globalness so that
|
||||
//
|
||||
// div {
|
||||
// @if ... {
|
||||
// $x: y;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// doesn't assign to the global scope.
|
||||
var wasInSemiGlobalScope = _inSemiGlobalScope;
|
||||
_inSemiGlobalScope = semiGlobal;
|
||||
try {
|
||||
return await callback();
|
||||
} finally {
|
||||
_inSemiGlobalScope = wasInSemiGlobalScope;
|
||||
}
|
||||
}
|
||||
|
||||
semiGlobal = semiGlobal && _inSemiGlobalScope;
|
||||
|
||||
// TODO: avoid creating a new scope if no variables are declared.
|
||||
var wasInSemiGlobalScope = _inSemiGlobalScope;
|
||||
_inSemiGlobalScope = semiGlobal;
|
||||
_variables.add(normalizedMap());
|
||||
|
@ -5,7 +5,7 @@
|
||||
// DO NOT EDIT. This file was generated from async_environment.dart.
|
||||
// See tool/synchronize.dart for details.
|
||||
//
|
||||
// Checksum: 97410abbd78c3bbc9899f3ac460cc0736218bfe3
|
||||
// Checksum: 4669f41a70664bd5f391c6b8627264a5d0ad8f6c
|
||||
|
||||
import 'ast/sass.dart';
|
||||
import 'callable.dart';
|
||||
@ -85,11 +85,11 @@ class Environment {
|
||||
bool get inMixin => _inMixin;
|
||||
var _inMixin = false;
|
||||
|
||||
/// Whether the environment is currently in a semi-global scope.
|
||||
/// Whether the environment is currently in a global or semi-global scope.
|
||||
///
|
||||
/// A semi-global scope can assign to global variables, but it doesn't declare
|
||||
/// them by default.
|
||||
var _inSemiGlobalScope = false;
|
||||
var _inSemiGlobalScope = true;
|
||||
|
||||
Environment()
|
||||
: _variables = [normalizedMap()],
|
||||
@ -282,10 +282,31 @@ class Environment {
|
||||
/// Variables, functions, and mixins declared in a given scope are
|
||||
/// inaccessible outside of it. If [semiGlobal] is passed, this scope can
|
||||
/// assign to global variables without a `!global` declaration.
|
||||
T scope<T>(T callback(), {bool semiGlobal: false}) {
|
||||
semiGlobal = semiGlobal && (_inSemiGlobalScope || _variables.length == 1);
|
||||
///
|
||||
/// If [when] is false, this doesn't create a new scope and instead just
|
||||
/// executes [callback] and returns its result.
|
||||
T scope<T>(T callback(), {bool semiGlobal: false, bool when: true}) {
|
||||
if (!when) {
|
||||
// We still have to track semi-globalness so that
|
||||
//
|
||||
// div {
|
||||
// @if ... {
|
||||
// $x: y;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// doesn't assign to the global scope.
|
||||
var wasInSemiGlobalScope = _inSemiGlobalScope;
|
||||
_inSemiGlobalScope = semiGlobal;
|
||||
try {
|
||||
return callback();
|
||||
} finally {
|
||||
_inSemiGlobalScope = wasInSemiGlobalScope;
|
||||
}
|
||||
}
|
||||
|
||||
semiGlobal = semiGlobal && _inSemiGlobalScope;
|
||||
|
||||
// TODO: avoid creating a new scope if no variables are declared.
|
||||
var wasInSemiGlobalScope = _inSemiGlobalScope;
|
||||
_inSemiGlobalScope = semiGlobal;
|
||||
_variables.add(normalizedMap());
|
||||
|
@ -671,16 +671,16 @@ abstract class StylesheetParser extends Parser {
|
||||
var expression = _expression();
|
||||
var children = this.children(child);
|
||||
|
||||
var clauses = [new Tuple2(expression, children)];
|
||||
List<Statement> lastClause;
|
||||
var clauses = [new IfClause(expression, children)];
|
||||
IfClause lastClause;
|
||||
|
||||
while (scanElse(ifIndentation)) {
|
||||
whitespace();
|
||||
if (scanIdentifier("if")) {
|
||||
whitespace();
|
||||
clauses.add(new Tuple2(_expression(), this.children(child)));
|
||||
clauses.add(new IfClause(_expression(), this.children(child)));
|
||||
} else {
|
||||
lastClause = this.children(child);
|
||||
lastClause = new IfClause.last(this.children(child));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -327,7 +327,7 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
await child.accept(this);
|
||||
}
|
||||
});
|
||||
}, when: node.hasDeclarations);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -341,7 +341,7 @@ class _EvaluateVisitor
|
||||
}
|
||||
|
||||
if (outerCopy != null) root.addChild(outerCopy);
|
||||
await _scopeForAtRoot(innerCopy ?? root, query, included)(() async {
|
||||
await _scopeForAtRoot(node, innerCopy ?? root, query, included)(() async {
|
||||
for (var child in node.children) {
|
||||
await child.accept(this);
|
||||
}
|
||||
@ -386,14 +386,14 @@ class _EvaluateVisitor
|
||||
/// This returns a callback that adjusts various instance variables for its
|
||||
/// duration, based on which rules are excluded by [query]. It always assigns
|
||||
/// [_parent] to [newParent].
|
||||
_ScopeCallback _scopeForAtRoot(CssParentNode newParent, AtRootQuery query,
|
||||
List<CssParentNode> included) {
|
||||
_ScopeCallback _scopeForAtRoot(AtRootRule node, CssParentNode newParent,
|
||||
AtRootQuery query, List<CssParentNode> included) {
|
||||
var scope = (Future callback()) async {
|
||||
// We can't use [_withParent] here because it'll add the node to the tree
|
||||
// in the wrong place.
|
||||
var oldParent = _parent;
|
||||
_parent = newParent;
|
||||
await _environment.scope(callback);
|
||||
await _environment.scope(callback, when: node.hasDeclarations);
|
||||
_parent = oldParent;
|
||||
};
|
||||
|
||||
@ -488,7 +488,7 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
await child.accept(this);
|
||||
}
|
||||
});
|
||||
}, when: node.hasDeclarations);
|
||||
_declarationName = oldDeclarationName;
|
||||
}
|
||||
|
||||
@ -588,9 +588,11 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
await child.accept(this);
|
||||
}
|
||||
});
|
||||
}, scopeWhen: false);
|
||||
}
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
|
||||
_inUnknownAtRule = wasInUnknownAtRule;
|
||||
_inKeyframes = wasInKeyframes;
|
||||
@ -634,17 +636,19 @@ class _EvaluateVisitor
|
||||
|
||||
Future<Value> visitIfRule(IfRule node) async {
|
||||
var clause = node.lastClause;
|
||||
for (var pair in node.clauses) {
|
||||
if ((await pair.item1.accept(this)).isTruthy) {
|
||||
clause = pair.item2;
|
||||
for (var clauseToCheck in node.clauses) {
|
||||
if ((await clauseToCheck.expression.accept(this)).isTruthy) {
|
||||
clause = clauseToCheck;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clause == null) return null;
|
||||
|
||||
return await _environment.scope(
|
||||
() => _handleReturn<Statement>(clause, (child) => child.accept(this)),
|
||||
semiGlobal: true);
|
||||
() => _handleReturn<Statement>(
|
||||
clause.children, (child) => child.accept(this)),
|
||||
semiGlobal: true,
|
||||
when: clause.hasDeclarations);
|
||||
}
|
||||
|
||||
Future<Value> visitImportRule(ImportRule node) async {
|
||||
@ -877,10 +881,12 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
await child.accept(this);
|
||||
}
|
||||
});
|
||||
}, scopeWhen: false);
|
||||
}
|
||||
});
|
||||
}, through: (node) => node is CssStyleRule || node is CssMediaRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule || node is CssMediaRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -930,7 +936,9 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
await child.accept(this);
|
||||
}
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -954,7 +962,9 @@ class _EvaluateVisitor
|
||||
await child.accept(this);
|
||||
}
|
||||
});
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
_atRootExcludingStyleRule = oldAtRootExcludingStyleRule;
|
||||
|
||||
if (!_inStyleRule) {
|
||||
@ -991,7 +1001,9 @@ class _EvaluateVisitor
|
||||
}
|
||||
});
|
||||
}
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -1067,7 +1079,7 @@ class _EvaluateVisitor
|
||||
if (result != null) return result;
|
||||
}
|
||||
return null;
|
||||
}, semiGlobal: true);
|
||||
}, semiGlobal: true, when: node.hasDeclarations);
|
||||
}
|
||||
|
||||
// ## Expressions
|
||||
@ -1249,9 +1261,6 @@ class _EvaluateVisitor
|
||||
_verifyArguments(
|
||||
positional.length, named, callable.declaration.arguments, span);
|
||||
|
||||
// TODO: if we get here and there are no rest params involved, mark
|
||||
// the callable as fast-path and don't do error checking or extra
|
||||
// allocations for future calls.
|
||||
var declaredArguments = callable.declaration.arguments.arguments;
|
||||
var minLength = math.min(positional.length, declaredArguments.length);
|
||||
for (var i = 0; i < minLength; i++) {
|
||||
@ -1632,9 +1641,11 @@ class _EvaluateVisitor
|
||||
/// If [through] is passed, [node] is added as a child of the first parent for
|
||||
/// which [through] returns `false`. That parent is copied unless it's the
|
||||
/// lattermost child of its parent.
|
||||
///
|
||||
/// Runs [callback] in a new environment scope unless [scopeWhen] is false.
|
||||
Future<T> _withParent<S extends CssParentNode, T>(
|
||||
S node, Future<T> callback(),
|
||||
{bool through(CssNode node)}) async {
|
||||
{bool through(CssNode node), bool scopeWhen: true}) async {
|
||||
var oldParent = _parent;
|
||||
|
||||
// Go up through parents that match [through].
|
||||
@ -1656,7 +1667,7 @@ class _EvaluateVisitor
|
||||
|
||||
parent.addChild(node);
|
||||
_parent = node;
|
||||
var result = await _environment.scope(callback);
|
||||
var result = await _environment.scope(callback, when: scopeWhen);
|
||||
_parent = oldParent;
|
||||
|
||||
return result;
|
||||
|
@ -5,7 +5,7 @@
|
||||
// DO NOT EDIT. This file was generated from async_evaluate.dart.
|
||||
// See tool/synchronize.dart for details.
|
||||
//
|
||||
// Checksum: ae64ba442752642066f0e9e038f5f2c1bbfa866b
|
||||
// Checksum: cdeeec2634c97638aef571043c0be1e99f22d7d5
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
@ -328,7 +328,7 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
});
|
||||
}, when: node.hasDeclarations);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -342,7 +342,7 @@ class _EvaluateVisitor
|
||||
}
|
||||
|
||||
if (outerCopy != null) root.addChild(outerCopy);
|
||||
_scopeForAtRoot(innerCopy ?? root, query, included)(() {
|
||||
_scopeForAtRoot(node, innerCopy ?? root, query, included)(() {
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
@ -387,14 +387,14 @@ class _EvaluateVisitor
|
||||
/// This returns a callback that adjusts various instance variables for its
|
||||
/// duration, based on which rules are excluded by [query]. It always assigns
|
||||
/// [_parent] to [newParent].
|
||||
_ScopeCallback _scopeForAtRoot(CssParentNode newParent, AtRootQuery query,
|
||||
List<CssParentNode> included) {
|
||||
_ScopeCallback _scopeForAtRoot(AtRootRule node, CssParentNode newParent,
|
||||
AtRootQuery query, List<CssParentNode> included) {
|
||||
var scope = (void callback()) {
|
||||
// We can't use [_withParent] here because it'll add the node to the tree
|
||||
// in the wrong place.
|
||||
var oldParent = _parent;
|
||||
_parent = newParent;
|
||||
_environment.scope(callback);
|
||||
_environment.scope(callback, when: node.hasDeclarations);
|
||||
_parent = oldParent;
|
||||
};
|
||||
|
||||
@ -487,7 +487,7 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
});
|
||||
}, when: node.hasDeclarations);
|
||||
_declarationName = oldDeclarationName;
|
||||
}
|
||||
|
||||
@ -583,9 +583,11 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
});
|
||||
}, scopeWhen: false);
|
||||
}
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
|
||||
_inUnknownAtRule = wasInUnknownAtRule;
|
||||
_inKeyframes = wasInKeyframes;
|
||||
@ -629,17 +631,19 @@ class _EvaluateVisitor
|
||||
|
||||
Value visitIfRule(IfRule node) {
|
||||
var clause = node.lastClause;
|
||||
for (var pair in node.clauses) {
|
||||
if (pair.item1.accept(this).isTruthy) {
|
||||
clause = pair.item2;
|
||||
for (var clauseToCheck in node.clauses) {
|
||||
if (clauseToCheck.expression.accept(this).isTruthy) {
|
||||
clause = clauseToCheck;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clause == null) return null;
|
||||
|
||||
return _environment.scope(
|
||||
() => _handleReturn<Statement>(clause, (child) => child.accept(this)),
|
||||
semiGlobal: true);
|
||||
() => _handleReturn<Statement>(
|
||||
clause.children, (child) => child.accept(this)),
|
||||
semiGlobal: true,
|
||||
when: clause.hasDeclarations);
|
||||
}
|
||||
|
||||
Value visitImportRule(ImportRule node) {
|
||||
@ -871,10 +875,12 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
});
|
||||
}, scopeWhen: false);
|
||||
}
|
||||
});
|
||||
}, through: (node) => node is CssStyleRule || node is CssMediaRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule || node is CssMediaRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -921,7 +927,9 @@ class _EvaluateVisitor
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -945,7 +953,9 @@ class _EvaluateVisitor
|
||||
child.accept(this);
|
||||
}
|
||||
});
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
_atRootExcludingStyleRule = oldAtRootExcludingStyleRule;
|
||||
|
||||
if (!_inStyleRule) {
|
||||
@ -982,7 +992,9 @@ class _EvaluateVisitor
|
||||
}
|
||||
});
|
||||
}
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
},
|
||||
through: (node) => node is CssStyleRule,
|
||||
scopeWhen: node.hasDeclarations);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -1057,7 +1069,7 @@ class _EvaluateVisitor
|
||||
if (result != null) return result;
|
||||
}
|
||||
return null;
|
||||
}, semiGlobal: true);
|
||||
}, semiGlobal: true, when: node.hasDeclarations);
|
||||
}
|
||||
|
||||
// ## Expressions
|
||||
@ -1230,9 +1242,6 @@ class _EvaluateVisitor
|
||||
_verifyArguments(
|
||||
positional.length, named, callable.declaration.arguments, span);
|
||||
|
||||
// TODO: if we get here and there are no rest params involved, mark
|
||||
// the callable as fast-path and don't do error checking or extra
|
||||
// allocations for future calls.
|
||||
var declaredArguments = callable.declaration.arguments.arguments;
|
||||
var minLength = math.min(positional.length, declaredArguments.length);
|
||||
for (var i = 0; i < minLength; i++) {
|
||||
@ -1603,8 +1612,10 @@ class _EvaluateVisitor
|
||||
/// If [through] is passed, [node] is added as a child of the first parent for
|
||||
/// which [through] returns `false`. That parent is copied unless it's the
|
||||
/// lattermost child of its parent.
|
||||
///
|
||||
/// Runs [callback] in a new environment scope unless [scopeWhen] is false.
|
||||
T _withParent<S extends CssParentNode, T>(S node, T callback(),
|
||||
{bool through(CssNode node)}) {
|
||||
{bool through(CssNode node), bool scopeWhen: true}) {
|
||||
var oldParent = _parent;
|
||||
|
||||
// Go up through parents that match [through].
|
||||
@ -1626,7 +1637,7 @@ class _EvaluateVisitor
|
||||
|
||||
parent.addChild(node);
|
||||
_parent = node;
|
||||
var result = _environment.scope(callback);
|
||||
var result = _environment.scope(callback, when: scopeWhen);
|
||||
_parent = oldParent;
|
||||
|
||||
return result;
|
||||
|
Loading…
x
Reference in New Issue
Block a user