Make SassList.contents private (#227)

This forces users to use Value.asList, which encourages them to be
aware that every value counts as a list.
This commit is contained in:
Natalie Weizenbaum 2018-02-02 17:36:29 -08:00 committed by GitHub
parent 854e7f57d6
commit f1e58e083e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 38 additions and 44 deletions

View File

@ -375,7 +375,7 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
new BuiltInCallable("adjust-color", r"$color, $kwargs...", (arguments) {
var color = arguments[0].assertColor("color");
var argumentList = arguments[1] as SassArgumentList;
if (argumentList.contents.isNotEmpty) {
if (argumentList.asList.isNotEmpty) {
throw new SassScriptException(
"Only only positional argument is allowed. All other arguments must "
"be passed by name.");
@ -428,7 +428,7 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
new BuiltInCallable("scale-color", r"$color, $kwargs...", (arguments) {
var color = arguments[0].assertColor("color");
var argumentList = arguments[1] as SassArgumentList;
if (argumentList.contents.isNotEmpty) {
if (argumentList.asList.isNotEmpty) {
throw new SassScriptException(
"Only only positional argument is allowed. All other arguments must "
"be passed by name.");
@ -489,7 +489,7 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
new BuiltInCallable("change-color", r"$color, $kwargs...", (arguments) {
var color = arguments[0].assertColor("color");
var argumentList = arguments[1] as SassArgumentList;
if (argumentList.contents.isNotEmpty) {
if (argumentList.asList.isNotEmpty) {
throw new SassScriptException(
"Only only positional argument is allowed. All other arguments must "
"be passed by name.");
@ -766,10 +766,7 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
}),
new BuiltInCallable("zip", r"$lists...", (arguments) {
var lists = (arguments[0] as SassArgumentList)
.contents
.map((list) => list.asList)
.toList();
var lists = arguments[0].asList.map((list) => list.asList).toList();
var i = 0;
var results = <SassList>[];
while (lists.every((list) => i != list.length)) {
@ -814,9 +811,9 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
new BuiltInCallable("map-remove", r"$map, $keys...", (arguments) {
var map = arguments[0].assertMap("map");
var keys = arguments[1] as SassArgumentList;
var keys = arguments[1];
var mutableMap = new Map<Value, Value>.from(map.contents);
for (var key in keys.contents) {
for (var key in keys.asList) {
mutableMap.remove(key);
}
return new SassMap(mutableMap);
@ -854,7 +851,7 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
// ## Selectors
new BuiltInCallable("selector-nest", r"$selectors...", (arguments) {
var selectors = (arguments[0] as SassArgumentList).contents;
var selectors = arguments[0].asList;
if (selectors.isEmpty) {
throw new SassScriptException(
"\$selectors: At least one selector must be passed.");
@ -867,7 +864,7 @@ final List<BuiltInCallable> coreFunctions = new UnmodifiableListView([
}),
new BuiltInCallable("selector-append", r"$selectors...", (arguments) {
var selectors = (arguments[0] as SassArgumentList).contents;
var selectors = arguments[0].asList;
if (selectors.isEmpty) {
throw new SassScriptException(
"\$selectors: At least one selector must be passed.");

View File

@ -185,11 +185,11 @@ abstract class Value implements ext.Value {
if (this is SassString) return (this as SassString).text;
if (this is! SassList) return null;
var list = this as SassList;
if (list.contents.isEmpty) return null;
if (list.asList.isEmpty) return null;
var result = <String>[];
if (list.separator == ListSeparator.comma) {
for (var complex in list.contents) {
for (var complex in list.asList) {
if (complex is SassString) {
result.add(complex.text);
} else if (complex is SassList &&
@ -202,7 +202,7 @@ abstract class Value implements ext.Value {
}
}
} else {
for (var compound in list.contents) {
for (var compound in list.asList) {
if (compound is SassString) {
result.add(compound.text);
} else {

View File

@ -10,14 +10,6 @@ import 'value.dart';
/// A SassScript list.
abstract class SassList extends Value {
// TODO(nweiz): Use persistent data structures rather than copying here. An
// RRB vector should fit our use-cases well.
//
// We may also want to fall back to a plain unmodifiable List for small lists
// (<32 items?).
/// The contents of the list.
List<Value> get contents;
ListSeparator get separator;
bool get hasBrackets;

View File

@ -8,27 +8,32 @@ import '../value.dart';
import 'external/value.dart' as ext;
class SassList extends Value implements ext.SassList {
final List<Value> contents;
// TODO(nweiz): Use persistent data structures rather than copying here. An
// RRB vector should fit our use-cases well.
//
// We may also want to fall back to a plain unmodifiable List for small lists
// (<32 items?).
final List<Value> _contents;
final ListSeparator separator;
final bool hasBrackets;
bool get isBlank => contents.every((element) => element.isBlank);
bool get isBlank => asList.every((element) => element.isBlank);
List<Value> get asList => contents;
List<Value> get asList => _contents;
int get lengthAsList => contents.length;
int get lengthAsList => asList.length;
const SassList.empty({ListSeparator separator, bool brackets: false})
: contents = const [],
: _contents = const [],
separator = separator ?? ListSeparator.undecided,
hasBrackets = brackets;
SassList(Iterable<Value> contents, this.separator, {bool brackets: false})
: contents = new List.unmodifiable(contents),
: _contents = new List.unmodifiable(contents),
hasBrackets = brackets {
if (separator == ListSeparator.undecided && contents.length > 1) {
if (separator == ListSeparator.undecided && asList.length > 1) {
throw new ArgumentError(
"A list with more than one element must have an explicit separator.");
}
@ -37,16 +42,16 @@ class SassList extends Value implements ext.SassList {
T accept<T>(ValueVisitor<T> visitor) => visitor.visitList(this);
SassMap assertMap([String name]) =>
contents.isEmpty ? const SassMap.empty() : super.assertMap(name);
asList.isEmpty ? const SassMap.empty() : super.assertMap(name);
bool operator ==(other) =>
(other is SassList &&
other.separator == separator &&
other.hasBrackets == hasBrackets &&
listEquals(other.contents, contents)) ||
(contents.isEmpty && other is SassMap && other.contents.isEmpty);
listEquals(other.asList, asList)) ||
(asList.isEmpty && other is SassMap && other.asList.isEmpty);
int get hashCode => listHash(contents);
int get hashCode => listHash(asList);
}
/// An enum of list separator types.

View File

@ -36,7 +36,7 @@ class SassMap extends Value implements ext.SassMap {
bool operator ==(other) =>
(other is SassMap &&
const MapEquality().equals(other.contents, contents)) ||
(contents.isEmpty && other is SassList && other.contents.isEmpty);
(contents.isEmpty && other is SassList && other.asList.isEmpty);
int get hashCode => contents.isEmpty
? const SassList.empty().hashCode

View File

@ -493,8 +493,8 @@ class _EvaluateVisitor
return null;
}
/// Returns whether [value] is an empty [SassList].
bool _isEmptyList(Value value) => value is SassList && value.contents.isEmpty;
/// Returns whether [value] is an empty list.
bool _isEmptyList(Value value) => value.asList.isEmpty;
Future<Value> visitEachRule(EachRule node) async {
var list = await node.list.accept(this);

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_evaluate.dart.
// See tool/synchronize.dart for details.
//
// Checksum: 0d20d8293daaad022a0a20115cc88b80704e2ad7
// Checksum: 199b3bd52a5c5c147444ee45c2f3d46ef8af2d28
import 'dart:math' as math;
@ -492,8 +492,8 @@ class _EvaluateVisitor
return null;
}
/// Returns whether [value] is an empty [SassList].
bool _isEmptyList(Value value) => value is SassList && value.contents.isEmpty;
/// Returns whether [value] is an empty list.
bool _isEmptyList(Value value) => value.asList.isEmpty;
Value visitEachRule(EachRule node) {
var list = node.list.accept(this);

View File

@ -512,7 +512,7 @@ class _SerializeVisitor implements CssVisitor, ValueVisitor, SelectorVisitor {
void visitList(SassList value) {
if (value.hasBrackets) {
_buffer.writeCharCode($lbracket);
} else if (value.contents.isEmpty) {
} else if (value.asList.isEmpty) {
if (!_inspect) {
throw new SassScriptException("() isn't a valid CSS value");
}
@ -521,14 +521,14 @@ class _SerializeVisitor implements CssVisitor, ValueVisitor, SelectorVisitor {
}
var singleton = _inspect &&
value.contents.length == 1 &&
value.asList.length == 1 &&
value.separator == ListSeparator.comma;
if (singleton && !value.hasBrackets) _buffer.writeCharCode($lparen);
_writeBetween<Value>(
_inspect
? value.contents
: value.contents.where((element) => !element.isBlank),
? value.asList
: value.asList.where((element) => !element.isBlank),
value.separator == ListSeparator.space ? " " : _commaSeparator,
_inspect
? (element) {
@ -553,7 +553,7 @@ class _SerializeVisitor implements CssVisitor, ValueVisitor, SelectorVisitor {
/// given [separator].
bool _elementNeedsParens(ListSeparator separator, Value value) {
if (value is SassList) {
if (value.contents.length < 2) return false;
if (value.asList.length < 2) return false;
if (value.hasBrackets) return false;
return separator == ListSeparator.comma
? value.separator == ListSeparator.comma