mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-21 21:31:11 +01:00
@content support.
This commit is contained in:
parent
3b3e3046fd
commit
0573fa7709
@ -24,6 +24,7 @@ export 'sass/node.dart';
|
||||
export 'sass/statement.dart';
|
||||
export 'sass/statement/at_rule.dart';
|
||||
export 'sass/statement/comment.dart';
|
||||
export 'sass/statement/content.dart';
|
||||
export 'sass/statement/declaration.dart';
|
||||
export 'sass/statement/extend_rule.dart';
|
||||
export 'sass/statement/function_declaration.dart';
|
||||
|
19
lib/src/ast/sass/statement/content.dart
Normal file
19
lib/src/ast/sass/statement/content.dart
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2016 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 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../statement.dart';
|
||||
|
||||
class Content implements Statement {
|
||||
final FileSpan span;
|
||||
|
||||
Content({this.span});
|
||||
|
||||
/*=T*/ accept/*<T>*/(StatementVisitor/*<T>*/ visitor) =>
|
||||
visitor.visitContent(this);
|
||||
|
||||
String toString() => "@content;";
|
||||
}
|
@ -10,8 +10,10 @@ import '../argument_declaration.dart';
|
||||
import '../statement.dart';
|
||||
|
||||
class MixinDeclaration extends CallableDeclaration {
|
||||
final bool hasContent;
|
||||
|
||||
MixinDeclaration(String name, ArgumentDeclaration arguments,
|
||||
Iterable<Statement> children, {FileSpan span})
|
||||
Iterable<Statement> children, {this.hasContent: false, FileSpan span})
|
||||
: super(name, arguments, children, span: span);
|
||||
|
||||
/*=T*/ accept/*<T>*/(StatementVisitor/*<T>*/ visitor) =>
|
||||
|
@ -2,6 +2,7 @@
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'ast/sass.dart';
|
||||
import 'callable.dart';
|
||||
import 'functions.dart';
|
||||
import 'value.dart';
|
||||
@ -24,6 +25,14 @@ class Environment {
|
||||
// Note: this is not necessarily complete
|
||||
final Map<String, int> _mixinIndices;
|
||||
|
||||
/// The content block passed to the lexically-current mixin, if any.
|
||||
List<Statement> get contentBlock => _contentBlock;
|
||||
List<Statement> _contentBlock;
|
||||
|
||||
/// The environment for [_contentBlock].
|
||||
Environment get contentEnvironment => _contentEnvironment;
|
||||
Environment _contentEnvironment;
|
||||
|
||||
Environment()
|
||||
: _variables = [normalizedMap()],
|
||||
_variableIndices = normalizedMap(),
|
||||
@ -35,7 +44,8 @@ class Environment {
|
||||
}
|
||||
|
||||
Environment._(this._variables, this._variableIndices, this._functions,
|
||||
this._functionIndices, this._mixins, this._mixinIndices);
|
||||
this._functionIndices, this._mixins, this._mixinIndices,
|
||||
this._contentBlock, this._contentEnvironment);
|
||||
|
||||
Environment closure() => new Environment._(
|
||||
_variables.toList(),
|
||||
@ -43,7 +53,9 @@ class Environment {
|
||||
_functions.toList(),
|
||||
new Map.from(_functionIndices),
|
||||
_mixins.toList(),
|
||||
new Map.from(_mixinIndices));
|
||||
new Map.from(_mixinIndices),
|
||||
_contentBlock,
|
||||
_contentEnvironment);
|
||||
|
||||
Value getVariable(String name) =>
|
||||
_variables[_variableIndices[name] ?? 0][name];
|
||||
@ -99,6 +111,17 @@ class Environment {
|
||||
_mixins[_mixins.length - 1][callable.name] = callable;
|
||||
}
|
||||
|
||||
void withContent(List<Statement> block, Environment environment,
|
||||
void callback()) {
|
||||
var oldBlock = _contentBlock;
|
||||
var oldEnvironment = _contentEnvironment;
|
||||
_contentBlock = block;
|
||||
_contentEnvironment = environment;
|
||||
callback();
|
||||
_contentBlock = oldBlock;
|
||||
_contentEnvironment = oldEnvironment;
|
||||
}
|
||||
|
||||
/*=T*/ scope/*<T>*/(/*=T*/ callback()) {
|
||||
// TODO: avoid creating a new scope if no variables are declared.
|
||||
_variables.add(normalizedMap());
|
||||
|
@ -23,7 +23,11 @@ final _prefixedSelectorPseudoClasses =
|
||||
class Parser {
|
||||
final SpanScanner _scanner;
|
||||
|
||||
bool _inMixin = false;
|
||||
var _inMixin = false;
|
||||
|
||||
var _inContentBlock = false;
|
||||
|
||||
bool _mixinHasContent;
|
||||
|
||||
Parser(String contents, {url})
|
||||
: _scanner = new SpanScanner(contents, sourceUrl: url);
|
||||
@ -116,6 +120,7 @@ class Parser {
|
||||
_ignoreComments();
|
||||
|
||||
switch (name) {
|
||||
case "content": return _content(start);
|
||||
case "extend":
|
||||
return new ExtendRule(_almostAnyValue(),
|
||||
span: _scanner.spanFrom(start));
|
||||
@ -140,6 +145,19 @@ class Parser {
|
||||
span: _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
Content _content(LineScannerState start) {
|
||||
if (_inMixin) {
|
||||
_mixinHasContent = true;
|
||||
return new Content(span: _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
_scanner.error(
|
||||
"@content is only allowed within mixin declarations.",
|
||||
position: start.position,
|
||||
length: "@content".length);
|
||||
return null;
|
||||
}
|
||||
|
||||
Include _include(LineScannerState start) {
|
||||
var name = _identifier();
|
||||
_ignoreComments();
|
||||
@ -150,9 +168,9 @@ class Parser {
|
||||
|
||||
List<Statement> children;
|
||||
if (_scanner.peekChar() == $lbrace) {
|
||||
_inMixin = true;
|
||||
_inContentBlock = true;
|
||||
children = _ruleChildren();
|
||||
_inMixin = false;
|
||||
_inContentBlock = false;
|
||||
}
|
||||
|
||||
return new Include(name, arguments,
|
||||
@ -166,7 +184,7 @@ class Parser {
|
||||
? _argumentDeclaration()
|
||||
: new ArgumentDeclaration.empty(span: _scanner.emptySpan);
|
||||
|
||||
if (_inMixin) {
|
||||
if (_inMixin || _inContentBlock) {
|
||||
throw new StringScannerException(
|
||||
"Mixins may not contain mixin declarations.",
|
||||
_scanner.spanFrom(start), _scanner.string);
|
||||
@ -174,10 +192,12 @@ class Parser {
|
||||
|
||||
_ignoreComments();
|
||||
_inMixin = true;
|
||||
_mixinHasContent = false;
|
||||
var children = _ruleChildren();
|
||||
_inMixin = false;
|
||||
|
||||
return new MixinDeclaration(name, arguments, children,
|
||||
hasContent: _mixinHasContent,
|
||||
span: _scanner.spanFrom(start));
|
||||
}
|
||||
|
||||
@ -186,7 +206,7 @@ class Parser {
|
||||
_ignoreComments();
|
||||
var arguments = _argumentDeclaration();
|
||||
|
||||
if (_inMixin) {
|
||||
if (_inMixin || _inContentBlock) {
|
||||
throw new StringScannerException(
|
||||
"Mixins may not contain function declarations.",
|
||||
_scanner.spanFrom(start), _scanner.string);
|
||||
|
@ -6,6 +6,7 @@ import '../../ast/sass.dart';
|
||||
|
||||
abstract class StatementVisitor<T> {
|
||||
T visitComment(Comment node) => null;
|
||||
T visitContent(Content node) => null;
|
||||
T visitExtendRule(ExtendRule node) => null;
|
||||
T visitReturn(Return node) => null;
|
||||
T visitVariableDeclaration(VariableDeclaration node) => null;
|
||||
|
@ -64,6 +64,17 @@ class PerformVisitor extends StatementVisitor
|
||||
_parent.addChild(new CssComment(node.text, span: node.span));
|
||||
}
|
||||
|
||||
void visitContent(Content node) {
|
||||
var block = _environment.contentBlock;
|
||||
if (block == null) return;
|
||||
|
||||
_withEnvironment(_environment.contentEnvironment, () {
|
||||
for (var statement in block) {
|
||||
statement.accept(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void visitDeclaration(Declaration node) {
|
||||
var name = _interpolationToValue(node.name);
|
||||
if (_declarationName != null) {
|
||||
@ -131,16 +142,27 @@ class PerformVisitor extends StatementVisitor
|
||||
void visitInclude(Include node) {
|
||||
var mixin = _environment.getMixin(node.name) as UserDefinedCallable;
|
||||
if (mixin == null) throw node.span.message("Undefined mixin.");
|
||||
if (node.children != null) {
|
||||
|
||||
if (node.children != null &&
|
||||
!(mixin.declaration as MixinDeclaration).hasContent) {
|
||||
throw node.span.message("Mixin doesn't accept a content block.");
|
||||
}
|
||||
|
||||
_runUserDefinedCallable(node, mixin, () {
|
||||
Value callback() {
|
||||
for (var statement in mixin.declaration.children) {
|
||||
statement.accept(this);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
if (node.children == null) {
|
||||
_runUserDefinedCallable(node, mixin, callback);
|
||||
} else {
|
||||
var environment = _environment.closure();
|
||||
_runUserDefinedCallable(node, mixin, () {
|
||||
_environment.withContent(node.children, environment, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void visitMixinDeclaration(MixinDeclaration node) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user