mirror of
https://github.com/danog/dart-sass.git
synced 2024-12-04 18:47:56 +01:00
Finish API docs.
This commit is contained in:
parent
38f15a96f3
commit
d276bfb206
@ -145,13 +145,23 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (outerCopy != null) root.addChild(outerCopy);
|
if (outerCopy != null) root.addChild(outerCopy);
|
||||||
_scopeForAtRule(innerCopy ?? root, query)(() {
|
_scopeForAtRoot(innerCopy ?? root, query)(() {
|
||||||
for (var child in node.children) {
|
for (var child in node.children) {
|
||||||
child.accept(this);
|
child.accept(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destructively trims a trailing sublist that matches the current list of
|
||||||
|
/// parents from [nodes].
|
||||||
|
///
|
||||||
|
/// [nodes] should be a list of parents included by an `@at-root` rule, from
|
||||||
|
/// innermost to outermost. If it contains a trailing sublist that's
|
||||||
|
/// contiguous—meaning that each node is a direct parent of the node before
|
||||||
|
/// it—and whose final node is a direct child of [_root], this removes that
|
||||||
|
/// sublist and returns the innermost removed parent.
|
||||||
|
///
|
||||||
|
/// Otherwise, this leaves [nodes] as-is and returns [_root].
|
||||||
CssParentNode _trimIncluded(List<CssParentNode> nodes) {
|
CssParentNode _trimIncluded(List<CssParentNode> nodes) {
|
||||||
var parent = _parent;
|
var parent = _parent;
|
||||||
int innermostContiguous;
|
int innermostContiguous;
|
||||||
@ -171,7 +181,12 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ScopeCallback _scopeForAtRule(CssNode newParent, AtRootQuery query) {
|
/// Returns a [_ScopeCallback] for [query].
|
||||||
|
///
|
||||||
|
/// 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(CssNode newParent, AtRootQuery query) {
|
||||||
var scope = (callback()) {
|
var scope = (callback()) {
|
||||||
// We can't use [_withParent] here because it'll add the node to the tree
|
// We can't use [_withParent] here because it'll add the node to the tree
|
||||||
// in the wrong place.
|
// in the wrong place.
|
||||||
@ -260,6 +275,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}, semiGlobal: true);
|
}, semiGlobal: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destructures [value] and assigns it to [variables], as in an `@each`
|
||||||
|
/// statement.
|
||||||
void _setMultipleVariables(List<String> variables, Value value) {
|
void _setMultipleVariables(List<String> variables, Value value) {
|
||||||
var list = value.asList;
|
var list = value.asList;
|
||||||
var minLength = math.min(variables.length, list.length);
|
var minLength = math.min(variables.length, list.length);
|
||||||
@ -377,6 +394,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads the [Stylesheet] imported by [node], or throws a
|
||||||
|
/// [SassRuntimeException] if loading fails.
|
||||||
Stylesheet _loadImport(ImportRule node) {
|
Stylesheet _loadImport(ImportRule node) {
|
||||||
var path = _importPaths.putIfAbsent(node, () {
|
var path = _importPaths.putIfAbsent(node, () {
|
||||||
var path = p.fromUri(node.url);
|
var path = p.fromUri(node.url);
|
||||||
@ -408,9 +427,14 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Like [_tryImportPath], but checks both `.sass` and `.scss` extensions.
|
||||||
String _tryImportPathWithExtensions(String path) =>
|
String _tryImportPathWithExtensions(String path) =>
|
||||||
_tryImportPath(path + '.sass') ?? _tryImportPath(path + '.scss');
|
_tryImportPath(path + '.sass') ?? _tryImportPath(path + '.scss');
|
||||||
|
|
||||||
|
/// If a file exists at [path], or a partial with the same name exists,
|
||||||
|
/// returns the resolved path.
|
||||||
|
///
|
||||||
|
/// Otherwise, returns `null`.
|
||||||
String _tryImportPath(String path) {
|
String _tryImportPath(String path) {
|
||||||
var partial = p.join(p.dirname(path), "_${p.basename(path)}");
|
var partial = p.join(p.dirname(path), "_${p.basename(path)}");
|
||||||
if (fileExists(partial)) return partial;
|
if (fileExists(partial)) return partial;
|
||||||
@ -484,6 +508,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}, through: (node) => node is CssStyleRule || node is CssMediaRule);
|
}, through: (node) => node is CssStyleRule || node is CssMediaRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of queries that selects for platforms that match both
|
||||||
|
/// [queries1] and [queries2].
|
||||||
List<CssMediaQuery> _mergeMediaQueries(
|
List<CssMediaQuery> _mergeMediaQueries(
|
||||||
Iterable<CssMediaQuery> queries1, Iterable<CssMediaQuery> queries2) {
|
Iterable<CssMediaQuery> queries1, Iterable<CssMediaQuery> queries2) {
|
||||||
return new List.unmodifiable(queries1.expand/*<CssMediaQuery>*/((query1) {
|
return new List.unmodifiable(queries1.expand/*<CssMediaQuery>*/((query1) {
|
||||||
@ -491,6 +517,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}).where((query) => query != null));
|
}).where((query) => query != null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates [query] and converts it to a plain CSS query.
|
||||||
CssMediaQuery _visitMediaQuery(MediaQuery query) {
|
CssMediaQuery _visitMediaQuery(MediaQuery query) {
|
||||||
var modifier =
|
var modifier =
|
||||||
query.modifier == null ? null : _interpolationToValue(query.modifier);
|
query.modifier == null ? null : _interpolationToValue(query.modifier);
|
||||||
@ -564,6 +591,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}, through: (node) => node is CssStyleRule);
|
}, through: (node) => node is CssStyleRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates [condition] and converts it to a plain CSS string.
|
||||||
String _visitSupportsCondition(SupportsCondition condition) {
|
String _visitSupportsCondition(SupportsCondition condition) {
|
||||||
if (condition is SupportsOperation) {
|
if (condition is SupportsOperation) {
|
||||||
return "${_parenthesize(condition.left, condition.operator)} "
|
return "${_parenthesize(condition.left, condition.operator)} "
|
||||||
@ -581,6 +609,12 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evlauates [condition] and converts it to a plain CSS string, with
|
||||||
|
/// parentheses if necessary.
|
||||||
|
///
|
||||||
|
/// If [operator] is passed, it's the operator for the surrounding
|
||||||
|
/// [SupportsOperation], and is used to determine whether parentheses are
|
||||||
|
/// necessary if [condition] is also a [SupportsOperation].
|
||||||
String _parenthesize(SupportsCondition condition, [String operator]) {
|
String _parenthesize(SupportsCondition condition, [String operator]) {
|
||||||
if ((condition is SupportsNegation) ||
|
if ((condition is SupportsNegation) ||
|
||||||
(condition is SupportsOperation &&
|
(condition is SupportsOperation &&
|
||||||
@ -760,6 +794,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
return new SassString("$name(${arguments.map(valueToCss).join(', ')})");
|
return new SassString("$name(${arguments.map(valueToCss).join(', ')})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates the arguments in [invocation] as applied to [callable], and
|
||||||
|
/// invokes [run] in a scope with those arguments defined.
|
||||||
Value _runUserDefinedCallable(CallableInvocation invocation,
|
Value _runUserDefinedCallable(CallableInvocation invocation,
|
||||||
UserDefinedCallable callable, Value run()) {
|
UserDefinedCallable callable, Value run()) {
|
||||||
var triple = _evaluateArguments(invocation);
|
var triple = _evaluateArguments(invocation);
|
||||||
@ -819,6 +855,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates [invocation] as applied to [callable], and invokes [callable]'s
|
||||||
|
/// body.
|
||||||
Value _runBuiltInCallable(
|
Value _runBuiltInCallable(
|
||||||
CallableInvocation invocation, BuiltInCallable callable) {
|
CallableInvocation invocation, BuiltInCallable callable) {
|
||||||
var triple = _evaluateArguments(invocation);
|
var triple = _evaluateArguments(invocation);
|
||||||
@ -881,6 +919,9 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
invocation.span);
|
invocation.span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates the arguments in [invocation] and returns the positional and
|
||||||
|
/// named arguments, as well as the [ListSeparator] for the rest argument
|
||||||
|
/// list, if any.
|
||||||
Tuple3<List<Value>, Map<String, Value>, ListSeparator> _evaluateArguments(
|
Tuple3<List<Value>, Map<String, Value>, ListSeparator> _evaluateArguments(
|
||||||
CallableInvocation invocation) {
|
CallableInvocation invocation) {
|
||||||
var positional = invocation.arguments.positional
|
var positional = invocation.arguments.positional
|
||||||
@ -925,6 +966,11 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates the arguments in [invocation] only as much as necessary to
|
||||||
|
/// separate out positional and named arguments.
|
||||||
|
///
|
||||||
|
/// Returns the arguments as expressions so that they can be lazily evaluated
|
||||||
|
/// for macros such as `if()`.
|
||||||
Tuple2<List<Expression>, Map<String, Expression>> _evaluateMacroArguments(
|
Tuple2<List<Expression>, Map<String, Expression>> _evaluateMacroArguments(
|
||||||
CallableInvocation invocation) {
|
CallableInvocation invocation) {
|
||||||
if (invocation.arguments.rest == null) {
|
if (invocation.arguments.rest == null) {
|
||||||
@ -966,6 +1012,13 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds the values in [map] to [values].
|
||||||
|
///
|
||||||
|
/// Throws a [SassRuntimeException] associated with [span] if any [map] keys
|
||||||
|
/// aren't strings.
|
||||||
|
///
|
||||||
|
/// If [convert] is passed, that's used to convert the map values to the value
|
||||||
|
/// type for [values]. Otherwise, the [Value]s are used as-is.
|
||||||
void _addRestMap/*<T>*/(
|
void _addRestMap/*<T>*/(
|
||||||
Map<String, Object/*=T*/ > values, SassMap map, FileSpan span,
|
Map<String, Object/*=T*/ > values, SassMap map, FileSpan span,
|
||||||
[/*=T*/ convert(Value value)]) {
|
[/*=T*/ convert(Value value)]) {
|
||||||
@ -982,6 +1035,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Throws a [SassRuntimeException] if [positional] and [named] aren't valid
|
||||||
|
/// when applied to [arguments].
|
||||||
void _verifyArguments(int positional, Map<String, dynamic> named,
|
void _verifyArguments(int positional, Map<String, dynamic> named,
|
||||||
ArgumentDeclaration arguments, FileSpan span) {
|
ArgumentDeclaration arguments, FileSpan span) {
|
||||||
for (var i = 0; i < arguments.arguments.length; i++) {
|
for (var i = 0; i < arguments.arguments.length; i++) {
|
||||||
@ -1031,6 +1086,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
|
|
||||||
// ## Utilities
|
// ## Utilities
|
||||||
|
|
||||||
|
/// Runs [callback] with [environment] as the current environment.
|
||||||
/*=T*/ _withEnvironment/*<T>*/(Environment environment, /*=T*/ callback()) {
|
/*=T*/ _withEnvironment/*<T>*/(Environment environment, /*=T*/ callback()) {
|
||||||
var oldEnvironment = _environment;
|
var oldEnvironment = _environment;
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
@ -1039,12 +1095,16 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates [interpolation] and wraps the result in a [CssValue].
|
||||||
|
///
|
||||||
|
/// If [trim] is `true`, removes whitespace around the result.
|
||||||
CssValue<String> _interpolationToValue(Interpolation interpolation,
|
CssValue<String> _interpolationToValue(Interpolation interpolation,
|
||||||
{bool trim: false}) {
|
{bool trim: false}) {
|
||||||
var result = _performInterpolation(interpolation);
|
var result = _performInterpolation(interpolation);
|
||||||
return new CssValue(trim ? result.trim() : result, interpolation.span);
|
return new CssValue(trim ? result.trim() : result, interpolation.span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates [interpolation].
|
||||||
String _performInterpolation(Interpolation interpolation) {
|
String _performInterpolation(Interpolation interpolation) {
|
||||||
return interpolation.contents.map((value) {
|
return interpolation.contents.map((value) {
|
||||||
if (value is String) return value;
|
if (value is String) return value;
|
||||||
@ -1053,9 +1113,15 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}).join();
|
}).join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluates [expression] and wraps the result in a [CssValue].
|
||||||
CssValue<Value> _performExpression(Expression expression) =>
|
CssValue<Value> _performExpression(Expression expression) =>
|
||||||
new CssValue(expression.accept(this), expression.span);
|
new CssValue(expression.accept(this), expression.span);
|
||||||
|
|
||||||
|
/// Adds [node] as a child of the current parent, then runs [callback] with
|
||||||
|
/// [node] as the current parent.
|
||||||
|
///
|
||||||
|
/// If [through] is passed, [node] is added as a child of the first parent for
|
||||||
|
/// which [through] returns `false`.
|
||||||
/*=T*/ _withParent/*<S extends CssParentNode, T>*/(
|
/*=T*/ _withParent/*<S extends CssParentNode, T>*/(
|
||||||
/*=S*/ node,
|
/*=S*/ node,
|
||||||
/*=T*/ callback(),
|
/*=T*/ callback(),
|
||||||
@ -1078,6 +1144,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs [callback] with [selector] as the current selector.
|
||||||
/*=T*/ _withSelector/*<T>*/(
|
/*=T*/ _withSelector/*<T>*/(
|
||||||
CssValue<SelectorList> selector,
|
CssValue<SelectorList> selector,
|
||||||
/*=T*/ callback()) {
|
/*=T*/ callback()) {
|
||||||
@ -1088,6 +1155,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs [callback] with [queries] as the current media queries.
|
||||||
/*=T*/ _withMediaQueries/*<T>*/(
|
/*=T*/ _withMediaQueries/*<T>*/(
|
||||||
List<CssMediaQuery> queries,
|
List<CssMediaQuery> queries,
|
||||||
/*=T*/ callback()) {
|
/*=T*/ callback()) {
|
||||||
@ -1098,6 +1166,10 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a frame to the stack with the given [member] name, and [span] as the
|
||||||
|
/// site of the new frame.
|
||||||
|
///
|
||||||
|
/// Runs [callback] with the new stack.
|
||||||
/*=T*/ _withStackFrame/*<T>*/(
|
/*=T*/ _withStackFrame/*<T>*/(
|
||||||
String member, FileSpan span, /*=T*/ callback()) {
|
String member, FileSpan span, /*=T*/ callback()) {
|
||||||
_stack.add(_stackFrame(span));
|
_stack.add(_stackFrame(span));
|
||||||
@ -1109,17 +1181,29 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new stack frame with location information from [span] and
|
||||||
|
/// [_member].
|
||||||
Frame _stackFrame(FileSpan span) => new Frame(
|
Frame _stackFrame(FileSpan span) => new Frame(
|
||||||
span.sourceUrl, span.start.line + 1, span.start.column + 1, _member);
|
span.sourceUrl, span.start.line + 1, span.start.column + 1, _member);
|
||||||
|
|
||||||
|
/// Returns a stack trace at the current point.
|
||||||
|
///
|
||||||
|
/// [span] is the current location, used for the bottom-most stack frame.
|
||||||
Trace _stackTrace(FileSpan span) {
|
Trace _stackTrace(FileSpan span) {
|
||||||
var frames = _stack.toList()..add(_stackFrame(span));
|
var frames = _stack.toList()..add(_stackFrame(span));
|
||||||
return new Trace(frames.reversed);
|
return new Trace(frames.reversed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Throws a [SassRuntimeException] with the given [message] and [span].
|
||||||
SassRuntimeException _exception(String message, FileSpan span) =>
|
SassRuntimeException _exception(String message, FileSpan span) =>
|
||||||
new SassRuntimeException(message, span, _stackTrace(span));
|
new SassRuntimeException(message, span, _stackTrace(span));
|
||||||
|
|
||||||
|
/// Runs [callback], and adjusts any [SassFormatException] to be within [span].
|
||||||
|
///
|
||||||
|
/// Specifically, this adjusts format exceptions so that the errors are
|
||||||
|
/// reported as though the text being parsed were exactly in [span]. This may
|
||||||
|
/// not be quite accurate if the source text contained interpolation, but
|
||||||
|
/// it'll still produce a useful error.
|
||||||
/*=T*/ _adjustParseError/*<T>*/(FileSpan span, /*=T*/ callback()) {
|
/*=T*/ _adjustParseError/*<T>*/(FileSpan span, /*=T*/ callback()) {
|
||||||
try {
|
try {
|
||||||
return callback();
|
return callback();
|
||||||
@ -1135,6 +1219,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs [callback], and converts any [InternalException]s it throws to
|
||||||
|
/// [SassRuntimeException]s with [span].
|
||||||
/*=T*/ _addExceptionSpan/*<T>*/(FileSpan span, /*=T*/ callback()) {
|
/*=T*/ _addExceptionSpan/*<T>*/(FileSpan span, /*=T*/ callback()) {
|
||||||
try {
|
try {
|
||||||
return callback();
|
return callback();
|
||||||
|
@ -16,6 +16,15 @@ import 'interface/css.dart';
|
|||||||
import 'interface/selector.dart';
|
import 'interface/selector.dart';
|
||||||
import 'interface/value.dart';
|
import 'interface/value.dart';
|
||||||
|
|
||||||
|
/// Converts [node] to a CSS string.
|
||||||
|
///
|
||||||
|
/// If [style] is passed, it controls the style of the resulting CSS. It
|
||||||
|
/// defaults to [OutputStyle.expanded].
|
||||||
|
///
|
||||||
|
/// If [inspect] is `true`, this will emit an unambiguous representation of the
|
||||||
|
/// source structure. Note however that, although this will be valid SCSS, it
|
||||||
|
/// may not be valid CSS. If [inspect] is `false` and [node] contains any values
|
||||||
|
/// that can't be represented in plain CSS, throws a [SassException].
|
||||||
String toCss(CssNode node, {OutputStyle style, bool inspect: false}) {
|
String toCss(CssNode node, {OutputStyle style, bool inspect: false}) {
|
||||||
var visitor = new _SerializeCssVisitor(style: style, inspect: inspect);
|
var visitor = new _SerializeCssVisitor(style: style, inspect: inspect);
|
||||||
node.accept(visitor);
|
node.accept(visitor);
|
||||||
@ -29,26 +38,41 @@ String toCss(CssNode node, {OutputStyle style, bool inspect: false}) {
|
|||||||
return result.trim();
|
return result.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this may throw an [InternalException] if [inspect] is `false`.
|
/// Converts [value] to a CSS string.
|
||||||
|
///
|
||||||
|
/// If [inspect] is `true`, this will emit an unambiguous representation of the
|
||||||
|
/// source structure. Note however that, although this will be valid SCSS, it
|
||||||
|
/// may not be valid CSS. If [inspect] is `false` and [value] can't be
|
||||||
|
/// represented in plain CSS, throws an [InternalException].
|
||||||
String valueToCss(Value value, {bool inspect: false}) {
|
String valueToCss(Value value, {bool inspect: false}) {
|
||||||
var visitor = new _SerializeCssVisitor(inspect: inspect);
|
var visitor = new _SerializeCssVisitor(inspect: inspect);
|
||||||
value.accept(visitor);
|
value.accept(visitor);
|
||||||
return visitor._buffer.toString();
|
return visitor._buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this may throw an [InternalException] if [inspect] is `false`.
|
/// Converts [selector] to a CSS string.
|
||||||
|
///
|
||||||
|
/// If [inspect] is `true`, this will emit an unambiguous representation of the
|
||||||
|
/// source structure. Note however that, although this will be valid SCSS, it
|
||||||
|
/// may not be valid CSS. If [inspect] is `false` and [selector] can't be
|
||||||
|
/// represented in plain CSS, throws an [InternalException].
|
||||||
String selectorToCss(Selector selector, {bool inspect: false}) {
|
String selectorToCss(Selector selector, {bool inspect: false}) {
|
||||||
var visitor = new _SerializeCssVisitor(inspect: inspect);
|
var visitor = new _SerializeCssVisitor(inspect: inspect);
|
||||||
selector.accept(visitor);
|
selector.accept(visitor);
|
||||||
return visitor._buffer.toString();
|
return visitor._buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A visitor that converts CSS syntax trees to plain strings.
|
||||||
class _SerializeCssVisitor
|
class _SerializeCssVisitor
|
||||||
implements CssVisitor, ValueVisitor, SelectorVisitor {
|
implements CssVisitor, ValueVisitor, SelectorVisitor {
|
||||||
|
/// A buffer that contains the CSS produced so far.
|
||||||
final _buffer = new StringBuffer();
|
final _buffer = new StringBuffer();
|
||||||
|
|
||||||
|
/// The current indentation of the CSS output.
|
||||||
var _indentation = 0;
|
var _indentation = 0;
|
||||||
|
|
||||||
|
/// Whether we're emitting an unambiguous representation of the source
|
||||||
|
/// structure, as opposed to valid CSS.
|
||||||
final bool _inspect;
|
final bool _inspect;
|
||||||
|
|
||||||
_SerializeCssVisitor({OutputStyle style, bool inspect: false})
|
_SerializeCssVisitor({OutputStyle style, bool inspect: false})
|
||||||
@ -162,6 +186,9 @@ class _SerializeCssVisitor
|
|||||||
_buffer.writeCharCode($semicolon);
|
_buffer.writeCharCode($semicolon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Emits the value of [node] as a custom property value.
|
||||||
|
///
|
||||||
|
/// This re-indents [node]'s value relative to the current indentation.
|
||||||
void _writeCustomPropertyValue(CssDeclaration node) {
|
void _writeCustomPropertyValue(CssDeclaration node) {
|
||||||
var value = (node.value.value as SassString).text;
|
var value = (node.value.value as SassString).text;
|
||||||
|
|
||||||
@ -179,6 +206,8 @@ class _SerializeCssVisitor
|
|||||||
_writeWithIndent(value, minimumIndentation);
|
_writeWithIndent(value, minimumIndentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the indentation level of the least-indented, non-empty line in
|
||||||
|
/// [text].
|
||||||
int _minimumIndentation(String text) {
|
int _minimumIndentation(String text) {
|
||||||
var scanner = new LineScanner(text);
|
var scanner = new LineScanner(text);
|
||||||
while (!scanner.isDone && scanner.readChar() != $lf) {}
|
while (!scanner.isDone && scanner.readChar() != $lf) {}
|
||||||
@ -195,6 +224,8 @@ class _SerializeCssVisitor
|
|||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes [text] to [_buffer], adding [minimumIndentation] to each non-empty
|
||||||
|
/// line.
|
||||||
void _writeWithIndent(String text, int minimumIndentation) {
|
void _writeWithIndent(String text, int minimumIndentation) {
|
||||||
var scanner = new LineScanner(text);
|
var scanner = new LineScanner(text);
|
||||||
while (!scanner.isDone && scanner.peekChar() != $lf) {
|
while (!scanner.isDone && scanner.peekChar() != $lf) {
|
||||||
@ -213,6 +244,8 @@ class _SerializeCssVisitor
|
|||||||
|
|
||||||
// ## Values
|
// ## Values
|
||||||
|
|
||||||
|
/// Converts [value] to a plain CSS string, converting any
|
||||||
|
/// [InternalException]s to [SassException]s.
|
||||||
void _visitValue(CssValue<Value> value) {
|
void _visitValue(CssValue<Value> value) {
|
||||||
try {
|
try {
|
||||||
value.value.accept(this);
|
value.value.accept(this);
|
||||||
@ -238,7 +271,9 @@ class _SerializeCssVisitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Emits [color] as a hex character pair.
|
||||||
void _writeHexComponent(int color) {
|
void _writeHexComponent(int color) {
|
||||||
|
assert(color < 0x100);
|
||||||
_buffer.writeCharCode(hexCharFor(color >> 4));
|
_buffer.writeCharCode(hexCharFor(color >> 4));
|
||||||
_buffer.writeCharCode(hexCharFor(color & 0xF));
|
_buffer.writeCharCode(hexCharFor(color & 0xF));
|
||||||
}
|
}
|
||||||
@ -267,6 +302,8 @@ class _SerializeCssVisitor
|
|||||||
if (value.hasBrackets) _buffer.writeCharCode($rbracket);
|
if (value.hasBrackets) _buffer.writeCharCode($rbracket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether [value] needs parentheses as an element in a list with the
|
||||||
|
/// given [separator].
|
||||||
bool _elementNeedsParens(ListSeparator separator, Value value) {
|
bool _elementNeedsParens(ListSeparator separator, Value value) {
|
||||||
if (value is SassList) {
|
if (value is SassList) {
|
||||||
if (value.contents.length < 2) return false;
|
if (value.contents.length < 2) return false;
|
||||||
@ -289,6 +326,7 @@ class _SerializeCssVisitor
|
|||||||
_buffer.writeCharCode($rparen);
|
_buffer.writeCharCode($rparen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes [value] as key or value in a map, with parentheses as necessary.
|
||||||
void _writeMapElement(Value value) {
|
void _writeMapElement(Value value) {
|
||||||
var needsParens = value is SassList &&
|
var needsParens = value is SassList &&
|
||||||
value.separator == ListSeparator.comma &&
|
value.separator == ListSeparator.comma &&
|
||||||
@ -328,6 +366,11 @@ class _SerializeCssVisitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes [string] to [_buffer].
|
||||||
|
///
|
||||||
|
/// By default, this detects which type of quote to use based on the contents
|
||||||
|
/// of the string. If [forceDoubleQuote] is `true`, this always uses a double
|
||||||
|
/// quote.
|
||||||
void _visitString(String string, {bool forceDoubleQuote: false}) {
|
void _visitString(String string, {bool forceDoubleQuote: false}) {
|
||||||
var includesSingleQuote = false;
|
var includesSingleQuote = false;
|
||||||
var includesDoubleQuote = false;
|
var includesDoubleQuote = false;
|
||||||
@ -491,6 +534,7 @@ class _SerializeCssVisitor
|
|||||||
|
|
||||||
// ## Utilities
|
// ## Utilities
|
||||||
|
|
||||||
|
/// Emits [children] in a block.
|
||||||
void _visitChildren(Iterable<CssNode> children) {
|
void _visitChildren(Iterable<CssNode> children) {
|
||||||
_buffer.writeCharCode($lbrace);
|
_buffer.writeCharCode($lbrace);
|
||||||
if (children.every(_isInvisible)) {
|
if (children.every(_isInvisible)) {
|
||||||
@ -510,6 +554,7 @@ class _SerializeCssVisitor
|
|||||||
_buffer.writeCharCode($rbrace);
|
_buffer.writeCharCode($rbrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes indentation based on [_indentation].
|
||||||
void _writeIndentation() {
|
void _writeIndentation() {
|
||||||
for (var i = 0; i < _indentation; i++) {
|
for (var i = 0; i < _indentation; i++) {
|
||||||
_buffer.writeCharCode($space);
|
_buffer.writeCharCode($space);
|
||||||
@ -517,6 +562,8 @@ class _SerializeCssVisitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calls [callback] to write each value in [iterable], and writes [text] in
|
||||||
|
/// between each one.
|
||||||
void _writeBetween/*<T>*/(
|
void _writeBetween/*<T>*/(
|
||||||
Iterable/*<T>*/ iterable, String text, void callback(/*=T*/ value)) {
|
Iterable/*<T>*/ iterable, String text, void callback(/*=T*/ value)) {
|
||||||
var first = true;
|
var first = true;
|
||||||
@ -530,19 +577,23 @@ class _SerializeCssVisitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs [callback] with indentation increased one level.
|
||||||
void _indent(void callback()) {
|
void _indent(void callback()) {
|
||||||
_indentation++;
|
_indentation++;
|
||||||
callback();
|
callback();
|
||||||
_indentation--;
|
_indentation--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether [node] is considered invisible.
|
||||||
bool _isInvisible(CssNode node) => !_inspect && node.isInvisible;
|
bool _isInvisible(CssNode node) => !_inspect && node.isInvisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An enum of generated CSS styles.
|
||||||
class OutputStyle {
|
class OutputStyle {
|
||||||
|
/// The standard CSS style, with each declaration on its own line.
|
||||||
static const expanded = const OutputStyle._("expanded");
|
static const expanded = const OutputStyle._("expanded");
|
||||||
static const nested = const OutputStyle._("nested");
|
|
||||||
|
|
||||||
|
/// The name of the style.
|
||||||
final String _name;
|
final String _name;
|
||||||
|
|
||||||
const OutputStyle._(this._name);
|
const OutputStyle._(this._name);
|
||||||
|
Loading…
Reference in New Issue
Block a user