dart-sass/lib/src/visitor/css/serialize.dart

168 lines
4.6 KiB
Dart
Raw Normal View History

2016-06-03 22:36:20 +02:00
// 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:charcode/charcode.dart';
import '../../ast/css/node.dart';
import '../../util/character.dart';
import '../../value.dart';
import '../css.dart';
2016-06-04 01:34:26 +02:00
String toCss(CssNode node) {
2016-06-03 22:36:20 +02:00
var visitor = new _SerializeCssVisitor();
node.accept(visitor);
var result = visitor._buffer.toString();
if (result.codeUnits.any((codeUnit) => codeUnit > 0x7F)) {
result = '@charset "UTF-8";\n$result';
}
// TODO(nweiz): Do this in a way that's not O(n), maybe using a custom buffer
// that's not append-only.
return result.trim();
}
class _SerializeCssVisitor extends CssVisitor {
final _buffer = new StringBuffer();
var _indentation = 0;
void visitStylesheet(CssStylesheet node) {
for (var child in node.children) {
child.accept(this);
_buffer.writeln();
}
}
void visitComment(CssComment node) {
// TODO: format this at all
_buffer.writeln(node.text);
}
void visitStyleRule(CssStyleRule node) {
_writeIndentation();
_buffer.write(node.selector.value);
_buffer.writeCharCode($space);
_buffer.writeCharCode($lbrace);
_buffer.writeln();
_indent(() {
for (var child in node.children) {
child.accept(this);
_buffer.writeln();
}
});
_writeIndentation();
_buffer.writeCharCode($rbrace);
// TODO: only add an extra newline if this is a group end
_buffer.writeln();
}
void visitDeclaration(CssDeclaration node) {
_writeIndentation();
_buffer.write(node.name.value);
_buffer.writeCharCode($colon);
_buffer.writeCharCode($space);
node.value.value.accept(this);
_buffer.writeCharCode($semicolon);
}
2016-06-04 01:34:26 +02:00
void visitBoolean(SassBoolean value) =>
_buffer.write(value.value.toString());
2016-06-03 22:36:20 +02:00
2016-06-04 01:31:29 +02:00
void visitIdentifier(SassIdentifier value) =>
2016-06-04 01:34:26 +02:00
_buffer.write(value.text.replaceAll("\n", " "));
2016-06-03 22:36:20 +02:00
void visitList(SassList value) {
if (value.contents.isEmpty) throw "() isn't a valid CSS value";
var separator = value.separator == ListSeparator.space ? " " : ", ";
var first = true;
for (var element in value.contents) {
if (element.isBlank) continue;
if (!first) _buffer.write(separator);
first = false;
element.accept(this);
}
}
// TODO(nweiz): Support precision and don't support exponent notation.
2016-06-04 01:31:29 +02:00
void visitNumber(SassNumber value) {
2016-06-03 22:36:20 +02:00
_buffer.write(value.value.toString());
}
2016-06-04 01:27:41 +02:00
void visitString(SassString string) =>
_buffer.write(_visitString(string.text));
2016-06-03 22:36:20 +02:00
String _visitString(String string, {bool forceDoubleQuote: false}) {
var includesSingleQuote = false;
var includesDoubleQuote = false;
var buffer = new StringBuffer();
for (var i = 0; i < string.length; i++) {
var char = string.codeUnitAt(i);
switch (char) {
case $single_quote:
if (forceDoubleQuote) {
buffer.writeCharCode($single_quote);
} else if (includesDoubleQuote) {
return _visitString(string, forceDoubleQuote: true);
} else {
includesSingleQuote = true;
buffer.writeCharCode($single_quote);
}
break;
case $double_quote:
if (forceDoubleQuote) {
buffer.writeCharCode($backslash);
buffer.writeCharCode($double_quote);
} else if (includesSingleQuote) {
return _visitString(string, forceDoubleQuote: true);
} else {
includesDoubleQuote = true;
buffer.writeCharCode($double_quote);
}
break;
2016-06-04 01:27:41 +02:00
case $cr:
case $lf:
case $ff:
2016-06-03 22:36:20 +02:00
buffer.writeCharCode($backslash);
2016-06-04 01:27:41 +02:00
buffer.writeCharCode(hexCharFor(char));
if (string.length == i + 1) break;
var next = string.codeUnitAt(i + 1);
if (isHex(next) || next == $space || next == $tab) {
2016-06-03 22:36:20 +02:00
buffer.writeCharCode($space);
}
break;
case $backslash:
buffer.writeCharCode($backslash);
buffer.writeCharCode($backslash);
break;
default:
buffer.writeCharCode(char);
break;
}
}
var doubleQuote = forceDoubleQuote || !includesDoubleQuote;
return doubleQuote ? '"$buffer"' : "'$buffer'";
}
void _writeIndentation() {
for (var i = 0; i < _indentation; i++) {
_buffer.writeCharCode($space);
_buffer.writeCharCode($space);
}
}
void _indent(void callback()) {
_indentation++;
callback();
_indentation--;
}
}