Partial selector newline support.

In particular, newlines in selectors lists are now preserved during
parent selector resolution and emitted by the serializer. The output
matches Ruby Sass, but I'm not sure it's actually the best possible.
This commit is contained in:
Natalie Weizenbaum 2016-09-24 06:27:24 -07:00 committed by Natalie Weizenbaum
parent 98c5ffae9a
commit 99c83a5834
4 changed files with 55 additions and 45 deletions

View File

@ -10,8 +10,8 @@ import '../selector.dart';
class ComplexSelector extends Selector {
final List<ComplexSelectorComponent> components;
// Indices of [components] that are followed by line breaks.
final List<int> lineBreaks;
// There's a line break *before* this selector.
final bool lineBreak;
int get minSpecificity {
if (_minSpecificity == null) _computeSpecificity();
@ -38,10 +38,8 @@ class ComplexSelector extends Selector {
bool _containsPlaceholder;
ComplexSelector(Iterable<ComplexSelectorComponent> components,
{Iterable<int> lineBreaks})
: components = new List.unmodifiable(components),
lineBreaks =
lineBreaks == null ? const [] : new List.unmodifiable(lineBreaks);
{this.lineBreak: false})
: components = new List.unmodifiable(components);
/*=T*/ accept/*<T>*/(SelectorVisitor/*<T>*/ visitor) =>
visitor.visitComplexSelector(this);

View File

@ -13,9 +13,6 @@ import '../selector.dart';
class SelectorList extends Selector {
final List<ComplexSelector> components;
// Indices of [components] that are followed by line breaks.
final List<int> lineBreaks;
bool get _containsParentSelector {
return components.any((complex) {
return complex.components.any((component) =>
@ -37,10 +34,8 @@ class SelectorList extends Selector {
}), ListSeparator.comma);
}
SelectorList(Iterable<ComplexSelector> components, {Iterable<int> lineBreaks})
: components = new List.unmodifiable(components),
lineBreaks =
lineBreaks == null ? const [] : new List.unmodifiable(lineBreaks);
SelectorList(Iterable<ComplexSelector> components)
: components = new List.unmodifiable(components);
factory SelectorList.parse(String contents, {url, bool allowParent: true}) =>
new SelectorParser(contents, url: url, allowParent: allowParent).parse();
@ -71,39 +66,51 @@ class SelectorList extends Selector {
if (!_containsParentSelector) {
return new SelectorList(parent.components.expand((parentComplex) {
return components.map((childComplex) => new ComplexSelector(
parentComplex.components.toList()
..addAll(childComplex.components)));
parentComplex.components.toList()..addAll(childComplex.components),
lineBreak: childComplex.lineBreak || parentComplex.lineBreak));
}));
}
// TODO: handle line breaks
return new SelectorList(flattenVertically(components.map((complex) {
var newComplexes = [<ComplexSelectorComponent>[]];
var lineBreaks = <bool>[false];
for (var component in complex.components) {
if (component is CompoundSelector) {
var resultList = _resolveParentSelectorsCompound(component, parent);
if (resultList == null) {
var resolved = _resolveParentSelectorsCompound(component, parent);
if (resolved == null) {
for (var newComplex in newComplexes) {
newComplex.add(component);
}
continue;
}
newComplexes = newComplexes
.expand((newComplex) => resultList.map((resultComplex) =>
newComplex.toList()..addAll(resultComplex)))
.toList();
var previousComplexes = newComplexes;
var previousLineBreaks = lineBreaks;
newComplexes = <List<ComplexSelectorComponent>>[];
lineBreaks = <bool>[];
var i = 0;
for (var newComplex in previousComplexes) {
var lineBreak = previousLineBreaks[i++];
for (var resolvedComplex in resolved) {
newComplexes
.add(newComplex.toList()..addAll(resolvedComplex.components));
lineBreaks.add(lineBreak || resolvedComplex.lineBreak);
}
}
} else {
for (var newComplex in newComplexes) {
newComplex.add(component);
}
}
}
return newComplexes.map((newComplex) => new ComplexSelector(newComplex));
var i = 0;
return newComplexes.map((newComplex) =>
new ComplexSelector(newComplex, lineBreak: lineBreaks[i++]));
})));
}
Iterable<Iterable<ComplexSelectorComponent>> _resolveParentSelectorsCompound(
Iterable<ComplexSelector> _resolveParentSelectorsCompound(
CompoundSelector compound, SelectorList parent) {
var containsSelectorPseudo = compound.components.any((simple) =>
simple is PseudoSelector &&
@ -130,11 +137,11 @@ class SelectorList extends Selector {
var parentSelector = compound.components.first;
if (parentSelector is ParentSelector) {
if (compound.components.length == 1 && parentSelector.suffix == null) {
return parent.components.map((complex) => complex.components);
return parent.components;
}
} else {
return [
[new CompoundSelector(resolvedMembers)]
new ComplexSelector([new CompoundSelector(resolvedMembers)])
];
}
@ -157,8 +164,10 @@ class SelectorList extends Selector {
last.components.toList()..addAll(resolvedMembers.skip(1)));
}
return complex.components.take(complex.components.length - 1).toList()
..add(last);
return new ComplexSelector(
complex.components.take(complex.components.length - 1).toList()
..add(last),
lineBreak: complex.lineBreak);
});
}

View File

@ -48,7 +48,6 @@ class SelectorParser extends Parser {
SelectorList _selectorList() {
var components = <ComplexSelector>[];
var lineBreaks = <int>[];
whitespace();
var previousLine = scanner.line;
@ -58,21 +57,17 @@ class SelectorParser extends Parser {
if (next == $comma) continue;
if (next == $lbrace) break;
if (scanner.line != previousLine) {
lineBreaks.add(components.length);
previousLine = scanner.line;
}
components.add(_complexSelector());
var lineBreak = scanner.line != previousLine;
if (lineBreak) previousLine = scanner.line;
components.add(_complexSelector(lineBreak: lineBreak));
} while (scanner.scanChar($comma));
return new SelectorList(components, lineBreaks: lineBreaks);
return new SelectorList(components);
}
ComplexSelector _complexSelector() {
ComplexSelector _complexSelector({bool lineBreak: false}) {
var components = <ComplexSelectorComponent>[];
var lineBreaks = <int>[];
var previousLine = scanner.line;
loop:
while (true) {
whitespace();
@ -112,14 +107,10 @@ class SelectorParser extends Parser {
break;
}
if (scanner.line != previousLine) {
lineBreaks.add(components.length);
previousLine = scanner.line;
}
components.add(component);
}
return new ComplexSelector(components, lineBreaks: lineBreaks);
return new ComplexSelector(components, lineBreak: lineBreak);
}
CompoundSelector _compoundSelector() {
@ -247,7 +238,9 @@ class SelectorParser extends Parser {
ParentSelector _parentSelector() {
scanner.expectChar($ampersand);
var next = scanner.peekChar();
var suffix = isName(next) || next == $backslash ? identifier() : null;
var suffix = next != null && (isName(next) || next == $backslash)
? identifier()
: null;
return new ParentSelector(suffix: suffix);
}

View File

@ -427,7 +427,17 @@ class _SerializeCssVisitor
var complexes = _inspect
? list.components
: list.components.where((complex) => !complex.containsPlaceholder);
_writeBetween(complexes, ", ", (complex) => visitComplexSelector(complex));
var first = true;
for (var complex in complexes) {
if (first) {
first = false;
} else {
_buffer.writeCharCode($comma);
_buffer.writeCharCode(complex.lineBreak ? $lf : $space);
}
visitComplexSelector(complex);
}
}
void visitParentSelector(ParentSelector parent) {