Roll Observatory deps (charted -> ^0.3.0)
I had to restrict both the analyzer and dart_style to old versions to
get the build to work :-/. Otherwise I got errors like this:
Transform polymer (PolymerBootstrapTransformer) on
observatory|web/index.html threw error: Class 'Parser' has no
instance setter 'parseConditionalDirectives='.
Added missing exclude to pubspec.yaml.
BUG=
Review URL: https://codereview.chromium.org//1521693002 .
diff --git a/packages/analyzer/CHANGELOG.md b/packages/analyzer/CHANGELOG.md
index 9e2232a..aa44b2c 100644
--- a/packages/analyzer/CHANGELOG.md
+++ b/packages/analyzer/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.26.1+13
+* (Internal) Plugin processing fixes.
+
+## 0.26.1+11
+* Fixes to address lint registry memory leaking.
+
+## 0.26.1+10
+* New `AnalysisContext` API for associating configuration data with contexts
+ (`setConfigurationData()` and `getConfigurationData()`).
+
## 0.26.1+9
* `OptionsProcessor` extension point API changed to pass associated
`AnalysisContext` instance into the `optionsProcessed` call-back.
diff --git a/packages/analyzer/lib/file_system/memory_file_system.dart b/packages/analyzer/lib/file_system/memory_file_system.dart
index 1684fe9..058f9eb 100644
--- a/packages/analyzer/lib/file_system/memory_file_system.dart
+++ b/packages/analyzer/lib/file_system/memory_file_system.dart
@@ -363,7 +363,13 @@
@override
Uri resolveRelativeUri(Uri relativeUri) {
- return uri.resolveUri(relativeUri);
+ Uri baseUri = uri;
+ String scheme = uri.scheme;
+ if (scheme == DartUriResolver.DART_SCHEME) {
+ String libraryName = uri.path;
+ baseUri = Uri.parse('$scheme:$libraryName/$libraryName.dart');
+ }
+ return baseUri.resolveUri(relativeUri);
}
@override
diff --git a/packages/analyzer/lib/source/analysis_options_provider.dart b/packages/analyzer/lib/source/analysis_options_provider.dart
index 1c05375..aeac053 100644
--- a/packages/analyzer/lib/source/analysis_options_provider.dart
+++ b/packages/analyzer/lib/source/analysis_options_provider.dart
@@ -5,18 +5,16 @@
library source.analysis_options_provider;
import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/generated/engine.dart';
import 'package:yaml/yaml.dart';
/// Provide the options found in the `.analysis_options` file.
class AnalysisOptionsProvider {
- /// The name of the analysis options source file.
- static const String ANALYSIS_OPTIONS_NAME = '.analysis_options';
-
- /// Provide the options found in [root]/[ANALYSIS_OPTIONS_NAME].
+ /// Provide the options found in [root]/[ANALYSIS_OPTIONS_FILE].
/// Return an empty options map if the file does not exist.
Map<String, YamlNode> getOptions(Folder root) {
- var optionsSource =
- _readAnalysisOptionsFile(root.getChild(ANALYSIS_OPTIONS_NAME));
+ var optionsSource = _readAnalysisOptionsFile(
+ root.getChild(AnalysisEngine.ANALYSIS_OPTIONS_FILE));
return getOptionsFromString(optionsSource);
}
diff --git a/packages/analyzer/lib/src/context/context.dart b/packages/analyzer/lib/src/context/context.dart
index 893ae9f..dd2893f 100644
--- a/packages/analyzer/lib/src/context/context.dart
+++ b/packages/analyzer/lib/src/context/context.dart
@@ -115,6 +115,12 @@
AnalysisCache _cache;
/**
+ * Configuration data associated with this context.
+ */
+ final HashMap<ResultDescriptor, Object> _configurationData =
+ new HashMap<ResultDescriptor, Object>();
+
+ /**
* The task manager used to manage the tasks used to analyze code.
*/
TaskManager _taskManager;
@@ -409,6 +415,13 @@
}
}
+ /**
+ * Invalidate analysis cache.
+ */
+ void invalidateCachedResults() {
+ _cache = createCacheFromSourceFactory(_sourceFactory);
+ }
+
@override
List<Source> get sources {
return _cache.sources.toList();
@@ -583,12 +596,10 @@
@override
List<AnalysisError> computeErrors(Source source) {
String name = source.shortName;
- if (AnalysisEngine.isDartFileName(name)) {
- return computeResult(source, DART_ERRORS);
- } else if (AnalysisEngine.isHtmlFileName(name)) {
+ if (AnalysisEngine.isHtmlFileName(name)) {
return computeResult(source, HTML_ERRORS);
}
- return AnalysisError.NO_ERRORS;
+ return computeResult(source, DART_ERRORS);
}
@override
@@ -766,6 +777,9 @@
}
@override
+ Object getConfigurationData(ResultDescriptor key) => _configurationData[key];
+
+ @override
TimestampedData<String> getContents(Source source) {
String contents = _contentCache.getContents(source);
if (contents != null) {
@@ -1152,8 +1166,10 @@
entry.setState(RESOLVED_UNIT7, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT8, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT9, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT10, CacheState.FLUSHED);
// USED_IMPORTED_ELEMENTS
// USED_LOCAL_ELEMENTS
+ setValue(STRONG_MODE_ERRORS, AnalysisError.NO_ERRORS);
setValue(VARIABLE_REFERENCE_ERRORS, AnalysisError.NO_ERRORS);
setValue(VERIFY_ERRORS, AnalysisError.NO_ERRORS);
});
@@ -1201,6 +1217,11 @@
}
@override
+ void setConfigurationData(ResultDescriptor key, Object data) {
+ _configurationData[key] = data;
+ }
+
+ @override
void setContents(Source source, String contents) {
_contentsChanged(source, contents, true);
}
@@ -1230,6 +1251,7 @@
entry.setState(RESOLVED_UNIT7, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT8, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT9, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT10, CacheState.FLUSHED);
entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
}
diff --git a/packages/analyzer/lib/src/generated/ast.dart b/packages/analyzer/lib/src/generated/ast.dart
index 4b83206..03cb8f1 100644
--- a/packages/analyzer/lib/src/generated/ast.dart
+++ b/packages/analyzer/lib/src/generated/ast.dart
@@ -423,10 +423,8 @@
@override
Token get beginToken => leftParenthesis;
- /**
- * TODO(paulberry): Add commas.
- */
@override
+ // TODO(paulberry): Add commas.
Iterable get childEntities => new ChildEntities()
..add(leftParenthesis)
..addAll(_arguments)
@@ -3836,7 +3834,7 @@
Token get keyword => breakKeyword;
/**
- * Sethe token representing the 'break' keyword to the given [token].
+ * Set the token representing the 'break' keyword to the given [token].
*/
@deprecated // Use "this.breakKeyword"
void set keyword(Token token) {
@@ -9478,10 +9476,8 @@
@override
Token get beginToken => implementsKeyword;
- /**
- * TODO(paulberry): add commas.
- */
@override
+ // TODO(paulberry): add commas.
Iterable get childEntities => new ChildEntities()
..add(implementsKeyword)
..addAll(interfaces);
@@ -11657,10 +11653,8 @@
@override
Element get bestElement => staticElement;
- /**
- * TODO(paulberry): add "." tokens.
- */
@override
+ // TODO(paulberry): add "." tokens.
Iterable get childEntities => new ChildEntities()..addAll(_components);
/**
@@ -11750,10 +11744,8 @@
return leftBracket;
}
- /**
- * TODO(paulberry): add commas.
- */
@override
+ // TODO(paulberry): add commas.
Iterable get childEntities => super._childEntities
..add(leftBracket)
..addAll(_elements)
@@ -11840,10 +11832,8 @@
return leftBracket;
}
- /**
- * TODO(paulberry): add commas.
- */
@override
+ // TODO(paulberry): add commas.
Iterable get childEntities => super._childEntities
..add(leftBracket)
..addAll(entries)
@@ -12611,7 +12601,7 @@
StringLiteral get name => _name;
/**
- * Sets the name of the native object that implements the class to the given
+ * Set the name of the native object that implements the class to the given
* [name].
*/
void set name(StringLiteral name) {
@@ -16198,10 +16188,8 @@
_shownNames = new NodeList<SimpleIdentifier>(this, shownNames);
}
- /**
- * TODO(paulberry): add commas.
- */
@override
+ // TODO(paulberry): add commas.
Iterable get childEntities => new ChildEntities()
..add(keyword)
..addAll(_shownNames);
@@ -16700,7 +16688,7 @@
Token get endToken => token;
/**
- * Returns `true` if this identifier is the "name" part of a prefixed
+ * Return `true` if this identifier is the "name" part of a prefixed
* identifier or a method invocation.
*/
bool get isQualified {
@@ -17785,10 +17773,8 @@
@override
Token get beginToken => poundSign;
- /**
- * TODO(paulberry): add "." tokens.
- */
@override
+ // TODO(paulberry): add "." tokens.
Iterable get childEntities => new ChildEntities()
..add(poundSign)
..addAll(components);
@@ -18359,6 +18345,7 @@
@override
Object visitFieldFormalParameter(FieldFormalParameter node) {
+ _visitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
_visitTokenWithSuffix(node.keyword, " ");
_visitNodeWithSuffix(node.type, " ");
_writer.print("this.");
@@ -18484,6 +18471,7 @@
@override
Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+ _visitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
_visitNodeWithSuffix(node.returnType, " ");
_visitNode(node.identifier);
_visitNode(node.typeParameters);
@@ -18814,6 +18802,7 @@
@override
Object visitSimpleFormalParameter(SimpleFormalParameter node) {
+ _visitNodeListWithSeparatorAndSuffix(node.metadata, ' ', ' ');
_visitTokenWithSuffix(node.keyword, " ");
_visitNodeWithSuffix(node.type, " ");
_visitNode(node.identifier);
@@ -19322,10 +19311,8 @@
@override
Token get beginToken => leftBracket;
- /**
- * TODO(paulberry): Add commas.
- */
@override
+ // TODO(paulberry): Add commas.
Iterable get childEntities => new ChildEntities()
..add(leftBracket)
..addAll(_arguments)
@@ -20303,10 +20290,8 @@
_variables = new NodeList<VariableDeclaration>(this, variables);
}
- /**
- * TODO(paulberry): include commas.
- */
@override
+ // TODO(paulberry): include commas.
Iterable get childEntities => super._childEntities
..add(keyword)
..add(_type)
@@ -20559,10 +20544,8 @@
@override
Token get beginToken => withKeyword;
- /**
- * TODO(paulberry): add commas.
- */
@override
+ // TODO(paulberry): add commas.
Iterable get childEntities => new ChildEntities()
..add(withKeyword)
..addAll(_mixinTypes);
diff --git a/packages/analyzer/lib/src/generated/constant.dart b/packages/analyzer/lib/src/generated/constant.dart
index 0f4c0f6..c2ec01e 100644
--- a/packages/analyzer/lib/src/generated/constant.dart
+++ b/packages/analyzer/lib/src/generated/constant.dart
@@ -311,7 +311,7 @@
if (!identical(argumentValues[0].type, typeProvider.stringType)) {
return false;
}
- String name = argumentValues[0].stringValue;
+ String name = argumentValues[0].toStringValue();
return isValidPublicSymbol(name);
}
@@ -626,7 +626,7 @@
return null;
}
String variableName =
- argumentCount < 1 ? null : argumentValues[0].stringValue;
+ argumentCount < 1 ? null : argumentValues[0].toStringValue();
if (identical(definingClass, typeProvider.boolType)) {
DartObject valueFromEnvironment;
valueFromEnvironment =
@@ -661,7 +661,7 @@
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
return null;
}
- String argumentValue = argumentValues[0].stringValue;
+ String argumentValue = argumentValues[0].toStringValue();
return new DartObjectImpl(
definingClass, new SymbolState(argumentValue));
}
@@ -1598,9 +1598,9 @@
if (conditionResult == null) {
return conditionResult;
}
- if (conditionResult.isTrue) {
+ if (conditionResult.toBoolValue() == true) {
return thenResult;
- } else if (conditionResult.isFalse) {
+ } else if (conditionResult.toBoolValue() == false) {
return elseResult;
}
ParameterizedType thenType = thenResult.type;
@@ -1936,64 +1936,214 @@
}
/**
- * The state of a Dart object.
+ * A representation of the value of a compile-time constant expression.
+ *
+ * Note that, unlike the mirrors system, the object being represented does *not*
+ * exist. This interface allows static analysis tools to determine something
+ * about the state of the object that would exist if the code that creates the
+ * object were executed, but none of the code being analyzed is actually
+ * executed.
*/
abstract class DartObject {
/**
* Return the boolean value of this object, or `null` if either the value of
* this object is not known or this object is not of type 'bool'.
+ *
+ * Deprecated. Use [toBoolValue].
*/
+ @deprecated
bool get boolValue;
/**
* Return the floating point value of this object, or `null` if either the
* value of this object is not known or this object is not of type 'double'.
+ *
+ * Deprecated. Use [toDoubleValue].
*/
+ @deprecated
double get doubleValue;
/**
* Return `true` if this object's value can be represented exactly.
+ *
+ * Deprecated. The semantics of this method were not clear. One semantic is
+ * covered by [hasKnownValue].
*/
+ @deprecated
bool get hasExactValue;
/**
+ * Return `true` if the value of the object being represented is known.
+ *
+ * This method will return `false` if
+ * * the value being represented is the value of a declared variable (a
+ * variable whose value is provided at run-time using a `-D` command-line
+ * option), or
+ * * the value is a function.
+ *
+ * The result of this method does not imply anything about the state of
+ * object representations returned by the method [getField], those that are
+ * elements of the list returned by [toListValue], or the keys or values in
+ * the map returned by [toMapValue]. For example, a representation of a list
+ * can return `true` even if one or more of the elements of that list would
+ * return `false`.
+ */
+ bool get hasKnownValue;
+
+ /**
* Return the integer value of this object, or `null` if either the value of
* this object is not known or this object is not of type 'int'.
+ *
+ * Deprecated. Use [toIntValue].
*/
+ @deprecated
int get intValue;
/**
* Return `true` if this object represents the value 'false'.
+ *
+ * Deprecated. Use `object.toBoolValue() == false`.
*/
+ @deprecated
bool get isFalse;
/**
- * Return `true` if this object represents the value 'null'.
+ * Return `true` if the object being represented represents the value 'null'.
*/
bool get isNull;
/**
* Return `true` if this object represents the value 'true'.
+ *
+ * Deprecated. Use `object.toBoolValue() == true`.
*/
+ @deprecated
bool get isTrue;
/**
* Return the string value of this object, or `null` if either the value of
* this object is not known or this object is not of type 'String'.
+ *
+ * Deprecated. Use [toStringValue].
*/
+ @deprecated
String get stringValue;
/**
- * Return the run-time type of this object.
+ * Return a representation of the type of the object being represented.
+ *
+ * For values resulting from the invocation of a 'const' constructor, this
+ * will be a representation of the run-time type of the object.
+ *
+ * For values resulting from a literal expression, this will be a
+ * representation of the static type of the value -- `int` for integer
+ * literals, `List` for list literals, etc. -- even when the static type is an
+ * abstract type (such as `List`) and hence will never be the run-time type of
+ * the represented object.
+ *
+ * For values resulting from any other kind of expression, this will be a
+ * representation of the result of evaluating the expression.
+ *
+ * Return `null` if the expression cannot be evaluated, either because it is
+ * not a valid constant expression or because one or more of the values used
+ * in the expression does not have a known value.
+ *
+ * This method can return a representation of the type, even if this object
+ * would return `false` from [hasKnownValue].
*/
ParameterizedType get type;
/**
* Return this object's value if it can be represented exactly, or `null` if
* either the value cannot be represented exactly or if the value is `null`.
- * Clients should use [hasExactValue] to distinguish between these two cases.
+ * Clients should use `hasExactValue` to distinguish between these two cases.
+ *
+ * Deprecated. Use one of the `isXValue()` methods.
*/
+ @deprecated
Object get value;
+
+ /**
+ * Return a representation of the value of the field with the given [name].
+ *
+ * Return `null` if either the object being represented does not have a field
+ * with the given name or if the implementation of the class of the object is
+ * invalid, making it impossible to determine that value of the field.
+ *
+ * Note that, unlike the mirrors API, this method does *not* invoke a getter;
+ * it simply returns a representation of the known state of a field.
+ */
+ DartObject getField(String name);
+
+ /**
+ * Return a boolean corresponding to the value of the object being
+ * represented, or `null` if
+ * * this object is not of type 'bool',
+ * * the value of the object being represented is not known, or
+ * * the value of the object being represented is `null`.
+ */
+ bool toBoolValue();
+
+ /**
+ * Return a double corresponding to the value of the object being represented,
+ * or `null`
+ * if
+ * * this object is not of type 'double',
+ * * the value of the object being represented is not known, or
+ * * the value of the object being represented is `null`.
+ */
+ double toDoubleValue();
+
+ /**
+ * Return an integer corresponding to the value of the object being
+ * represented, or `null` if
+ * * this object is not of type 'int',
+ * * the value of the object being represented is not known, or
+ * * the value of the object being represented is `null`.
+ */
+ int toIntValue();
+
+ /**
+ * Return a list corresponding to the value of the object being represented,
+ * or `null` if
+ * * this object is not of type 'List', or
+ * * the value of the object being represented is `null`.
+ */
+ List<DartObject> toListValue();
+
+ /**
+ * Return a map corresponding to the value of the object being represented, or
+ * `null` if
+ * * this object is not of type 'Map', or
+ * * the value of the object being represented is `null`.
+ */
+ Map<DartObject, DartObject> toMapValue();
+
+ /**
+ * Return a string corresponding to the value of the object being represented,
+ * or `null` if
+ * * this object is not of type 'String',
+ * * the value of the object being represented is not known, or
+ * * the value of the object being represented is `null`.
+ */
+ String toStringValue();
+
+ /**
+ * Return a string corresponding to the value of the object being represented,
+ * or `null` if
+ * * this object is not of type 'Symbol', or
+ * * the value of the object being represented is `null`.
+ * (We return the string
+ */
+ String toSymbolValue();
+
+ /**
+ * Return the representation of the type corresponding to the value of the
+ * object being represented, or `null` if
+ * * this object is not of type 'Type', or
+ * * the value of the object being represented is `null`.
+ */
+ ParameterizedType toTypeValue();
}
/**
@@ -2390,24 +2540,17 @@
return new DartObjectImpl(type, GenericState.UNKNOWN_VALUE);
}
+ @deprecated
@override
- bool get boolValue {
- if (_state is BoolState) {
- return (_state as BoolState).value;
- }
- return null;
- }
+ bool get boolValue => toBoolValue();
+ @deprecated
@override
- double get doubleValue {
- if (_state is DoubleState) {
- return (_state as DoubleState).value;
- }
- return null;
- }
+ double get doubleValue => toDoubleValue();
HashMap<String, DartObjectImpl> get fields => _state.fields;
+ @deprecated
@override
bool get hasExactValue => _state.hasExactValue;
@@ -2415,12 +2558,11 @@
int get hashCode => JenkinsSmiHash.hash2(type.hashCode, _state.hashCode);
@override
- int get intValue {
- if (_state is IntState) {
- return (_state as IntState).value;
- }
- return null;
- }
+ bool get hasKnownValue => !_state.isUnknown;
+
+ @deprecated
+ @override
+ int get intValue => toIntValue();
/**
* Return `true` if this object represents an object whose type is 'bool'.
@@ -2433,13 +2575,14 @@
*/
bool get isBoolNumStringOrNull => _state.isBoolNumStringOrNull;
+ @deprecated
@override
- bool get isFalse =>
- _state is BoolState && identical((_state as BoolState).value, false);
+ bool get isFalse => toBoolValue() == false;
@override
bool get isNull => _state is NullState;
+ @deprecated
@override
bool get isTrue =>
_state is BoolState && identical((_state as BoolState).value, true);
@@ -2455,14 +2598,11 @@
*/
bool get isUserDefinedObject => _state is GenericState;
+ @deprecated
@override
- String get stringValue {
- if (_state is StringState) {
- return (_state as StringState).value;
- }
- return null;
- }
+ String get stringValue => toStringValue();
+ @deprecated
@override
Object get value => _state.value;
@@ -2624,6 +2764,14 @@
typeProvider.boolType, _state.equalEqual(rightOperand._state));
}
+ @override
+ DartObject getField(String name) {
+ if (_state is GenericState) {
+ return (_state as GenericState).fields[name];
+ }
+ return null;
+ }
+
/**
* Return the result of invoking the '>' operator on this object with the
* [rightOperand]. The [typeProvider] is the type provider used to find known
@@ -2897,7 +3045,77 @@
}
@override
+ bool toBoolValue() {
+ if (_state is BoolState) {
+ return (_state as BoolState).value;
+ }
+ return null;
+ }
+
+ @override
+ double toDoubleValue() {
+ if (_state is DoubleState) {
+ return (_state as DoubleState).value;
+ }
+ return null;
+ }
+
+ @override
+ int toIntValue() {
+ if (_state is IntState) {
+ return (_state as IntState).value;
+ }
+ return null;
+ }
+
+ @override
+ List<DartObject> toListValue() {
+ if (_state is ListState) {
+ return (_state as ListState)._elements;
+ }
+ return null;
+ }
+
+ @override
+ Map<DartObject, DartObject> toMapValue() {
+ if (_state is MapState) {
+ return (_state as MapState)._entries;
+ }
+ return null;
+ }
+
+ @override
String toString() => "${type.displayName} ($_state)";
+
+ @override
+ String toStringValue() {
+ if (_state is StringState) {
+ return (_state as StringState).value;
+ }
+ return null;
+ }
+
+ @override
+ String toSymbolValue() {
+ if (_state is SymbolState) {
+ return (_state as SymbolState).value;
+ }
+ return null;
+ }
+
+ @override
+ ParameterizedType toTypeValue() {
+ if (_state is TypeState) {
+ Element element = (_state as TypeState).value;
+ if (element is ClassElement) {
+ return element.type;
+ }
+ if (element is FunctionElement) {
+ return element.type;
+ }
+ }
+ return null;
+ }
}
/**
diff --git a/packages/analyzer/lib/src/generated/element.dart b/packages/analyzer/lib/src/generated/element.dart
index 9e7aeb4..0fcb8e5 100644
--- a/packages/analyzer/lib/src/generated/element.dart
+++ b/packages/analyzer/lib/src/generated/element.dart
@@ -13,7 +13,7 @@
show AnalysisTarget, ConstantEvaluationTarget;
import 'ast.dart';
-import 'constant.dart' show EvaluationResultImpl;
+import 'constant.dart' show DartObject, EvaluationResultImpl;
import 'engine.dart' show AnalysisContext, AnalysisEngine, AnalysisException;
import 'html.dart' show XmlAttributeNode, XmlTagNode;
import 'java_core.dart';
@@ -1760,6 +1760,9 @@
ConstFieldElementImpl.forNode(Identifier name) : super.forNode(name);
@override
+ DartObject get constantValue => _result.value;
+
+ @override
EvaluationResultImpl get evaluationResult => _result;
@override
@@ -1791,6 +1794,9 @@
ConstLocalVariableElementImpl.forNode(Identifier name) : super.forNode(name);
@override
+ DartObject get constantValue => _result.value;
+
+ @override
EvaluationResultImpl get evaluationResult => _result;
@override
@@ -2109,6 +2115,9 @@
ConstTopLevelVariableElementImpl(Identifier name) : super.forNode(name);
@override
+ DartObject get constantValue => _result.value;
+
+ @override
EvaluationResultImpl get evaluationResult => _result;
@override
@@ -2273,6 +2282,9 @@
DefaultFieldFormalParameterElementImpl(Identifier name) : super(name);
@override
+ DartObject get constantValue => _result.value;
+
+ @override
EvaluationResultImpl get evaluationResult => _result;
@override
@@ -2297,6 +2309,9 @@
DefaultParameterElementImpl(Identifier name) : super.forNode(name);
@override
+ DartObject get constantValue => _result.value;
+
+ @override
EvaluationResultImpl get evaluationResult => _result;
@override
@@ -2659,6 +2674,14 @@
static const List<ElementAnnotation> EMPTY_LIST = const <ElementAnnotation>[];
/**
+ * Return a representation of the value of this annotation.
+ *
+ * Return `null` if the value of this annotation could not be computed because
+ * of errors.
+ */
+ DartObject get constantValue;
+
+ /**
* Return the element representing the field, variable, or const constructor
* being used as an annotation.
*/
@@ -2738,6 +2761,9 @@
ElementAnnotationImpl(this.element);
@override
+ DartObject get constantValue => evaluationResult.value;
+
+ @override
bool get isDeprecated {
if (element != null) {
LibraryElement library = element.library;
@@ -10610,6 +10636,15 @@
static const List<VariableElement> EMPTY_LIST = const <VariableElement>[];
/**
+ * Return a representation of the value of this variable.
+ *
+ * Return `null` if either this variable was not declared with the 'const'
+ * modifier or if the value of this variable could not be computed because of
+ * errors.
+ */
+ DartObject get constantValue;
+
+ /**
* Return `true` if this variable element did not have an explicit type
* specified for it.
*/
@@ -10712,6 +10747,9 @@
setModifier(Modifier.CONST, isConst);
}
+ @override
+ DartObject get constantValue => null;
+
/**
* Return the result of evaluating this variable's initializer as a
* compile-time constant expression, or `null` if this variable is not a
@@ -10805,6 +10843,9 @@
VariableElement get baseElement => super.baseElement as VariableElement;
@override
+ DartObject get constantValue => baseElement.constantValue;
+
+ @override
bool get hasImplicitType => baseElement.hasImplicitType;
@override
diff --git a/packages/analyzer/lib/src/generated/element_handle.dart b/packages/analyzer/lib/src/generated/element_handle.dart
index 338a279..f496602 100644
--- a/packages/analyzer/lib/src/generated/element_handle.dart
+++ b/packages/analyzer/lib/src/generated/element_handle.dart
@@ -4,14 +4,15 @@
library engine.element_handle;
-import 'ast.dart';
-import 'element.dart';
-import 'engine.dart';
-import 'java_core.dart';
-import 'java_engine.dart';
-import 'resolver.dart';
-import 'source.dart';
-import 'utilities_dart.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/java_core.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart';
/**
* Instances of the class `ClassElementHandle` implement a handle to a `ClassElement`.
@@ -1092,6 +1093,9 @@
VariableElement get actualElement => super.actualElement as VariableElement;
@override
+ DartObject get constantValue => actualElement.constantValue;
+
+ @override
bool get hasImplicitType => actualElement.hasImplicitType;
@override
diff --git a/packages/analyzer/lib/src/generated/element_resolver.dart b/packages/analyzer/lib/src/generated/element_resolver.dart
index ab8fe91..ffe623b 100644
--- a/packages/analyzer/lib/src/generated/element_resolver.dart
+++ b/packages/analyzer/lib/src/generated/element_resolver.dart
@@ -974,10 +974,18 @@
return null;
}
//
- // We ignore identifiers that have already been resolved, such as
- // identifiers representing the name in a declaration.
+ // Ignore nodes that should have been resolved before getting here.
//
- if (node.staticElement != null) {
+ if (node.inDeclarationContext()) {
+ return null;
+ }
+ AstNode parent = node.parent;
+ if (parent is FieldFormalParameter) {
+ return null;
+ } else if (parent is ConstructorFieldInitializer &&
+ parent.fieldName == node) {
+ return null;
+ } else if (parent is Annotation && parent.constructorName == node) {
return null;
}
//
diff --git a/packages/analyzer/lib/src/generated/engine.dart b/packages/analyzer/lib/src/generated/engine.dart
index 9560bff..acb156e 100644
--- a/packages/analyzer/lib/src/generated/engine.dart
+++ b/packages/analyzer/lib/src/generated/engine.dart
@@ -18,8 +18,10 @@
import 'package:analyzer/src/services/lint.dart';
import 'package:analyzer/src/task/manager.dart';
import 'package:analyzer/task/dart.dart';
+import 'package:analyzer/task/model.dart' as newContext;
import 'package:analyzer/task/model.dart';
import 'package:html/dom.dart' show Document;
+import 'package:path/path.dart' as pathos;
import 'package:plugin/manager.dart';
import 'package:plugin/plugin.dart';
@@ -589,6 +591,14 @@
Source unitSource, Source librarySource);
/**
+ * Return configuration data associated with the given key or `null` if no
+ * state has been associated with the given [key].
+ *
+ * See [setConfigurationData].
+ */
+ Object getConfigurationData(ResultDescriptor key);
+
+ /**
* Return the contents and timestamp of the given [source].
*
* This method should be used rather than the method [Source.getContents]
@@ -885,6 +895,13 @@
Source source, String contents, int offset, int oldLength, int newLength);
/**
+ * Associate this configuration [data] object with the given descriptor [key].
+ *
+ * See [getConfigurationData].
+ */
+ void setConfigurationData(ResultDescriptor key, Object data);
+
+ /**
* Set the contents of the given [source] to the given [contents] and mark the
* source as having changed. This has the effect of overriding the default
* contents of the source. If the contents are `null` the override is removed
@@ -937,6 +954,12 @@
String name;
/**
+ * Configuration data associated with this context.
+ */
+ final HashMap<ResultDescriptor, Object> _configurationData =
+ new HashMap<ResultDescriptor, Object>();
+
+ /**
* The set of analysis options controlling the behavior of this context.
*/
AnalysisOptionsImpl _options = new AnalysisOptionsImpl();
@@ -1568,6 +1591,11 @@
}
@override
+ List<newContext.WorkManager> get workManagers {
+ throw new NotImplementedException('In not task-based AnalysisContext.');
+ }
+
+ @override
void addListener(AnalysisListener listener) {
if (!_listeners.contains(listener)) {
_listeners.add(listener);
@@ -1934,6 +1962,9 @@
}
@override
+ Object getConfigurationData(ResultDescriptor key) => _configurationData[key];
+
+ @override
TimestampedData<String> getContents(Source source) {
String contents = _contentCache.getContents(source);
if (contents != null) {
@@ -2630,6 +2661,11 @@
}
@override
+ void setConfigurationData(ResultDescriptor key, Object data) {
+ _configurationData[key] = data;
+ }
+
+ @override
void setContents(Source source, String contents) {
_contentsChanged(source, contents, true);
}
@@ -5840,6 +5876,11 @@
static const String SUFFIX_HTML = "html";
/**
+ * The file name used for analysis options files.
+ */
+ static const String ANALYSIS_OPTIONS_FILE = '.analysis_options';
+
+ /**
* The unique instance of this class.
*/
static final AnalysisEngine instance = new AnalysisEngine._();
@@ -6008,6 +6049,18 @@
}
/**
+ * Return `true` if the given [fileName] is an analysis options file.
+ */
+ static bool isAnalysisOptionsFileName(String fileName,
+ [pathos.Context context]) {
+ if (fileName == null) {
+ return false;
+ }
+ return (context ?? pathos.posix).basename(fileName) ==
+ ANALYSIS_OPTIONS_FILE;
+ }
+
+ /**
* Return `true` if the given [fileName] is assumed to contain Dart source
* code.
*/
@@ -9395,6 +9448,11 @@
TypeResolverVisitorFactory get typeResolverVisitorFactory;
/**
+ * A list of all [WorkManager]s used by this context.
+ */
+ List<newContext.WorkManager> get workManagers;
+
+ /**
* Return a list containing the sources of the libraries that are exported by
* the library with the given [source]. The list will be empty if the given
* source is invalid, if the given source does not represent a library, or if
diff --git a/packages/analyzer/lib/src/generated/incremental_resolver.dart b/packages/analyzer/lib/src/generated/incremental_resolver.dart
index 86a87bd..4a8dada 100644
--- a/packages/analyzer/lib/src/generated/incremental_resolver.dart
+++ b/packages/analyzer/lib/src/generated/incremental_resolver.dart
@@ -921,6 +921,7 @@
isByTask(ResolveUnitTask.DESCRIPTOR) ||
isByTask(ResolveUnitTypeNamesTask.DESCRIPTOR) ||
isByTask(ResolveVariableReferencesTask.DESCRIPTOR) ||
+ isByTask(StrongModeVerifyUnitTask.DESCRIPTOR) ||
isByTask(VerifyUnitTask.DESCRIPTOR)) {
return DeltaResult.KEEP_CONTINUE;
}
@@ -1245,6 +1246,7 @@
_shiftErrors_NEW(LIBRARY_UNIT_ERRORS);
_shiftErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS);
_shiftErrors_NEW(RESOLVE_UNIT_ERRORS);
+ _shiftErrors_NEW(STRONG_MODE_ERRORS);
_shiftErrors_NEW(VARIABLE_REFERENCE_ERRORS);
_shiftErrors_NEW(VERIFY_ERRORS);
}
@@ -1316,6 +1318,7 @@
_updateErrors_NEW(VARIABLE_REFERENCE_ERRORS, []);
_updateErrors_NEW(VERIFY_ERRORS, _verifyErrors);
// invalidate results we don't update incrementally
+ newUnitEntry.setState(STRONG_MODE_ERRORS, CacheState.INVALID);
newUnitEntry.setState(USED_IMPORTED_ELEMENTS, CacheState.INVALID);
newUnitEntry.setState(USED_LOCAL_ELEMENTS, CacheState.INVALID);
newUnitEntry.setState(HINTS, CacheState.INVALID);
diff --git a/packages/analyzer/lib/src/generated/parser.dart b/packages/analyzer/lib/src/generated/parser.dart
index 113da91..76f9dc7 100644
--- a/packages/analyzer/lib/src/generated/parser.dart
+++ b/packages/analyzer/lib/src/generated/parser.dart
@@ -4038,22 +4038,52 @@
TokenType.INDEX
]);
}
+ bool allowAdditionalTokens = true;
// We know that we have an identifier, and need to see whether it might be
// a type name.
+ if (_currentToken.type != TokenType.IDENTIFIER) {
+ allowAdditionalTokens = false;
+ }
Token token = _skipTypeName(_currentToken);
if (token == null) {
// There was no type name, so this can't be a declaration.
return false;
}
+ if (token.type != TokenType.IDENTIFIER) {
+ allowAdditionalTokens = false;
+ }
token = _skipSimpleIdentifier(token);
if (token == null) {
return false;
}
TokenType type = token.type;
- return type == TokenType.EQ ||
+ // Usual cases in valid code:
+ // String v = '';
+ // String v, v2;
+ // String v;
+ // for (String item in items) {}
+ if (type == TokenType.EQ ||
type == TokenType.COMMA ||
type == TokenType.SEMICOLON ||
- _tokenMatchesKeyword(token, Keyword.IN);
+ _tokenMatchesKeyword(token, Keyword.IN)) {
+ return true;
+ }
+ // It is OK to parse as a variable declaration in these cases:
+ // String v }
+ // String v if (true) print('OK');
+ // String v { print(42); }
+ // ...but not in these cases:
+ // get getterName {
+ // String get getterName
+ if (allowAdditionalTokens) {
+ if (type == TokenType.CLOSE_CURLY_BRACKET ||
+ type == TokenType.KEYWORD ||
+ type == TokenType.IDENTIFIER ||
+ type == TokenType.OPEN_CURLY_BRACKET) {
+ return true;
+ }
+ }
+ return false;
}
bool _isLikelyParameterList() {
@@ -5829,12 +5859,12 @@
_reportErrorForToken(
ParserErrorCode.INVALID_AWAIT_IN_FOR, awaitKeyword);
}
- Token leftSeparator = _expect(TokenType.SEMICOLON);
+ Token leftSeparator = _expectSemicolon();
Expression condition = null;
if (!_matches(TokenType.SEMICOLON)) {
condition = parseExpression2();
}
- Token rightSeparator = _expect(TokenType.SEMICOLON);
+ Token rightSeparator = _expectSemicolon();
List<Expression> updaters = null;
if (!_matches(TokenType.CLOSE_PAREN)) {
updaters = _parseExpressionList();
@@ -8281,8 +8311,7 @@
*/
Token _skipSimpleIdentifier(Token startToken) {
if (_tokenMatches(startToken, TokenType.IDENTIFIER) ||
- (_tokenMatches(startToken, TokenType.KEYWORD) &&
- (startToken as KeywordToken).keyword.isPseudoKeyword)) {
+ _tokenMatchesPseudoKeyword(startToken)) {
return startToken.next;
}
return null;
@@ -8506,8 +8535,7 @@
*/
bool _tokenMatchesIdentifier(Token token) =>
_tokenMatches(token, TokenType.IDENTIFIER) ||
- (_tokenMatches(token, TokenType.KEYWORD) &&
- (token as KeywordToken).keyword.isPseudoKeyword);
+ _tokenMatchesPseudoKeyword(token);
/**
* Return `true` if the given [token] matches the given [keyword].
@@ -8517,6 +8545,13 @@
(token as KeywordToken).keyword == keyword;
/**
+ * Return `true` if the given [token] matches a pseudo keyword.
+ */
+ bool _tokenMatchesPseudoKeyword(Token token) =>
+ _tokenMatches(token, TokenType.KEYWORD) &&
+ (token as KeywordToken).keyword.isPseudoKeyword;
+
+ /**
* Return `true` if the given [token] matches the given [identifier].
*/
bool _tokenMatchesString(Token token, String identifier) =>
diff --git a/packages/analyzer/lib/src/generated/resolver.dart b/packages/analyzer/lib/src/generated/resolver.dart
index a5b2e92..47a68ee 100644
--- a/packages/analyzer/lib/src/generated/resolver.dart
+++ b/packages/analyzer/lib/src/generated/resolver.dart
@@ -1521,14 +1521,14 @@
if (!_isDebugConstant(lhsCondition)) {
EvaluationResultImpl lhsResult = _getConstantBooleanValue(lhsCondition);
if (lhsResult != null) {
- if (lhsResult.value.isTrue && isBarBar) {
+ if (lhsResult.value.toBoolValue() == true && isBarBar) {
// report error on else block: true || !e!
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, node.rightOperand);
// only visit the LHS:
_safelyVisit(lhsCondition);
return null;
- } else if (lhsResult.value.isFalse && isAmpAmp) {
+ } else if (lhsResult.value.toBoolValue() == false && isAmpAmp) {
// report error on if block: false && !e!
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, node.rightOperand);
@@ -1582,7 +1582,7 @@
EvaluationResultImpl result =
_getConstantBooleanValue(conditionExpression);
if (result != null) {
- if (result.value.isTrue) {
+ if (result.value.toBoolValue() == true) {
// report error on else block: true ? 1 : !2!
_errorReporter.reportErrorForNode(
HintCode.DEAD_CODE, node.elseExpression);
@@ -1608,7 +1608,7 @@
EvaluationResultImpl result =
_getConstantBooleanValue(conditionExpression);
if (result != null) {
- if (result.value.isTrue) {
+ if (result.value.toBoolValue() == true) {
// report error on else block: if(true) {} else {!}
Statement elseStatement = node.elseStatement;
if (elseStatement != null) {
@@ -1717,7 +1717,7 @@
EvaluationResultImpl result =
_getConstantBooleanValue(conditionExpression);
if (result != null) {
- if (result.value.isFalse) {
+ if (result.value.toBoolValue() == false) {
// report error on if block: while (false) {!}
_errorReporter.reportErrorForNode(HintCode.DEAD_CODE, node.body);
return null;
@@ -3234,6 +3234,9 @@
variable = new TopLevelVariableElementImpl.forNode(variableName);
}
element = variable;
+ if (node.parent.parent is TopLevelVariableDeclaration) {
+ _setDocRange(element, node.parent.parent);
+ }
if ((node.parent as VariableDeclarationList).type == null) {
variable.hasImplicitType = true;
}
@@ -14419,11 +14422,11 @@
PropertyInducingElementImpl variable =
accessor.variable as PropertyInducingElementImpl;
if (accessor.isGetter) {
- variable.type = type.returnType;
+ variable.type = type.baseReturnType;
} else if (variable.type == null) {
- List<DartType> parameterTypes = type.normalParameterTypes;
- if (parameterTypes != null && parameterTypes.length > 0) {
- variable.type = parameterTypes[0];
+ List<ParameterElement> parameters = type.baseParameters;
+ if (parameters != null && parameters.length > 0) {
+ variable.type = parameters[0].type;
}
}
}
@@ -15721,11 +15724,20 @@
@override
Object visitSimpleIdentifier(SimpleIdentifier node) {
// Ignore if already resolved - declaration or type.
- if (node.staticElement != null) {
+ if (node.inDeclarationContext()) {
+ return null;
+ }
+ // Ignore if it cannot be a reference to a local variable.
+ AstNode parent = node.parent;
+ if (parent is FieldFormalParameter) {
+ return null;
+ } else if (parent is ConstructorDeclaration && parent.returnType == node) {
+ return null;
+ } else if (parent is ConstructorFieldInitializer &&
+ parent.fieldName == node) {
return null;
}
// Ignore if qualified.
- AstNode parent = node.parent;
if (parent is PrefixedIdentifier && identical(parent.identifier, node)) {
return null;
}
@@ -15775,6 +15787,11 @@
}
return null;
}
+
+ @override
+ Object visitTypeName(TypeName node) {
+ return null;
+ }
}
class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor {
diff --git a/packages/analyzer/lib/src/generated/sdk.dart b/packages/analyzer/lib/src/generated/sdk.dart
index 1bf502b..c4f034f 100644
--- a/packages/analyzer/lib/src/generated/sdk.dart
+++ b/packages/analyzer/lib/src/generated/sdk.dart
@@ -145,7 +145,7 @@
* The name of the optional parameter used to specify the category of the
* library.
*/
- static String _CATEGORY = "category";
+ static String _CATEGORIES = "categories";
/**
* The name of the optional parameter used to specify the platforms on which
@@ -183,6 +183,20 @@
*/
LibraryMap get librariesMap => _librariesMap;
+
+ // To be backwards-compatible the new categories field is translated to
+ // an old approximation.
+ String convertCategories(String categories) {
+ switch (categories) {
+ case "": return "Internal";
+ case "Client": return "Client";
+ case "Server": return "Server";
+ case "Client,Server": return "Shared";
+ case "Client,Server,Embedded": return "Shared";
+ }
+ return "Shared";
+ }
+
@override
Object visitMapLiteralEntry(MapLiteralEntry node) {
String libraryName = null;
@@ -200,8 +214,9 @@
} else if (argument is NamedExpression) {
String name = argument.name.label.name;
Expression expression = argument.expression;
- if (name == _CATEGORY) {
- library.category = (expression as SimpleStringLiteral).value;
+ if (name == _CATEGORIES) {
+ library.category =
+ convertCategories((expression as StringLiteral).stringValue);
} else if (name == _IMPLEMENTATION) {
library.implementation = (expression as BooleanLiteral).value;
} else if (name == _DOCUMENTED) {
diff --git a/packages/analyzer/lib/src/generated/static_type_analyzer.dart b/packages/analyzer/lib/src/generated/static_type_analyzer.dart
index 7362c4d..ce0016c 100644
--- a/packages/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/packages/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -1419,12 +1419,17 @@
}
// TODO(vsm): Use leafp's matchType here?
- DartType _findIteratedType(InterfaceType type, DartType targetType) {
+ DartType _findIteratedType(DartType type, DartType targetType) {
// Set by _find if match is found
DartType result = null;
// Elements we've already visited on a given inheritance path.
HashSet<ClassElement> visitedClasses = null;
+ while (type is TypeParameterType) {
+ TypeParameterElement element = type.element;
+ type = element.bound;
+ }
+
bool _find(InterfaceType type) {
ClassElement element = type.element;
if (type == _typeProvider.objectType || element == null) {
@@ -1451,7 +1456,9 @@
visitedClasses.remove(element);
}
}
- _find(type);
+ if (type is InterfaceType) {
+ _find(type);
+ }
return result;
}
@@ -1691,15 +1698,13 @@
Expression expr = loop.iterable;
LocalVariableElementImpl element = loopVariable.element;
DartType exprType = expr.staticType;
- if (exprType is InterfaceType) {
- DartType targetType = (loop.awaitKeyword == null)
- ? _typeProvider.iterableType
+ DartType targetType = (loop.awaitKeyword == null)
+ ? _typeProvider.iterableType
: _typeProvider.streamType;
- DartType iteratedType = _findIteratedType(exprType, targetType);
- if (element != null && iteratedType != null) {
- element.type = iteratedType;
- loopVariable.identifier.staticType = iteratedType;
- }
+ DartType iteratedType = _findIteratedType(exprType, targetType);
+ if (element != null && iteratedType != null) {
+ element.type = iteratedType;
+ loopVariable.identifier.staticType = iteratedType;
}
}
}
diff --git a/packages/analyzer/lib/src/plugin/engine_plugin.dart b/packages/analyzer/lib/src/plugin/engine_plugin.dart
index 6573b0a..a53deb8 100644
--- a/packages/analyzer/lib/src/plugin/engine_plugin.dart
+++ b/packages/analyzer/lib/src/plugin/engine_plugin.dart
@@ -99,21 +99,24 @@
* Return a list containing all of the contributed analysis error result
* descriptors for Dart sources.
*/
- List<TaskDescriptor> get dartErrorsForSource =>
+ @ExtensionPointId('DART_ERRORS_FOR_SOURCE_EXTENSION_POINT_ID')
+ List<ResultDescriptor> get dartErrorsForSource =>
dartErrorsForSourceExtensionPoint.extensions;
/**
* Return a list containing all of the contributed analysis error result
* descriptors for Dart library specific units.
*/
- List<TaskDescriptor> get dartErrorsForUnit =>
+ @ExtensionPointId('DART_ERRORS_FOR_UNIT_EXTENSION_POINT_ID')
+ List<ResultDescriptor> get dartErrorsForUnit =>
dartErrorsForUnitExtensionPoint.extensions;
/**
* Return a list containing all of the contributed analysis error result
* descriptors for HTML sources.
*/
- List<TaskDescriptor> get htmlErrors => htmlErrorsExtensionPoint.extensions;
+ @ExtensionPointId('HTML_ERRORS_EXTENSION_POINT_ID')
+ List<ResultDescriptor> get htmlErrors => htmlErrorsExtensionPoint.extensions;
/**
* Return a list containing all of the task descriptors that were contributed.
@@ -158,19 +161,17 @@
}
void _registerDartErrorsForSource(RegisterExtension registerExtension) {
- String id = DART_ERRORS_FOR_SOURCE_EXTENSION_POINT_ID;
- registerExtension(id, PARSE_ERRORS);
- registerExtension(id, SCAN_ERRORS);
+ registerExtension(DART_ERRORS_FOR_SOURCE_EXTENSION_POINT_ID, PARSE_ERRORS);
+ registerExtension(DART_ERRORS_FOR_SOURCE_EXTENSION_POINT_ID, SCAN_ERRORS);
}
void _registerDartErrorsForUnit(RegisterExtension registerExtension) {
- String id = DART_ERRORS_FOR_UNIT_EXTENSION_POINT_ID;
- registerExtension(id, LIBRARY_UNIT_ERRORS);
+ registerExtension(
+ DART_ERRORS_FOR_UNIT_EXTENSION_POINT_ID, LIBRARY_UNIT_ERRORS);
}
void _registerHtmlErrors(RegisterExtension registerExtension) {
- String id = HTML_ERRORS_EXTENSION_POINT_ID;
- registerExtension(id, HTML_DOCUMENT_ERRORS);
+ registerExtension(HTML_ERRORS_EXTENSION_POINT_ID, HTML_DOCUMENT_ERRORS);
}
void _registerTaskExtensions(RegisterExtension registerExtension) {
@@ -217,6 +218,7 @@
registerExtension(taskId, ResolveUnitTypeNamesTask.DESCRIPTOR);
registerExtension(taskId, ResolveVariableReferencesTask.DESCRIPTOR);
registerExtension(taskId, ScanDartTask.DESCRIPTOR);
+ registerExtension(taskId, StrongModeVerifyUnitTask.DESCRIPTOR);
registerExtension(taskId, VerifyUnitTask.DESCRIPTOR);
//
// Register HTML tasks.
@@ -270,3 +272,17 @@
}
}
}
+
+/**
+ * Annotation describing the relationship between a getter in [EnginePlugin]
+ * and the associated identifier (in '../../plugin/task.dart') which can be
+ * passed to the extension manager to populate it.
+ *
+ * This annotation is not used at runtime; it is used to aid in static analysis
+ * of the task model during development.
+ */
+class ExtensionPointId {
+ final String extensionPointId;
+
+ const ExtensionPointId(this.extensionPointId);
+}
diff --git a/packages/analyzer/lib/src/plugin/options_plugin.dart b/packages/analyzer/lib/src/plugin/options_plugin.dart
index fc82910..3fb5e91 100644
--- a/packages/analyzer/lib/src/plugin/options_plugin.dart
+++ b/packages/analyzer/lib/src/plugin/options_plugin.dart
@@ -23,7 +23,9 @@
/// All contributed options processors.
List<OptionsProcessor> get optionsProcessors =>
- optionsProcessorExtensionPoint.extensions;
+ optionsProcessorExtensionPoint == null
+ ? const []
+ : optionsProcessorExtensionPoint.extensions;
@override
String get uniqueIdentifier => UNIQUE_IDENTIFIER;
diff --git a/packages/analyzer/lib/src/services/lint.dart b/packages/analyzer/lib/src/services/lint.dart
index 5efc3ea..a6b55cc 100644
--- a/packages/analyzer/lib/src/services/lint.dart
+++ b/packages/analyzer/lib/src/services/lint.dart
@@ -9,11 +9,25 @@
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/visitors.dart';
+import 'package:analyzer/src/task/model.dart';
+import 'package:analyzer/task/model.dart';
-/// A registry containing mappings of contexts to their associated configured
-/// lints.
-final Map<AnalysisContext, List<Linter>> lintRegistry =
- <AnalysisContext, List<Linter>>{};
+const List<Linter> _noLints = const <Linter>[];
+
+/// The descriptor used to associate lints with analysis contexts in
+/// configuration data.
+final ResultDescriptor<List<Linter>> CONFIGURED_LINTS_KEY =
+ new ResultDescriptorImpl('configured.lints', _noLints);
+
+/// Return lints associated with this [context], or an empty list if there are
+/// none.
+List<Linter> getLints(AnalysisContext context) =>
+ context.getConfigurationData(CONFIGURED_LINTS_KEY) ?? _noLints;
+
+/// Associate these [lints] with the given [context].
+void setLints(AnalysisContext context, List<Linter> lints) {
+ context.setConfigurationData(CONFIGURED_LINTS_KEY, lints);
+}
/// Implementers contribute lint warnings via the provided error [reporter].
abstract class Linter {
@@ -32,9 +46,7 @@
///
/// See [LintCode].
class LintGenerator {
- /// A global container for contributed linters.
- @deprecated // Use lintRegistry.
- static final List<Linter> LINTERS = <Linter>[];
+ static const List<Linter> _noLints = const <Linter>[];
final Iterable<CompilationUnit> _compilationUnits;
final AnalysisErrorListener _errorListener;
@@ -42,7 +54,7 @@
LintGenerator(this._compilationUnits, this._errorListener,
[Iterable<Linter> linters])
- : _linters = linters ?? LINTERS;
+ : _linters = linters ?? _noLints;
void generate() {
PerformanceStatistics.lint.makeCurrentWhile(() {
diff --git a/packages/analyzer/lib/src/task/dart.dart b/packages/analyzer/lib/src/task/dart.dart
index 81745f7..b87f776 100644
--- a/packages/analyzer/lib/src/task/dart.dart
+++ b/packages/analyzer/lib/src/task/dart.dart
@@ -29,6 +29,8 @@
import 'package:analyzer/src/task/html.dart';
import 'package:analyzer/src/task/inputs.dart';
import 'package:analyzer/src/task/model.dart';
+import 'package:analyzer/src/task/strong/checker.dart';
+import 'package:analyzer/src/task/strong/rules.dart';
import 'package:analyzer/src/task/strong_mode.dart';
import 'package:analyzer/task/dart.dart';
import 'package:analyzer/task/general.dart';
@@ -160,43 +162,6 @@
new ListResultDescriptor<Source>('IMPORT_EXPORT_SOURCE_CLOSURE', null);
/**
- * A list of the [LibraryElement]s that make up the strongly connected
- * component in the import/export graph in which the target resides.
- *
- * Only non-empty in strongMode
- *
- * The result is only available for [LibrarySpecificUnit]s.
- */
-final ListResultDescriptor<LibraryElement> LIBRARY_CYCLE =
- new ListResultDescriptor<LibraryElement>('LIBRARY_CYCLE', null);
-
-/**
- * A list of the [CompilationUnitElement]s (including all parts) that make up
- * the strongly connected component in the import/export graph in which the
- * target resides.
- *
- * Only non-empty in strongMode
- *
- * The result is only available for [LibrarySpecificUnit]s.
- */
-final ListResultDescriptor<CompilationUnitElement> LIBRARY_CYCLE_UNITS =
- new ListResultDescriptor<CompilationUnitElement>(
- 'LIBRARY_CYCLE_UNITS', null);
-
-/**
- * A list of the [CompilationUnitElement]s that comprise all of the parts and
- * libraries in the direct import/export dependencies of the library cycle
- * of the target, with the intra-component dependencies excluded.
- *
- * Only non-empty in strongMode
- *
- * The result is only available for [LibrarySpecificUnit]s.
- */
-final ListResultDescriptor<CompilationUnitElement> LIBRARY_CYCLE_DEPENDENCIES =
- new ListResultDescriptor<CompilationUnitElement>(
- 'LIBRARY_CYCLE_DEPENDENCIES', null);
-
-/**
* A list of the [VariableElement]s whose type should be inferred that another
* inferable static variable (the target) depends on.
*
@@ -232,6 +197,43 @@
cachingPolicy: ELEMENT_CACHING_POLICY);
/**
+ * A list of the [LibraryElement]s that make up the strongly connected
+ * component in the import/export graph in which the target resides.
+ *
+ * Only non-empty in strongMode.
+ *
+ * The result is only available for [LibrarySpecificUnit]s.
+ */
+final ListResultDescriptor<LibraryElement> LIBRARY_CYCLE =
+ new ListResultDescriptor<LibraryElement>('LIBRARY_CYCLE', null);
+
+/**
+ * A list of the [CompilationUnitElement]s that comprise all of the parts and
+ * libraries in the direct import/export dependencies of the library cycle
+ * of the target, with the intra-component dependencies excluded.
+ *
+ * Only non-empty in strongMode.
+ *
+ * The result is only available for [LibrarySpecificUnit]s.
+ */
+final ListResultDescriptor<CompilationUnitElement> LIBRARY_CYCLE_DEPENDENCIES =
+ new ListResultDescriptor<CompilationUnitElement>(
+ 'LIBRARY_CYCLE_DEPENDENCIES', null);
+
+/**
+ * A list of the [CompilationUnitElement]s (including all parts) that make up
+ * the strongly connected component in the import/export graph in which the
+ * target resides.
+ *
+ * Only non-empty in strongMode.
+ *
+ * The result is only available for [LibrarySpecificUnit]s.
+ */
+final ListResultDescriptor<CompilationUnitElement> LIBRARY_CYCLE_UNITS =
+ new ListResultDescriptor<CompilationUnitElement>(
+ 'LIBRARY_CYCLE_UNITS', null);
+
+/**
* The partial [LibraryElement] associated with a library.
*
* The [LibraryElement] and its [CompilationUnitElement]s are attached to each
@@ -341,17 +343,6 @@
new ResultDescriptor<ReferencedNames>('REFERENCED_NAMES', null);
/**
- * The errors produced while resolving a full compilation unit.
- *
- * The list will be empty if there were no errors, but will not be `null`.
- *
- * The result is only available for [LibrarySpecificUnit]s.
- */
-final ListResultDescriptor<AnalysisError> RESOLVE_UNIT_ERRORS =
- new ListResultDescriptor<AnalysisError>(
- 'RESOLVE_UNIT_ERRORS', AnalysisError.NO_ERRORS);
-
-/**
* The errors produced while resolving type names.
*
* The list will be empty if there were no errors, but will not be `null`.
@@ -363,9 +354,22 @@
'RESOLVE_TYPE_NAMES_ERRORS', AnalysisError.NO_ERRORS);
/**
- * The partially resolved [CompilationUnit] associated with a unit.
+ * The errors produced while resolving a full compilation unit.
*
- * All declarations bound to the element defined by the declaration.
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for [LibrarySpecificUnit]s.
+ */
+final ListResultDescriptor<AnalysisError> RESOLVE_UNIT_ERRORS =
+ new ListResultDescriptor<AnalysisError>(
+ 'RESOLVE_UNIT_ERRORS', AnalysisError.NO_ERRORS);
+
+/**
+ * The partially resolved [CompilationUnit] associated with a compilation unit.
+ *
+ * Tasks that use this value as an input can assume that the [SimpleIdentifier]s
+ * at all declaration sites have been bound to the element defined by the
+ * declaration, except for the constants defined in an 'enum' declaration.
*
* The result is only available for [LibrarySpecificUnit]s.
*/
@@ -374,9 +378,21 @@
cachingPolicy: AST_CACHING_POLICY);
/**
- * The partially resolved [CompilationUnit] associated with a unit.
+ * The resolved [CompilationUnit] associated with a compilation unit, with
+ * constants resolved.
*
- * All the enum member elements are built.
+ * The result is only available for [LibrarySpecificUnit]s.
+ */
+final ResultDescriptor<CompilationUnit> RESOLVED_UNIT10 =
+ new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT10', null,
+ cachingPolicy: AST_CACHING_POLICY);
+
+/**
+ * The partially resolved [CompilationUnit] associated with a compilation unit.
+ *
+ * Tasks that use this value as an input can assume that the [SimpleIdentifier]s
+ * at all declaration sites have been bound to the element defined by the
+ * declaration, including the constants defined in an 'enum' declaration.
*
* The result is only available for [LibrarySpecificUnit]s.
*/
@@ -385,9 +401,12 @@
cachingPolicy: AST_CACHING_POLICY);
/**
- * The partially resolved [CompilationUnit] associated with a unit.
+ * The partially resolved [CompilationUnit] associated with a compilation unit.
*
- * [RESOLVED_UNIT2] with resolved type names.
+ * In addition to what is true of a [RESOLVED_UNIT2], tasks that use this value
+ * as an input can assume that the types associated with declarations have been
+ * resolved. This includes the types of superclasses, mixins, interfaces,
+ * fields, return types, parameters, and local variables.
*
* The result is only available for [LibrarySpecificUnit]s.
*/
@@ -396,9 +415,11 @@
cachingPolicy: AST_CACHING_POLICY);
/**
- * The partially resolved [CompilationUnit] associated with a unit.
+ * The partially resolved [CompilationUnit] associated with a compilation unit.
*
- * [RESOLVED_UNIT3] plus resolved local variables and formal parameters.
+ * In addition to what is true of a [RESOLVED_UNIT3], tasks that use this value
+ * as an input can assume that references to local variables and formal
+ * parameters have been resolved.
*
* The result is only available for [LibrarySpecificUnit]s.
*/
@@ -407,9 +428,12 @@
cachingPolicy: AST_CACHING_POLICY);
/**
- * The resolved [CompilationUnit] associated with a compilation unit in which
- * elements and types have been initially resolved outside of method bodies in
- * addition to everything that is true of a [RESOLVED_UNIT4].
+ * The partially resolved [CompilationUnit] associated with a compilation unit.
+ *
+ * In addition to what is true of a [RESOLVED_UNIT4], tasks that use this value
+ * as an input can assume that elements and types associated with expressions
+ * outside of method bodies (essentially initializers) have been initially
+ * resolved.
*
* The result is only available for [LibrarySpecificUnit]s.
*/
@@ -418,9 +442,10 @@
cachingPolicy: AST_CACHING_POLICY);
/**
- * The resolved [CompilationUnit] associated with a compilation unit in which
- * the types of static variables have been inferred in addition to everything
- * that is true of a [RESOLVED_UNIT5].
+ * The partially resolved [CompilationUnit] associated with a compilation unit.
+ *
+ * In addition to what is true of a [RESOLVED_UNIT5], tasks that use this value
+ * as an input can assume that the types of static variables have been inferred.
*
* The result is only available for [LibrarySpecificUnit]s.
*/
@@ -429,9 +454,11 @@
cachingPolicy: AST_CACHING_POLICY);
/**
- * The resolved [CompilationUnit] associated with a compilation unit in which
- * the right hand sides of instance variables have been re-resolved in addition
- * to everything that is true of a [RESOLVED_UNIT6].
+ * The partially resolved [CompilationUnit] associated with a compilation unit.
+ *
+ * In addition to what is true of a [RESOLVED_UNIT6], tasks that use this value
+ * as an input can assume that the initializers of instance variables have been
+ * re-resolved.
*
* The result is only available for [LibrarySpecificUnit]s.
*/
@@ -472,6 +499,20 @@
'SCAN_ERRORS', AnalysisError.NO_ERRORS);
/**
+ * The additional strong mode errors produced while verifying a
+ * compilation unit.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for [LibrarySpecificUnits]s representing a
+ * compilation unit.
+ *
+ */
+final ListResultDescriptor<AnalysisError> STRONG_MODE_ERRORS =
+ new ListResultDescriptor<AnalysisError>(
+ 'STRONG_MODE_ERRORS', AnalysisError.NO_ERRORS);
+
+/**
* The [TypeProvider] of the [AnalysisContext].
*/
final ResultDescriptor<TypeProvider> TYPE_PROVIDER =
@@ -2109,7 +2150,7 @@
}
/**
- * A task that builds [RESOLVED_UNIT] for a unit.
+ * A task that builds [RESOLVED_UNIT10] for a unit.
*/
class EvaluateUnitConstantsTask extends SourceBasedAnalysisTask {
/**
@@ -2129,7 +2170,7 @@
'EvaluateUnitConstantsTask',
createTask,
buildInputs,
- <ResultDescriptor>[RESOLVED_UNIT]);
+ <ResultDescriptor>[RESOLVED_UNIT10]);
EvaluateUnitConstantsTask(AnalysisContext context, LibrarySpecificUnit target)
: super(context, target);
@@ -2142,7 +2183,7 @@
// No actual work needs to be performed; the task manager will ensure that
// all constants are evaluated before this method is called.
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
- outputs[RESOLVED_UNIT] = unit;
+ outputs[RESOLVED_UNIT10] = unit;
}
/**
@@ -2535,26 +2576,11 @@
*/
class GenerateLintsTask extends SourceBasedAnalysisTask {
/**
- * The name of the [RESOLVED_UNIT8] input.
+ * The name of the [RESOLVED_UNIT] input.
*/
static const String RESOLVED_UNIT_INPUT = 'RESOLVED_UNIT';
/**
- * The name of a list of [USED_LOCAL_ELEMENTS] for each library unit input.
- */
- static const String USED_LOCAL_ELEMENTS_INPUT = 'USED_LOCAL_ELEMENTS';
-
- /**
- * The name of a list of [USED_IMPORTED_ELEMENTS] for each library unit input.
- */
- static const String USED_IMPORTED_ELEMENTS_INPUT = 'USED_IMPORTED_ELEMENTS';
-
- /**
- * The name of the [TYPE_PROVIDER] input.
- */
- static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
-
- /**
* The task descriptor describing this kind of task.
*/
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
@@ -2587,9 +2613,9 @@
//
// Generate lints.
//
- LintGenerator.LINTERS.forEach((l) => l.reporter = errorReporter);
- Iterable<AstVisitor> visitors =
- LintGenerator.LINTERS.map((l) => l.getVisitor()).toList();
+ List<Linter> linters = getLints(context);
+ linters.forEach((l) => l.reporter = errorReporter);
+ Iterable<AstVisitor> visitors = linters.map((l) => l.getVisitor()).toList();
unit.accept(new DelegatingAstVisitor(visitors.where((v) => v != null)));
//
@@ -2710,122 +2736,6 @@
}
/**
- * A task that ensures that all of the inferrable instance members in a
- * compilation unit have had their right hand sides re-resolved
- */
-class ResolveInstanceFieldsInUnitTask extends SourceBasedAnalysisTask {
- /**
- * The name of the [LIBRARY_ELEMENT5] input.
- */
- static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
-
- /**
- * The name of the [TYPE_PROVIDER] input.
- */
- static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
-
- /**
- * The name of the input whose value is the [RESOLVED_UNIT6] for the
- * compilation unit.
- */
- static const String UNIT_INPUT = 'UNIT_INPUT';
-
- /**
- * The task descriptor describing this kind of task.
- */
- static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
- 'ResolveInstanceFieldsInUnitTask',
- createTask,
- buildInputs,
- <ResultDescriptor>[RESOLVED_UNIT7]);
-
- /**
- * Initialize a newly created task to build a library element for the given
- * [unit] in the given [context].
- */
- ResolveInstanceFieldsInUnitTask(
- InternalAnalysisContext context, LibrarySpecificUnit unit)
- : super(context, unit);
-
- @override
- TaskDescriptor get descriptor => DESCRIPTOR;
-
- @override
- void internalPerform() {
- //
- // Prepare inputs.
- //
- LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
- CompilationUnit unit = getRequiredInput(UNIT_INPUT);
- TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
-
- CompilationUnitElement unitElement = unit.element;
- if (context.analysisOptions.strongMode) {
- //
- // Resolve references.
- //
- // TODO(leafp): This code only needs to re-resolve the right hand sides of
- // instance fields. We could do incremental resolution on each field
- // only using the incremental resolver. However, this caused a massive
- // performance degredation on the large_class_declaration_test.dart test.
- // I would hypothesize that incremental resolution of field is linear in
- // the size of the enclosing class, and hence incrementally resolving each
- // field was quadratic. We may wish to revisit this if we can resolve
- // this performance issue.
- InheritanceManager inheritanceManager =
- new InheritanceManager(libraryElement);
- PartialResolverVisitor visitor = new PartialResolverVisitor(
- libraryElement,
- unitElement.source,
- typeProvider,
- AnalysisErrorListener.NULL_LISTENER,
- inheritanceManager: inheritanceManager);
- unit.accept(visitor);
- }
- //
- // Record outputs.
- //
- outputs[RESOLVED_UNIT7] = unit;
- }
-
- /**
- * Return a map from the names of the inputs of this kind of task to the task
- * input descriptors describing those inputs for a task with the given
- * [libSource].
- */
- static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
- LibrarySpecificUnit unit = target;
- return <String, TaskInput>{
- UNIT_INPUT: RESOLVED_UNIT6.of(unit),
- LIBRARY_INPUT: LIBRARY_ELEMENT5.of(unit.library),
- TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request),
- // In strong mode, add additional dependencies to enforce inference
- // ordering.
-
- // Require that static variable inference be complete for all units in
- // the current library cycle.
- 'orderLibraryCycleTasks': LIBRARY_CYCLE_UNITS.of(unit).toList(
- (CompilationUnitElementImpl unit) => RESOLVED_UNIT6
- .of(new LibrarySpecificUnit(unit.librarySource, unit.source))),
- // Require that full inference be complete for all dependencies of the
- // current library cycle.
- 'orderLibraryCycles': LIBRARY_CYCLE_DEPENDENCIES.of(unit).toList(
- (CompilationUnitElementImpl unit) => RESOLVED_UNIT8
- .of(new LibrarySpecificUnit(unit.librarySource, unit.source)))
- };
- }
-
- /**
- * Create a [ResolveInstanceFieldsInUnitTask] based on the given [target] in
- * the given [context].
- */
- static ResolveInstanceFieldsInUnitTask createTask(
- AnalysisContext context, AnalysisTarget target) {
- return new ResolveInstanceFieldsInUnitTask(context, target);
- }
-}
-
-/**
* An abstract class that defines utility methods that are useful for tasks
* operating on static variables.
*/
@@ -3138,6 +3048,11 @@
static const String LINTS_INPUT = 'LINTS';
/**
+ * The name of the [STRONG_MODE_ERRORS] input.
+ */
+ static const String STRONG_MODE_ERRORS_INPUT = 'STRONG_MODE_ERRORS';
+
+ /**
* The name of the [RESOLVE_TYPE_NAMES_ERRORS] input.
*/
static const String RESOLVE_TYPE_NAMES_ERRORS_INPUT =
@@ -3186,6 +3101,7 @@
errorLists.add(getRequiredInput(LINTS_INPUT));
errorLists.add(getRequiredInput(RESOLVE_TYPE_NAMES_ERRORS_INPUT));
errorLists.add(getRequiredInput(RESOLVE_UNIT_ERRORS_INPUT));
+ errorLists.add(getRequiredInput(STRONG_MODE_ERRORS_INPUT));
errorLists.add(getRequiredInput(VARIABLE_REFERENCE_ERRORS_INPUT));
errorLists.add(getRequiredInput(VERIFY_ERRORS_INPUT));
//
@@ -3206,6 +3122,7 @@
LINTS_INPUT: LINTS.of(unit),
RESOLVE_TYPE_NAMES_ERRORS_INPUT: RESOLVE_TYPE_NAMES_ERRORS.of(unit),
RESOLVE_UNIT_ERRORS_INPUT: RESOLVE_UNIT_ERRORS.of(unit),
+ STRONG_MODE_ERRORS_INPUT: STRONG_MODE_ERRORS.of(unit),
VARIABLE_REFERENCE_ERRORS_INPUT: VARIABLE_REFERENCE_ERRORS.of(unit),
VERIFY_ERRORS_INPUT: VERIFY_ERRORS.of(unit)
};
@@ -3234,7 +3151,9 @@
}
/**
- * A task that parses the content of a Dart file, producing an AST structure.
+ * A task that parses the content of a Dart file, producing an AST structure,
+ * any lexical errors found in the process, the kind of the file (library or
+ * part), and several lists based on the AST.
*/
class ParseDartTask extends SourceBasedAnalysisTask {
/**
@@ -3465,10 +3384,10 @@
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
CompilationUnitElement unitElement = unit.element;
TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+ //
+ // Resolve references and record outputs.
+ //
if (context.analysisOptions.strongMode) {
- //
- // Resolve references.
- //
InheritanceManager inheritanceManager =
new InheritanceManager(libraryElement);
PartialResolverVisitor visitor = new PartialResolverVisitor(
@@ -3478,9 +3397,7 @@
AnalysisErrorListener.NULL_LISTENER,
inheritanceManager: inheritanceManager);
unit.accept(visitor);
- //
- // Record outputs.
- //
+
outputs[INFERABLE_STATIC_VARIABLES_IN_UNIT] = visitor.variablesAndFields;
} else {
outputs[INFERABLE_STATIC_VARIABLES_IN_UNIT] = [];
@@ -3661,12 +3578,12 @@
}
/**
- * A task that resolves the bodies of top-level functions, constructors, and
- * methods within a single compilation unit.
+ * A task that ensures that all of the inferrable instance members in a
+ * compilation unit have had their right hand sides re-resolved
*/
-class ResolveUnitTask extends SourceBasedAnalysisTask {
+class ResolveInstanceFieldsInUnitTask extends SourceBasedAnalysisTask {
/**
- * The name of the input whose value is the defining [LIBRARY_ELEMENT5].
+ * The name of the [LIBRARY_ELEMENT5] input.
*/
static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
@@ -3676,19 +3593,27 @@
static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
/**
- * The name of the [RESOLVED_UNIT8] input.
+ * The name of the input whose value is the [RESOLVED_UNIT6] for the
+ * compilation unit.
*/
static const String UNIT_INPUT = 'UNIT_INPUT';
+ /**
+ * The task descriptor describing this kind of task.
+ */
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
- 'ResolveUnitTask',
+ 'ResolveInstanceFieldsInUnitTask',
createTask,
buildInputs,
- <ResultDescriptor>[RESOLVE_UNIT_ERRORS, RESOLVED_UNIT9]);
+ <ResultDescriptor>[RESOLVED_UNIT7]);
- ResolveUnitTask(
- InternalAnalysisContext context, LibrarySpecificUnit compilationUnit)
- : super(context, compilationUnit);
+ /**
+ * Initialize a newly created task to build a library element for the given
+ * [unit] in the given [context].
+ */
+ ResolveInstanceFieldsInUnitTask(
+ InternalAnalysisContext context, LibrarySpecificUnit unit)
+ : super(context, unit);
@override
TaskDescriptor get descriptor => DESCRIPTOR;
@@ -3701,50 +3626,70 @@
LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
- //
- // Resolve everything
- //
+
CompilationUnitElement unitElement = unit.element;
- RecordingErrorListener errorListener = new RecordingErrorListener();
- ResolverVisitor visitor = new ResolverVisitor(
- libraryElement, unitElement.source, typeProvider, errorListener);
- unit.accept(visitor);
+ if (context.analysisOptions.strongMode) {
+ //
+ // Resolve references.
+ //
+ // TODO(leafp): This code only needs to re-resolve the right hand sides of
+ // instance fields. We could do incremental resolution on each field
+ // only using the incremental resolver. However, this caused a massive
+ // performance degredation on the large_class_declaration_test.dart test.
+ // I would hypothesize that incremental resolution of field is linear in
+ // the size of the enclosing class, and hence incrementally resolving each
+ // field was quadratic. We may wish to revisit this if we can resolve
+ // this performance issue.
+ InheritanceManager inheritanceManager =
+ new InheritanceManager(libraryElement);
+ PartialResolverVisitor visitor = new PartialResolverVisitor(
+ libraryElement,
+ unitElement.source,
+ typeProvider,
+ AnalysisErrorListener.NULL_LISTENER,
+ inheritanceManager: inheritanceManager);
+ unit.accept(visitor);
+ }
//
// Record outputs.
//
- outputs[RESOLVE_UNIT_ERRORS] = errorListener.errors;
- outputs[RESOLVED_UNIT9] = unit;
+ outputs[RESOLVED_UNIT7] = unit;
}
/**
* Return a map from the names of the inputs of this kind of task to the task
* input descriptors describing those inputs for a task with the given
- * [target].
+ * [libSource].
*/
static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
LibrarySpecificUnit unit = target;
return <String, TaskInput>{
+ UNIT_INPUT: RESOLVED_UNIT6.of(unit),
LIBRARY_INPUT: LIBRARY_ELEMENT5.of(unit.library),
TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request),
- UNIT_INPUT: RESOLVED_UNIT8.of(unit),
// In strong mode, add additional dependencies to enforce inference
// ordering.
- // Require that inference be complete for all units in the
- // current library cycle.
+ // Require that static variable inference be complete for all units in
+ // the current library cycle.
'orderLibraryCycleTasks': LIBRARY_CYCLE_UNITS.of(unit).toList(
+ (CompilationUnitElementImpl unit) => RESOLVED_UNIT6
+ .of(new LibrarySpecificUnit(unit.librarySource, unit.source))),
+ // Require that full inference be complete for all dependencies of the
+ // current library cycle.
+ 'orderLibraryCycles': LIBRARY_CYCLE_DEPENDENCIES.of(unit).toList(
(CompilationUnitElementImpl unit) => RESOLVED_UNIT8
.of(new LibrarySpecificUnit(unit.librarySource, unit.source)))
};
}
/**
- * Create a [ResolveUnitTask] based on the given [target] in
+ * Create a [ResolveInstanceFieldsInUnitTask] based on the given [target] in
* the given [context].
*/
- static ResolveUnitTask createTask(
+ static ResolveInstanceFieldsInUnitTask createTask(
AnalysisContext context, AnalysisTarget target) {
- return new ResolveUnitTask(context, target);
+ return new ResolveInstanceFieldsInUnitTask(context, target);
}
}
@@ -3884,6 +3829,94 @@
}
/**
+ * A task that resolves the bodies of top-level functions, constructors, and
+ * methods within a single compilation unit.
+ */
+class ResolveUnitTask extends SourceBasedAnalysisTask {
+ /**
+ * The name of the input whose value is the defining [LIBRARY_ELEMENT5].
+ */
+ static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
+
+ /**
+ * The name of the [TYPE_PROVIDER] input.
+ */
+ static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
+
+ /**
+ * The name of the [RESOLVED_UNIT8] input.
+ */
+ static const String UNIT_INPUT = 'UNIT_INPUT';
+
+ static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+ 'ResolveUnitTask',
+ createTask,
+ buildInputs,
+ <ResultDescriptor>[RESOLVE_UNIT_ERRORS, RESOLVED_UNIT9]);
+
+ ResolveUnitTask(
+ InternalAnalysisContext context, LibrarySpecificUnit compilationUnit)
+ : super(context, compilationUnit);
+
+ @override
+ TaskDescriptor get descriptor => DESCRIPTOR;
+
+ @override
+ void internalPerform() {
+ //
+ // Prepare inputs.
+ //
+ LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
+ CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+ TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+ //
+ // Resolve everything.
+ //
+ CompilationUnitElement unitElement = unit.element;
+ RecordingErrorListener errorListener = new RecordingErrorListener();
+ ResolverVisitor visitor = new ResolverVisitor(
+ libraryElement, unitElement.source, typeProvider, errorListener);
+ unit.accept(visitor);
+ //
+ // Record outputs.
+ //
+ outputs[RESOLVE_UNIT_ERRORS] = errorListener.errors;
+ outputs[RESOLVED_UNIT9] = unit;
+ }
+
+ /**
+ * Return a map from the names of the inputs of this kind of task to the task
+ * input descriptors describing those inputs for a task with the given
+ * [target].
+ */
+ static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
+ LibrarySpecificUnit unit = target;
+ return <String, TaskInput>{
+ LIBRARY_INPUT: LIBRARY_ELEMENT5.of(unit.library),
+ TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request),
+ UNIT_INPUT: RESOLVED_UNIT8.of(unit),
+ // In strong mode, add additional dependencies to enforce inference
+ // ordering.
+
+ // Require that inference be complete for all units in the
+ // current library cycle.
+ 'orderLibraryCycleTasks': LIBRARY_CYCLE_UNITS.of(unit).toList(
+ (CompilationUnitElementImpl unit) => RESOLVED_UNIT8
+ .of(new LibrarySpecificUnit(unit.librarySource, unit.source)))
+ };
+ }
+
+ /**
+ * Create a [ResolveUnitTask] based on the given [target] in
+ * the given [context].
+ */
+ static ResolveUnitTask createTask(
+ AnalysisContext context, AnalysisTarget target) {
+ return new ResolveUnitTask(context, target);
+ }
+}
+
+/**
* A task that builds [RESOLVED_UNIT3] for a unit.
*/
class ResolveUnitTypeNamesTask extends SourceBasedAnalysisTask {
@@ -3920,7 +3953,6 @@
@override
void internalPerform() {
- RecordingErrorListener errorListener = new RecordingErrorListener();
//
// Prepare inputs.
//
@@ -3931,6 +3963,7 @@
//
// Resolve TypeName nodes.
//
+ RecordingErrorListener errorListener = new RecordingErrorListener();
TypeResolverVisitor visitor = new TypeResolverVisitor(
library, unitElement.source, typeProvider, errorListener);
unit.accept(visitor);
@@ -3948,6 +3981,9 @@
* given [target].
*/
static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
+ // TODO(brianwilkerson) This task updates the element model to have type
+ // information and updates the class hierarchy. It should produce a new
+ // version of the element model in order to record those changes.
LibrarySpecificUnit unit = target;
return <String, TaskInput>{
'importsExportNamespace':
@@ -4005,19 +4041,19 @@
@override
void internalPerform() {
- RecordingErrorListener errorListener = new RecordingErrorListener();
//
// Prepare inputs.
//
LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
CompilationUnitElement unitElement = unit.element;
+ TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
//
// Resolve local variables.
//
- TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+ RecordingErrorListener errorListener = new RecordingErrorListener();
Scope nameScope = new LibraryScope(libraryElement, errorListener);
- AstVisitor visitor = new VariableResolverVisitor(
+ VariableResolverVisitor visitor = new VariableResolverVisitor(
libraryElement, unitElement.source, typeProvider, errorListener,
nameScope: nameScope);
unit.accept(visitor);
@@ -4054,7 +4090,8 @@
}
/**
- * A task that scans the content of a file, producing a set of Dart tokens.
+ * A task that scans the content of a Dart file, producing a stream of Dart
+ * tokens, line information, and any lexical errors encountered in the process.
*/
class ScanDartTask extends SourceBasedAnalysisTask {
/**
@@ -4166,6 +4203,84 @@
}
/**
+ * A task that builds [STRONG_MODE_ERRORS] for a unit. Also builds
+ * [RESOLVED_UNIT] for a unit.
+ */
+class StrongModeVerifyUnitTask extends SourceBasedAnalysisTask {
+ /**
+ * The name of the [RESOLVED_UNIT10] input.
+ */
+ static const String UNIT_INPUT = 'UNIT_INPUT';
+
+ /**
+ * The name of the [TYPE_PROVIDER] input.
+ */
+ static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
+
+ /**
+ * The task descriptor describing this kind of task.
+ */
+ static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+ 'StrongModeVerifyUnitTask',
+ createTask,
+ buildInputs,
+ <ResultDescriptor>[STRONG_MODE_ERRORS, RESOLVED_UNIT]);
+
+ StrongModeVerifyUnitTask(
+ InternalAnalysisContext context, AnalysisTarget target)
+ : super(context, target);
+
+ @override
+ TaskDescriptor get descriptor => DESCRIPTOR;
+
+ @override
+ void internalPerform() {
+ RecordingErrorListener errorListener = new RecordingErrorListener();
+ //
+ // Prepare inputs.
+ //
+ TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+ CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+ if (context.analysisOptions.strongMode) {
+ unit.accept(new CodeChecker(new TypeRules(typeProvider), errorListener));
+ }
+
+ //
+ // Record outputs.
+ //
+ outputs[STRONG_MODE_ERRORS] = removeDuplicateErrors(errorListener.errors);
+ outputs[RESOLVED_UNIT] = unit;
+ }
+
+ /**
+ * Return a map from the names of the inputs of this kind of task to the task
+ * input descriptors describing those inputs for a task with the
+ * given [target].
+ */
+ static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
+ LibrarySpecificUnit unit = target;
+ return <String, TaskInput>{
+ 'resolvedUnits': IMPORT_EXPORT_SOURCE_CLOSURE
+ .of(unit.library)
+ .toMapOf(UNITS)
+ .toFlattenList((Source library, Source unit) =>
+ RESOLVED_UNIT10.of(new LibrarySpecificUnit(library, unit))),
+ UNIT_INPUT: RESOLVED_UNIT10.of(unit),
+ TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request)
+ };
+ }
+
+ /**
+ * Create a [StrongModeVerifyUnitTask] based on the given [target] in
+ * the given [context].
+ */
+ static StrongModeVerifyUnitTask createTask(
+ AnalysisContext context, AnalysisTarget target) {
+ return new StrongModeVerifyUnitTask(context, target);
+ }
+}
+
+/**
* A task that builds [VERIFY_ERRORS] for a unit.
*/
class VerifyUnitTask extends SourceBasedAnalysisTask {
@@ -4228,6 +4343,7 @@
new InheritanceManager(libraryElement),
context.analysisOptions.enableSuperMixins);
unit.accept(errorVerifier);
+
//
// Record outputs.
//
diff --git a/packages/analyzer/lib/src/task/dart_work_manager.dart b/packages/analyzer/lib/src/task/dart_work_manager.dart
index 7114053..61f92e5 100644
--- a/packages/analyzer/lib/src/task/dart_work_manager.dart
+++ b/packages/analyzer/lib/src/task/dart_work_manager.dart
@@ -46,6 +46,7 @@
LIBRARY_UNIT_ERRORS,
RESOLVE_TYPE_NAMES_ERRORS,
RESOLVE_UNIT_ERRORS,
+ STRONG_MODE_ERRORS,
VARIABLE_REFERENCE_ERRORS,
VERIFY_ERRORS
];
@@ -193,6 +194,12 @@
* Maybe empty, but not null.
*/
List<Source> getLibrariesContainingPart(Source part) {
+ if (part.isInSystemLibrary) {
+ DartWorkManager sdkDartWorkManager = _getSdkDartWorkManager();
+ if (sdkDartWorkManager != this) {
+ return sdkDartWorkManager.getLibrariesContainingPart(part);
+ }
+ }
List<Source> libraries = partLibrariesMap[part];
return libraries != null ? libraries : Source.EMPTY_LIST;
}
@@ -265,6 +272,14 @@
void resultsComputed(
AnalysisTarget target, Map<ResultDescriptor, dynamic> outputs) {
bool isDartSource = _isDartSource(target);
+ // Route SDK outputs to the SDK WorkManager.
+ if (isDartSource && target.source.isInSystemLibrary) {
+ DartWorkManager sdkWorkManager = _getSdkDartWorkManager();
+ if (sdkWorkManager != this) {
+ sdkWorkManager.resultsComputed(target, outputs);
+ return;
+ }
+ }
// Organize sources.
bool isDartLibrarySource = false;
if (isDartSource) {
@@ -339,6 +354,22 @@
}
/**
+ * Return the SDK [DartWorkManager] or this one.
+ */
+ DartWorkManager _getSdkDartWorkManager() {
+ SourceFactory sourceFactory = context.sourceFactory;
+ InternalAnalysisContext sdkContext = sourceFactory.dartSdk.context;
+ if (sdkContext != context) {
+ for (WorkManager workManager in sdkContext.workManagers) {
+ if (workManager is DartWorkManager) {
+ return workManager;
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
* Invalidate all of the resolution results computed by this context. The flag
* [invalidateUris] should be `true` if the cached results of converting URIs
* to source files should also be invalidated.
diff --git a/packages/analyzer/lib/src/task/html.dart b/packages/analyzer/lib/src/task/html.dart
index 0ab546a..1804c50 100644
--- a/packages/analyzer/lib/src/task/html.dart
+++ b/packages/analyzer/lib/src/task/html.dart
@@ -322,6 +322,9 @@
List<ParseError> parseErrors = parser.errors;
List<AnalysisError> errors = <AnalysisError>[];
for (ParseError parseError in parseErrors) {
+ if (parseError.errorCode == 'expected-doctype-but-got-start-tag') {
+ continue;
+ }
SourceSpan span = parseError.span;
errors.add(new AnalysisError(target.source, span.start.offset,
span.length, HtmlErrorCode.PARSE_ERROR, [parseError.message]));
diff --git a/packages/analyzer/lib/src/task/strong/checker.dart b/packages/analyzer/lib/src/task/strong/checker.dart
new file mode 100644
index 0000000..9cfefe8
--- /dev/null
+++ b/packages/analyzer/lib/src/task/strong/checker.dart
@@ -0,0 +1,985 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
+// refactored to fit into analyzer.
+library analyzer.src.task.strong.checker;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/scanner.dart' show Token, TokenType;
+
+import 'info.dart';
+import 'rules.dart';
+
+/// Checks for overriding declarations of fields and methods. This is used to
+/// check overrides between classes and superclasses, interfaces, and mixin
+/// applications.
+class _OverrideChecker {
+ bool _failure = false;
+ final TypeRules _rules;
+ final AnalysisErrorListener _reporter;
+
+ _OverrideChecker(this._rules, this._reporter);
+
+ void check(ClassDeclaration node) {
+ if (node.element.type.isObject) return;
+ _checkSuperOverrides(node);
+ _checkMixinApplicationOverrides(node);
+ _checkAllInterfaceOverrides(node);
+ }
+
+ /// Check overrides from mixin applications themselves. For example, in:
+ ///
+ /// A extends B with E, F
+ ///
+ /// we check:
+ ///
+ /// B & E against B (equivalently how E overrides B)
+ /// B & E & F against B & E (equivalently how F overrides both B and E)
+ void _checkMixinApplicationOverrides(ClassDeclaration node) {
+ var type = node.element.type;
+ var parent = type.superclass;
+ var mixins = type.mixins;
+
+ // Check overrides from applying mixins
+ for (int i = 0; i < mixins.length; i++) {
+ var seen = new Set<String>();
+ var current = mixins[i];
+ var errorLocation = node.withClause.mixinTypes[i];
+ for (int j = i - 1; j >= 0; j--) {
+ _checkIndividualOverridesFromType(
+ current, mixins[j], errorLocation, seen);
+ }
+ _checkIndividualOverridesFromType(current, parent, errorLocation, seen);
+ }
+ }
+
+ /// Check overrides between a class and its superclasses and mixins. For
+ /// example, in:
+ ///
+ /// A extends B with E, F
+ ///
+ /// we check A against B, B super classes, E, and F.
+ ///
+ /// Internally we avoid reporting errors twice and we visit classes bottom up
+ /// to ensure we report the most immediate invalid override first. For
+ /// example, in the following code we'll report that `Test` has an invalid
+ /// override with respect to `Parent` (as opposed to an invalid override with
+ /// respect to `Grandparent`):
+ ///
+ /// class Grandparent {
+ /// m(A a) {}
+ /// }
+ /// class Parent extends Grandparent {
+ /// m(A a) {}
+ /// }
+ /// class Test extends Parent {
+ /// m(B a) {} // invalid override
+ /// }
+ void _checkSuperOverrides(ClassDeclaration node) {
+ var seen = new Set<String>();
+ var current = node.element.type;
+ var visited = new Set<InterfaceType>();
+ do {
+ visited.add(current);
+ current.mixins.reversed
+ .forEach((m) => _checkIndividualOverridesFromClass(node, m, seen));
+ _checkIndividualOverridesFromClass(node, current.superclass, seen);
+ current = current.superclass;
+ } while (!current.isObject && !visited.contains(current));
+ }
+
+ /// Checks that implementations correctly override all reachable interfaces.
+ /// In particular, we need to check these overrides for the definitions in
+ /// the class itself and each its superclasses. If a superclass is not
+ /// abstract, then we can skip its transitive interfaces. For example, in:
+ ///
+ /// B extends C implements G
+ /// A extends B with E, F implements H, I
+ ///
+ /// we check:
+ ///
+ /// C against G, H, and I
+ /// B against G, H, and I
+ /// E against H and I // no check against G because B is a concrete class
+ /// F against H and I
+ /// A against H and I
+ void _checkAllInterfaceOverrides(ClassDeclaration node) {
+ var seen = new Set<String>();
+ // Helper function to collect all reachable interfaces.
+ find(InterfaceType interfaceType, Set result) {
+ if (interfaceType == null || interfaceType.isObject) return;
+ if (result.contains(interfaceType)) return;
+ result.add(interfaceType);
+ find(interfaceType.superclass, result);
+ interfaceType.mixins.forEach((i) => find(i, result));
+ interfaceType.interfaces.forEach((i) => find(i, result));
+ }
+
+ // Check all interfaces reachable from the `implements` clause in the
+ // current class against definitions here and in superclasses.
+ var localInterfaces = new Set<InterfaceType>();
+ var type = node.element.type;
+ type.interfaces.forEach((i) => find(i, localInterfaces));
+ _checkInterfacesOverrides(node, localInterfaces, seen,
+ includeParents: true);
+
+ // Check also how we override locally the interfaces from parent classes if
+ // the parent class is abstract. Otherwise, these will be checked as
+ // overrides on the concrete superclass.
+ var superInterfaces = new Set<InterfaceType>();
+ var parent = type.superclass;
+ // TODO(sigmund): we don't seem to be reporting the analyzer error that a
+ // non-abstract class is not implementing an interface. See
+ // https://github.com/dart-lang/dart-dev-compiler/issues/25
+ while (parent != null && parent.element.isAbstract) {
+ parent.interfaces.forEach((i) => find(i, superInterfaces));
+ parent = parent.superclass;
+ }
+ _checkInterfacesOverrides(node, superInterfaces, seen,
+ includeParents: false);
+ }
+
+ /// Checks that [cls] and its super classes (including mixins) correctly
+ /// overrides each interface in [interfaces]. If [includeParents] is false,
+ /// then mixins are still checked, but the base type and it's transitive
+ /// supertypes are not.
+ ///
+ /// [cls] can be either a [ClassDeclaration] or a [InterfaceType]. For
+ /// [ClassDeclaration]s errors are reported on the member that contains the
+ /// invalid override, for [InterfaceType]s we use [errorLocation] instead.
+ void _checkInterfacesOverrides(
+ cls, Iterable<InterfaceType> interfaces, Set<String> seen,
+ {Set<InterfaceType> visited,
+ bool includeParents: true,
+ AstNode errorLocation}) {
+ var node = cls is ClassDeclaration ? cls : null;
+ var type = cls is InterfaceType ? cls : node.element.type;
+
+ if (visited == null) {
+ visited = new Set<InterfaceType>();
+ } else if (visited.contains(type)) {
+ // Malformed type.
+ return;
+ } else {
+ visited.add(type);
+ }
+
+ // Check direct overrides on [type]
+ for (var interfaceType in interfaces) {
+ if (node != null) {
+ _checkIndividualOverridesFromClass(node, interfaceType, seen);
+ } else {
+ _checkIndividualOverridesFromType(
+ type, interfaceType, errorLocation, seen);
+ }
+ }
+
+ // Check overrides from its mixins
+ for (int i = 0; i < type.mixins.length; i++) {
+ var loc =
+ errorLocation != null ? errorLocation : node.withClause.mixinTypes[i];
+ for (var interfaceType in interfaces) {
+ // We copy [seen] so we can report separately if more than one mixin or
+ // the base class have an invalid override.
+ _checkIndividualOverridesFromType(
+ type.mixins[i], interfaceType, loc, new Set.from(seen));
+ }
+ }
+
+ // Check overrides from its superclasses
+ if (includeParents) {
+ var parent = type.superclass;
+ if (parent.isObject) return;
+ var loc = errorLocation != null ? errorLocation : node.extendsClause;
+ // No need to copy [seen] here because we made copies above when reporting
+ // errors on mixins.
+ _checkInterfacesOverrides(parent, interfaces, seen,
+ visited: visited, includeParents: true, errorLocation: loc);
+ }
+ }
+
+ /// Check that individual methods and fields in [subType] correctly override
+ /// the declarations in [baseType].
+ ///
+ /// The [errorLocation] node indicates where errors are reported, see
+ /// [_checkSingleOverride] for more details.
+ ///
+ /// The set [seen] is used to avoid reporting overrides more than once. It
+ /// is used when invoking this function multiple times when checking several
+ /// types in a class hierarchy. Errors are reported only the first time an
+ /// invalid override involving a specific member is encountered.
+ _checkIndividualOverridesFromType(InterfaceType subType,
+ InterfaceType baseType, AstNode errorLocation, Set<String> seen) {
+ void checkHelper(ExecutableElement e) {
+ if (e.isStatic) return;
+ if (seen.contains(e.name)) return;
+ if (_checkSingleOverride(e, baseType, null, errorLocation)) {
+ seen.add(e.name);
+ }
+ }
+ subType.methods.forEach(checkHelper);
+ subType.accessors.forEach(checkHelper);
+ }
+
+ /// Check that individual methods and fields in [subType] correctly override
+ /// the declarations in [baseType].
+ ///
+ /// The [errorLocation] node indicates where errors are reported, see
+ /// [_checkSingleOverride] for more details.
+ _checkIndividualOverridesFromClass(
+ ClassDeclaration node, InterfaceType baseType, Set<String> seen) {
+ for (var member in node.members) {
+ if (member is ConstructorDeclaration) continue;
+ if (member is FieldDeclaration) {
+ if (member.isStatic) continue;
+ for (var variable in member.fields.variables) {
+ var element = variable.element as PropertyInducingElement;
+ var name = element.name;
+ if (seen.contains(name)) continue;
+ var getter = element.getter;
+ var setter = element.setter;
+ bool found = _checkSingleOverride(getter, baseType, variable, member);
+ if (!variable.isFinal &&
+ !variable.isConst &&
+ _checkSingleOverride(setter, baseType, variable, member)) {
+ found = true;
+ }
+ if (found) seen.add(name);
+ }
+ } else {
+ if ((member as MethodDeclaration).isStatic) continue;
+ var method = (member as MethodDeclaration).element;
+ if (seen.contains(method.name)) continue;
+ if (_checkSingleOverride(method, baseType, member, member)) {
+ seen.add(method.name);
+ }
+ }
+ }
+ }
+
+ /// Checks that [element] correctly overrides its corresponding member in
+ /// [type]. Returns `true` if an override was found, that is, if [element] has
+ /// a corresponding member in [type] that it overrides.
+ ///
+ /// The [errorLocation] is a node where the error is reported. For example, a
+ /// bad override of a method in a class with respect to its superclass is
+ /// reported directly at the method declaration. However, invalid overrides
+ /// from base classes to interfaces, mixins to the base they are applied to,
+ /// or mixins to interfaces are reported at the class declaration, since the
+ /// base class or members on their own were not incorrect, only combining them
+ /// with the interface was problematic. For example, these are example error
+ /// locations in these cases:
+ ///
+ /// error: base class introduces an invalid override. The type of B.foo is
+ /// not a subtype of E.foo:
+ /// class A extends B implements E { ... }
+ /// ^^^^^^^^^
+ ///
+ /// error: mixin introduces an invalid override. The type of C.foo is not
+ /// a subtype of E.foo:
+ /// class A extends B with C implements E { ... }
+ /// ^
+ ///
+ /// When checking for overrides from a type and it's super types, [node] is
+ /// the AST node that defines [element]. This is used to determine whether the
+ /// type of the element could be inferred from the types in the super classes.
+ bool _checkSingleOverride(ExecutableElement element, InterfaceType type,
+ AstNode node, AstNode errorLocation) {
+ assert(!element.isStatic);
+
+ FunctionType subType = _rules.elementType(element);
+ // TODO(vsm): Test for generic
+ FunctionType baseType = _getMemberType(type, element);
+
+ if (baseType == null) return false;
+ if (!_rules.isAssignable(subType, baseType)) {
+ // See whether non-assignable cases fit one of our common patterns:
+ //
+ // Common pattern 1: Inferable return type (on getters and methods)
+ // class A {
+ // int get foo => ...;
+ // String toString() { ... }
+ // }
+ // class B extends A {
+ // get foo => e; // no type specified.
+ // toString() { ... } // no return type specified.
+ // }
+ _recordMessage(new InvalidMethodOverride(
+ errorLocation, element, type, subType, baseType));
+ }
+ return true;
+ }
+
+ void _recordMessage(StaticInfo info) {
+ if (info == null) return;
+ var error = info.toAnalysisError();
+ if (error.errorCode.errorSeverity == ErrorSeverity.ERROR) _failure = true;
+ _reporter.onError(error);
+ }
+}
+
+/// Checks the body of functions and properties.
+class CodeChecker extends RecursiveAstVisitor {
+ final TypeRules rules;
+ final AnalysisErrorListener reporter;
+ final _OverrideChecker _overrideChecker;
+ final bool _hints;
+
+ bool _failure = false;
+ bool get failure => _failure || _overrideChecker._failure;
+
+ void reset() {
+ _failure = false;
+ _overrideChecker._failure = false;
+ }
+
+ CodeChecker(TypeRules rules, AnalysisErrorListener reporter,
+ {bool hints: false})
+ : rules = rules,
+ reporter = reporter,
+ _hints = hints,
+ _overrideChecker = new _OverrideChecker(rules, reporter);
+
+ @override
+ void visitComment(Comment node) {
+ // skip, no need to do typechecking inside comments (they may contain
+ // comment references which would require resolution).
+ }
+
+ @override
+ void visitClassDeclaration(ClassDeclaration node) {
+ _overrideChecker.check(node);
+ super.visitClassDeclaration(node);
+ }
+
+ @override
+ void visitAssignmentExpression(AssignmentExpression node) {
+ var token = node.operator;
+ if (token.type != TokenType.EQ) {
+ _checkCompoundAssignment(node);
+ } else {
+ DartType staticType = _getStaticType(node.leftHandSide);
+ checkAssignment(node.rightHandSide, staticType);
+ }
+ node.visitChildren(this);
+ }
+
+ /// Check constructor declaration to ensure correct super call placement.
+ @override
+ void visitConstructorDeclaration(ConstructorDeclaration node) {
+ node.visitChildren(this);
+
+ final init = node.initializers;
+ for (int i = 0, last = init.length - 1; i < last; i++) {
+ final node = init[i];
+ if (node is SuperConstructorInvocation) {
+ _recordMessage(new InvalidSuperInvocation(node));
+ }
+ }
+ }
+
+ @override
+ void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+ var field = node.fieldName;
+ var element = field.staticElement;
+ DartType staticType = rules.elementType(element);
+ checkAssignment(node.expression, staticType);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitForEachStatement(ForEachStatement node) {
+ // Check that the expression is an Iterable.
+ var expr = node.iterable;
+ var iterableType = node.awaitKeyword != null
+ ? rules.provider.streamType
+ : rules.provider.iterableType;
+ var loopVariable = node.identifier != null
+ ? node.identifier
+ : node.loopVariable?.identifier;
+ if (loopVariable != null) {
+ var iteratorType = loopVariable.staticType;
+ var checkedType = iterableType.substitute4([iteratorType]);
+ checkAssignment(expr, checkedType);
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitForStatement(ForStatement node) {
+ if (node.condition != null) {
+ checkBoolean(node.condition);
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitIfStatement(IfStatement node) {
+ checkBoolean(node.condition);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDoStatement(DoStatement node) {
+ checkBoolean(node.condition);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitWhileStatement(WhileStatement node) {
+ checkBoolean(node.condition);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSwitchStatement(SwitchStatement node) {
+ // SwitchStatement defines a boolean conversion to check the result of the
+ // case value == the switch value, but in dev_compiler we require a boolean
+ // return type from an overridden == operator (because Object.==), so
+ // checking in SwitchStatement shouldn't be necessary.
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitListLiteral(ListLiteral node) {
+ var type = rules.provider.dynamicType;
+ if (node.typeArguments != null) {
+ var targs = node.typeArguments.arguments;
+ if (targs.length > 0) type = targs[0].type;
+ }
+ var elements = node.elements;
+ for (int i = 0; i < elements.length; i++) {
+ checkArgument(elements[i], type);
+ }
+ super.visitListLiteral(node);
+ }
+
+ @override
+ void visitMapLiteral(MapLiteral node) {
+ var ktype = rules.provider.dynamicType;
+ var vtype = rules.provider.dynamicType;
+ if (node.typeArguments != null) {
+ var targs = node.typeArguments.arguments;
+ if (targs.length > 0) ktype = targs[0].type;
+ if (targs.length > 1) vtype = targs[1].type;
+ }
+ var entries = node.entries;
+ for (int i = 0; i < entries.length; i++) {
+ var entry = entries[i];
+ checkArgument(entry.key, ktype);
+ checkArgument(entry.value, vtype);
+ }
+ super.visitMapLiteral(node);
+ }
+
+ // Check invocations
+ void checkArgumentList(ArgumentList node, FunctionType type) {
+ NodeList<Expression> list = node.arguments;
+ int len = list.length;
+ for (int i = 0; i < len; ++i) {
+ Expression arg = list[i];
+ ParameterElement element = arg.staticParameterElement;
+ if (element == null) {
+ if (type.parameters.length < len) {
+ // We found an argument mismatch, the analyzer will report this too,
+ // so no need to insert an error for this here.
+ continue;
+ }
+ element = type.parameters[i];
+ // TODO(vsm): When can this happen?
+ assert(element != null);
+ }
+ DartType expectedType = rules.elementType(element);
+ if (expectedType == null) expectedType = rules.provider.dynamicType;
+ checkArgument(arg, expectedType);
+ }
+ }
+
+ void checkArgument(Expression arg, DartType expectedType) {
+ // Preserve named argument structure, so their immediate parent is the
+ // method invocation.
+ if (arg is NamedExpression) {
+ arg = (arg as NamedExpression).expression;
+ }
+ checkAssignment(arg, expectedType);
+ }
+
+ void checkFunctionApplication(
+ Expression node, Expression f, ArgumentList list) {
+ if (rules.isDynamicCall(f)) {
+ // If f is Function and this is a method invocation, we should have
+ // gotten an analyzer error, so no need to issue another error.
+ _recordDynamicInvoke(node, f);
+ } else {
+ checkArgumentList(list, rules.getTypeAsCaller(f));
+ }
+ }
+
+ @override
+ visitMethodInvocation(MethodInvocation node) {
+ var target = node.realTarget;
+ if (rules.isDynamicTarget(target) &&
+ !_isObjectMethod(node, node.methodName)) {
+ _recordDynamicInvoke(node, target);
+
+ // Mark the tear-off as being dynamic, too. This lets us distinguish
+ // cases like:
+ //
+ // dynamic d;
+ // d.someMethod(...); // the whole method call must be a dynamic send.
+ //
+ // ... from case like:
+ //
+ // SomeType s;
+ // s.someDynamicField(...); // static get, followed by dynamic call.
+ //
+ // The first case is handled here, the second case is handled below when
+ // we call [checkFunctionApplication].
+ DynamicInvoke.set(node.methodName, true);
+ } else {
+ checkFunctionApplication(node, node.methodName, node.argumentList);
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+ checkFunctionApplication(node, node.function, node.argumentList);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitRedirectingConstructorInvocation(
+ RedirectingConstructorInvocation node) {
+ var type = node.staticElement.type;
+ checkArgumentList(node.argumentList, type);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+ var element = node.staticElement;
+ if (element != null) {
+ var type = node.staticElement.type;
+ checkArgumentList(node.argumentList, type);
+ }
+ node.visitChildren(this);
+ }
+
+ void _checkReturnOrYield(Expression expression, AstNode node,
+ {bool yieldStar: false}) {
+ var body = node.getAncestor((n) => n is FunctionBody);
+ var type = rules.getExpectedReturnType(body, yieldStar: yieldStar);
+ if (type == null) {
+ // We have a type mismatch: the async/async*/sync* modifier does
+ // not match the return or yield type. We should have already gotten an
+ // analyzer error in this case.
+ return;
+ }
+ // TODO(vsm): Enforce void or dynamic (to void?) when expression is null.
+ if (expression != null) checkAssignment(expression, type);
+ }
+
+ @override
+ void visitExpressionFunctionBody(ExpressionFunctionBody node) {
+ _checkReturnOrYield(node.expression, node);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitReturnStatement(ReturnStatement node) {
+ _checkReturnOrYield(node.expression, node);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitYieldStatement(YieldStatement node) {
+ _checkReturnOrYield(node.expression, node, yieldStar: node.star != null);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitPropertyAccess(PropertyAccess node) {
+ var target = node.realTarget;
+ if (rules.isDynamicTarget(target) &&
+ !_isObjectProperty(target, node.propertyName)) {
+ _recordDynamicInvoke(node, target);
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitPrefixedIdentifier(PrefixedIdentifier node) {
+ final target = node.prefix;
+ if (rules.isDynamicTarget(target) &&
+ !_isObjectProperty(target, node.identifier)) {
+ _recordDynamicInvoke(node, target);
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitDefaultFormalParameter(DefaultFormalParameter node) {
+ // Check that defaults have the proper subtype.
+ var parameter = node.parameter;
+ var parameterType = rules.elementType(parameter.element);
+ assert(parameterType != null);
+ var defaultValue = node.defaultValue;
+ if (defaultValue != null) {
+ checkAssignment(defaultValue, parameterType);
+ }
+
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitFieldFormalParameter(FieldFormalParameter node) {
+ var element = node.element;
+ var typeName = node.type;
+ if (typeName != null) {
+ var type = rules.elementType(element);
+ var fieldElement =
+ node.identifier.staticElement as FieldFormalParameterElement;
+ var fieldType = rules.elementType(fieldElement.field);
+ if (!rules.isSubTypeOf(type, fieldType)) {
+ var staticInfo =
+ new InvalidParameterDeclaration(rules, node, fieldType);
+ _recordMessage(staticInfo);
+ }
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitInstanceCreationExpression(InstanceCreationExpression node) {
+ var arguments = node.argumentList;
+ var element = node.staticElement;
+ if (element != null) {
+ var type = rules.elementType(node.staticElement);
+ checkArgumentList(arguments, type);
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitVariableDeclarationList(VariableDeclarationList node) {
+ TypeName type = node.type;
+ if (type == null) {
+ // No checks are needed when the type is var. Although internally the
+ // typing rules may have inferred a more precise type for the variable
+ // based on the initializer.
+ } else {
+ var dartType = getType(type);
+ for (VariableDeclaration variable in node.variables) {
+ var initializer = variable.initializer;
+ if (initializer != null) {
+ checkAssignment(initializer, dartType);
+ }
+ }
+ }
+ node.visitChildren(this);
+ }
+
+ void _checkRuntimeTypeCheck(AstNode node, TypeName typeName) {
+ var type = getType(typeName);
+ if (!rules.isGroundType(type)) {
+ _recordMessage(new NonGroundTypeCheckInfo(node, type));
+ }
+ }
+
+ @override
+ void visitAsExpression(AsExpression node) {
+ // We could do the same check as the IsExpression below, but that is
+ // potentially too conservative. Instead, at runtime, we must fail hard
+ // if the Dart as and the DDC as would return different values.
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitIsExpression(IsExpression node) {
+ _checkRuntimeTypeCheck(node, node.type);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitPrefixExpression(PrefixExpression node) {
+ if (node.operator.type == TokenType.BANG) {
+ checkBoolean(node.operand);
+ } else {
+ _checkUnary(node);
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitPostfixExpression(PostfixExpression node) {
+ _checkUnary(node);
+ node.visitChildren(this);
+ }
+
+ void _checkUnary(/*PrefixExpression|PostfixExpression*/ node) {
+ var op = node.operator;
+ if (op.isUserDefinableOperator ||
+ op.type == TokenType.PLUS_PLUS ||
+ op.type == TokenType.MINUS_MINUS) {
+ if (rules.isDynamicTarget(node.operand)) {
+ _recordDynamicInvoke(node, node.operand);
+ }
+ // For ++ and --, even if it is not dynamic, we still need to check
+ // that the user defined method accepts an `int` as the RHS.
+ // We assume Analyzer has done this already.
+ }
+ }
+
+ @override
+ void visitBinaryExpression(BinaryExpression node) {
+ var op = node.operator;
+ if (op.isUserDefinableOperator) {
+ if (rules.isDynamicTarget(node.leftOperand)) {
+ // Dynamic invocation
+ // TODO(vsm): Move this logic to the resolver?
+ if (op.type != TokenType.EQ_EQ && op.type != TokenType.BANG_EQ) {
+ _recordDynamicInvoke(node, node.leftOperand);
+ }
+ } else {
+ var element = node.staticElement;
+ // Method invocation.
+ if (element is MethodElement) {
+ var type = element.type;
+ // Analyzer should enforce number of parameter types, but check in
+ // case we have erroneous input.
+ if (type.normalParameterTypes.isNotEmpty) {
+ checkArgument(node.rightOperand, type.normalParameterTypes[0]);
+ }
+ } else {
+ // TODO(vsm): Assert that the analyzer found an error here?
+ }
+ }
+ } else {
+ // Non-method operator.
+ switch (op.type) {
+ case TokenType.AMPERSAND_AMPERSAND:
+ case TokenType.BAR_BAR:
+ checkBoolean(node.leftOperand);
+ checkBoolean(node.rightOperand);
+ break;
+ case TokenType.BANG_EQ:
+ break;
+ case TokenType.QUESTION_QUESTION:
+ break;
+ default:
+ assert(false);
+ }
+ }
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitConditionalExpression(ConditionalExpression node) {
+ checkBoolean(node.condition);
+ node.visitChildren(this);
+ }
+
+ @override
+ void visitIndexExpression(IndexExpression node) {
+ var target = node.realTarget;
+ if (rules.isDynamicTarget(target)) {
+ _recordDynamicInvoke(node, target);
+ } else {
+ var element = node.staticElement;
+ if (element is MethodElement) {
+ var type = element.type;
+ // Analyzer should enforce number of parameter types, but check in
+ // case we have erroneous input.
+ if (type.normalParameterTypes.isNotEmpty) {
+ checkArgument(node.index, type.normalParameterTypes[0]);
+ }
+ } else {
+ // TODO(vsm): Assert that the analyzer found an error here?
+ }
+ }
+ node.visitChildren(this);
+ }
+
+ DartType getType(TypeName name) {
+ return (name == null) ? rules.provider.dynamicType : name.type;
+ }
+
+ /// Analyzer checks boolean conversions, but we need to check too, because
+ /// it uses the default assignability rules that allow `dynamic` and `Object`
+ /// to be assigned to bool with no message.
+ void checkBoolean(Expression expr) =>
+ checkAssignment(expr, rules.provider.boolType);
+
+ void checkAssignment(Expression expr, DartType type) {
+ if (expr is ParenthesizedExpression) {
+ checkAssignment(expr.expression, type);
+ } else {
+ _recordMessage(rules.checkAssignment(expr, type));
+ }
+ }
+
+ DartType _specializedBinaryReturnType(
+ TokenType op, DartType t1, DartType t2, DartType normalReturnType) {
+ // This special cases binary return types as per 16.26 and 16.27 of the
+ // Dart language spec.
+ switch (op) {
+ case TokenType.PLUS:
+ case TokenType.MINUS:
+ case TokenType.STAR:
+ case TokenType.TILDE_SLASH:
+ case TokenType.PERCENT:
+ case TokenType.PLUS_EQ:
+ case TokenType.MINUS_EQ:
+ case TokenType.STAR_EQ:
+ case TokenType.TILDE_SLASH_EQ:
+ case TokenType.PERCENT_EQ:
+ if (t1 == rules.provider.intType &&
+ t2 == rules.provider.intType) return t1;
+ if (t1 == rules.provider.doubleType &&
+ t2 == rules.provider.doubleType) return t1;
+ // This particular combo is not spelled out in the spec, but all
+ // implementations and analyzer seem to follow this.
+ if (t1 == rules.provider.doubleType &&
+ t2 == rules.provider.intType) return t1;
+ }
+ return normalReturnType;
+ }
+
+ void _checkCompoundAssignment(AssignmentExpression expr) {
+ var op = expr.operator.type;
+ assert(op.isAssignmentOperator && op != TokenType.EQ);
+ var methodElement = expr.staticElement;
+ if (methodElement == null) {
+ // Dynamic invocation
+ _recordDynamicInvoke(expr, expr.leftHandSide);
+ } else {
+ // Sanity check the operator
+ assert(methodElement.isOperator);
+ var functionType = methodElement.type;
+ var paramTypes = functionType.normalParameterTypes;
+ assert(paramTypes.length == 1);
+ assert(functionType.namedParameterTypes.isEmpty);
+ assert(functionType.optionalParameterTypes.isEmpty);
+
+ // Check the lhs type
+ var staticInfo;
+ var rhsType = _getStaticType(expr.rightHandSide);
+ var lhsType = _getStaticType(expr.leftHandSide);
+ var returnType = _specializedBinaryReturnType(
+ op, lhsType, rhsType, functionType.returnType);
+
+ if (!rules.isSubTypeOf(returnType, lhsType)) {
+ final numType = rules.provider.numType;
+ // Try to fix up the numerical case if possible.
+ if (rules.isSubTypeOf(lhsType, numType) &&
+ rules.isSubTypeOf(lhsType, rhsType)) {
+ // This is also slightly different from spec, but allows us to keep
+ // compound operators in the int += num and num += dynamic cases.
+ staticInfo = DownCast.create(
+ rules, expr.rightHandSide, Coercion.cast(rhsType, lhsType));
+ rhsType = lhsType;
+ } else {
+ // Static type error
+ staticInfo = new StaticTypeError(rules, expr, lhsType);
+ }
+ _recordMessage(staticInfo);
+ }
+
+ // Check the rhs type
+ if (staticInfo is! CoercionInfo) {
+ var paramType = paramTypes.first;
+ staticInfo = rules.checkAssignment(expr.rightHandSide, paramType);
+ _recordMessage(staticInfo);
+ }
+ }
+ }
+
+ bool _isObjectGetter(Expression target, SimpleIdentifier id) {
+ PropertyAccessorElement element =
+ rules.provider.objectType.element.getGetter(id.name);
+ return (element != null && !element.isStatic);
+ }
+
+ bool _isObjectMethod(Expression target, SimpleIdentifier id) {
+ MethodElement element =
+ rules.provider.objectType.element.getMethod(id.name);
+ return (element != null && !element.isStatic);
+ }
+
+ bool _isObjectProperty(Expression target, SimpleIdentifier id) {
+ return _isObjectGetter(target, id) || _isObjectMethod(target, id);
+ }
+
+ DartType _getStaticType(Expression expr) {
+ return expr.staticType ?? rules.provider.dynamicType;
+ }
+
+ void _recordDynamicInvoke(AstNode node, AstNode target) {
+ if (_hints) {
+ reporter.onError(new DynamicInvoke(rules, node).toAnalysisError());
+ }
+ // TODO(jmesserly): we may eventually want to record if the whole operation
+ // (node) was dynamic, rather than the target, but this is an easier fit
+ // with what we used to do.
+ DynamicInvoke.set(target, true);
+ }
+
+ void _recordMessage(StaticInfo info) {
+ if (info == null) return;
+ var error = info.toAnalysisError();
+
+ var severity = error.errorCode.errorSeverity;
+ if (severity == ErrorSeverity.ERROR) _failure = true;
+ if (severity != ErrorSeverity.INFO || _hints) {
+ reporter.onError(error);
+ }
+
+ if (info is CoercionInfo) {
+ // TODO(jmesserly): if we're run again on the same AST, we'll produce the
+ // same annotations. This should be harmless. This might go away once
+ // CodeChecker is integrated better with analyzer, as it will know that
+ // checking has already been performed.
+ // assert(CoercionInfo.get(info.node) == null);
+ CoercionInfo.set(info.node, info);
+ }
+ }
+}
+
+/// Looks up the declaration that matches [member] in [type] and returns it's
+/// declared type.
+FunctionType _getMemberType(InterfaceType type, ExecutableElement member) =>
+ _memberTypeGetter(member)(type);
+
+typedef FunctionType _MemberTypeGetter(InterfaceType type);
+
+_MemberTypeGetter _memberTypeGetter(ExecutableElement member) {
+ String memberName = member.name;
+ final isGetter = member is PropertyAccessorElement && member.isGetter;
+ final isSetter = member is PropertyAccessorElement && member.isSetter;
+
+ FunctionType f(InterfaceType type) {
+ ExecutableElement baseMethod;
+ try {
+ if (isGetter) {
+ assert(!isSetter);
+ // Look for getter or field.
+ baseMethod = type.getGetter(memberName);
+ } else if (isSetter) {
+ baseMethod = type.getSetter(memberName);
+ } else {
+ baseMethod = type.getMethod(memberName);
+ }
+ } catch (e) {
+ // TODO(sigmund): remove this try-catch block (see issue #48).
+ }
+ if (baseMethod == null || baseMethod.isStatic) return null;
+ return baseMethod.type;
+ }
+ ;
+ return f;
+}
diff --git a/packages/analyzer/lib/src/task/strong/info.dart b/packages/analyzer/lib/src/task/strong/info.dart
new file mode 100644
index 0000000..a18f76f
--- /dev/null
+++ b/packages/analyzer/lib/src/task/strong/info.dart
@@ -0,0 +1,472 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Defines static information collected by the type checker and used later by
+/// emitters to generate code.
+// TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
+// refactored to fit into analyzer.
+library analyzer.src.task.strong.info;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/error.dart';
+
+import 'rules.dart';
+
+// The abstract type of coercions mapping one type to another.
+// This class also exposes static builder functions which
+// check for errors and reduce redundant coercions to the identity.
+abstract class Coercion {
+ final DartType fromType;
+ final DartType toType;
+ Coercion(this.fromType, this.toType);
+ static Coercion cast(DartType fromT, DartType toT) => new Cast(fromT, toT);
+ static Coercion identity(DartType type) => new Identity(type);
+ static Coercion error() => new CoercionError();
+}
+
+// Coercion which casts one type to another
+class Cast extends Coercion {
+ Cast(DartType fromType, DartType toType) : super(fromType, toType);
+}
+
+// The identity coercion
+class Identity extends Coercion {
+ Identity(DartType fromType) : super(fromType, fromType);
+}
+
+// The error coercion. This coercion signals that a coercion
+// could not be generated. The code generator should not see
+// these.
+class CoercionError extends Coercion {
+ CoercionError() : super(null, null);
+}
+
+// TODO(jmesserly): this could use some refactoring. These are essentially
+// like ErrorCodes in analyzer, but we're including some details in our message.
+// Analyzer instead has template strings, and replaces '{0}' with the first
+// argument.
+abstract class StaticInfo {
+ /// AST Node this info is attached to.
+ AstNode get node;
+
+ // TODO(jmesserly): review the usage of error codes. We probably want our own,
+ // as well as some DDC specific [ErrorType]s.
+ ErrorCode toErrorCode();
+
+ // TODO(jmesserly): what convention to use here?
+ String get name => 'dev_compiler.$runtimeType';
+
+ List<Object> get arguments => [node];
+
+ AnalysisError toAnalysisError() {
+ int begin = node is AnnotatedNode
+ ? (node as AnnotatedNode).firstTokenAfterCommentAndMetadata.offset
+ : node.offset;
+ int length = node.end - begin;
+ var source = (node.root as CompilationUnit).element.source;
+ return new AnalysisError(source, begin, length, toErrorCode(), arguments);
+ }
+}
+
+/// Implicitly injected expression conversion.
+abstract class CoercionInfo extends StaticInfo {
+ final TypeRules rules;
+
+ final Expression node;
+
+ DartType get convertedType;
+
+ CoercionInfo(this.rules, this.node);
+
+ DartType get baseType => rules.getStaticType(node);
+ DartType get staticType => convertedType;
+
+ String get message;
+ toErrorCode() => new HintCode(name, message);
+
+ static const String _propertyName = 'dev_compiler.src.info.CoercionInfo';
+
+ /// Gets the coercion info associated with this node.
+ static CoercionInfo get(AstNode node) => node.getProperty(_propertyName);
+
+ /// Sets the coercion info associated with this node.
+ static CoercionInfo set(AstNode node, CoercionInfo info) {
+ node.setProperty(_propertyName, info);
+ return info;
+ }
+}
+
+// Base class for all casts from base type to sub type.
+abstract class DownCast extends CoercionInfo {
+ Cast _cast;
+
+ DownCast._internal(TypeRules rules, Expression expression, this._cast)
+ : super(rules, expression) {
+ assert(_cast.toType != baseType &&
+ _cast.fromType == baseType &&
+ (baseType.isDynamic ||
+ // Call methods make the following non-redundant
+ _cast.toType.isSubtypeOf(baseType) ||
+ baseType.isAssignableTo(_cast.toType)));
+ }
+
+ Cast get cast => _cast;
+
+ DartType get convertedType => _cast.toType;
+
+ @override List<Object> get arguments => [node, baseType, convertedType];
+ @override String get message => '{0} ({1}) will need runtime check '
+ 'to cast to type {2}';
+
+ // Factory to create correct DownCast variant.
+ static StaticInfo create(TypeRules rules, Expression expression, Cast cast,
+ {String reason}) {
+ final fromT = cast.fromType;
+ final toT = cast.toType;
+
+ // toT <:_R fromT => to <: fromT
+ // NB: classes with call methods are subtypes of function
+ // types, but the function type is not assignable to the class
+ assert(toT.isSubtypeOf(fromT) || fromT.isAssignableTo(toT));
+
+ // Handle null call specially.
+ if (expression is NullLiteral) {
+ // TODO(vsm): Create a NullCast for this once we revisit nonnullability.
+ return new DownCastImplicit(rules, expression, cast);
+ }
+
+ // Inference "casts":
+ if (expression is Literal) {
+ // fromT should be an exact type - this will almost certainly fail at
+ // runtime.
+ return new StaticTypeError(rules, expression, toT, reason: reason);
+ }
+ if (expression is FunctionExpression) {
+ // fromT should be an exact type - this will almost certainly fail at
+ // runtime.
+ return new UninferredClosure(rules, expression, cast);
+ }
+ if (expression is InstanceCreationExpression) {
+ // fromT should be an exact type - this will almost certainly fail at
+ // runtime.
+ return new StaticTypeError(rules, expression, toT, reason: reason);
+ }
+
+ // Composite cast: these are more likely to fail.
+ if (!rules.isGroundType(toT)) {
+ // This cast is (probably) due to our different treatment of dynamic.
+ // It may be more likely to fail at runtime.
+ return new DownCastComposite(rules, expression, cast);
+ }
+
+ // Dynamic cast
+ if (fromT.isDynamic) {
+ return new DynamicCast(rules, expression, cast);
+ }
+
+ // Assignment cast
+ var parent = expression.parent;
+ if (parent is VariableDeclaration && (parent.initializer == expression)) {
+ return new AssignmentCast(rules, expression, cast);
+ }
+
+ // Other casts
+ return new DownCastImplicit(rules, expression, cast);
+ }
+}
+
+//
+// Standard down casts. These casts are implicitly injected by the compiler.
+//
+
+// A down cast from dynamic to T.
+class DynamicCast extends DownCast {
+ DynamicCast(TypeRules rules, Expression expression, Cast cast)
+ : super._internal(rules, expression, cast);
+
+ toErrorCode() => new HintCode(name, message);
+}
+
+// A down cast due to a variable declaration to a ground type. E.g.,
+// T x = expr;
+// where T is ground. We exclude non-ground types as these behave differently
+// compared to standard Dart.
+class AssignmentCast extends DownCast {
+ AssignmentCast(TypeRules rules, Expression expression, Cast cast)
+ : super._internal(rules, expression, cast);
+
+ toErrorCode() => new HintCode(name, message);
+}
+
+//
+// Temporary "casts" of allocation sites - literals, constructor invocations,
+// and closures. These should be handled by contextual inference. In most
+// cases, inference will be sufficient, though in some it may unmask an actual
+// error: e.g.,
+// List<int> l = [1, 2, 3]; // Inference succeeds
+// List<String> l = [1, 2, 3]; // Inference reveals static type error
+// We're marking all as warnings for now.
+//
+// TODO(vsm,leafp): Remove this.
+class UninferredClosure extends DownCast {
+ UninferredClosure(TypeRules rules, FunctionExpression expression, Cast cast)
+ : super._internal(rules, expression, cast);
+
+ toErrorCode() => new StaticTypeWarningCode(name, message);
+}
+
+//
+// Implicit down casts. These are only injected by the compiler by flag.
+//
+
+// A down cast to a non-ground type. These behave differently from standard
+// Dart and may be more likely to fail at runtime.
+class DownCastComposite extends DownCast {
+ DownCastComposite(TypeRules rules, Expression expression, Cast cast)
+ : super._internal(rules, expression, cast);
+
+ toErrorCode() => new StaticTypeWarningCode(name, message);
+}
+
+// A down cast to a non-ground type. These behave differently from standard
+// Dart and may be more likely to fail at runtime.
+class DownCastImplicit extends DownCast {
+ DownCastImplicit(TypeRules rules, Expression expression, Cast cast)
+ : super._internal(rules, expression, cast);
+
+ toErrorCode() => new HintCode(name, message);
+}
+
+// An inferred type for the wrapped expression, which may need to be
+// reified into the term
+abstract class InferredTypeBase extends CoercionInfo {
+ final DartType _type;
+
+ InferredTypeBase._internal(TypeRules rules, Expression expression, this._type)
+ : super(rules, expression);
+
+ DartType get type => _type;
+ DartType get convertedType => type;
+ @override String get message => '{0} has inferred type {1}';
+ @override List get arguments => [node, type];
+
+ toErrorCode() => new HintCode(name, message);
+}
+
+// Standard / unspecialized inferred type
+class InferredType extends InferredTypeBase {
+ InferredType(TypeRules rules, Expression expression, DartType type)
+ : super._internal(rules, expression, type);
+
+ // Factory to create correct InferredType variant.
+ static InferredTypeBase create(
+ TypeRules rules, Expression expression, DartType type) {
+ // Specialized inference:
+ if (expression is Literal) {
+ return new InferredTypeLiteral(rules, expression, type);
+ }
+ if (expression is InstanceCreationExpression) {
+ return new InferredTypeAllocation(rules, expression, type);
+ }
+ if (expression is FunctionExpression) {
+ return new InferredTypeClosure(rules, expression, type);
+ }
+ return new InferredType(rules, expression, type);
+ }
+}
+
+// An infered type for a literal expression.
+class InferredTypeLiteral extends InferredTypeBase {
+ InferredTypeLiteral(TypeRules rules, Expression expression, DartType type)
+ : super._internal(rules, expression, type);
+}
+
+// An inferred type for a non-literal allocation site.
+class InferredTypeAllocation extends InferredTypeBase {
+ InferredTypeAllocation(TypeRules rules, Expression expression, DartType type)
+ : super._internal(rules, expression, type);
+}
+
+// An inferred type for a closure expression
+class InferredTypeClosure extends InferredTypeBase {
+ InferredTypeClosure(TypeRules rules, Expression expression, DartType type)
+ : super._internal(rules, expression, type);
+}
+
+class DynamicInvoke extends CoercionInfo {
+ DynamicInvoke(TypeRules rules, Expression expression)
+ : super(rules, expression);
+
+ DartType get convertedType => rules.provider.dynamicType;
+ String get message => '{0} requires dynamic invoke';
+ toErrorCode() => new HintCode(name, message);
+
+ static const String _propertyName = 'dev_compiler.src.info.DynamicInvoke';
+
+ /// Whether this [node] is the target of a dynamic operation.
+ static bool get(AstNode node) {
+ var value = node.getProperty(_propertyName);
+ return value != null ? value : false;
+ }
+
+ /// Sets whether this node is the target of a dynamic operation.
+ static bool set(AstNode node, bool value) {
+ // Free the storage for things that aren't dynamic.
+ if (value == false) value = null;
+ node.setProperty(_propertyName, value);
+ return value;
+ }
+}
+
+abstract class StaticError extends StaticInfo {
+ final AstNode node;
+
+ StaticError(this.node);
+
+ String get message;
+
+ toErrorCode() => new CompileTimeErrorCode(name, message);
+}
+
+class StaticTypeError extends StaticError {
+ final DartType baseType;
+ final DartType expectedType;
+ String reason = null;
+
+ StaticTypeError(TypeRules rules, Expression expression, this.expectedType,
+ {this.reason})
+ : baseType = rules.getStaticType(expression),
+ super(expression);
+
+ @override List<Object> get arguments => [node, baseType, expectedType];
+ @override String get message =>
+ 'Type check failed: {0} ({1}) is not of type {2}' +
+ ((reason == null) ? '' : ' because $reason');
+}
+
+class InvalidVariableDeclaration extends StaticError {
+ final DartType expectedType;
+
+ InvalidVariableDeclaration(
+ TypeRules rules, AstNode declaration, this.expectedType)
+ : super(declaration);
+
+ @override List<Object> get arguments => [expectedType];
+ @override String get message => 'Type check failed: null is not of type {0}';
+}
+
+class InvalidParameterDeclaration extends StaticError {
+ final DartType expectedType;
+
+ InvalidParameterDeclaration(
+ TypeRules rules, FormalParameter declaration, this.expectedType)
+ : super(declaration);
+
+ @override List<Object> get arguments => [node, expectedType];
+ @override String get message => 'Type check failed: {0} is not of type {1}';
+}
+
+class NonGroundTypeCheckInfo extends StaticInfo {
+ final DartType type;
+ final AstNode node;
+
+ NonGroundTypeCheckInfo(this.node, this.type) {
+ assert(node is IsExpression || node is AsExpression);
+ }
+
+ @override List<Object> get arguments => [type];
+ String get message =>
+ "Runtime check on non-ground type {0} may throw StrongModeError";
+
+ toErrorCode() => new HintCode(name, message);
+}
+
+// Invalid override of an instance member of a class.
+abstract class InvalidOverride extends StaticError {
+ /// Member declaration with the invalid override.
+ final ExecutableElement element;
+
+ /// Type (class or interface) that provides the base declaration.
+ final InterfaceType base;
+
+ /// Actual type of the overridden member.
+ final DartType subType;
+
+ /// Actual type of the base member.
+ final DartType baseType;
+
+ /// Whether the error comes from combining a base class and an interface
+ final bool fromBaseClass;
+
+ /// Whether the error comes from a mixin (either overriding a base class or an
+ /// interface declaration).
+ final bool fromMixin;
+
+ InvalidOverride(
+ AstNode node, this.element, this.base, this.subType, this.baseType)
+ : fromBaseClass = node is ExtendsClause,
+ fromMixin = node.parent is WithClause,
+ super(node);
+
+ ClassElement get parent => element.enclosingElement;
+
+ @override List<Object> get arguments =>
+ [parent.name, element.name, subType, base, baseType];
+
+ String _messageHelper(String errorName) {
+ var lcErrorName = errorName.toLowerCase();
+ var intro = fromBaseClass
+ ? 'Base class introduces an $lcErrorName'
+ : (fromMixin ? 'Mixin introduces an $lcErrorName' : errorName);
+ return '$intro. The type of {0}.{1} ({2}) is not a '
+ 'subtype of {3}.{1} ({4}).';
+ }
+}
+
+// Invalid override due to incompatible type. I.e., the overridden signature
+// is not compatible with the original.
+class InvalidMethodOverride extends InvalidOverride {
+ InvalidMethodOverride(AstNode node, ExecutableElement element,
+ InterfaceType base, FunctionType subType, FunctionType baseType)
+ : super(node, element, base, subType, baseType);
+
+ String get message => _messageHelper('Invalid override');
+}
+
+/// Dart constructors have one weird quirk, illustrated with this example:
+///
+/// class Base {
+/// var x;
+/// Base() : x = print('Base.1') {
+/// print('Base.2');
+/// }
+/// }
+///
+/// class Derived extends Base {
+/// var y, z;
+/// Derived()
+/// : y = print('Derived.1'),
+/// super(),
+/// z = print('Derived.2') {
+/// print('Derived.3');
+/// }
+/// }
+///
+/// The order will be Derived.1, Base.1, Derived.2, Base.2, Derived.3; this
+/// ordering preserves the invariant that code can't observe uninitialized
+/// state, however it results in super constructor body not being run
+/// immediately after super initializers. Normally this isn't observable, but it
+/// could be if initializers have side effects.
+///
+/// Better to have `super` at the end, as required by the Dart style guide:
+/// <http://goo.gl/q1T4BB>
+///
+/// For now this is the only pattern we support.
+class InvalidSuperInvocation extends StaticError {
+ InvalidSuperInvocation(SuperConstructorInvocation node) : super(node);
+
+ @override String get message => "super call must be last in an initializer "
+ "list (see http://goo.gl/q1T4BB): {0}";
+}
diff --git a/packages/analyzer/lib/src/task/strong/rules.dart b/packages/analyzer/lib/src/task/strong/rules.dart
new file mode 100644
index 0000000..b575dbf
--- /dev/null
+++ b/packages/analyzer/lib/src/task/strong/rules.dart
@@ -0,0 +1,770 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// TODO(jmesserly): this was ported from package:dev_compiler, and needs to be
+// refactored to fit into analyzer.
+library analyzer.src.task.strong.rules;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+
+import 'info.dart';
+
+// TODO(jmesserly): this entire file needs to be removed in favor of TypeSystem.
+
+final _objectMap = new Expando('providerToObjectMap');
+Map<String, DartType> getObjectMemberMap(TypeProvider typeProvider) {
+ var map = _objectMap[typeProvider] as Map<String, DartType>;
+ if (map == null) {
+ map = <String, DartType>{};
+ _objectMap[typeProvider] = map;
+ var objectType = typeProvider.objectType;
+ var element = objectType.element;
+ // Only record methods (including getters) with no parameters. As parameters are contravariant wrt
+ // type, using Object's version may be too strict.
+ // Add instance methods.
+ element.methods.where((method) => !method.isStatic).forEach((method) {
+ map[method.name] = method.type;
+ });
+ // Add getters.
+ element.accessors
+ .where((member) => !member.isStatic && member.isGetter)
+ .forEach((member) {
+ map[member.name] = member.type.returnType;
+ });
+ }
+ return map;
+}
+
+class TypeRules {
+ final TypeProvider provider;
+
+ /// Map of fields / properties / methods on Object.
+ final Map<String, DartType> objectMembers;
+
+ DownwardsInference inferrer;
+
+ TypeRules(TypeProvider provider)
+ : provider = provider,
+ objectMembers = getObjectMemberMap(provider) {
+ inferrer = new DownwardsInference(this);
+ }
+
+ /// Given a type t, if t is an interface type with a call method
+ /// defined, return the function type for the call method, otherwise
+ /// return null.
+ FunctionType getCallMethodType(DartType t) {
+ if (t is InterfaceType) {
+ ClassElement element = t.element;
+ InheritanceManager manager = new InheritanceManager(element.library);
+ FunctionType callType = manager.lookupMemberType(t, "call");
+ return callType;
+ }
+ return null;
+ }
+
+ /// Given an expression, return its type assuming it is
+ /// in the caller position of a call (that is, accounting
+ /// for the possibility of a call method). Returns null
+ /// if expression is not statically callable.
+ FunctionType getTypeAsCaller(Expression applicand) {
+ var t = getStaticType(applicand);
+ if (t is InterfaceType) {
+ return getCallMethodType(t);
+ }
+ if (t is FunctionType) return t;
+ return null;
+ }
+
+ /// Gets the expected return type of the given function [body], either from
+ /// a normal return/yield, or from a yield*.
+ DartType getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) {
+ FunctionType functionType;
+ var parent = body.parent;
+ if (parent is Declaration) {
+ functionType = elementType(parent.element);
+ } else {
+ assert(parent is FunctionExpression);
+ functionType = getStaticType(parent);
+ }
+
+ var type = functionType.returnType;
+
+ InterfaceType expectedType = null;
+ if (body.isAsynchronous) {
+ if (body.isGenerator) {
+ // Stream<T> -> T
+ expectedType = provider.streamType;
+ } else {
+ // Future<T> -> T
+ // TODO(vsm): Revisit with issue #228.
+ expectedType = provider.futureType;
+ }
+ } else {
+ if (body.isGenerator) {
+ // Iterable<T> -> T
+ expectedType = provider.iterableType;
+ } else {
+ // T -> T
+ return type;
+ }
+ }
+ if (yieldStar) {
+ if (type.isDynamic) {
+ // Ensure it's at least a Stream / Iterable.
+ return expectedType.substitute4([provider.dynamicType]);
+ } else {
+ // Analyzer will provide a separate error if expected type
+ // is not compatible with type.
+ return type;
+ }
+ }
+ if (type.isDynamic) {
+ return type;
+ } else if (type is InterfaceType && type.element == expectedType.element) {
+ return type.typeArguments[0];
+ } else {
+ // Malformed type - fallback on analyzer error.
+ return null;
+ }
+ }
+
+ DartType getStaticType(Expression expr) {
+ return expr.staticType ?? provider.dynamicType;
+ }
+
+ bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
+ if (t.isDynamic && dynamicIsBottom) return true;
+ // TODO(vsm): We need direct support for non-nullability in DartType.
+ // This should check on "true/nonnullable" Bottom
+ if (t.isBottom) return true;
+ return false;
+ }
+
+ bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
+ if (t.isDynamic && !dynamicIsBottom) return true;
+ if (t.isObject) return true;
+ return false;
+ }
+
+ bool _anyParameterType(FunctionType ft, bool predicate(DartType t)) {
+ return ft.normalParameterTypes.any(predicate) ||
+ ft.optionalParameterTypes.any(predicate) ||
+ ft.namedParameterTypes.values.any(predicate);
+ }
+
+ // TODO(leafp): Revisit this.
+ bool isGroundType(DartType t) {
+ if (t is TypeParameterType) return false;
+ if (_isTop(t)) return true;
+
+ if (t is FunctionType) {
+ if (!_isTop(t.returnType) ||
+ _anyParameterType(t, (pt) => !_isBottom(pt, dynamicIsBottom: true))) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ if (t is InterfaceType) {
+ var typeArguments = t.typeArguments;
+ for (var typeArgument in typeArguments) {
+ if (!_isTop(typeArgument)) return false;
+ }
+ return true;
+ }
+
+ // We should not see any other type aside from malformed code.
+ return false;
+ }
+
+ /// Check that f1 is a subtype of f2. [ignoreReturn] is used in the DDC
+ /// checker to determine whether f1 would be a subtype of f2 if the return
+ /// type of f1 is set to match f2's return type.
+ // [fuzzyArrows] indicates whether or not the f1 and f2 should be
+ // treated as fuzzy arrow types (and hence dynamic parameters to f2 treated as
+ // bottom).
+ bool isFunctionSubTypeOf(FunctionType f1, FunctionType f2,
+ {bool fuzzyArrows: true, bool ignoreReturn: false}) {
+ final r1s = f1.normalParameterTypes;
+ final o1s = f1.optionalParameterTypes;
+ final n1s = f1.namedParameterTypes;
+ final r2s = f2.normalParameterTypes;
+ final o2s = f2.optionalParameterTypes;
+ final n2s = f2.namedParameterTypes;
+ final ret1 = ignoreReturn ? f2.returnType : f1.returnType;
+ final ret2 = f2.returnType;
+
+ // A -> B <: C -> D if C <: A and
+ // either D is void or B <: D
+ if (!ret2.isVoid && !isSubTypeOf(ret1, ret2)) return false;
+
+ // Reject if one has named and the other has optional
+ if (n1s.length > 0 && o2s.length > 0) return false;
+ if (n2s.length > 0 && o1s.length > 0) return false;
+
+ // f2 has named parameters
+ if (n2s.length > 0) {
+ // Check that every named parameter in f2 has a match in f1
+ for (String k2 in n2s.keys) {
+ if (!n1s.containsKey(k2)) return false;
+ if (!isSubTypeOf(n2s[k2], n1s[k2],
+ dynamicIsBottom: fuzzyArrows)) return false;
+ }
+ }
+ // If we get here, we either have no named parameters,
+ // or else the named parameters match and we have no optional
+ // parameters
+
+ // If f1 has more required parameters, reject
+ if (r1s.length > r2s.length) return false;
+
+ // If f2 has more required + optional parameters, reject
+ if (r2s.length + o2s.length > r1s.length + o1s.length) return false;
+
+ // The parameter lists must look like the following at this point
+ // where rrr is a region of required, and ooo is a region of optionals.
+ // f1: rrr ooo ooo ooo
+ // f2: rrr rrr ooo
+ int rr = r1s.length; // required in both
+ int or = r2s.length - r1s.length; // optional in f1, required in f2
+ int oo = o2s.length; // optional in both
+
+ for (int i = 0; i < rr; ++i) {
+ if (!isSubTypeOf(r2s[i], r1s[i],
+ dynamicIsBottom: fuzzyArrows)) return false;
+ }
+ for (int i = 0, j = rr; i < or; ++i, ++j) {
+ if (!isSubTypeOf(r2s[j], o1s[i],
+ dynamicIsBottom: fuzzyArrows)) return false;
+ }
+ for (int i = or, j = 0; i < oo; ++i, ++j) {
+ if (!isSubTypeOf(o2s[j], o1s[i],
+ dynamicIsBottom: fuzzyArrows)) return false;
+ }
+ return true;
+ }
+
+ bool _isInterfaceSubTypeOf(InterfaceType i1, InterfaceType i2) {
+ if (i1 == i2) return true;
+
+ if (i1.element == i2.element) {
+ List<DartType> tArgs1 = i1.typeArguments;
+ List<DartType> tArgs2 = i2.typeArguments;
+
+ // TODO(leafp): Verify that this is always true
+ // Do raw types get filled in?
+ assert(tArgs1.length == tArgs2.length);
+
+ for (int i = 0; i < tArgs1.length; i++) {
+ DartType t1 = tArgs1[i];
+ DartType t2 = tArgs2[i];
+ if (!isSubTypeOf(t1, t2)) return false;
+ }
+ return true;
+ }
+
+ if (i2.isDartCoreFunction) {
+ if (i1.element.getMethod("call") != null) return true;
+ }
+
+ if (i1 == provider.objectType) return false;
+
+ if (_isInterfaceSubTypeOf(i1.superclass, i2)) return true;
+
+ for (final parent in i1.interfaces) {
+ if (_isInterfaceSubTypeOf(parent, i2)) return true;
+ }
+
+ for (final parent in i1.mixins) {
+ if (_isInterfaceSubTypeOf(parent, i2)) return true;
+ }
+
+ return false;
+ }
+
+ bool isSubTypeOf(DartType t1, DartType t2, {bool dynamicIsBottom: false}) {
+ if (t1 == t2) return true;
+
+ // Trivially true.
+ if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
+ _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
+ return true;
+ }
+
+ // Trivially false.
+ if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
+ _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
+ return false;
+ }
+
+ // The null type is a subtype of any nullable type, which is all Dart types.
+ // TODO(vsm): Note, t1.isBottom still allows for null confusingly.
+ // _isBottom(t1) does not necessarily imply t1.isBottom if there are
+ // nonnullable types in the system.
+ if (t1.isBottom) {
+ return true;
+ }
+
+ // S <: T where S is a type variable
+ // T is not dynamic or object (handled above)
+ // S != T (handled above)
+ // So only true if bound of S is S' and
+ // S' <: T
+ if (t1 is TypeParameterType) {
+ DartType bound = t1.element.bound;
+ if (bound == null) return false;
+ return isSubTypeOf(bound, t2);
+ }
+
+ if (t2 is TypeParameterType) {
+ return false;
+ }
+
+ if (t2.isDartCoreFunction) {
+ if (t1 is FunctionType) return true;
+ if (t1.element is ClassElement) {
+ if ((t1.element as ClassElement).getMethod("call") != null) return true;
+ }
+ }
+
+ // "Traditional" name-based subtype check.
+ if (t1 is InterfaceType && t2 is InterfaceType) {
+ return _isInterfaceSubTypeOf(t1, t2);
+ }
+
+ if (t1 is! FunctionType && t2 is! FunctionType) return false;
+
+ if (t1 is InterfaceType && t2 is FunctionType) {
+ var callType = getCallMethodType(t1);
+ if (callType == null) return false;
+ return isFunctionSubTypeOf(callType, t2);
+ }
+
+ if (t1 is FunctionType && t2 is InterfaceType) {
+ return false;
+ }
+
+ // Functions
+ // Note: it appears under the hood all Dart functions map to a class /
+ // hidden type that:
+ // (a) subtypes Object (an internal _FunctionImpl in the VM)
+ // (b) implements Function
+ // (c) provides standard Object members (hashCode, toString)
+ // (d) contains private members (corresponding to _FunctionImpl?)
+ // (e) provides a call method to handle the actual function invocation
+ //
+ // The standard Dart subtyping rules are structural in nature. I.e.,
+ // bivariant on arguments and return type.
+ //
+ // The below tries for a more traditional subtyping rule:
+ // - covariant on return type
+ // - contravariant on parameters
+ // - 'sensible' (?) rules on optional and/or named params
+ // but doesn't properly mix with class subtyping. I suspect Java 8 lambdas
+ // essentially map to dynamic (and rely on invokedynamic) due to similar
+ // issues.
+ return isFunctionSubTypeOf(t1 as FunctionType, t2 as FunctionType);
+ }
+
+ bool isAssignable(DartType t1, DartType t2) {
+ return isSubTypeOf(t1, t2);
+ }
+
+ // Produce a coercion which coerces something of type fromT
+ // to something of type toT.
+ // If wrap is true and both are function types, a closure
+ // wrapper coercion is produced using _wrapTo (see above)
+ // Returns the error coercion if the types cannot be coerced
+ // according to our current criteria.
+ Coercion _coerceTo(DartType fromT, DartType toT) {
+ // We can use anything as void
+ if (toT.isVoid) return Coercion.identity(toT);
+
+ // fromT <: toT, no coercion needed
+ if (isSubTypeOf(fromT, toT)) return Coercion.identity(toT);
+
+ // For now, reject conversions between function types and
+ // call method objects. We could choose to allow casts here.
+ // Wrapping a function type to assign it to a call method
+ // object will never succeed. Wrapping the other way could
+ // be allowed.
+ if ((fromT is FunctionType && getCallMethodType(toT) != null) ||
+ (toT is FunctionType && getCallMethodType(fromT) != null)) {
+ return Coercion.error();
+ }
+
+ // Downcast if toT <: fromT
+ if (isSubTypeOf(toT, fromT)) return Coercion.cast(fromT, toT);
+
+ // Downcast if toT <===> fromT
+ // The intention here is to allow casts that are sideways in the restricted
+ // type system, but allowed in the regular dart type system, since these
+ // are likely to succeed. The canonical example is List<dynamic> and
+ // Iterable<T> for some concrete T (e.g. Object). These are unrelated
+ // in the restricted system, but List<dynamic> <: Iterable<T> in dart.
+ if (fromT.isAssignableTo(toT)) {
+ return Coercion.cast(fromT, toT);
+ }
+ return Coercion.error();
+ }
+
+ StaticInfo checkAssignment(Expression expr, DartType toT) {
+ final fromT = getStaticType(expr);
+ final Coercion c = _coerceTo(fromT, toT);
+ if (c is Identity) return null;
+ if (c is CoercionError) return new StaticTypeError(this, expr, toT);
+ var reason = null;
+
+ var errors = <String>[];
+ var ok = inferrer.inferExpression(expr, toT, errors);
+ if (ok) return InferredType.create(this, expr, toT);
+ reason = (errors.isNotEmpty) ? errors.first : null;
+
+ if (c is Cast) return DownCast.create(this, expr, c, reason: reason);
+ assert(false);
+ return null;
+ }
+
+ DartType elementType(Element e) {
+ if (e == null) {
+ // Malformed code - just return dynamic.
+ return provider.dynamicType;
+ }
+ return (e as dynamic).type;
+ }
+
+ bool _isLibraryPrefix(Expression node) =>
+ node is SimpleIdentifier && node.staticElement is PrefixElement;
+
+ /// Returns `true` if the target expression is dynamic.
+ bool isDynamicTarget(Expression node) {
+ if (node == null) return false;
+
+ if (_isLibraryPrefix(node)) return false;
+
+ // Null type happens when we have unknown identifiers, like a dart: import
+ // that doesn't resolve.
+ var type = node.staticType;
+ return type == null || type.isDynamic;
+ }
+
+ /// Returns `true` if the expression is a dynamic function call or method
+ /// invocation.
+ bool isDynamicCall(Expression call) {
+ var ft = getTypeAsCaller(call);
+ // TODO(leafp): This will currently return true if t is Function
+ // This is probably the most correct thing to do for now, since
+ // this code is also used by the back end. Maybe revisit at some
+ // point?
+ if (ft == null) return true;
+ // Dynamic as the parameter type is treated as bottom. A function with
+ // a dynamic parameter type requires a dynamic call in general.
+ // However, as an optimization, if we have an original definition, we know
+ // dynamic is reified as Object - in this case a regular call is fine.
+ if (call is SimpleIdentifier) {
+ var element = call.staticElement;
+ if (element is FunctionElement || element is MethodElement) {
+ // An original declaration.
+ return false;
+ }
+ }
+
+ return _anyParameterType(ft, (pt) => pt.isDynamic);
+ }
+}
+
+class DownwardsInference {
+ final TypeRules rules;
+
+ DownwardsInference(this.rules);
+
+ /// Called for each list literal which gets inferred
+ void annotateListLiteral(ListLiteral e, List<DartType> targs) {}
+
+ /// Called for each map literal which gets inferred
+ void annotateMapLiteral(MapLiteral e, List<DartType> targs) {}
+
+ /// Called for each new/const which gets inferred
+ void annotateInstanceCreationExpression(
+ InstanceCreationExpression e, List<DartType> targs) {}
+
+ /// Called for cast from dynamic required for inference to succeed
+ void annotateCastFromDynamic(Expression e, DartType t) {}
+
+ /// Called for each function expression return type inferred
+ void annotateFunctionExpression(FunctionExpression e, DartType returnType) {}
+
+ /// Downward inference
+ bool inferExpression(Expression e, DartType t, List<String> errors) {
+ // Don't cast top level expressions, only sub-expressions
+ return _inferExpression(e, t, errors, cast: false);
+ }
+
+ /// Downward inference
+ bool _inferExpression(Expression e, DartType t, List<String> errors,
+ {cast: true}) {
+ if (e is ConditionalExpression) {
+ return _inferConditionalExpression(e, t, errors);
+ }
+ if (e is ParenthesizedExpression) {
+ return _inferParenthesizedExpression(e, t, errors);
+ }
+ if (rules.isSubTypeOf(rules.getStaticType(e), t)) return true;
+ if (cast && rules.getStaticType(e).isDynamic) {
+ annotateCastFromDynamic(e, t);
+ return true;
+ }
+ if (e is FunctionExpression) return _inferFunctionExpression(e, t, errors);
+ if (e is ListLiteral) return _inferListLiteral(e, t, errors);
+ if (e is MapLiteral) return _inferMapLiteral(e, t, errors);
+ if (e is NamedExpression) return _inferNamedExpression(e, t, errors);
+ if (e is InstanceCreationExpression) {
+ return _inferInstanceCreationExpression(e, t, errors);
+ }
+ errors.add("$e cannot be typed as $t");
+ return false;
+ }
+
+ /// If t1 = I<dynamic, ..., dynamic>, then look for a supertype
+ /// of t1 of the form K<S0, ..., Sm> where t2 = K<S0', ..., Sm'>
+ /// If the supertype exists, use the constraints S0 <: S0', ... Sm <: Sm'
+ /// to derive a concrete instantation for I of the form <T0, ..., Tn>,
+ /// such that I<T0, .., Tn> <: t2
+ List<DartType> _matchTypes(InterfaceType t1, InterfaceType t2) {
+ if (t1 == t2) return t2.typeArguments;
+ var tArgs1 = t1.typeArguments;
+ var tArgs2 = t2.typeArguments;
+ // If t1 isn't a raw type, bail out
+ if (tArgs1 != null && tArgs1.any((t) => !t.isDynamic)) return null;
+
+ // This is our inferred type argument list. We start at all dynamic,
+ // and fill in with inferred types when we reach a match.
+ var actuals =
+ new List<DartType>.filled(tArgs1.length, rules.provider.dynamicType);
+
+ // When we find the supertype of t1 with the same
+ // classname as t2 (see below), we have the following:
+ // If t1 is an instantiation of a class T1<X0, ..., Xn>
+ // and t2 is an instantiation of a class T2<Y0, ...., Ym>
+ // of the form t2 = T2<S0, ..., Sm>
+ // then we want to choose instantiations for the Xi
+ // T0, ..., Tn such that T1<T0, ..., Tn> <: t2 .
+ // To find this, we simply instantate T1 with
+ // X0, ..., Xn, and then find its superclass
+ // T2<T0', ..., Tn'>. We then solve the constraint
+ // set T0' <: S0, ..., Tn' <: Sn for the Xi.
+ // Currently, we only handle constraints where
+ // the Ti' is one of the Xi'. If there are multiple
+ // constraints on some Xi, we choose the lower of the
+ // two (if it exists).
+ bool permute(List<DartType> permutedArgs) {
+ if (permutedArgs == null) return false;
+ var ps = t1.typeParameters;
+ var ts = ps.map((p) => p.type).toList();
+ for (int i = 0; i < permutedArgs.length; i++) {
+ var tVar = permutedArgs[i];
+ var tActual = tArgs2[i];
+ var index = ts.indexOf(tVar);
+ if (index >= 0 && rules.isSubTypeOf(tActual, actuals[index])) {
+ actuals[index] = tActual;
+ }
+ }
+ return actuals.any((x) => !x.isDynamic);
+ }
+
+ // Look for the first supertype of t1 with the same class name as t2.
+ bool match(InterfaceType t1) {
+ if (t1.element == t2.element) {
+ return permute(t1.typeArguments);
+ }
+
+ if (t1 == rules.provider.objectType) return false;
+
+ if (match(t1.superclass)) return true;
+
+ for (final parent in t1.interfaces) {
+ if (match(parent)) return true;
+ }
+
+ for (final parent in t1.mixins) {
+ if (match(parent)) return true;
+ }
+ return false;
+ }
+
+ // We have that t1 = T1<dynamic, ..., dynamic>.
+ // To match t1 against t2, we use the uninstantiated version
+ // of t1, essentially treating it as an instantiation with
+ // fresh variables, and solve for the variables.
+ // t1.element.type will be of the form T1<X0, ..., Xn>
+ if (!match(t1.element.type)) return null;
+ var newT1 = t1.element.type.substitute4(actuals);
+ // If we found a solution, return it.
+ if (rules.isSubTypeOf(newT1, t2)) return actuals;
+ return null;
+ }
+
+ /// These assume that e is not already a subtype of t
+
+ bool _inferConditionalExpression(
+ ConditionalExpression e, DartType t, errors) {
+ return _inferExpression(e.thenExpression, t, errors) &&
+ _inferExpression(e.elseExpression, t, errors);
+ }
+
+ bool _inferParenthesizedExpression(
+ ParenthesizedExpression e, DartType t, errors) {
+ return _inferExpression(e.expression, t, errors);
+ }
+
+ bool _inferInstanceCreationExpression(
+ InstanceCreationExpression e, DartType t, errors) {
+ var arguments = e.argumentList.arguments;
+ var rawType = rules.getStaticType(e);
+ // rawType is the instantiated type of the instance
+ if (rawType is! InterfaceType) return false;
+ var type = (rawType as InterfaceType);
+ if (type.typeParameters == null ||
+ type.typeParameters.length == 0) return false;
+ if (e.constructorName.type == null) return false;
+ // classTypeName is the type name of the class being instantiated
+ var classTypeName = e.constructorName.type;
+ // Check that we were not passed any type arguments
+ if (classTypeName.typeArguments != null) return false;
+ // Infer type arguments
+ if (t is! InterfaceType) return false;
+ var targs = _matchTypes(type, t);
+ if (targs == null) return false;
+ if (e.staticElement == null) return false;
+ var constructorElement = e.staticElement;
+ // From the constructor element get:
+ // the instantiated type of the constructor, then
+ // the uninstantiated element for the constructor, then
+ // the uninstantiated type for the constructor
+ var rawConstructorElement =
+ constructorElement.type.element as ConstructorElement;
+ var baseType = rawConstructorElement.type;
+ if (baseType == null) return false;
+ // From the interface type (instantiated), get:
+ // the uninstantiated element, then
+ // the uninstantiated type, then
+ // the type arguments (aka the type parameters)
+ var tparams = type.element.type.typeArguments;
+ // Take the uninstantiated constructor type, and replace the type
+ // parameters with the inferred arguments.
+ var fType = baseType.substitute2(targs, tparams);
+ {
+ var rTypes = fType.normalParameterTypes;
+ var oTypes = fType.optionalParameterTypes;
+ var pTypes = new List.from(rTypes)..addAll(oTypes);
+ var pArgs = arguments.where((x) => x is! NamedExpression);
+ var pi = 0;
+ for (var arg in pArgs) {
+ if (pi >= pTypes.length) return false;
+ var argType = pTypes[pi];
+ if (!_inferExpression(arg, argType, errors)) return false;
+ pi++;
+ }
+ var nTypes = fType.namedParameterTypes;
+ for (var arg0 in arguments) {
+ if (arg0 is! NamedExpression) continue;
+ var arg = arg0 as NamedExpression;
+ SimpleIdentifier nameNode = arg.name.label;
+ String name = nameNode.name;
+ var argType = nTypes[name];
+ if (argType == null) return false;
+ if (!_inferExpression(arg, argType, errors)) return false;
+ }
+ }
+ annotateInstanceCreationExpression(e, targs);
+ return true;
+ }
+
+ bool _inferNamedExpression(NamedExpression e, DartType t, errors) {
+ return _inferExpression(e.expression, t, errors);
+ }
+
+ bool _inferFunctionExpression(FunctionExpression e, DartType t, errors) {
+ if (t is! FunctionType) return false;
+ var fType = t as FunctionType;
+ var eType = e.staticType as FunctionType;
+ if (eType is! FunctionType) return false;
+
+ // We have a function literal, so we can treat the arrow type
+ // as non-fuzzy. Since we're not improving on parameter types
+ // currently, if this check fails then we cannot succeed, so
+ // bail out. Otherwise, we never need to check the parameter types
+ // again.
+ if (!rules.isFunctionSubTypeOf(eType, fType,
+ fuzzyArrows: false, ignoreReturn: true)) return false;
+
+ // This only entered inference because of fuzzy typing.
+ // The function type is already specific enough, we can just
+ // succeed and treat it as a successful inference
+ if (rules.isSubTypeOf(eType.returnType, fType.returnType)) return true;
+
+ // Fuzzy typing again, handle the void case (not caught by the previous)
+ if (fType.returnType.isVoid) return true;
+
+ if (e.body is! ExpressionFunctionBody) return false;
+ var body = (e.body as ExpressionFunctionBody).expression;
+ if (!_inferExpression(body, fType.returnType, errors)) return false;
+
+ // TODO(leafp): Try narrowing the argument types if possible
+ // to get better code in the function body. This requires checking
+ // that the body is well-typed at the more specific type.
+
+ // At this point, we know that the parameter types are in the appropriate subtype
+ // relation, and we have checked that we can type the body at the appropriate return
+ // type, so we can are done.
+ annotateFunctionExpression(e, fType.returnType);
+ return true;
+ }
+
+ bool _inferListLiteral(ListLiteral e, DartType t, errors) {
+ var dyn = rules.provider.dynamicType;
+ var listT = rules.provider.listType.substitute4([dyn]);
+ // List <: t (using dart rules) must be true
+ if (!listT.isSubtypeOf(t)) return false;
+ // The list literal must have no type arguments
+ if (e.typeArguments != null) return false;
+ if (t is! InterfaceType) return false;
+ var targs = _matchTypes(listT, t);
+ if (targs == null) return false;
+ assert(targs.length == 1);
+ var etype = targs[0];
+ assert(!etype.isDynamic);
+ var elements = e.elements;
+ var b = elements.every((e) => _inferExpression(e, etype, errors));
+ if (b) annotateListLiteral(e, targs);
+ return b;
+ }
+
+ bool _inferMapLiteral(MapLiteral e, DartType t, errors) {
+ var dyn = rules.provider.dynamicType;
+ var mapT = rules.provider.mapType.substitute4([dyn, dyn]);
+ // Map <: t (using dart rules) must be true
+ if (!mapT.isSubtypeOf(t)) return false;
+ // The map literal must have no type arguments
+ if (e.typeArguments != null) return false;
+ if (t is! InterfaceType) return false;
+ var targs = _matchTypes(mapT, t);
+ if (targs == null) return false;
+ assert(targs.length == 2);
+ var kType = targs[0];
+ var vType = targs[1];
+ assert(!(kType.isDynamic && vType.isDynamic));
+ var entries = e.entries;
+ bool inferEntry(MapLiteralEntry entry) {
+ return _inferExpression(entry.key, kType, errors) &&
+ _inferExpression(entry.value, vType, errors);
+ }
+ var b = entries.every(inferEntry);
+ if (b) annotateMapLiteral(e, targs);
+ return b;
+ }
+}
diff --git a/packages/analyzer/lib/task/model.dart b/packages/analyzer/lib/task/model.dart
index 9d949c3..7b70f8e 100644
--- a/packages/analyzer/lib/task/model.dart
+++ b/packages/analyzer/lib/task/model.dart
@@ -59,7 +59,7 @@
/**
* An object with which an analysis result can be associated.
*
- * Clients are allowed to subtype this class when creating new kinds of targets.
+ * Clients may implement this class when creating new kinds of targets.
* Instances of this type are used in hashed data structures, so subtypes are
* required to correctly implement [==] and [hashCode].
*/
@@ -75,7 +75,7 @@
* An object used to compute one or more analysis results associated with a
* single target.
*
- * Clients are expected to extend this class when creating new tasks.
+ * Clients must extend this class when creating new tasks.
*/
abstract class AnalysisTask {
/**
@@ -198,7 +198,7 @@
* map should be fully populated (have a key/value pair for each result that
* this task is expected to produce) or the [caughtException] should be set.
*
- * Clients should not override this method.
+ * Clients may not override this method.
*/
void perform() {
try {
@@ -217,7 +217,7 @@
* Perform this analysis task, ensuring that all exceptions are wrapped in an
* [AnalysisException].
*
- * Clients should not override this method.
+ * Clients may not override this method.
*/
void _safelyPerform() {
try {
@@ -273,7 +273,7 @@
* A description of a [List]-based analysis result that can be computed by an
* [AnalysisTask].
*
- * Clients are not expected to subtype this class.
+ * Clients may not extend, implement or mix-in this class.
*/
abstract class ListResultDescriptor<E> implements ResultDescriptor<List<E>> {
/**
@@ -293,7 +293,7 @@
* A description of an input to an [AnalysisTask] that can be used to compute
* that input.
*
- * Clients are not expected to subtype this class.
+ * Clients may not extend, implement or mix-in this class.
*/
abstract class ListTaskInput<E> extends TaskInput<List<E>> {
/**
@@ -328,7 +328,7 @@
/**
* A description of an input with a [Map] based values.
*
- * Clients are not expected to subtype this class.
+ * Clients may not extend, implement or mix-in this class.
*/
abstract class MapTaskInput<K, V> extends TaskInput<Map<K, V>> {
/**
@@ -347,6 +347,8 @@
*
* All the [ResultDescriptor]s with the same [ResultCachingPolicy] instance
* share the same total size in a cache.
+ *
+ * Clients may implement this class when implementing plugins.
*/
abstract class ResultCachingPolicy<T> {
/**
@@ -370,7 +372,7 @@
/**
* A description of an analysis result that can be computed by an [AnalysisTask].
*
- * Clients are not expected to subtype this class.
+ * Clients may not extend, implement or mix-in this class.
*/
abstract class ResultDescriptor<V> {
/**
@@ -410,7 +412,7 @@
/**
* A specification of the given [result] for the given [target].
*
- * Clients are not expected to subtype this class.
+ * Clients may not extend, implement or mix-in this class.
*/
class TargetedResult {
/**
@@ -451,6 +453,8 @@
/**
* A description of an [AnalysisTask].
+ *
+ * Clients may not extend, implement or mix-in this class.
*/
abstract class TaskDescriptor {
/**
@@ -492,7 +496,7 @@
* A description of an input to an [AnalysisTask] that can be used to compute
* that input.
*
- * Clients are not expected to subtype this class.
+ * Clients may not extend, implement or mix-in this class.
*/
abstract class TaskInput<V> {
/**
@@ -520,7 +524,7 @@
* indicating that there are no more requests, the method [inputValue] can be
* used to access the value of the input that was built.
*
- * Clients are not expected to subtype this class.
+ * Clients may not extend, implement or mix-in this class.
*/
abstract class TaskInputBuilder<V> {
/**
@@ -588,6 +592,8 @@
*
* They know specific of the targets and results they care about,
* so they can request analysis results in optimal order.
+ *
+ * Clients may implement this class when implementing plugins.
*/
abstract class WorkManager {
/**
diff --git a/packages/analyzer/pubspec.yaml b/packages/analyzer/pubspec.yaml
index 3e8fa88..8ae644d 100644
--- a/packages/analyzer/pubspec.yaml
+++ b/packages/analyzer/pubspec.yaml
@@ -1,10 +1,10 @@
name: analyzer
-version: 0.26.1+9
+version: 0.26.1+14
author: Dart Team <misc@dartlang.org>
description: Static analyzer for Dart.
homepage: http://www.dartlang.org
environment:
- sdk: '>=1.11.0 <2.0.0'
+ sdk: '>=1.12.0 <2.0.0'
dependencies:
args: '>=0.12.1 <0.14.0'
glob: ^1.0.3
diff --git a/packages/analyzer/test/file_system/memory_file_system_test.dart b/packages/analyzer/test/file_system/memory_file_system_test.dart
index 3d239ae..d0d3e55 100644
--- a/packages/analyzer/test/file_system/memory_file_system_test.dart
+++ b/packages/analyzer/test/file_system/memory_file_system_test.dart
@@ -339,6 +339,13 @@
expect(relative.path, '/foo/bar/baz.dart');
}
+ void test_resolveRelative_dart() {
+ File file = provider.newFile('/sdk/lib/core/core.dart', '');
+ Source source = file.createSource(Uri.parse('dart:core'));
+ Uri resolved = source.resolveRelativeUri(Uri.parse('int.dart'));
+ expect(resolved.toString(), 'dart:core/int.dart');
+ }
+
void test_shortName() {
expect(source.shortName, 'test.dart');
}
diff --git a/packages/analyzer/test/generated/all_the_rest_test.dart b/packages/analyzer/test/generated/all_the_rest_test.dart
index cabe5d7..25c3465 100644
--- a/packages/analyzer/test/generated/all_the_rest_test.dart
+++ b/packages/analyzer/test/generated/all_the_rest_test.dart
@@ -609,7 +609,7 @@
expect(result.isValid, isTrue);
DartObject value = result.value;
expect(value.type.name, "double");
- expect(value.doubleValue.isInfinite, isTrue);
+ expect(value.toDoubleValue().isInfinite, isTrue);
}
void test_divide_int_int() {
@@ -825,7 +825,7 @@
EvaluationResult result = _getExpressionValue(contents);
DartObject value = result.value;
expect(value.type.name, "bool");
- expect(value.boolValue, expectedValue);
+ expect(value.toBoolValue(), expectedValue);
}
void _assertValue2(double expectedValue, String contents) {
@@ -833,7 +833,7 @@
expect(result.isValid, isTrue);
DartObject value = result.value;
expect(value.type.name, "double");
- expect(value.doubleValue, expectedValue);
+ expect(value.toDoubleValue(), expectedValue);
}
void _assertValue3(int expectedValue, String contents) {
@@ -841,7 +841,7 @@
expect(result.isValid, isTrue);
DartObject value = result.value;
expect(value.type.name, "int");
- expect(value.intValue, expectedValue);
+ expect(value.toIntValue(), expectedValue);
}
void _assertValue4(String expectedValue, String contents) {
@@ -851,7 +851,7 @@
ParameterizedType type = value.type;
expect(type, isNotNull);
expect(type.name, "String");
- expect(value.stringValue, expectedValue);
+ expect(value.toStringValue(), expectedValue);
}
EvaluationResult _getExpressionValue(String contents) {
@@ -2165,7 +2165,7 @@
Map<String, DartObjectImpl> fields, String fieldName, int expectedValue) {
DartObjectImpl field = fields[fieldName];
expect(field.type.name, "int");
- expect(field.intValue, expectedValue);
+ expect(field.toIntValue(), expectedValue);
}
void _assertNullField(Map<String, DartObjectImpl> fields, String fieldName) {
@@ -2198,7 +2198,7 @@
expect(result.value, isNotNull);
DartObjectImpl value = result.value;
expect(value.type, typeProvider.boolType);
- bool boolValue = value.boolValue;
+ bool boolValue = value.toBoolValue();
expect(boolValue, isNotNull);
return boolValue;
}
@@ -2207,7 +2207,7 @@
expect(result.value, isNotNull);
DartObjectImpl value = result.value;
expect(value.type, typeProvider.intType);
- return value.intValue;
+ return value.toIntValue();
}
void _assertValidNull(EvaluationResultImpl result) {
@@ -2527,7 +2527,7 @@
void _assertValue(int expectedValue, DartObjectImpl result) {
expect(result, isNotNull);
expect(result.type.name, "int");
- expect(result.intValue, expectedValue);
+ expect(result.toIntValue(), expectedValue);
}
NonExistingSource _dummySource() {
@@ -3140,84 +3140,84 @@
_assertGreaterThanOrEqual(_boolValue(null), _intValue(null), _intValue(2));
}
- void test_hasExactValue_bool_false() {
- expect(_boolValue(false).hasExactValue, isTrue);
+ void test_hasKnownValue_bool_false() {
+ expect(_boolValue(false).hasKnownValue, isTrue);
}
- void test_hasExactValue_bool_true() {
- expect(_boolValue(true).hasExactValue, isTrue);
+ void test_hasKnownValue_bool_true() {
+ expect(_boolValue(true).hasKnownValue, isTrue);
}
- void test_hasExactValue_bool_unknown() {
- expect(_boolValue(null).hasExactValue, isTrue);
+ void test_hasKnownValue_bool_unknown() {
+ expect(_boolValue(null).hasKnownValue, isFalse);
}
- void test_hasExactValue_double_known() {
- expect(_doubleValue(2.3).hasExactValue, isTrue);
+ void test_hasKnownValue_double_known() {
+ expect(_doubleValue(2.3).hasKnownValue, isTrue);
}
- void test_hasExactValue_double_unknown() {
- expect(_doubleValue(null).hasExactValue, isTrue);
+ void test_hasKnownValue_double_unknown() {
+ expect(_doubleValue(null).hasKnownValue, isFalse);
}
- void test_hasExactValue_dynamic() {
- expect(_dynamicValue().hasExactValue, isFalse);
+ void test_hasKnownValue_dynamic() {
+ expect(_dynamicValue().hasKnownValue, isTrue);
}
- void test_hasExactValue_int_known() {
- expect(_intValue(23).hasExactValue, isTrue);
+ void test_hasKnownValue_int_known() {
+ expect(_intValue(23).hasKnownValue, isTrue);
}
- void test_hasExactValue_int_unknown() {
- expect(_intValue(null).hasExactValue, isTrue);
+ void test_hasKnownValue_int_unknown() {
+ expect(_intValue(null).hasKnownValue, isFalse);
}
- void test_hasExactValue_list_empty() {
- expect(_listValue().hasExactValue, isTrue);
+ void test_hasKnownValue_list_empty() {
+ expect(_listValue().hasKnownValue, isTrue);
}
- void test_hasExactValue_list_invalid() {
- expect(_dynamicValue().hasExactValue, isFalse);
+ void test_hasKnownValue_list_invalidElement() {
+ expect(_listValue([_dynamicValue]).hasKnownValue, isTrue);
}
- void test_hasExactValue_list_valid() {
- expect(_listValue([_intValue(23)]).hasExactValue, isTrue);
+ void test_hasKnownValue_list_valid() {
+ expect(_listValue([_intValue(23)]).hasKnownValue, isTrue);
}
- void test_hasExactValue_map_empty() {
- expect(_mapValue().hasExactValue, isTrue);
+ void test_hasKnownValue_map_empty() {
+ expect(_mapValue().hasKnownValue, isTrue);
}
- void test_hasExactValue_map_invalidKey() {
- expect(_mapValue([_dynamicValue(), _stringValue("value")]).hasExactValue,
- isFalse);
- }
-
- void test_hasExactValue_map_invalidValue() {
- expect(_mapValue([_stringValue("key"), _dynamicValue()]).hasExactValue,
- isFalse);
- }
-
- void test_hasExactValue_map_valid() {
- expect(
- _mapValue([_stringValue("key"), _stringValue("value")]).hasExactValue,
+ void test_hasKnownValue_map_invalidKey() {
+ expect(_mapValue([_dynamicValue(), _stringValue("value")]).hasKnownValue,
isTrue);
}
- void test_hasExactValue_null() {
- expect(_nullValue().hasExactValue, isTrue);
+ void test_hasKnownValue_map_invalidValue() {
+ expect(_mapValue([_stringValue("key"), _dynamicValue()]).hasKnownValue,
+ isTrue);
}
- void test_hasExactValue_num() {
- expect(_numValue().hasExactValue, isFalse);
+ void test_hasKnownValue_map_valid() {
+ expect(
+ _mapValue([_stringValue("key"), _stringValue("value")]).hasKnownValue,
+ isTrue);
}
- void test_hasExactValue_string_known() {
- expect(_stringValue("twenty-three").hasExactValue, isTrue);
+ void test_hasKnownValue_null() {
+ expect(_nullValue().hasKnownValue, isTrue);
}
- void test_hasExactValue_string_unknown() {
- expect(_stringValue(null).hasExactValue, isTrue);
+ void test_hasKnownValue_num() {
+ expect(_numValue().hasKnownValue, isFalse);
+ }
+
+ void test_hasKnownValue_string_known() {
+ expect(_stringValue("twenty-three").hasKnownValue, isTrue);
+ }
+
+ void test_hasKnownValue_string_unknown() {
+ expect(_stringValue(null).hasKnownValue, isFalse);
}
void test_identical_bool_false() {
@@ -4774,7 +4774,7 @@
variables.define(variableName, "false");
DartObject object = variables.getBool(typeProvider, variableName);
expect(object, isNotNull);
- expect(object.boolValue, false);
+ expect(object.toBoolValue(), false);
}
void test_getBool_invalid() {
@@ -4793,7 +4793,7 @@
variables.define(variableName, "true");
DartObject object = variables.getBool(typeProvider, variableName);
expect(object, isNotNull);
- expect(object.boolValue, true);
+ expect(object.toBoolValue(), true);
}
void test_getBool_undefined() {
@@ -4828,7 +4828,7 @@
variables.define(variableName, "23");
DartObject object = variables.getInt(typeProvider, variableName);
expect(object, isNotNull);
- expect(object.intValue, 23);
+ expect(object.toIntValue(), 23);
}
void test_getString_defined() {
@@ -4839,7 +4839,7 @@
variables.define(variableName, value);
DartObject object = variables.getString(typeProvider, variableName);
expect(object, isNotNull);
- expect(object.stringValue, value);
+ expect(object.toStringValue(), value);
}
void test_getString_undefined() {
@@ -6673,6 +6673,34 @@
expect(variable.setter, isNull);
}
+ void test_visitVariableDeclaration_top_docRange() {
+ // final a, b;
+ ElementHolder holder = new ElementHolder();
+ ElementBuilder builder = new ElementBuilder(holder);
+ VariableDeclaration variableDeclaration1 =
+ AstFactory.variableDeclaration('a');
+ VariableDeclaration variableDeclaration2 =
+ AstFactory.variableDeclaration('b');
+ TopLevelVariableDeclaration topLevelVariableDeclaration = AstFactory
+ .topLevelVariableDeclaration(
+ Keyword.FINAL, null, [variableDeclaration1, variableDeclaration2]);
+ topLevelVariableDeclaration.documentationComment = AstFactory
+ .documentationComment(
+ [TokenFactory.tokenFromString('/// aaa')..offset = 50], []);
+
+ topLevelVariableDeclaration.accept(builder);
+ List<TopLevelVariableElement> variables = holder.topLevelVariables;
+ expect(variables, hasLength(2));
+
+ TopLevelVariableElement variable1 = variables[0];
+ expect(variable1, isNotNull);
+ _assertHasDocRange(variable1, 50, 7);
+
+ TopLevelVariableElement variable2 = variables[1];
+ expect(variable2, isNotNull);
+ _assertHasDocRange(variable2, 50, 7);
+ }
+
void test_visitVariableDeclaration_top_final() {
// final v;
ElementHolder holder = new ElementHolder();
@@ -8944,7 +8972,7 @@
final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
'first' : const LibraryInfo(
'first/first.dart',
- category: 'First',
+ categories: 'Client',
documented: true,
platforms: VM_PLATFORM,
dart2jsPath: 'first/first_dart2js.dart'),
@@ -8953,7 +8981,7 @@
expect(libraryMap.size(), 1);
SdkLibrary first = libraryMap.getLibrary("dart:first");
expect(first, isNotNull);
- expect(first.category, "First");
+ expect(first.category, "Client");
expect(first.path, "first/first_dart2js.dart");
expect(first.shortName, "dart:first");
expect(first.isDart2JsLibrary, false);
@@ -8976,13 +9004,13 @@
final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> {
'first' : const LibraryInfo(
'first/first.dart',
- category: 'First',
+ categories: 'Client',
documented: true,
platforms: VM_PLATFORM),
'second' : const LibraryInfo(
'second/second.dart',
- category: 'Second',
+ categories: 'Server',
documented: false,
implementation: true,
platforms: 0),
@@ -8991,7 +9019,7 @@
expect(libraryMap.size(), 2);
SdkLibrary first = libraryMap.getLibrary("dart:first");
expect(first, isNotNull);
- expect(first.category, "First");
+ expect(first.category, "Client");
expect(first.path, "first/first.dart");
expect(first.shortName, "dart:first");
expect(first.isDart2JsLibrary, false);
@@ -9000,7 +9028,7 @@
expect(first.isVmLibrary, true);
SdkLibrary second = libraryMap.getLibrary("dart:second");
expect(second, isNotNull);
- expect(second.category, "Second");
+ expect(second.category, "Server");
expect(second.path, "second/second.dart");
expect(second.shortName, "dart:second");
expect(second.isDart2JsLibrary, false);
diff --git a/packages/analyzer/test/generated/ast_test.dart b/packages/analyzer/test/generated/ast_test.dart
index e7c2025..e25c253 100644
--- a/packages/analyzer/test/generated/ast_test.dart
+++ b/packages/analyzer/test/generated/ast_test.dart
@@ -2318,6 +2318,13 @@
AstFactory.simpleFormalParameter3("p"), AstFactory.integer(0)));
}
+ void test_visitDefaultFormalParameter_annotation() {
+ DefaultFormalParameter parameter = AstFactory.positionalFormalParameter(
+ AstFactory.simpleFormalParameter3("p"), AstFactory.integer(0));
+ parameter.metadata.add(AstFactory.annotation(AstFactory.identifier3("A")));
+ _assertSource('@A p = 0', parameter);
+ }
+
void test_visitDoStatement() {
_assertSource(
"do {} while (c);",
@@ -2460,6 +2467,12 @@
AstFactory.fieldFormalParameter(null, AstFactory.typeName4("A"), "a"));
}
+ void test_visitFieldFormalParameter_annotation() {
+ FieldFormalParameter parameter = AstFactory.fieldFormalParameter2('f');
+ parameter.metadata.add(AstFactory.annotation(AstFactory.identifier3("A")));
+ _assertSource('@A this.f', parameter);
+ }
+
void test_visitForEachStatement_declared() {
_assertSource(
"for (var a in b) {}",
@@ -2898,6 +2911,12 @@
_assertSource("f()", AstFactory.functionTypedFormalParameter(null, "f"));
}
+ void test_visitFunctionTypedFormalParameter_annotation() {
+ FunctionTypedFormalParameter parameter = AstFactory.functionTypedFormalParameter(null, "f");
+ parameter.metadata.add(AstFactory.annotation(AstFactory.identifier3("A")));
+ _assertSource('@A f()', parameter);
+ }
+
void test_visitFunctionTypedFormalParameter_type() {
_assertSource(
"T f()",
@@ -3521,6 +3540,12 @@
AstFactory.simpleFormalParameter4(AstFactory.typeName4("A"), "a"));
}
+ void test_visitSimpleFormalParameter_annotation() {
+ SimpleFormalParameter parameter = AstFactory.simpleFormalParameter3('x');
+ parameter.metadata.add(AstFactory.annotation(AstFactory.identifier3("A")));
+ _assertSource('@A x', parameter);
+ }
+
void test_visitSimpleIdentifier() {
_assertSource("a", AstFactory.identifier3("a"));
}
diff --git a/packages/analyzer/test/generated/engine_test.dart b/packages/analyzer/test/generated/engine_test.dart
index 310df94..7224060 100644
--- a/packages/analyzer/test/generated/engine_test.dart
+++ b/packages/analyzer/test/generated/engine_test.dart
@@ -31,7 +31,7 @@
import 'package:analyzer/src/generated/utilities_collection.dart';
import 'package:analyzer/src/services/lint.dart';
import 'package:analyzer/src/string_source.dart';
-import 'package:analyzer/task/model.dart' hide AnalysisTask, WorkManager;
+import 'package:analyzer/task/model.dart' as newContext;
import 'package:html/dom.dart' show Document;
import 'package:path/path.dart' as pathos;
import 'package:typed_mock/typed_mock.dart';
@@ -865,6 +865,15 @@
});
}
+ void test_configurationData() {
+ var key = new newContext.ResultDescriptor('test_key', '');
+ var testData = ['test', 'data'];
+ _context.setConfigurationData(key, testData);
+ expect(_context.getConfigurationData(key), testData);
+ var unusedKey = new newContext.ResultDescriptor('unused_key', '');
+ expect(_context.getConfigurationData(unusedKey), null);
+ }
+
void test_dispose() {
expect(_context.isDisposed, isFalse);
_context.dispose();
@@ -5695,7 +5704,7 @@
}
@override
- List<AnalysisTarget> get explicitTargets {
+ List<newContext.AnalysisTarget> get explicitTargets {
fail("Unexpected invocation of visitCacheItems");
return null;
}
@@ -5766,7 +5775,7 @@
}
@override
- List<AnalysisTarget> get priorityTargets {
+ List<newContext.AnalysisTarget> get priorityTargets {
fail("Unexpected invocation of visitCacheItems");
return null;
}
@@ -5818,15 +5827,20 @@
}
@override
+ TypeResolverVisitorFactory get typeResolverVisitorFactory {
+ fail("Unexpected invocation of getTypeResolverVisitorFactory");
+ return null;
+ }
+
+ @override
TypeSystem get typeSystem {
fail("Unexpected invocation of getTypeSystem");
return null;
}
@override
- TypeResolverVisitorFactory get typeResolverVisitorFactory {
- fail("Unexpected invocation of getTypeResolverVisitorFactory");
- return null;
+ List<newContext.WorkManager> get workManagers {
+ fail("Unexpected invocation of workManagers");
}
@override
@@ -5907,7 +5921,8 @@
}
@override
- Object computeResult(AnalysisTarget target, ResultDescriptor result) {
+ Object computeResult(
+ newContext.AnalysisTarget target, newContext.ResultDescriptor result) {
fail("Unexpected invocation of computeResult");
return null;
}
@@ -5930,7 +5945,7 @@
}
@override
- CacheEntry getCacheEntry(AnalysisTarget target) {
+ CacheEntry getCacheEntry(newContext.AnalysisTarget target) {
fail("Unexpected invocation of visitCacheItems");
return null;
}
@@ -5943,6 +5958,11 @@
}
@override
+ Object getConfigurationData(newContext.ResultDescriptor key) {
+ fail("Unexpected invocation of getConfigurationData");
+ }
+
+ @override
TimestampedData<String> getContents(Source source) {
fail("Unexpected invocation of getContents");
return null;
@@ -6055,7 +6075,8 @@
}
@override
- Object getResult(AnalysisTarget target, ResultDescriptor result) {
+ Object getResult(
+ newContext.AnalysisTarget target, newContext.ResultDescriptor result) {
fail("Unexpected invocation of getResult");
return null;
}
@@ -6091,7 +6112,8 @@
}
@override
- Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor) {
+ Stream<ComputedResult> onResultComputed(
+ newContext.ResultDescriptor descriptor) {
fail("Unexpected invocation of onResultComputed");
return null;
}
@@ -6158,6 +6180,11 @@
}
@override
+ void setConfigurationData(newContext.ResultDescriptor key, Object data) {
+ fail("Unexpected invocation of setConfigurationData");
+ }
+
+ @override
void setContents(Source source, String contents) {
fail("Unexpected invocation of setContents");
}
diff --git a/packages/analyzer/test/generated/non_error_resolver_test.dart b/packages/analyzer/test/generated/non_error_resolver_test.dart
index dedbb2a..8a6c71f 100644
--- a/packages/analyzer/test/generated/non_error_resolver_test.dart
+++ b/packages/analyzer/test/generated/non_error_resolver_test.dart
@@ -691,6 +691,32 @@
verify([source]);
}
+ void test_bug_24539_getter() {
+ Source source = addSource('''
+class C<T> {
+ List<Foo> get x => null;
+}
+
+typedef Foo(param);
+''');
+ computeLibrarySourceErrors(source);
+ assertNoErrors(source);
+ verify([source]);
+ }
+
+ void test_bug_24539_setter() {
+ Source source = addSource('''
+class C<T> {
+ void set x(List<Foo> value) {}
+}
+
+typedef Foo(param);
+''');
+ computeLibrarySourceErrors(source);
+ assertNoErrors(source);
+ verify([source]);
+ }
+
void test_builtInIdentifierAsType_dynamic() {
Source source = addSource(r'''
f() {
diff --git a/packages/analyzer/test/generated/parser_test.dart b/packages/analyzer/test/generated/parser_test.dart
index 98e80de..51e4941 100644
--- a/packages/analyzer/test/generated/parser_test.dart
+++ b/packages/analyzer/test/generated/parser_test.dart
@@ -3221,6 +3221,17 @@
expect(expression.thenExpression.isSynthetic, isTrue);
}
+ void test_declarationBeforeDirective() {
+ CompilationUnit unit = ParserTestCase.parseCompilationUnit(
+ "class foo { } import 'bar.dart';",
+ [ParserErrorCode.DIRECTIVE_AFTER_DECLARATION]);
+ expect(unit.directives, hasLength(1));
+ expect(unit.declarations, hasLength(1));
+ ClassDeclaration classDecl = unit.childEntities.first;
+ expect(classDecl, isNotNull);
+ expect(classDecl.name.name, 'foo');
+ }
+
void test_equalityExpression_missing_LHS() {
BinaryExpression expression =
parseExpression("== y", [ParserErrorCode.MISSING_IDENTIFIER]);
@@ -3338,17 +3349,6 @@
parseExpression("m(f() => 0);", [ParserErrorCode.EXPECTED_TOKEN]);
}
- void test_declarationBeforeDirective() {
- CompilationUnit unit = ParserTestCase.parseCompilationUnit(
- "class foo { } import 'bar.dart';",
- [ParserErrorCode.DIRECTIVE_AFTER_DECLARATION]);
- expect(unit.directives, hasLength(1));
- expect(unit.declarations, hasLength(1));
- ClassDeclaration classDecl = unit.childEntities.first;
- expect(classDecl, isNotNull);
- expect(classDecl.name.name, 'foo');
- }
-
void test_importDirectivePartial_as() {
CompilationUnit unit = ParserTestCase.parseCompilationUnit(
"import 'b.dart' d as b;", [ParserErrorCode.UNEXPECTED_TOKEN]);
@@ -3549,6 +3549,53 @@
expect(field.name.isSynthetic, isTrue);
}
+ void test_incompleteForEach() {
+ ForStatement statement = ParserTestCase.parseStatement(
+ 'for (String item i) {}',
+ [ParserErrorCode.EXPECTED_TOKEN, ParserErrorCode.EXPECTED_TOKEN]);
+ expect(statement, new isInstanceOf<ForStatement>());
+ expect(statement.toSource(), 'for (String item; i;) {}');
+ expect(statement.leftSeparator, isNotNull);
+ expect(statement.leftSeparator.type, TokenType.SEMICOLON);
+ expect(statement.rightSeparator, isNotNull);
+ expect(statement.rightSeparator.type, TokenType.SEMICOLON);
+ }
+
+ void test_incompleteLocalVariable_atTheEndOfBlock() {
+ Statement statement = ParserTestCase.parseStatement(
+ 'String v }', [ParserErrorCode.EXPECTED_TOKEN]);
+ expect(statement, new isInstanceOf<VariableDeclarationStatement>());
+ expect(statement.toSource(), 'String v;');
+ }
+
+ void test_incompleteLocalVariable_beforeIdentifier() {
+ Statement statement = ParserTestCase.parseStatement(
+ 'String v String v2;', [ParserErrorCode.EXPECTED_TOKEN]);
+ expect(statement, new isInstanceOf<VariableDeclarationStatement>());
+ expect(statement.toSource(), 'String v;');
+ }
+
+ void test_incompleteLocalVariable_beforeKeyword() {
+ Statement statement = ParserTestCase.parseStatement(
+ 'String v if (true) {}', [ParserErrorCode.EXPECTED_TOKEN]);
+ expect(statement, new isInstanceOf<VariableDeclarationStatement>());
+ expect(statement.toSource(), 'String v;');
+ }
+
+ void test_incompleteLocalVariable_beforeNextBlock() {
+ Statement statement = ParserTestCase.parseStatement(
+ 'String v {}', [ParserErrorCode.EXPECTED_TOKEN]);
+ expect(statement, new isInstanceOf<VariableDeclarationStatement>());
+ expect(statement.toSource(), 'String v;');
+ }
+
+ void test_incompleteLocalVariable_parameterizedType() {
+ Statement statement = ParserTestCase.parseStatement(
+ 'List<String> v {}', [ParserErrorCode.EXPECTED_TOKEN]);
+ expect(statement, new isInstanceOf<VariableDeclarationStatement>());
+ expect(statement.toSource(), 'List<String> v;');
+ }
+
void test_invalidFunctionBodyModifier() {
ParserTestCase.parseCompilationUnit(
"f() sync {}", [ParserErrorCode.MISSING_STAR_AFTER_SYNC]);
@@ -9423,7 +9470,7 @@
expect(identifier.name, lexeme);
}
- void test_parseStatement_emptyTypeArgumentListt() {
+ void test_parseStatement_emptyTypeArgumentList() {
VariableDeclarationStatement statement = parse4(
"parseStatement", "C<> c;", [ParserErrorCode.EXPECTED_TYPE_NAME]);
VariableDeclarationList variables = statement.variables;
diff --git a/packages/analyzer/test/src/context/context_test.dart b/packages/analyzer/test/src/context/context_test.dart
index 0544bf5..12f6fa7 100644
--- a/packages/analyzer/test/src/context/context_test.dart
+++ b/packages/analyzer/test/src/context/context_test.dart
@@ -649,6 +649,15 @@
});
}
+ void test_configurationData() {
+ var key = new ResultDescriptor('test_key', '');
+ var testData = ['test', 'data'];
+ context.setConfigurationData(key, testData);
+ expect(context.getConfigurationData(key), testData);
+ var unusedKey = new ResultDescriptor('unused_key', '');
+ expect(context.getConfigurationData(unusedKey), null);
+ }
+
void test_dispose() {
expect(context.isDisposed, isFalse);
context.dispose();
@@ -905,7 +914,7 @@
List<AnalysisError> errors = errorInfo.errors;
expect(errors, hasLength(0));
errors = context.computeErrors(source);
- expect(errors, hasLength(3));
+ expect(errors, hasLength(2));
}
void test_getHtmlFilesReferencing_html() {
@@ -1191,6 +1200,43 @@
fail("The added source was not in the list of library sources");
}
+ void test_getLibrarySources_inSDK() {
+ Source source = addSource(
+ '/test.dart',
+ r'''
+import 'dart:async';
+Stream S = null;
+''');
+ LibraryElement testLibrary = context.computeLibraryElement(source);
+ // prepare "Stream" ClassElement
+ ClassElement streamElement;
+ {
+ CompilationUnitElement testUnit = testLibrary.definingCompilationUnit;
+ InterfaceType streamType = testUnit.topLevelVariables[0].type;
+ streamElement = streamType.element;
+ }
+ // must be from SDK context
+ AnalysisContext sdkContext = context.sourceFactory.dartSdk.context;
+ expect(sdkContext, streamElement.context);
+ Source intSource = streamElement.source;
+ // must be in the "async" library - SDK context
+ {
+ List<Source> coreLibraries = sdkContext.getLibrariesContaining(intSource);
+ expect(coreLibraries, hasLength(1));
+ Source coreSource = coreLibraries[0];
+ expect(coreSource.isInSystemLibrary, isTrue);
+ expect(coreSource.shortName, 'async.dart');
+ }
+ // must be in the "async" library - main context
+ {
+ List<Source> coreLibraries = context.getLibrariesContaining(intSource);
+ expect(coreLibraries, hasLength(1));
+ Source coreSource = coreLibraries[0];
+ expect(coreSource.isInSystemLibrary, isTrue);
+ expect(coreSource.shortName, 'async.dart');
+ }
+ }
+
void test_getLineInfo() {
Source source = addSource(
"/test.dart",
@@ -1718,9 +1764,9 @@
</script></body></html>''');
_analyzeAll_assertFinished();
context.computeErrors(htmlSource);
- expect(_hasAnalysisErrorWithErrorSeverity(context.getErrors(htmlSource)),
- isTrue,
- reason: "htmlSource has an error");
+// expect(_hasAnalysisErrorWithErrorSeverity(context.getErrors(htmlSource)),
+// isTrue,
+// reason: "htmlSource has an error");
// add libB.dart and analyze
Source libBSource = addSource("/libB.dart", "library libB;");
_analyzeAll_assertFinished();
diff --git a/packages/analyzer/test/src/context/mock_sdk.dart b/packages/analyzer/test/src/context/mock_sdk.dart
index 25de8b3..121fb5d 100644
--- a/packages/analyzer/test/src/context/mock_sdk.dart
+++ b/packages/analyzer/test/src/context/mock_sdk.dart
@@ -118,15 +118,23 @@
import 'dart:math';
+part 'stream.dart';
+
class Future<T> {
factory Future.delayed(Duration duration, [T computation()]) => null;
factory Future.value([value]) => null;
static Future wait(List<Future> futures) => null;
}
-
+''',
+ const <_MockSdkFile>[
+ const _MockSdkFile(
+ '/lib/async/stream.dart',
+ r'''
+part of dart.async;
class Stream<T> {}
abstract class StreamTransformer<S, T> {}
-''');
+''')
+ ]);
static const _MockSdkLibrary LIB_COLLECTION = const _MockSdkLibrary(
'dart:collection',
@@ -197,6 +205,9 @@
MockSdk() {
LIBRARIES.forEach((_MockSdkLibrary library) {
provider.newFile(library.path, library.content);
+ library.parts.forEach((file) {
+ provider.newFile(file.path, file.content);
+ });
});
}
@@ -280,6 +291,7 @@
"dart:core": "/lib/core/core.dart",
"dart:html": "/lib/html/dartium/html_dartium.dart",
"dart:async": "/lib/async/async.dart",
+ "dart:async/stream.dart": "/lib/async/stream.dart",
"dart:collection": "/lib/collection/collection.dart",
"dart:convert": "/lib/convert/convert.dart",
"dart:math": "/lib/math/math.dart"
@@ -302,8 +314,10 @@
final String shortName;
final String path;
final String content;
+ final List<_MockSdkFile> parts;
- const _MockSdkLibrary(this.shortName, this.path, this.content);
+ const _MockSdkLibrary(this.shortName, this.path, this.content,
+ [this.parts = const <_MockSdkFile>[]]);
@override
String get category => throw unimplemented;
@@ -329,6 +343,13 @@
UnimplementedError get unimplemented => new UnimplementedError();
}
+class _MockSdkFile {
+ final String path;
+ final String content;
+
+ const _MockSdkFile(this.path, this.content);
+}
+
/**
* An [AnalysisContextImpl] that only contains sources for a Dart SDK.
*/
diff --git a/packages/analyzer/test/src/task/dart_test.dart b/packages/analyzer/test/src/task/dart_test.dart
index 0f39004..547df97 100644
--- a/packages/analyzer/test/src/task/dart_test.dart
+++ b/packages/analyzer/test/src/task/dart_test.dart
@@ -18,6 +18,7 @@
import 'package:analyzer/src/services/lint.dart';
import 'package:analyzer/src/task/dart.dart';
import 'package:analyzer/src/task/html.dart';
+import 'package:analyzer/src/task/strong/info.dart';
import 'package:analyzer/task/dart.dart';
import 'package:analyzer/task/general.dart';
import 'package:analyzer/task/model.dart';
@@ -65,6 +66,7 @@
runReflectiveTests(ResolveVariableReferencesTaskTest);
runReflectiveTests(ScanDartTaskTest);
runReflectiveTests(StrongModeInferenceTest);
+ runReflectiveTests(StrongModeVerifyUnitTaskTest);
runReflectiveTests(VerifyUnitTaskTest);
}
@@ -124,6 +126,8 @@
isInstanceOf isResolveVariableReferencesTask =
new isInstanceOf<ResolveVariableReferencesTask>();
isInstanceOf isScanDartTask = new isInstanceOf<ScanDartTask>();
+isInstanceOf isStrongModeVerifyUnitTask =
+ new isInstanceOf<StrongModeVerifyUnitTask>();
isInstanceOf isVerifyUnitTask = new isInstanceOf<VerifyUnitTask>();
final LintCode _testLintCode = new LintCode('test lint', 'test lint code');
@@ -1263,7 +1267,7 @@
expect(evaluationResult.value.type, isNotNull);
expect(evaluationResult.value.type.name, 'D');
expect(evaluationResult.value.fields, contains('value'));
- expect(evaluationResult.value.fields['value'].intValue, 1);
+ expect(evaluationResult.value.fields['value'].toIntValue(), 1);
}
test_annotation_without_args() {
@@ -1281,7 +1285,7 @@
// And check that it has the expected value.
expect(evaluationResult, isNotNull);
expect(evaluationResult.value, isNotNull);
- expect(evaluationResult.value.intValue, 1);
+ expect(evaluationResult.value.toIntValue(), 1);
}
test_circular_reference() {
@@ -1338,7 +1342,7 @@
''');
expect(evaluationResult, isNotNull);
expect(evaluationResult.value, isNotNull);
- expect(evaluationResult.value.intValue, 2);
+ expect(evaluationResult.value.toIntValue(), 2);
}
test_external_const_factory() {
@@ -1364,7 +1368,7 @@
''');
expect(evaluationResult, isNotNull);
expect(evaluationResult.value, isNotNull);
- expect(evaluationResult.value.intValue, 1);
+ expect(evaluationResult.value.toIntValue(), 1);
}
void _checkCircularities(
@@ -1844,8 +1848,9 @@
const x = const C();
''');
LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
- computeResult(target, RESOLVED_UNIT, matcher: isEvaluateUnitConstantsTask);
- CompilationUnit unit = outputs[RESOLVED_UNIT];
+ computeResult(target, RESOLVED_UNIT10,
+ matcher: isEvaluateUnitConstantsTask);
+ CompilationUnit unit = outputs[RESOLVED_UNIT10];
CompilationUnitElement unitElement = unit.element;
expect(
(unitElement.types[0].constructors[0] as ConstructorElementImpl)
@@ -1907,7 +1912,7 @@
UsedLocalElements usedElements;
Set<String> usedElementNames;
- fail_perform_forPart_afterLibraryUpdate() {
+ test_perform_forPart_afterLibraryUpdate() {
Source libSource = newSource(
'/my_lib.dart',
'''
@@ -2249,11 +2254,12 @@
void setUp() {
super.setUp();
enableLints();
+ setLints(context, [new GenerateLintsTaskTest_TestLinter()]);
}
@override
void tearDown() {
- LintGenerator.LINTERS.clear();
+ setLints(context, []);
super.tearDown();
}
@@ -2264,9 +2270,6 @@
class a { }
''');
- LintGenerator.LINTERS.clear();
- LintGenerator.LINTERS.add(new GenerateLintsTaskTest_TestLinter());
-
LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
computeResult(target, LINTS, matcher: isGenerateLintsTask);
// validate
@@ -3972,6 +3975,59 @@
}
@reflectiveTest
+class StrongModeVerifyUnitTaskTest extends _AbstractDartTaskTest {
+ @override
+ void setUp() {
+ super.setUp();
+ enableStrongMode();
+ }
+
+ void test_perform_recordDynamicInvoke() {
+ enableStrongMode();
+ AnalysisTarget source = newSource(
+ '/test.dart',
+ '''
+void main() {
+ dynamic a = [];
+ a[0];
+}
+''');
+ computeResult(new LibrarySpecificUnit(source, source), STRONG_MODE_ERRORS);
+ CompilationUnit unit = outputs[RESOLVED_UNIT];
+
+ // validate
+ _fillErrorListener(STRONG_MODE_ERRORS);
+ expect(errorListener.errors, isEmpty);
+
+ List<Statement> statements = getStatementsInTopLevelFunction(unit, "main");
+ ExpressionStatement statement = statements[1];
+ IndexExpression idx = statement.expression;
+ expect(DynamicInvoke.get(idx.target), isNotNull);
+ expect(DynamicInvoke.get(idx.target), isNotNull);
+ expect(DynamicInvoke.get(idx.target), isTrue);
+ }
+
+ void test_perform_verifyError() {
+ enableStrongMode();
+ AnalysisTarget source = newSource(
+ '/test.dart',
+ '''
+int topLevel = 3;
+class C {
+ String field = topLevel;
+}
+''');
+ computeResult(new LibrarySpecificUnit(source, source), STRONG_MODE_ERRORS);
+ // validate
+ _fillErrorListener(STRONG_MODE_ERRORS);
+
+ var errors = errorListener.errors;
+ expect(errors.length, 1);
+ expect(errors[0].errorCode.name, "dev_compiler.StaticTypeError");
+ }
+}
+
+@reflectiveTest
class VerifyUnitTaskTest extends _AbstractDartTaskTest {
test_perform_constantError() {
Source source = newSource(
@@ -4017,8 +4073,10 @@
computeResult(new LibrarySpecificUnit(source, source), VERIFY_ERRORS);
// validate
_fillErrorListener(VERIFY_ERRORS);
- errorListener.assertErrorsWithCodes(
- <ErrorCode>[StaticTypeWarningCode.INVALID_ASSIGNMENT]);
+
+ var errors = errorListener.errors;
+ expect(errors.length, 1);
+ expect(errors[0].errorCode, StaticTypeWarningCode.INVALID_ASSIGNMENT);
}
test_perform_verifyError() {
diff --git a/packages/analyzer/test/src/task/dart_work_manager_test.dart b/packages/analyzer/test/src/task/dart_work_manager_test.dart
index e15aa7b..8dd7dbc 100644
--- a/packages/analyzer/test/src/task/dart_work_manager_test.dart
+++ b/packages/analyzer/test/src/task/dart_work_manager_test.dart
@@ -16,6 +16,7 @@
import 'package:analyzer/src/generated/error.dart' show AnalysisError;
import 'package:analyzer/src/generated/java_engine.dart' show CaughtException;
import 'package:analyzer/src/generated/scanner.dart' show ScannerErrorCode;
+import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/testing/ast_factory.dart';
import 'package:analyzer/src/task/dart.dart';
@@ -331,6 +332,32 @@
expect(manager.getLibrariesContainingPart(part3), isEmpty);
}
+ void test_getLibrariesContainingPart_inSDK() {
+ Source part = new _SourceMock('part.dart');
+ when(part.isInSystemLibrary).thenReturn(true);
+ // SDK work manager
+ DartWorkManager sdkDartWorkManagerMock = new _DartWorkManagerMock();
+ when(sdkDartWorkManagerMock.getLibrariesContainingPart(part))
+ .thenReturn([source2, source3]);
+ // SDK context mock
+ InternalAnalysisContext sdkContextMock = new _InternalAnalysisContextMock();
+ when(sdkContextMock.workManagers).thenReturn([sdkDartWorkManagerMock]);
+ // SDK mock
+ DartSdk sdkMock = new _DartSdkMock();
+ when(sdkMock.context).thenReturn(sdkContextMock);
+ // SourceFactory mock
+ SourceFactory sourceFactory = new _SourceFactoryMock();
+ when(sourceFactory.dartSdk).thenReturn(sdkMock);
+ when(context.sourceFactory).thenReturn(sourceFactory);
+ // SDK source mock
+ Source source = new _SourceMock('test.dart');
+ when(source.source).thenReturn(source);
+ when(source.isInSystemLibrary).thenReturn(true);
+ // validate
+ expect(manager.getLibrariesContainingPart(part),
+ unorderedEquals([source2, source3]));
+ }
+
void test_getNextResult_hasLibraries_firstIsError() {
entry1.setErrorState(caughtException, [LIBRARY_ERRORS_READY]);
manager.librarySourceQueue.addAll([source1, source2]);
@@ -604,6 +631,28 @@
expect(cache.getState(part1, CONTAINING_LIBRARIES), CacheState.INVALID);
}
+ void test_resultsComputed_inSDK() {
+ DartWorkManager sdkDartWorkManagerMock = new _DartWorkManagerMock();
+ // SDK context mock
+ InternalAnalysisContext sdkContextMock = new _InternalAnalysisContextMock();
+ when(sdkContextMock.workManagers).thenReturn([sdkDartWorkManagerMock]);
+ // SDK mock
+ DartSdk sdkMock = new _DartSdkMock();
+ when(sdkMock.context).thenReturn(sdkContextMock);
+ // SourceFactory mock
+ SourceFactory sourceFactory = new _SourceFactoryMock();
+ when(sourceFactory.dartSdk).thenReturn(sdkMock);
+ when(context.sourceFactory).thenReturn(sourceFactory);
+ // SDK source mock
+ Source source = new _SourceMock('test.dart');
+ when(source.source).thenReturn(source);
+ when(source.isInSystemLibrary).thenReturn(true);
+ // notify and validate
+ Map<ResultDescriptor, dynamic> outputs = <ResultDescriptor, dynamic>{};
+ manager.resultsComputed(source, outputs);
+ verify(sdkDartWorkManagerMock.resultsComputed(source, outputs)).once();
+ }
+
void test_resultsComputed_noSourceKind() {
manager.unknownSourceQueue.addAll([source1, source2]);
manager.resultsComputed(source1, {});
@@ -706,6 +755,14 @@
}
}
+class _DartSdkMock extends TypedMock implements DartSdk {
+ noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+}
+
+class _DartWorkManagerMock extends TypedMock implements DartWorkManager {
+ noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+}
+
class _InternalAnalysisContextMock extends TypedMock
implements InternalAnalysisContext {
@override
@@ -739,3 +796,17 @@
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
+
+class _SourceFactoryMock extends TypedMock implements SourceFactory {
+ noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+}
+
+class _SourceMock extends TypedMock implements Source {
+ final String shortName;
+ _SourceMock(this.shortName);
+ @override
+ String get fullName => '/' + shortName;
+ noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+ @override
+ String toString() => fullName;
+}
diff --git a/packages/analyzer/test/src/task/html_test.dart b/packages/analyzer/test/src/task/html_test.dart
index 7d8da56..a901165 100644
--- a/packages/analyzer/test/src/task/html_test.dart
+++ b/packages/analyzer/test/src/task/html_test.dart
@@ -9,6 +9,7 @@
import 'package:analyzer/task/general.dart';
import 'package:analyzer/task/html.dart';
import 'package:analyzer/task/model.dart';
+import 'package:html/dom.dart';
import 'package:unittest/unittest.dart';
import '../../reflective_tests.dart';
@@ -228,7 +229,7 @@
r'''
<html>
<head>
- <title>test page</title>
+ <title>test page</not-title>
</head>
<body>
Test
@@ -335,4 +336,33 @@
}
}
}
+
+ test_perform_noDocType() {
+ String code = r'''
+<div>AAA</div>
+<span>BBB</span>
+''';
+ AnalysisTarget target = newSource('/test.html', code);
+ computeResult(target, HTML_DOCUMENT);
+ expect(task, isParseHtmlTask);
+ // validate Document
+ {
+ Document document = outputs[HTML_DOCUMENT];
+ expect(document, isNotNull);
+ // artificial <html>
+ expect(document.nodes, hasLength(1));
+ Element htmlElement = document.nodes[0];
+ expect(htmlElement.localName, 'html');
+ // artificial <body>
+ expect(htmlElement.nodes, hasLength(2));
+ Element bodyElement = htmlElement.nodes[1];
+ expect(bodyElement.localName, 'body');
+ // actual nodes
+ expect(bodyElement.nodes, hasLength(4));
+ expect((bodyElement.nodes[0] as Element).localName, 'div');
+ expect((bodyElement.nodes[2] as Element).localName, 'span');
+ }
+ // it's OK to don't have DOCTYPE
+ expect(outputs[HTML_DOCUMENT_ERRORS], isEmpty);
+ }
}
diff --git a/packages/analyzer/test/src/task/strong/checker_test.dart b/packages/analyzer/test/src/task/strong/checker_test.dart
new file mode 100644
index 0000000..874916c
--- /dev/null
+++ b/packages/analyzer/test/src/task/strong/checker_test.dart
@@ -0,0 +1,2311 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// TODO(jmesserly): this file needs to be refactored, it's a port from
+// package:dev_compiler's tests
+/// General type checking tests
+library test.src.task.strong.checker_test;
+
+import 'package:unittest/unittest.dart';
+
+import 'strong_test_helper.dart';
+
+void main() {
+ testChecker('ternary operator', {
+ '/main.dart': '''
+ abstract class Comparable<T> {
+ int compareTo(T other);
+ static int compare(Comparable a, Comparable b) => a.compareTo(b);
+ }
+ typedef int Comparator<T>(T a, T b);
+
+ typedef bool _Predicate<T>(T value);
+
+ class SplayTreeMap<K, V> {
+ Comparator<K> _comparator;
+ _Predicate _validKey;
+
+ // Initializing _comparator needs a cast, since K may not always be
+ // Comparable.
+ // Initializing _validKey shouldn't need a cast. Currently
+ // it requires inference to work because of dartbug.com/23381
+ SplayTreeMap([int compare(K key1, K key2),
+ bool isValidKey(potentialKey)]) {
+ : _comparator = /*warning:DownCastComposite*/(compare == null) ? Comparable.compare : compare,
+ _validKey = /*info:InferredType should be pass*/(isValidKey != null) ? isValidKey : ((v) => true);
+ _Predicate<Object> _v = /*warning:DownCastComposite*/(isValidKey != null) ? isValidKey : ((v) => true);
+ _v = /*info:InferredType should be pass*/(isValidKey != null) ? _v : ((v) => true);
+ }
+ }
+ void main() {
+ Object obj = 42;
+ dynamic dyn = 42;
+ int i = 42;
+
+ // Check the boolean conversion of the condition.
+ print((/*severe:StaticTypeError*/i) ? false : true);
+ print((/*info:DownCastImplicit*/obj) ? false : true);
+ print((/*info:DynamicCast*/dyn) ? false : true);
+ }
+ '''
+ });
+
+ testChecker('if/for/do/while statements use boolean conversion', {
+ '/main.dart': '''
+ main() {
+ dynamic d = 42;
+ Object obj = 42;
+ int i = 42;
+ bool b = false;
+
+ if (b) {}
+ if (/*info:DynamicCast*/dyn) {}
+ if (/*info:DownCastImplicit*/obj) {}
+ if (/*severe:StaticTypeError*/i) {}
+
+ while (b) {}
+ while (/*info:DynamicCast*/dyn) {}
+ while (/*info:DownCastImplicit*/obj) {}
+ while (/*severe:StaticTypeError*/i) {}
+
+ do {} while (b);
+ do {} while (/*info:DynamicCast*/dyn);
+ do {} while (/*info:DownCastImplicit*/obj);
+ do {} while (/*severe:StaticTypeError*/i);
+
+ for (;b;) {}
+ for (;/*info:DynamicCast*/dyn;) {}
+ for (;/*info:DownCastImplicit*/obj;) {}
+ for (;/*severe:StaticTypeError*/i;) {}
+ }
+ '''
+ });
+
+ testChecker('dynamic invocation', {
+ '/main.dart': '''
+
+ class A {
+ dynamic call(dynamic x) => x;
+ }
+ class B extends A {
+ int call(int x) => x;
+ double col(double x) => x;
+ }
+ void main() {
+ {
+ B f = new B();
+ int x;
+ double y;
+ // The analyzer has what I believe is a bug (dartbug.com/23252) which
+ // causes the return type of calls to f to be treated as dynamic.
+ x = /*info:DynamicCast should be pass*/f(3);
+ x = /*severe:StaticTypeError*/f.col(3.0);
+ y = /*info:DynamicCast should be severe:StaticTypeError*/f(3);
+ y = f.col(3.0);
+ f(/*severe:StaticTypeError*/3.0);
+ f.col(/*severe:StaticTypeError*/3);
+ }
+ {
+ Function f = new B();
+ int x;
+ double y;
+ x = /*info:DynamicCast, info:DynamicInvoke*/f(3);
+ x = /*info:DynamicCast, info:DynamicInvoke*/f.col(3.0);
+ y = /*info:DynamicCast, info:DynamicInvoke*/f(3);
+ y = /*info:DynamicCast, info:DynamicInvoke*/f.col(3.0);
+ (/*info:DynamicInvoke*/f(3.0));
+ (/*info:DynamicInvoke*/f.col(3));
+ }
+ {
+ A f = new B();
+ int x;
+ double y;
+ x = /*info:DynamicCast, info:DynamicInvoke*/f(3);
+ y = /*info:DynamicCast, info:DynamicInvoke*/f(3);
+ (/*info:DynamicInvoke*/f(3.0));
+ }
+ }
+ '''
+ });
+
+ testChecker('conversion and dynamic invoke', {
+ '/helper.dart': '''
+ dynamic toString = (int x) => x + 42;
+ dynamic hashCode = "hello";
+ ''',
+ '/main.dart': '''
+ import 'helper.dart' as helper;
+
+ class A {
+ String x = "hello world";
+
+ void baz1(y) => x + /*info:DynamicCast*/y;
+ static baz2(y) => /*info:DynamicInvoke*/y + y;
+ }
+
+ void foo(String str) {
+ print(str);
+ }
+
+ class B {
+ String toString([int arg]) => arg.toString();
+ }
+
+ void bar(a) {
+ foo(/*info:DynamicCast,info:DynamicInvoke*/a.x);
+ }
+
+ baz() => new B();
+
+ typedef DynFun(x);
+ typedef StrFun(String x);
+
+ var bar1 = bar;
+
+ void main() {
+ var a = new A();
+ bar(a);
+ (/*info:DynamicInvoke*/bar1(a));
+ var b = bar;
+ (/*info:DynamicInvoke*/b(a));
+ var f1 = foo;
+ f1("hello");
+ dynamic f2 = foo;
+ (/*info:DynamicInvoke*/f2("hello"));
+ DynFun f3 = foo;
+ (/*info:DynamicInvoke*/f3("hello"));
+ (/*info:DynamicInvoke*/f3(42));
+ StrFun f4 = foo;
+ f4("hello");
+ a.baz1("hello");
+ var b1 = a.baz1;
+ (/*info:DynamicInvoke*/b1("hello"));
+ A.baz2("hello");
+ var b2 = A.baz2;
+ (/*info:DynamicInvoke*/b2("hello"));
+
+ dynamic a1 = new B();
+ (/*info:DynamicInvoke*/a1.x);
+ a1.toString();
+ (/*info:DynamicInvoke*/a1.toString(42));
+ var toStringClosure = a1.toString;
+ (/*info:DynamicInvoke*/a1.toStringClosure());
+ (/*info:DynamicInvoke*/a1.toStringClosure(42));
+ (/*info:DynamicInvoke*/a1.toStringClosure("hello"));
+ a1.hashCode;
+
+ dynamic toString = () => null;
+ (/*info:DynamicInvoke*/toString());
+
+ (/*info:DynamicInvoke*/helper.toString());
+ var toStringClosure2 = helper.toString;
+ (/*info:DynamicInvoke*/toStringClosure2());
+ int hashCode = /*info:DynamicCast*/helper.hashCode;
+
+ baz().toString();
+ baz().hashCode;
+ }
+ '''
+ });
+
+ testChecker('Constructors', {
+ '/main.dart': '''
+ const num z = 25;
+ Object obj = "world";
+
+ class A {
+ int x;
+ String y;
+
+ A(this.x) : this.y = /*severe:StaticTypeError*/42;
+
+ A.c1(p): this.x = /*info:DownCastImplicit*/z, this.y = /*info:DynamicCast*/p;
+
+ A.c2(this.x, this.y);
+
+ A.c3(/*severe:InvalidParameterDeclaration*/num this.x, String this.y);
+ }
+
+ class B extends A {
+ B() : super(/*severe:StaticTypeError*/"hello");
+
+ B.c2(int x, String y) : super.c2(/*severe:StaticTypeError*/y,
+ /*severe:StaticTypeError*/x);
+
+ B.c3(num x, Object y) : super.c3(x, /*info:DownCastImplicit*/y);
+ }
+
+ void main() {
+ A a = new A.c2(/*info:DownCastImplicit*/z, /*severe:StaticTypeError*/z);
+ var b = new B.c2(/*severe:StaticTypeError*/"hello", /*info:DownCastImplicit*/obj);
+ }
+ '''
+ });
+
+ testChecker('Unbound variable', {
+ '/main.dart': '''
+ void main() {
+ dynamic y = /*pass should be severe:StaticTypeError*/unboundVariable;
+ }
+ '''
+ });
+
+ testChecker('Unbound type name', {
+ '/main.dart': '''
+ void main() {
+ /*pass should be severe:StaticTypeError*/AToB y;
+ }
+ '''
+ });
+
+ testChecker('Ground type subtyping: dynamic is top', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+
+ void main() {
+ dynamic y;
+ Object o;
+ int i = 0;
+ double d = 0.0;
+ num n;
+ A a;
+ B b;
+ y = o;
+ y = i;
+ y = d;
+ y = n;
+ y = a;
+ y = b;
+ }
+ '''
+ });
+
+ testChecker('Ground type subtyping: dynamic downcasts', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+
+ void main() {
+ dynamic y;
+ Object o;
+ int i = 0;
+ double d = 0.0;
+ num n;
+ A a;
+ B b;
+ o = y;
+ i = /*info:DynamicCast*/y;
+ d = /*info:DynamicCast*/y;
+ n = /*info:DynamicCast*/y;
+ a = /*info:DynamicCast*/y;
+ b = /*info:DynamicCast*/y;
+ }
+ '''
+ });
+
+ testChecker('Ground type subtyping: assigning a class', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+
+ void main() {
+ dynamic y;
+ Object o;
+ int i = 0;
+ double d = 0.0;
+ num n;
+ A a;
+ B b;
+ y = a;
+ o = a;
+ i = /*severe:StaticTypeError*/a;
+ d = /*severe:StaticTypeError*/a;
+ n = /*severe:StaticTypeError*/a;
+ a = a;
+ b = /*info:DownCastImplicit*/a;
+ }
+ '''
+ });
+
+ testChecker('Ground type subtyping: assigning a subclass', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+ class C extends A {}
+
+ void main() {
+ dynamic y;
+ Object o;
+ int i = 0;
+ double d = 0.0;
+ num n;
+ A a;
+ B b;
+ C c;
+ y = b;
+ o = b;
+ i = /*severe:StaticTypeError*/b;
+ d = /*severe:StaticTypeError*/b;
+ n = /*severe:StaticTypeError*/b;
+ a = b;
+ b = b;
+ c = /*severe:StaticTypeError*/b;
+ }
+ '''
+ });
+
+ testChecker('Ground type subtyping: interfaces', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+ class C extends A {}
+ class D extends B implements C {}
+
+ void main() {
+ A top;
+ B left;
+ C right;
+ D bot;
+ {
+ top = top;
+ top = left;
+ top = right;
+ top = bot;
+ }
+ {
+ left = /*info:DownCastImplicit*/top;
+ left = left;
+ left = /*severe:StaticTypeError*/right;
+ left = bot;
+ }
+ {
+ right = /*info:DownCastImplicit*/top;
+ right = /*severe:StaticTypeError*/left;
+ right = right;
+ right = bot;
+ }
+ {
+ bot = /*info:DownCastImplicit*/top;
+ bot = /*info:DownCastImplicit*/left;
+ bot = /*info:DownCastImplicit*/right;
+ bot = bot;
+ }
+ }
+ '''
+ });
+
+ testChecker('Function typing and subtyping: int and object', {
+ '/main.dart': '''
+
+ typedef Object Top(int x); // Top of the lattice
+ typedef int Left(int x); // Left branch
+ typedef int Left2(int x); // Left branch
+ typedef Object Right(Object x); // Right branch
+ typedef int Bot(Object x); // Bottom of the lattice
+
+ Object top(int x) => x;
+ int left(int x) => x;
+ Object right(Object x) => x;
+ int _bot(Object x) => /*info:DownCastImplicit*/x;
+ int bot(Object x) => x as int;
+
+ void main() {
+ { // Check typedef equality
+ Left f = left;
+ Left2 g = f;
+ }
+ {
+ Top f;
+ f = top;
+ f = left;
+ f = right;
+ f = bot;
+ }
+ {
+ Left f;
+ f = /*warning:DownCastComposite*/top;
+ f = left;
+ f = /*warning:DownCastComposite*/right; // Should we reject this?
+ f = bot;
+ }
+ {
+ Right f;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left; // Should we reject this?
+ f = right;
+ f = bot;
+ }
+ {
+ Bot f;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left;
+ f = /*warning:DownCastComposite*/right;
+ f = bot;
+ }
+ }
+ '''
+ });
+
+ testChecker('Function typing and subtyping: classes', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+
+ typedef A Top(B x); // Top of the lattice
+ typedef B Left(B x); // Left branch
+ typedef B Left2(B x); // Left branch
+ typedef A Right(A x); // Right branch
+ typedef B Bot(A x); // Bottom of the lattice
+
+ B left(B x) => x;
+ B _bot(A x) => /*info:DownCastImplicit*/x;
+ B bot(A x) => x as B;
+ A top(B x) => x;
+ A right(A x) => x;
+
+ void main() {
+ { // Check typedef equality
+ Left f = left;
+ Left2 g = f;
+ }
+ {
+ Top f;
+ f = top;
+ f = left;
+ f = right;
+ f = bot;
+ }
+ {
+ Left f;
+ f = /*warning:DownCastComposite*/top;
+ f = left;
+ f = /*warning:DownCastComposite*/right; // Should we reject this?
+ f = bot;
+ }
+ {
+ Right f;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left; // Should we reject this?
+ f = right;
+ f = bot;
+ }
+ {
+ Bot f;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left;
+ f = /*warning:DownCastComposite*/right;
+ f = bot;
+ }
+ }
+ '''
+ });
+
+ testChecker('Function typing and subtyping: dynamic', {
+ '/main.dart': '''
+
+ class A {}
+
+ typedef dynamic Top(dynamic x); // Top of the lattice
+ typedef dynamic Left(A x); // Left branch
+ typedef A Right(dynamic x); // Right branch
+ typedef A Bottom(A x); // Bottom of the lattice
+
+ dynamic left(A x) => x;
+ A bot(A x) => x;
+ dynamic top(dynamic x) => x;
+ A right(dynamic x) => /*info:DynamicCast*/x;
+
+ void main() {
+ {
+ Top f;
+ f = top;
+ f = left;
+ f = right;
+ f = bot;
+ }
+ {
+ Left f;
+ f = /*warning:DownCastComposite*/top;
+ f = left;
+ f = /*warning:DownCastComposite*/right;
+ f = bot;
+ }
+ {
+ Right f;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left;
+ f = right;
+ f = bot;
+ }
+ {
+ Bottom f;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left;
+ f = /*warning:DownCastComposite*/right;
+ f = bot;
+ }
+ }
+ '''
+ });
+
+ testChecker('Function typing and subtyping: function literal variance', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+
+ typedef T Function2<S, T>(S z);
+
+ A top(B x) => x;
+ B left(B x) => x;
+ A right(A x) => x;
+ B bot(A x) => x as B;
+
+ void main() {
+ {
+ Function2<B, A> f;
+ f = top;
+ f = left;
+ f = right;
+ f = bot;
+ }
+ {
+ Function2<B, B> f;
+ f = /*warning:DownCastComposite*/top;
+ f = left;
+ f = /*warning:DownCastComposite*/right; // Should we reject this?
+ f = bot;
+ }
+ {
+ Function2<A, A> f;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left; // Should we reject this?
+ f = right;
+ f = bot;
+ }
+ {
+ Function2<A, B> f;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left;
+ f = /*warning:DownCastComposite*/right;
+ f = bot;
+ }
+ }
+ '''
+ });
+
+ testChecker('Function typing and subtyping: function variable variance', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+
+ typedef T Function2<S, T>(S z);
+
+ void main() {
+ {
+ Function2<B, A> top;
+ Function2<B, B> left;
+ Function2<A, A> right;
+ Function2<A, B> bot;
+
+ top = right;
+ top = bot;
+ top = top;
+ top = left;
+
+ left = /*warning:DownCastComposite*/top;
+ left = left;
+ left = /*warning:DownCastComposite*/right; // Should we reject this?
+ left = bot;
+
+ right = /*warning:DownCastComposite*/top;
+ right = /*warning:DownCastComposite*/left; // Should we reject this?
+ right = right;
+ right = bot;
+
+ bot = /*warning:DownCastComposite*/top;
+ bot = /*warning:DownCastComposite*/left;
+ bot = /*warning:DownCastComposite*/right;
+ bot = bot;
+ }
+ }
+ '''
+ });
+
+ testChecker('Function typing and subtyping: higher order function literals', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+
+ typedef T Function2<S, T>(S z);
+
+ typedef A BToA(B x); // Top of the base lattice
+ typedef B AToB(A x); // Bot of the base lattice
+
+ BToA top(AToB f) => f;
+ AToB left(AToB f) => f;
+ BToA right(BToA f) => f;
+ AToB _bot(BToA f) => /*warning:DownCastComposite*/f;
+ AToB bot(BToA f) => f as AToB;
+
+ Function2<B, A> top(AToB f) => f;
+ Function2<A, B> left(AToB f) => f;
+ Function2<B, A> right(BToA f) => f;
+ Function2<A, B> _bot(BToA f) => /*warning:DownCastComposite*/f;
+ Function2<A, B> bot(BToA f) => f as Function2<A, B>;
+
+
+ BToA top(Function2<A, B> f) => f;
+ AToB left(Function2<A, B> f) => f;
+ BToA right(Function2<B, A> f) => f;
+ AToB _bot(Function2<B, A> f) => /*warning:DownCastComposite*/f;
+ AToB bot(Function2<B, A> f) => f as AToB;
+
+ void main() {
+ {
+ Function2<AToB, BToA> f; // Top
+ f = top;
+ f = left;
+ f = right;
+ f = bot;
+ }
+ {
+ Function2<AToB, AToB> f; // Left
+ f = /*warning:DownCastComposite*/top;
+ f = left;
+ f = /*warning:DownCastComposite*/right; // Should we reject this?
+ f = bot;
+ }
+ {
+ Function2<BToA, BToA> f; // Right
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left; // Should we reject this?
+ f = right;
+ f = bot;
+ }
+ {
+ Function2<BToA, AToB> f; // Bot
+ f = bot;
+ f = /*warning:DownCastComposite*/left;
+ f = /*warning:DownCastComposite*/top;
+ f = /*warning:DownCastComposite*/left;
+ }
+ }
+ '''
+ });
+
+ testChecker(
+ 'Function typing and subtyping: higher order function variables', {
+ '/main.dart': '''
+
+ class A {}
+ class B extends A {}
+
+ typedef T Function2<S, T>(S z);
+
+ void main() {
+ {
+ Function2<Function2<A, B>, Function2<B, A>> top;
+ Function2<Function2<B, A>, Function2<B, A>> right;
+ Function2<Function2<A, B>, Function2<A, B>> left;
+ Function2<Function2<B, A>, Function2<A, B>> bot;
+
+ top = right;
+ top = bot;
+ top = top;
+ top = left;
+
+ left = /*warning:DownCastComposite*/top;
+ left = left;
+ left =
+ /*warning:DownCastComposite should be severe:StaticTypeError*/right;
+ left = bot;
+
+ right = /*warning:DownCastComposite*/top;
+ right =
+ /*warning:DownCastComposite should be severe:StaticTypeError*/left;
+ right = right;
+ right = bot;
+
+ bot = /*warning:DownCastComposite*/top;
+ bot = /*warning:DownCastComposite*/left;
+ bot = /*warning:DownCastComposite*/right;
+ bot = bot;
+ }
+ }
+ '''
+ });
+
+ testChecker('Function typing and subtyping: named and optional parameters', {
+ '/main.dart': '''
+
+ class A {}
+
+ typedef A FR(A x);
+ typedef A FO([A x]);
+ typedef A FN({A x});
+ typedef A FRR(A x, A y);
+ typedef A FRO(A x, [A y]);
+ typedef A FRN(A x, {A n});
+ typedef A FOO([A x, A y]);
+ typedef A FNN({A x, A y});
+ typedef A FNNN({A z, A y, A x});
+
+ void main() {
+ FR r;
+ FO o;
+ FN n;
+ FRR rr;
+ FRO ro;
+ FRN rn;
+ FOO oo;
+ FNN nn;
+ FNNN nnn;
+
+ r = r;
+ r = o;
+ r = /*severe:StaticTypeError*/n;
+ r = /*severe:StaticTypeError*/rr;
+ r = ro;
+ r = rn;
+ r = oo;
+ r = /*severe:StaticTypeError*/nn;
+ r = /*severe:StaticTypeError*/nnn;
+
+ o = /*warning:DownCastComposite*/r;
+ o = o;
+ o = /*severe:StaticTypeError*/n;
+ o = /*severe:StaticTypeError*/rr;
+ o = /*severe:StaticTypeError*/ro;
+ o = /*severe:StaticTypeError*/rn;
+ o = oo;
+ o = /*severe:StaticTypeError*/nn
+ o = /*severe:StaticTypeError*/nnn;
+
+ n = /*severe:StaticTypeError*/r;
+ n = /*severe:StaticTypeError*/o;
+ n = n;
+ n = /*severe:StaticTypeError*/rr;
+ n = /*severe:StaticTypeError*/ro;
+ n = /*severe:StaticTypeError*/rn;
+ n = /*severe:StaticTypeError*/oo;
+ n = nn;
+ n = nnn;
+
+ rr = /*severe:StaticTypeError*/r;
+ rr = /*severe:StaticTypeError*/o;
+ rr = /*severe:StaticTypeError*/n;
+ rr = rr;
+ rr = ro;
+ rr = /*severe:StaticTypeError*/rn;
+ rr = oo;
+ rr = /*severe:StaticTypeError*/nn;
+ rr = /*severe:StaticTypeError*/nnn;
+
+ ro = /*warning:DownCastComposite*/r;
+ ro = /*severe:StaticTypeError*/o;
+ ro = /*severe:StaticTypeError*/n;
+ ro = /*warning:DownCastComposite*/rr;
+ ro = ro;
+ ro = /*severe:StaticTypeError*/rn;
+ ro = oo;
+ ro = /*severe:StaticTypeError*/nn;
+ ro = /*severe:StaticTypeError*/nnn;
+
+ rn = /*warning:DownCastComposite*/r;
+ rn = /*severe:StaticTypeError*/o;
+ rn = /*severe:StaticTypeError*/n;
+ rn = /*severe:StaticTypeError*/rr;
+ rn = /*severe:StaticTypeError*/ro;
+ rn = rn;
+ rn = /*severe:StaticTypeError*/oo;
+ rn = /*severe:StaticTypeError*/nn;
+ rn = /*severe:StaticTypeError*/nnn;
+
+ oo = /*warning:DownCastComposite*/r;
+ oo = /*warning:DownCastComposite*/o;
+ oo = /*severe:StaticTypeError*/n;
+ oo = /*warning:DownCastComposite*/rr;
+ oo = /*warning:DownCastComposite*/ro;
+ oo = /*severe:StaticTypeError*/rn;
+ oo = oo;
+ oo = /*severe:StaticTypeError*/nn;
+ oo = /*severe:StaticTypeError*/nnn;
+
+ nn = /*severe:StaticTypeError*/r;
+ nn = /*severe:StaticTypeError*/o;
+ nn = /*warning:DownCastComposite*/n;
+ nn = /*severe:StaticTypeError*/rr;
+ nn = /*severe:StaticTypeError*/ro;
+ nn = /*severe:StaticTypeError*/rn;
+ nn = /*severe:StaticTypeError*/oo;
+ nn = nn;
+ nn = nnn;
+
+ nnn = /*severe:StaticTypeError*/r;
+ nnn = /*severe:StaticTypeError*/o;
+ nnn = /*warning:DownCastComposite*/n;
+ nnn = /*severe:StaticTypeError*/rr;
+ nnn = /*severe:StaticTypeError*/ro;
+ nnn = /*severe:StaticTypeError*/rn;
+ nnn = /*severe:StaticTypeError*/oo;
+ nnn = /*warning:DownCastComposite*/nn;
+ nnn = nnn;
+ }
+ '''
+ });
+
+ testChecker('Function subtyping: objects with call methods', {
+ '/main.dart': '''
+
+ typedef int I2I(int x);
+ typedef num N2N(num x);
+ class A {
+ int call(int x) => x;
+ }
+ class B {
+ num call(num x) => x;
+ }
+ int i2i(int x) => x;
+ num n2n(num x) => x;
+ void main() {
+ {
+ I2I f;
+ f = new A();
+ f = /*severe:StaticTypeError*/new B();
+ f = i2i;
+ f = /*warning:DownCastComposite*/n2n;
+ f = /*warning:DownCastComposite*/i2i as Object;
+ f = /*warning:DownCastComposite*/n2n as Function;
+ }
+ {
+ N2N f;
+ f = /*severe:StaticTypeError*/new A();
+ f = new B();
+ f = /*warning:DownCastComposite*/i2i;
+ f = n2n;
+ f = /*warning:DownCastComposite*/i2i as Object;
+ f = /*warning:DownCastComposite*/n2n as Function;
+ }
+ {
+ A f;
+ f = new A();
+ f = /*severe:StaticTypeError*/new B();
+ f = /*severe:StaticTypeError*/i2i;
+ f = /*severe:StaticTypeError*/n2n;
+ f = /*info:DownCastImplicit*/i2i as Object;
+ f = /*info:DownCastImplicit*/n2n as Function;
+ }
+ {
+ B f;
+ f = /*severe:StaticTypeError*/new A();
+ f = new B();
+ f = /*severe:StaticTypeError*/i2i;
+ f = /*severe:StaticTypeError*/n2n;
+ f = /*info:DownCastImplicit*/i2i as Object;
+ f = /*info:DownCastImplicit*/n2n as Function;
+ }
+ {
+ Function f;
+ f = new A();
+ f = new B();
+ f = i2i;
+ f = n2n;
+ f = /*info:DownCastImplicit*/i2i as Object;
+ f = (n2n as Function);
+ }
+ }
+ '''
+ });
+
+ testChecker('Function typing and subtyping: void', {
+ '/main.dart': '''
+
+ class A {
+ void bar() => null;
+ void foo() => bar; // allowed
+ }
+ '''
+ });
+
+ testChecker('Relaxed casts', {
+ '/main.dart': '''
+
+ class A {}
+
+ class L<T> {}
+ class M<T> extends L<T> {}
+ // L<dynamic|Object>
+ // / \
+ // M<dynamic|Object> L<A>
+ // \ /
+ // M<A>
+ // In normal Dart, there are additional edges
+ // from M<A> to M<dynamic>
+ // from L<A> to M<dynamic>
+ // from L<A> to L<dynamic>
+ void main() {
+ L lOfDs;
+ L<Object> lOfOs;
+ L<A> lOfAs;
+
+ M mOfDs;
+ M<Object> mOfOs;
+ M<A> mOfAs;
+
+ {
+ lOfDs = mOfDs;
+ lOfDs = mOfOs;
+ lOfDs = mOfAs;
+ lOfDs = lOfDs;
+ lOfDs = lOfOs;
+ lOfDs = lOfAs;
+ }
+ {
+ lOfOs = mOfDs;
+ lOfOs = mOfOs;
+ lOfOs = mOfAs;
+ lOfOs = lOfDs;
+ lOfOs = lOfOs;
+ lOfOs = lOfAs;
+ }
+ {
+ lOfAs = /*warning:DownCastComposite*/mOfDs;
+ lOfAs = /*severe:StaticTypeError*/mOfOs;
+ lOfAs = mOfAs;
+ lOfAs = /*warning:DownCastComposite*/lOfDs;
+ lOfAs = /*warning:DownCastComposite*/lOfOs;
+ lOfAs = lOfAs;
+ }
+ {
+ mOfDs = mOfDs;
+ mOfDs = mOfOs;
+ mOfDs = mOfAs;
+ mOfDs = /*info:DownCastImplicit*/lOfDs;
+ mOfDs = /*info:DownCastImplicit*/lOfOs;
+ mOfDs = /*info:DownCastImplicit*/lOfAs;
+ }
+ {
+ mOfOs = mOfDs;
+ mOfOs = mOfOs;
+ mOfOs = mOfAs;
+ mOfOs = /*info:DownCastImplicit*/lOfDs;
+ mOfOs = /*info:DownCastImplicit*/lOfOs;
+ mOfOs = /*severe:StaticTypeError*/lOfAs;
+ }
+ {
+ mOfAs = /*warning:DownCastComposite*/mOfDs;
+ mOfAs = /*warning:DownCastComposite*/mOfOs;
+ mOfAs = mOfAs;
+ mOfAs = /*warning:DownCastComposite*/lOfDs;
+ mOfAs = /*warning:DownCastComposite*/lOfOs;
+ mOfAs = /*warning:DownCastComposite*/lOfAs;
+ }
+
+ }
+ '''
+ });
+
+ testChecker('Type checking literals', {
+ '/main.dart': '''
+ test() {
+ num n = 3;
+ int i = 3;
+ String s = "hello";
+ {
+ List<int> l = <int>[i];
+ l = <int>[/*severe:StaticTypeError*/s];
+ l = <int>[/*info:DownCastImplicit*/n];
+ l = <int>[i, /*info:DownCastImplicit*/n, /*severe:StaticTypeError*/s];
+ }
+ {
+ List l = [i];
+ l = [s];
+ l = [n];
+ l = [i, n, s];
+ }
+ {
+ Map<String, int> m = <String, int>{s: i};
+ m = <String, int>{s: /*severe:StaticTypeError*/s};
+ m = <String, int>{s: /*info:DownCastImplicit*/n};
+ m = <String, int>{s: i,
+ s: /*info:DownCastImplicit*/n,
+ s: /*severe:StaticTypeError*/s};
+ }
+ // TODO(leafp): We can't currently test for key errors since the
+ // error marker binds to the entire entry.
+ {
+ Map m = {s: i};
+ m = {s: s};
+ m = {s: n};
+ m = {s: i,
+ s: n,
+ s: s};
+ m = {i: s,
+ n: s,
+ s: s};
+ }
+ }
+ '''
+ });
+
+ testChecker('casts in constant contexts', {
+ '/main.dart': '''
+ class A {
+ static const num n = 3.0;
+ static const int i = /*info:AssignmentCast*/n;
+ final int fi;
+ const A(num a) : this.fi = /*info:DownCastImplicit*/a;
+ }
+ class B extends A {
+ const B(Object a) : super(/*info:DownCastImplicit*/a);
+ }
+ void foo(Object o) {
+ var a = const A(/*info:DownCastImplicit*/o);
+ }
+ '''
+ });
+
+ testChecker('casts in conditionals', {
+ '/main.dart': '''
+ main() {
+ bool b = true;
+ num x = b ? 1 : 2.3;
+ int y = /*info:AssignmentCast*/b ? 1 : 2.3;
+ String z = !b ? "hello" : null;
+ z = b ? null : "hello";
+ }
+ '''
+ });
+
+ testChecker('redirecting constructor', {
+ '/main.dart': '''
+ class A {
+ A(A x) {}
+ A.two() : this(/*severe:StaticTypeError*/3);
+ }
+ '''
+ });
+
+ testChecker('super constructor', {
+ '/main.dart': '''
+ class A { A(A x) {} }
+ class B extends A {
+ B() : super(/*severe:StaticTypeError*/3);
+ }
+ '''
+ });
+
+ testChecker('field/field override', {
+ '/main.dart': '''
+ class A {}
+ class B extends A {}
+ class C extends B {}
+
+ class Base {
+ B f1;
+ B f2;
+ B f3;
+ B f4;
+ }
+
+ class Child extends Base {
+ /*severe:InvalidMethodOverride*/A f1; // invalid for getter
+ /*severe:InvalidMethodOverride*/C f2; // invalid for setter
+ var f3;
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/dynamic f4;
+ }
+ '''
+ });
+
+ testChecker('getter/getter override', {
+ '/main.dart': '''
+ class A {}
+ class B extends A {}
+ class C extends B {}
+
+ abstract class Base {
+ B get f1;
+ B get f2;
+ B get f3;
+ B get f4;
+ }
+
+ class Child extends Base {
+ /*severe:InvalidMethodOverride*/A get f1 => null;
+ C get f2 => null;
+ get f3 => null;
+ /*severe:InvalidMethodOverride*/dynamic get f4 => null;
+ }
+ '''
+ });
+
+ testChecker('field/getter override', {
+ '/main.dart': '''
+ class A {}
+ class B extends A {}
+ class C extends B {}
+
+ abstract class Base {
+ B f1;
+ B f2;
+ B f3;
+ B f4;
+ }
+
+ class Child extends Base {
+ /*severe:InvalidMethodOverride*/A get f1 => null;
+ C get f2 => null;
+ get f3 => null;
+ /*severe:InvalidMethodOverride*/dynamic get f4 => null;
+ }
+ '''
+ });
+
+ testChecker('setter/setter override', {
+ '/main.dart': '''
+ class A {}
+ class B extends A {}
+ class C extends B {}
+
+ abstract class Base {
+ void set f1(B value);
+ void set f2(B value);
+ void set f3(B value);
+ void set f4(B value);
+ void set f5(B value);
+ }
+
+ class Child extends Base {
+ void set f1(A value) {}
+ /*severe:InvalidMethodOverride*/void set f2(C value) {}
+ void set f3(value) {}
+ /*severe:InvalidMethodOverride*/void set f4(dynamic value) {}
+ set f5(B value) {}
+ }
+ '''
+ });
+
+ testChecker('field/setter override', {
+ '/main.dart': '''
+ class A {}
+ class B extends A {}
+ class C extends B {}
+
+ class Base {
+ B f1;
+ B f2;
+ B f3;
+ B f4;
+ B f5;
+ }
+
+ class Child extends Base {
+ B get f1 => null;
+ B get f2 => null;
+ B get f3 => null;
+ B get f4 => null;
+ B get f5 => null;
+
+ void set f1(A value) {}
+ /*severe:InvalidMethodOverride*/void set f2(C value) {}
+ void set f3(value) {}
+ /*severe:InvalidMethodOverride*/void set f4(dynamic value) {}
+ set f5(B value) {}
+ }
+ '''
+ });
+
+ testChecker('method override', {
+ '/main.dart': '''
+ class A {}
+ class B extends A {}
+ class C extends B {}
+
+ class Base {
+ B m1(B a);
+ B m2(B a);
+ B m3(B a);
+ B m4(B a);
+ B m5(B a);
+ B m6(B a);
+ }
+
+ class Child extends Base {
+ /*severe:InvalidMethodOverride*/A m1(A value) {}
+ /*severe:InvalidMethodOverride*/C m2(C value) {}
+ /*severe:InvalidMethodOverride*/A m3(C value) {}
+ C m4(A value) {}
+ m5(value) {}
+ /*severe:InvalidMethodOverride*/dynamic m6(dynamic value) {}
+ }
+ '''
+ });
+
+ testChecker('unary operators', {
+ '/main.dart': '''
+ class A {
+ A operator ~() {}
+ A operator +(int x) {}
+ A operator -(int x) {}
+ A operator -() {}
+ }
+
+ foo() => new A();
+
+ test() {
+ A a = new A();
+ var c = foo();
+
+ ~a;
+ (/*info:DynamicInvoke*/~d);
+
+ !/*severe:StaticTypeError*/a;
+ !/*info:DynamicCast*/d;
+
+ -a;
+ (/*info:DynamicInvoke*/-d);
+
+ ++a;
+ --a;
+ (/*info:DynamicInvoke*/++d);
+ (/*info:DynamicInvoke*/--d);
+
+ a++;
+ a--;
+ (/*info:DynamicInvoke*/d++);
+ (/*info:DynamicInvoke*/d--);
+ }'''
+ });
+
+ testChecker('binary and index operators', {
+ '/main.dart': '''
+ class A {
+ A operator *(B b) {}
+ A operator /(B b) {}
+ A operator ~/(B b) {}
+ A operator %(B b) {}
+ A operator +(B b) {}
+ A operator -(B b) {}
+ A operator <<(B b) {}
+ A operator >>(B b) {}
+ A operator &(B b) {}
+ A operator ^(B b) {}
+ A operator |(B b) {}
+ A operator[](B b) {}
+ }
+
+ class B {
+ A operator -(B b) {}
+ }
+
+ foo() => new A();
+
+ test() {
+ A a = new A();
+ B b = new B();
+ var c = foo();
+ a = a * b;
+ a = a * /*info:DynamicCast*/c;
+ a = a / b;
+ a = a ~/ b;
+ a = a % b;
+ a = a + b;
+ a = a + /*severe:StaticTypeError*/a;
+ a = a - b;
+ b = /*severe:StaticTypeError*/b - b;
+ a = a << b;
+ a = a >> b;
+ a = a & b;
+ a = a ^ b;
+ a = a | b;
+ c = (/*info:DynamicInvoke*/c + b);
+
+ String x = 'hello';
+ int y = 42;
+ x = x + x;
+ x = x + /*info:DynamicCast*/c;
+ x = x + /*severe:StaticTypeError*/y;
+
+ bool p = true;
+ p = p && p;
+ p = p && /*info:DynamicCast*/c;
+ p = (/*info:DynamicCast*/c) && p;
+ p = (/*info:DynamicCast*/c) && /*info:DynamicCast*/c;
+ p = (/*severe:StaticTypeError*/y) && p;
+ p = c == y;
+
+ a = a[b];
+ a = a[/*info:DynamicCast*/c];
+ c = (/*info:DynamicInvoke*/c[b]);
+ a[/*severe:StaticTypeError*/y];
+ }
+ '''
+ });
+
+ testChecker('compound assignments', {
+ '/main.dart': '''
+ class A {
+ A operator *(B b) {}
+ A operator /(B b) {}
+ A operator ~/(B b) {}
+ A operator %(B b) {}
+ A operator +(B b) {}
+ A operator -(B b) {}
+ A operator <<(B b) {}
+ A operator >>(B b) {}
+ A operator &(B b) {}
+ A operator ^(B b) {}
+ A operator |(B b) {}
+ D operator [](B index) {}
+ void operator []=(B index, D value) {}
+ }
+
+ class B {
+ A operator -(B b) {}
+ }
+
+ class D {
+ D operator +(D d) {}
+ }
+
+ foo() => new A();
+
+ test() {
+ int x = 0;
+ x += 5;
+ (/*severe:StaticTypeError*/x += 3.14);
+
+ double y = 0.0;
+ y += 5;
+ y += 3.14;
+
+ num z = 0;
+ z += 5;
+ z += 3.14;
+
+ x = /*info:DownCastImplicit*/x + z;
+ x += /*info:DownCastImplicit*/z;
+ y = /*info:DownCastImplicit*/y + z;
+ y += /*info:DownCastImplicit*/z;
+
+ dynamic w = 42;
+ x += /*info:DynamicCast*/w;
+ y += /*info:DynamicCast*/w;
+ z += /*info:DynamicCast*/w;
+
+ A a = new A();
+ B b = new B();
+ var c = foo();
+ a = a * b;
+ a *= b;
+ a *= /*info:DynamicCast*/c;
+ a /= b;
+ a ~/= b;
+ a %= b;
+ a += b;
+ a += /*severe:StaticTypeError*/a;
+ a -= b;
+ (/*severe:StaticTypeError*/b -= b);
+ a <<= b;
+ a >>= b;
+ a &= b;
+ a ^= b;
+ a |= b;
+ (/*info:DynamicInvoke*/c += b);
+
+ var d = new D();
+ a[b] += d;
+ a[/*info:DynamicCast*/c] += d;
+ a[/*severe:StaticTypeError*/z] += d;
+ a[b] += /*info:DynamicCast*/c;
+ a[b] += /*severe:StaticTypeError*/z;
+ (/*info:DynamicInvoke*/(/*info:DynamicInvoke*/c[b]) += d);
+ }
+ '''
+ });
+
+ testChecker('super call placement', {
+ '/main.dart': '''
+ class Base {
+ var x;
+ Base() : x = print('Base.1') { print('Base.2'); }
+ }
+
+ class Derived extends Base {
+ var y, z;
+ Derived()
+ : y = print('Derived.1'),
+ /*severe:InvalidSuperInvocation*/super(),
+ z = print('Derived.2') {
+ print('Derived.3');
+ }
+ }
+
+ class Valid extends Base {
+ var y, z;
+ Valid()
+ : y = print('Valid.1'),
+ z = print('Valid.2'),
+ super() {
+ print('Valid.3');
+ }
+ }
+
+ class AlsoValid extends Base {
+ AlsoValid() : super();
+ }
+
+ main() => new Derived();
+ '''
+ });
+
+ testChecker('for loop variable', {
+ '/main.dart': '''
+ foo() {
+ for (int i = 0; i < 10; i++) {
+ i = /*severe:StaticTypeError*/"hi";
+ }
+ }
+ bar() {
+ for (var i = 0; i < 10; i++) {
+ int j = i + 1;
+ }
+ }
+ '''
+ });
+
+ group('invalid overrides', () {
+ testChecker('child override', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Base {
+ A f;
+ }
+
+ class T1 extends Base {
+ /*severe:InvalidMethodOverride*/B get f => null;
+ }
+
+ class T2 extends Base {
+ /*severe:InvalidMethodOverride*/set f(B b) => null;
+ }
+
+ class T3 extends Base {
+ /*severe:InvalidMethodOverride*/final B f;
+ }
+ class T4 extends Base {
+ // two: one for the getter one for the setter.
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/B f;
+ }
+ '''
+ });
+
+ testChecker('child override 2', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Base {
+ m(A a) {}
+ }
+
+ class Test extends Base {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+ testChecker('grandchild override', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Grandparent {
+ m(A a) {}
+ }
+ class Parent extends Grandparent {
+ }
+
+ class Test extends Parent {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+
+ testChecker('double override', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Grandparent {
+ m(A a) {}
+ }
+ class Parent extends Grandparent {
+ m(A a) {}
+ }
+
+ class Test extends Parent {
+ // Reported only once
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+
+ testChecker('double override 2', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Grandparent {
+ m(A a) {}
+ }
+ class Parent extends Grandparent {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+
+ class Test extends Parent {
+ m(B a) {}
+ }
+ '''
+ });
+
+ testChecker('mixin override to base', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Base {
+ m(A a) {}
+ }
+
+ class M1 {
+ m(B a) {}
+ }
+
+ class M2 {}
+
+ class T1 extends Base with /*severe:InvalidMethodOverride*/M1 {}
+ class T2 extends Base with /*severe:InvalidMethodOverride*/M1, M2 {}
+ class T3 extends Base with M2, /*severe:InvalidMethodOverride*/M1 {}
+ '''
+ });
+
+ testChecker('mixin override to mixin', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Base {
+ }
+
+ class M1 {
+ m(B a) {}
+ }
+
+ class M2 {
+ m(A a) {}
+ }
+
+ class T1 extends Base with M1, /*severe:InvalidMethodOverride*/M2 {}
+ '''
+ });
+
+ // This is a regression test for a bug in an earlier implementation were
+ // names were hiding errors if the first mixin override looked correct,
+ // but subsequent ones did not.
+ testChecker('no duplicate mixin override', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Base {
+ m(A a) {}
+ }
+
+ class M1 {
+ m(A a) {}
+ }
+
+ class M2 {
+ m(B a) {}
+ }
+
+ class M3 {
+ m(B a) {}
+ }
+
+ class T1 extends Base
+ with M1, /*severe:InvalidMethodOverride*/M2, M3 {}
+ '''
+ });
+
+ testChecker('class override of interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I {
+ m(A a);
+ }
+
+ class T1 implements I {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+
+ testChecker('base class override to child interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I {
+ m(A a);
+ }
+
+ class Base {
+ m(B a) {}
+ }
+
+
+ class T1 /*severe:InvalidMethodOverride*/extends Base implements I {
+ }
+ '''
+ });
+
+ testChecker('mixin override of interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I {
+ m(A a);
+ }
+
+ class M {
+ m(B a) {}
+ }
+
+ class T1 extends Object with /*severe:InvalidMethodOverride*/M
+ implements I {}
+ '''
+ });
+
+ // This is a case were it is incorrect to say that the base class
+ // incorrectly overrides the interface.
+ testChecker(
+ 'no errors if subclass correctly overrides base and interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ class Base {
+ m(A a) {}
+ }
+
+ class I1 {
+ m(B a) {}
+ }
+
+ class T1 /*severe:InvalidMethodOverride*/extends Base
+ implements I1 {}
+
+ class T2 extends Base implements I1 {
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/m(a) {}
+ }
+
+ class T3 extends Object with /*severe:InvalidMethodOverride*/Base
+ implements I1 {}
+
+ class T4 extends Object with Base implements I1 {
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/m(a) {}
+ }
+ '''
+ });
+ });
+
+ group('class override of grand interface', () {
+ testChecker('interface of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class I2 implements I1 {}
+
+ class T1 implements I2 {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+ testChecker('superclass of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class I2 extends I1 {}
+
+ class T1 implements I2 {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+ testChecker('mixin of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class M1 {
+ m(A a);
+ }
+ abstract class I2 extends Object with M1 {}
+
+ class T1 implements I2 {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+ testChecker('interface of abstract superclass', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class Base implements I1 {}
+
+ class T1 extends Base {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+ testChecker('interface of concrete superclass', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ // See issue #25
+ /*pass should be warning:AnalyzerError*/class Base implements I1 {
+ }
+
+ class T1 extends Base {
+ // not reported technically because if the class is concrete,
+ // it should implement all its interfaces and hence it is
+ // sufficient to check overrides against it.
+ m(B a) {}
+ }
+ '''
+ });
+ });
+
+ group('mixin override of grand interface', () {
+ testChecker('interface of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class I2 implements I1 {}
+
+ class M {
+ m(B a) {}
+ }
+
+ class T1 extends Object with /*severe:InvalidMethodOverride*/M
+ implements I2 {
+ }
+ '''
+ });
+ testChecker('superclass of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class I2 extends I1 {}
+
+ class M {
+ m(B a) {}
+ }
+
+ class T1 extends Object with /*severe:InvalidMethodOverride*/M
+ implements I2 {
+ }
+ '''
+ });
+ testChecker('mixin of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class M1 {
+ m(A a);
+ }
+ abstract class I2 extends Object with M1 {}
+
+ class M {
+ m(B a) {}
+ }
+
+ class T1 extends Object with /*severe:InvalidMethodOverride*/M
+ implements I2 {
+ }
+ '''
+ });
+ testChecker('interface of abstract superclass', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class Base implements I1 {}
+
+ class M {
+ m(B a) {}
+ }
+
+ class T1 extends Base with /*severe:InvalidMethodOverride*/M {
+ }
+ '''
+ });
+ testChecker('interface of concrete superclass', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ // See issue #25
+ /*pass should be warning:AnalyzerError*/class Base implements I1 {
+ }
+
+ class M {
+ m(B a) {}
+ }
+
+ class T1 extends Base with M {
+ }
+ '''
+ });
+ });
+
+ group('superclass override of grand interface', () {
+ testChecker('interface of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class I2 implements I1 {}
+
+ class Base {
+ m(B a) {}
+ }
+
+ class T1 /*severe:InvalidMethodOverride*/extends Base
+ implements I2 {
+ }
+ '''
+ });
+ testChecker('superclass of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class I2 extends I1 {}
+
+ class Base {
+ m(B a) {}
+ }
+
+ class T1 /*severe:InvalidMethodOverride*/extends Base
+ implements I2 {
+ }
+ '''
+ });
+ testChecker('mixin of interface of child', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class M1 {
+ m(A a);
+ }
+ abstract class I2 extends Object with M1 {}
+
+ class Base {
+ m(B a) {}
+ }
+
+ class T1 /*severe:InvalidMethodOverride*/extends Base
+ implements I2 {
+ }
+ '''
+ });
+ testChecker('interface of abstract superclass', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ abstract class Base implements I1 {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+
+ class T1 extends Base {
+ // we consider the base class incomplete because it is
+ // abstract, so we report the error here too.
+ // TODO(sigmund): consider tracking overrides in a fine-grain
+ // manner, then this and the double-overrides would not be
+ // reported.
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+ testChecker('interface of concrete superclass', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ class Base implements I1 {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+
+ class T1 extends Base {
+ m(B a) {}
+ }
+ '''
+ });
+ });
+
+ group('no duplicate reports from overriding interfaces', () {
+ testChecker('type overrides same method in multiple interfaces', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+ abstract class I2 implements I1 {
+ m(A a);
+ }
+
+ class Base {
+ }
+
+ class T1 implements I2 {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+ '''
+ });
+
+ testChecker('type and base type override same method in interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ class Base {
+ m(B a);
+ }
+
+ // Note: no error reported in `extends Base` to avoid duplicating
+ // the error in T1.
+ class T1 extends Base implements I1 {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+
+ // If there is no error in the class, we do report the error at
+ // the base class:
+ class T2 /*severe:InvalidMethodOverride*/extends Base
+ implements I1 {
+ }
+ '''
+ });
+
+ testChecker('type and mixin override same method in interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ class M {
+ m(B a);
+ }
+
+ class T1 extends Object with M implements I1 {
+ /*severe:InvalidMethodOverride*/m(B a) {}
+ }
+
+ class T2 extends Object with /*severe:InvalidMethodOverride*/M
+ implements I1 {
+ }
+ '''
+ });
+
+ testChecker('two grand types override same method in interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ class Grandparent {
+ m(B a) {}
+ }
+
+ class Parent1 extends Grandparent {
+ m(B a) {}
+ }
+ class Parent2 extends Grandparent {
+ }
+
+ // Note: otherwise both errors would be reported on this line
+ class T1 /*severe:InvalidMethodOverride*/extends Parent1
+ implements I1 {
+ }
+ class T2 /*severe:InvalidMethodOverride*/extends Parent2
+ implements I1 {
+ }
+ '''
+ });
+
+ testChecker('two mixins override same method in interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ class M1 {
+ m(B a) {}
+ }
+
+ class M2 {
+ m(B a) {}
+ }
+
+ // Here we want to report both, because the error location is
+ // different.
+ // TODO(sigmund): should we merge these as well?
+ class T1 extends Object
+ with /*severe:InvalidMethodOverride*/M1
+ with /*severe:InvalidMethodOverride*/M2
+ implements I1 {
+ }
+ '''
+ });
+
+ testChecker('base type and mixin override same method in interface', {
+ '/main.dart': '''
+ class A {}
+ class B {}
+
+ abstract class I1 {
+ m(A a);
+ }
+
+ class Base {
+ m(B a) {}
+ }
+
+ class M {
+ m(B a) {}
+ }
+
+ // Here we want to report both, because the error location is
+ // different.
+ // TODO(sigmund): should we merge these as well?
+ class T1 /*severe:InvalidMethodOverride*/extends Base
+ with /*severe:InvalidMethodOverride*/M
+ implements I1 {
+ }
+ '''
+ });
+ });
+
+ testChecker('invalid runtime checks', {
+ '/main.dart': '''
+ typedef int I2I(int x);
+ typedef int D2I(x);
+ typedef int II2I(int x, int y);
+ typedef int DI2I(x, int y);
+ typedef int ID2I(int x, y);
+ typedef int DD2I(x, y);
+
+ typedef I2D(int x);
+ typedef D2D(x);
+ typedef II2D(int x, int y);
+ typedef DI2D(x, int y);
+ typedef ID2D(int x, y);
+ typedef DD2D(x, y);
+
+ int foo(int x) => x;
+ int bar(int x, int y) => x + y;
+
+ void main() {
+ bool b;
+ b = /*info:NonGroundTypeCheckInfo*/foo is I2I;
+ b = /*info:NonGroundTypeCheckInfo*/foo is D2I;
+ b = /*info:NonGroundTypeCheckInfo*/foo is I2D;
+ b = foo is D2D;
+
+ b = /*info:NonGroundTypeCheckInfo*/bar is II2I;
+ b = /*info:NonGroundTypeCheckInfo*/bar is DI2I;
+ b = /*info:NonGroundTypeCheckInfo*/bar is ID2I;
+ b = /*info:NonGroundTypeCheckInfo*/bar is II2D;
+ b = /*info:NonGroundTypeCheckInfo*/bar is DD2I;
+ b = /*info:NonGroundTypeCheckInfo*/bar is DI2D;
+ b = /*info:NonGroundTypeCheckInfo*/bar is ID2D;
+ b = bar is DD2D;
+
+ // For as, the validity of checks is deferred to runtime.
+ Function f;
+ f = foo as I2I;
+ f = foo as D2I;
+ f = foo as I2D;
+ f = foo as D2D;
+
+ f = bar as II2I;
+ f = bar as DI2I;
+ f = bar as ID2I;
+ f = bar as II2D;
+ f = bar as DD2I;
+ f = bar as DI2D;
+ f = bar as ID2D;
+ f = bar as DD2D;
+ }
+ '''
+ });
+
+ group('function modifiers', () {
+ testChecker('async', {
+ '/main.dart': '''
+ import 'dart:async';
+ import 'dart:math' show Random;
+
+ dynamic x;
+
+ foo1() async => x;
+ Future foo2() async => x;
+ Future<int> foo3() async => (/*info:DynamicCast*/x);
+ Future<int> foo4() async => (/*severe:StaticTypeError*/new Future<int>.value(/*info:DynamicCast*/x));
+
+ bar1() async { return x; }
+ Future bar2() async { return x; }
+ Future<int> bar3() async { return (/*info:DynamicCast*/x); }
+ Future<int> bar4() async { return (/*severe:StaticTypeError*/new Future<int>.value(/*info:DynamicCast*/x)); }
+
+ int y;
+ Future<int> z;
+
+ void baz() async {
+ int a = /*info:DynamicCast*/await x;
+ int b = await y;
+ int c = await z;
+ String d = /*severe:StaticTypeError*/await z;
+ }
+
+ Future<bool> get issue_264 async {
+ await 42;
+ if (new Random().nextBool()) {
+ return true;
+ } else {
+ return /*severe:StaticTypeError*/new Future<bool>.value(false);
+ }
+ }
+ '''
+ });
+
+ testChecker('async*', {
+ '/main.dart': '''
+ import 'dart:async';
+
+ dynamic x;
+
+ bar1() async* { yield x; }
+ Stream bar2() async* { yield x; }
+ Stream<int> bar3() async* { yield (/*info:DynamicCast*/x); }
+ Stream<int> bar4() async* { yield (/*severe:StaticTypeError*/new Stream<int>()); }
+
+ baz1() async* { yield* (/*info:DynamicCast*/x); }
+ Stream baz2() async* { yield* (/*info:DynamicCast*/x); }
+ Stream<int> baz3() async* { yield* (/*warning:DownCastComposite*/x); }
+ Stream<int> baz4() async* { yield* new Stream<int>(); }
+ Stream<int> baz5() async* { yield* (/*info:InferredTypeAllocation*/new Stream()); }
+ '''
+ });
+
+ testChecker('sync*', {
+ '/main.dart': '''
+ import 'dart:async';
+
+ dynamic x;
+
+ bar1() sync* { yield x; }
+ Iterable bar2() sync* { yield x; }
+ Iterable<int> bar3() sync* { yield (/*info:DynamicCast*/x); }
+ Iterable<int> bar4() sync* { yield (/*severe:StaticTypeError*/new Iterable<int>()); }
+
+ baz1() sync* { yield* (/*info:DynamicCast*/x); }
+ Iterable baz2() sync* { yield* (/*info:DynamicCast*/x); }
+ Iterable<int> baz3() sync* { yield* (/*warning:DownCastComposite*/x); }
+ Iterable<int> baz4() sync* { yield* new Iterable<int>(); }
+ Iterable<int> baz5() sync* { yield* (/*info:InferredTypeAllocation*/new Iterable()); }
+ '''
+ });
+ });
+}
diff --git a/packages/analyzer/test/src/task/strong/inferred_type_test.dart b/packages/analyzer/test/src/task/strong/inferred_type_test.dart
new file mode 100644
index 0000000..125b356
--- /dev/null
+++ b/packages/analyzer/test/src/task/strong/inferred_type_test.dart
@@ -0,0 +1,1310 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// TODO(jmesserly): this file needs to be refactored, it's a port from
+// package:dev_compiler's tests
+/// Tests for type inference.
+library test.src.task.strong.inferred_type_test;
+
+import 'package:unittest/unittest.dart';
+
+import 'strong_test_helper.dart';
+
+void main() {
+ // Error also expected when declared type is `int`.
+ testChecker('infer type on var', {
+ '/main.dart': '''
+ test1() {
+ int x = 3;
+ x = /*severe:StaticTypeError*/"hi";
+ }
+ '''
+ });
+
+ // If inferred type is `int`, error is also reported
+ testChecker('infer type on var 2', {
+ '/main.dart': '''
+ test2() {
+ var x = 3;
+ x = /*severe:StaticTypeError*/"hi";
+ }
+ '''
+ });
+
+ testChecker('No error when declared type is `num` and assigned null.', {
+ '/main.dart': '''
+ test1() {
+ num x = 3;
+ x = null;
+ }
+ '''
+ });
+
+ testChecker('do not infer type on dynamic', {
+ '/main.dart': '''
+ test() {
+ dynamic x = 3;
+ x = "hi";
+ }
+ '''
+ });
+
+ testChecker('do not infer type when initializer is null', {
+ '/main.dart': '''
+ test() {
+ var x = null;
+ x = "hi";
+ x = 3;
+ }
+ '''
+ });
+
+ testChecker('infer type on var from field', {
+ '/main.dart': '''
+ class A {
+ int x = 0;
+
+ test1() {
+ var a = x;
+ a = /*severe:StaticTypeError*/"hi";
+ a = 3;
+ var b = y;
+ b = /*severe:StaticTypeError*/"hi";
+ b = 4;
+ var c = z;
+ c = /*severe:StaticTypeError*/"hi";
+ c = 4;
+ }
+
+ int y; // field def after use
+ final z = 42; // should infer `int`
+ }
+ '''
+ });
+
+ testChecker('infer type on var from top-level', {
+ '/main.dart': '''
+ int x = 0;
+
+ test1() {
+ var a = x;
+ a = /*severe:StaticTypeError*/"hi";
+ a = 3;
+ var b = y;
+ b = /*severe:StaticTypeError*/"hi";
+ b = 4;
+ var c = z;
+ c = /*severe:StaticTypeError*/"hi";
+ c = 4;
+ }
+
+ int y = 0; // field def after use
+ final z = 42; // should infer `int`
+ '''
+ });
+
+ testChecker('do not infer field type when initializer is null', {
+ '/main.dart': '''
+ var x = null;
+ var y = 3;
+ class A {
+ static var x = null;
+ static var y = 3;
+
+ var x2 = null;
+ var y2 = 3;
+ }
+
+ test() {
+ x = "hi";
+ y = /*severe:StaticTypeError*/"hi";
+ A.x = "hi";
+ A.y = /*severe:StaticTypeError*/"hi";
+ new A().x2 = "hi";
+ new A().y2 = /*severe:StaticTypeError*/"hi";
+ }
+ '''
+ });
+
+ testChecker('infer from variables in non-cycle imports with flag', {
+ '/a.dart': '''
+ var x = 2;
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+ var y = x;
+
+ test1() {
+ x = /*severe:StaticTypeError*/"hi";
+ y = /*severe:StaticTypeError*/"hi";
+ }
+ '''
+ });
+
+ testChecker('infer from variables in non-cycle imports with flag 2', {
+ '/a.dart': '''
+ class A { static var x = 2; }
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+ class B { static var y = A.x; }
+
+ test1() {
+ A.x = /*severe:StaticTypeError*/"hi";
+ B.y = /*severe:StaticTypeError*/"hi";
+ }
+ '''
+ });
+
+ testChecker('infer from variables in cycle libs when flag is on', {
+ '/a.dart': '''
+ import 'main.dart';
+ var x = 2; // ok to infer
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+ var y = x; // now ok :)
+
+ test1() {
+ int t = 3;
+ t = x;
+ t = y;
+ }
+ '''
+ });
+
+ testChecker('infer from variables in cycle libs when flag is on 2', {
+ '/a.dart': '''
+ import 'main.dart';
+ class A { static var x = 2; }
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+ class B { static var y = A.x; }
+
+ test1() {
+ int t = 3;
+ t = A.x;
+ t = B.y;
+ }
+ '''
+ });
+
+ testChecker('can infer also from static and instance fields (flag on)', {
+ '/a.dart': '''
+ import 'b.dart';
+ class A {
+ static final a1 = B.b1;
+ final a2 = new B().b2;
+ }
+ ''',
+ '/b.dart': '''
+ class B {
+ static final b1 = 1;
+ final b2 = 1;
+ }
+ ''',
+ '/main.dart': '''
+ import "a.dart";
+
+ test1() {
+ int x = 0;
+ // inference in A now works.
+ x = A.a1;
+ x = new A().a2;
+ }
+ '''
+ });
+
+ testChecker('inference in cycles is deterministic', {
+ '/a.dart': '''
+ import 'b.dart';
+ class A {
+ static final a1 = B.b1;
+ final a2 = new B().b2;
+ }
+ ''',
+ '/b.dart': '''
+ class B {
+ static final b1 = 1;
+ final b2 = 1;
+ }
+ ''',
+ '/c.dart': '''
+ import "main.dart"; // creates a cycle
+
+ class C {
+ static final c1 = 1;
+ final c2 = 1;
+ }
+ ''',
+ '/e.dart': '''
+ import 'a.dart';
+ part 'e2.dart';
+
+ class E {
+ static final e1 = 1;
+ static final e2 = F.f1;
+ static final e3 = A.a1;
+ final e4 = 1;
+ final e5 = new F().f2;
+ final e6 = new A().a2;
+ }
+ ''',
+ '/f.dart': '''
+ part 'f2.dart';
+ ''',
+ '/e2.dart': '''
+ class F {
+ static final f1 = 1;
+ final f2 = 1;
+ }
+ ''',
+ '/main.dart': '''
+ import "a.dart";
+ import "c.dart";
+ import "e.dart";
+
+ class D {
+ static final d1 = A.a1 + 1;
+ static final d2 = C.c1 + 1;
+ final d3 = new A().a2;
+ final d4 = new C().c2;
+ }
+
+ test1() {
+ int x = 0;
+ // inference in A works, it's not in a cycle
+ x = A.a1;
+ x = new A().a2;
+
+ // Within a cycle we allow inference when the RHS is well known, but
+ // not when it depends on other fields within the cycle
+ x = C.c1;
+ x = D.d1;
+ x = D.d2;
+ x = new C().c2;
+ x = new D().d3;
+ x = /*info:DynamicCast*/new D().d4;
+
+
+ // Similarly if the library contains parts.
+ x = E.e1;
+ x = E.e2;
+ x = E.e3;
+ x = new E().e4;
+ x = /*info:DynamicCast*/new E().e5;
+ x = new E().e6;
+ x = F.f1;
+ x = new F().f2;
+ }
+ '''
+ });
+
+ testChecker(
+ 'infer from complex expressions if the outer-most value is precise', {
+ '/main.dart': '''
+ class A { int x; B operator+(other) {} }
+ class B extends A { B(ignore); }
+ var a = new A();
+ // Note: it doesn't matter that some of these refer to 'x'.
+ var b = new B(x); // allocations
+ var c1 = [x]; // list literals
+ var c2 = const [];
+ var d = {'a': 'b'}; // map literals
+ var e = new A()..x = 3; // cascades
+ var f = 2 + 3; // binary expressions are OK if the left operand
+ // is from a library in a different strongest
+ // conected component.
+ var g = -3;
+ var h = new A() + 3;
+ var i = - new A();
+ var j = null as B;
+
+ test1() {
+ a = /*severe:StaticTypeError*/"hi";
+ a = new B(3);
+ b = /*severe:StaticTypeError*/"hi";
+ b = new B(3);
+ c1 = [];
+ c1 = /*severe:StaticTypeError*/{};
+ c2 = [];
+ c2 = /*severe:StaticTypeError*/{};
+ d = {};
+ d = /*severe:StaticTypeError*/3;
+ e = new A();
+ e = /*severe:StaticTypeError*/{};
+ f = 3;
+ f = /*severe:StaticTypeError*/false;
+ g = 1;
+ g = /*severe:StaticTypeError*/false;
+ h = /*severe:StaticTypeError*/false;
+ h = new B();
+ i = false;
+ j = new B();
+ j = /*severe:StaticTypeError*/false;
+ j = /*severe:StaticTypeError*/[];
+ }
+ '''
+ });
+
+ // but flags can enable this behavior.
+ testChecker('infer if complex expressions read possibly inferred field', {
+ '/a.dart': '''
+ class A {
+ var x = 3;
+ }
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+ class B {
+ var y = 3;
+ }
+ final t1 = new A();
+ final t2 = new A().x;
+ final t3 = new B();
+ final t4 = new B().y;
+
+ test1() {
+ int i = 0;
+ A a;
+ B b;
+ a = t1;
+ i = t2;
+ b = t3;
+ i = /*info:DynamicCast*/t4;
+ i = new B().y; // B.y was inferred though
+ }
+ '''
+ });
+
+ group('infer types on loop indices', () {
+ testChecker('foreach loop', {
+ '/main.dart': '''
+ class Foo {
+ int bar = 42;
+ }
+
+ class Bar<T extends Iterable<String>> {
+ void foo(T t) {
+ for (var i in t) {
+ int x = /*severe:StaticTypeError*/i;
+ }
+ }
+ }
+
+ class Baz<T, E extends Iterable<T>, S extends E> {
+ void foo(S t) {
+ for (var i in t) {
+ int x = /*severe:StaticTypeError*/i;
+ T y = i;
+ }
+ }
+ }
+
+ test() {
+ var list = <Foo>[];
+ for (var x in list) {
+ String y = /*severe:StaticTypeError*/x;
+ }
+
+ for (dynamic x in list) {
+ String y = /*info:DynamicCast*/x;
+ }
+
+ for (String x in /*severe:StaticTypeError*/list) {
+ String y = x;
+ }
+
+ var z;
+ for(z in list) {
+ String y = /*info:DynamicCast*/z;
+ }
+
+ Iterable iter = list;
+ for (Foo x in /*warning:DownCastComposite*/iter) {
+ var y = x;
+ }
+
+ dynamic iter2 = list;
+ for (Foo x in /*warning:DownCastComposite*/iter2) {
+ var y = x;
+ }
+
+ var map = <String, Foo>{};
+ // Error: map must be an Iterable.
+ for (var x in /*severe:StaticTypeError*/map) {
+ String y = /*info:DynamicCast*/x;
+ }
+
+ // We're not properly inferring that map.keys is an Iterable<String>
+ // and that x is a String.
+ for (var x in map.keys) {
+ String y = x;
+ }
+ }
+ '''
+ });
+
+ testChecker('for loop, with inference', {
+ '/main.dart': '''
+ test() {
+ for (var i = 0; i < 10; i++) {
+ int j = i + 1;
+ }
+ }
+ '''
+ });
+ });
+
+ testChecker('propagate inference to field in class', {
+ '/main.dart': '''
+ class A {
+ int x = 2;
+ }
+
+ test() {
+ var a = new A();
+ A b = a; // doesn't require down cast
+ print(a.x); // doesn't require dynamic invoke
+ print(a.x + 2); // ok to use in bigger expression
+ }
+ '''
+ });
+
+ testChecker('propagate inference to field in class dynamic warnings', {
+ '/main.dart': '''
+ class A {
+ int x = 2;
+ }
+
+ test() {
+ dynamic a = new A();
+ A b = /*info:DynamicCast*/a;
+ print(/*info:DynamicInvoke*/a.x);
+ print(/*info:DynamicInvoke*/(/*info:DynamicInvoke*/a.x) + 2);
+ }
+ '''
+ });
+
+ testChecker('propagate inference transitively', {
+ '/main.dart': '''
+ class A {
+ int x = 2;
+ }
+
+ test5() {
+ var a1 = new A();
+ a1.x = /*severe:StaticTypeError*/"hi";
+
+ A a2 = new A();
+ a2.x = /*severe:StaticTypeError*/"hi";
+ }
+ '''
+ });
+
+ testChecker('propagate inference transitively 2', {
+ '/main.dart': '''
+ class A {
+ int x = 42;
+ }
+
+ class B {
+ A a = new A();
+ }
+
+ class C {
+ B b = new B();
+ }
+
+ class D {
+ C c = new C();
+ }
+
+ void main() {
+ var d1 = new D();
+ print(d1.c.b.a.x);
+
+ D d2 = new D();
+ print(d2.c.b.a.x);
+ }
+ '''
+ });
+
+ group('infer type on overridden fields', () {
+ testChecker('2', {
+ '/main.dart': '''
+ class A {
+ int x = 2;
+ }
+
+ class B extends A {
+ get x => 3;
+ }
+
+ foo() {
+ String y = /*severe:StaticTypeError*/new B().x;
+ int z = new B().x;
+ }
+ '''
+ });
+
+ testChecker('4', {
+ '/main.dart': '''
+ class A {
+ int x = 2;
+ }
+
+ class B implements A {
+ get x => 3;
+ }
+
+ foo() {
+ String y = /*severe:StaticTypeError*/new B().x;
+ int z = new B().x;
+ }
+ '''
+ });
+ });
+
+ group('infer types on generic instantiations', () {
+ testChecker('infer', {
+ '/main.dart': '''
+ class A<T> {
+ T x;
+ }
+
+ class B implements A<int> {
+ /*severe:InvalidMethodOverride*/dynamic get x => 3;
+ }
+
+ foo() {
+ String y = /*info:DynamicCast*/new B().x;
+ int z = /*info:DynamicCast*/new B().x;
+ }
+ '''
+ });
+
+ testChecker('3', {
+ '/main.dart': '''
+ class A<T> {
+ T x;
+ T w;
+ }
+
+ class B implements A<int> {
+ get x => 3;
+ get w => /*severe:StaticTypeError*/"hello";
+ }
+
+ foo() {
+ String y = /*severe:StaticTypeError*/new B().x;
+ int z = new B().x;
+ }
+ '''
+ });
+
+ testChecker('4', {
+ '/main.dart': '''
+ class A<T> {
+ T x;
+ }
+
+ class B<E> extends A<E> {
+ E y;
+ get x => y;
+ }
+
+ foo() {
+ int y = /*severe:StaticTypeError*/new B<String>().x;
+ String z = new B<String>().x;
+ }
+ '''
+ });
+
+ testChecker('5', {
+ '/main.dart': '''
+ abstract class I<E> {
+ String m(a, String f(v, T e));
+ }
+
+ abstract class A<E> implements I<E> {
+ const A();
+ String m(a, String f(v, T e));
+ }
+
+ abstract class M {
+ int y;
+ }
+
+ class B<E> extends A<E> implements M {
+ const B();
+ int get y => 0;
+
+ m(a, f(v, T e)) {}
+ }
+
+ foo () {
+ int y = /*severe:StaticTypeError*/new B().m(null, null);
+ String z = new B().m(null, null);
+ }
+ '''
+ });
+ });
+
+ testChecker('infer type regardless of declaration order or cycles', {
+ '/b.dart': '''
+ import 'main.dart';
+
+ class B extends A { }
+ ''',
+ '/main.dart': '''
+ import 'b.dart';
+ class C extends B {
+ get x;
+ }
+ class A {
+ int get x;
+ }
+ foo () {
+ int y = new C().x;
+ String y = /*severe:StaticTypeError*/new C().x;
+ }
+ '''
+ });
+
+ // Note: this is a regression test for a non-deterministic behavior we used to
+ // have with inference in library cycles. If you see this test flake out,
+ // change `test` to `skip_test` and reopen bug #48.
+ testChecker('infer types on generic instantiations in library cycle', {
+ '/a.dart': '''
+ import 'main.dart';
+ abstract class I<E> {
+ A<E> m(a, String f(v, int e));
+ }
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+
+ abstract class A<E> implements I<E> {
+ const A();
+
+ E value;
+ }
+
+ abstract class M {
+ int y;
+ }
+
+ class B<E> extends A<E> implements M {
+ const B();
+ int get y => 0;
+
+ m(a, f(v, int e)) {}
+ }
+
+ foo () {
+ int y = /*severe:StaticTypeError*/new B<String>().m(null, null).value;
+ String z = new B<String>().m(null, null).value;
+ }
+ '''
+ });
+
+ group('do not infer overridden fields that explicitly say dynamic', () {
+ testChecker('infer', {
+ '/main.dart': '''
+ class A {
+ int x = 2;
+ }
+
+ class B implements A {
+ /*severe:InvalidMethodOverride*/dynamic get x => 3;
+ }
+
+ foo() {
+ String y = /*info:DynamicCast*/new B().x;
+ int z = /*info:DynamicCast*/new B().x;
+ }
+ '''
+ });
+ });
+
+ testChecker('conflicts can happen', {
+ '/main.dart': '''
+ class I1 {
+ int x;
+ }
+ class I2 extends I1 {
+ int y;
+ }
+
+ class A {
+ final I1 a;
+ }
+
+ class B {
+ final I2 a;
+ }
+
+ class C1 extends A implements B {
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a => null;
+ }
+
+ // Still ambiguous
+ class C2 extends B implements A {
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a => null;
+ }
+ '''
+ });
+
+ testChecker('conflicts can happen 2', {
+ '/main.dart': '''
+ class I1 {
+ int x;
+ }
+ class I2 {
+ int y;
+ }
+
+ class I3 implements I1, I2 {
+ int x;
+ int y;
+ }
+
+ class A {
+ final I1 a;
+ }
+
+ class B {
+ final I2 a;
+ }
+
+ class C1 extends A implements B {
+ I3 get a => null;
+ }
+
+ class C2 extends A implements B {
+ /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a => null;
+ }
+ '''
+ });
+
+ testChecker(
+ 'infer from RHS only if it wont conflict with overridden fields', {
+ '/main.dart': '''
+ class A {
+ var x;
+ }
+
+ class B extends A {
+ var x = 2;
+ }
+
+ foo() {
+ String y = /*info:DynamicCast*/new B().x;
+ int z = /*info:DynamicCast*/new B().x;
+ }
+ '''
+ });
+
+ testChecker(
+ 'infer from RHS only if it wont conflict with overridden fields 2', {
+ '/main.dart': '''
+ class A {
+ final x;
+ }
+
+ class B extends A {
+ final x = 2;
+ }
+
+ foo() {
+ String y = /*severe:StaticTypeError*/new B().x;
+ int z = new B().x;
+ }
+ '''
+ });
+
+ testChecker('infer correctly on multiple variables declared together', {
+ '/main.dart': '''
+ class A {
+ var x, y = 2, z = "hi";
+ }
+
+ class B extends A {
+ var x = 2, y = 3, z, w = 2;
+ }
+
+ foo() {
+ String s;
+ int i;
+
+ s = /*info:DynamicCast*/new B().x;
+ s = /*severe:StaticTypeError*/new B().y;
+ s = new B().z;
+ s = /*severe:StaticTypeError*/new B().w;
+
+ i = /*info:DynamicCast*/new B().x;
+ i = new B().y;
+ i = /*severe:StaticTypeError*/new B().z;
+ i = new B().w;
+ }
+ '''
+ });
+
+ testChecker('infer consts transitively', {
+ '/b.dart': '''
+ const b1 = 2;
+ ''',
+ '/a.dart': '''
+ import 'main.dart';
+ import 'b.dart';
+ const a1 = m2;
+ const a2 = b1;
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+ const m1 = a1;
+ const m2 = a2;
+
+ foo() {
+ int i;
+ i = m1;
+ }
+ '''
+ });
+
+ testChecker('infer statics transitively', {
+ '/b.dart': '''
+ final b1 = 2;
+ ''',
+ '/a.dart': '''
+ import 'main.dart';
+ import 'b.dart';
+ final a1 = m2;
+ class A {
+ static final a2 = b1;
+ }
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+ final m1 = a1;
+ final m2 = A.a2;
+
+ foo() {
+ int i;
+ i = m1;
+ }
+ '''
+ });
+
+ testChecker('infer statics transitively 2', {
+ '/main.dart': '''
+ const x1 = 1;
+ final x2 = 1;
+ final y1 = x1;
+ final y2 = x2;
+
+ foo() {
+ int i;
+ i = y1;
+ i = y2;
+ }
+ '''
+ });
+
+ testChecker('infer statics transitively 3', {
+ '/a.dart': '''
+ const a1 = 3;
+ const a2 = 4;
+ class A {
+ a3;
+ }
+ ''',
+ '/main.dart': '''
+ import 'a.dart' show a1, A;
+ import 'a.dart' as p show a2, A;
+ const t1 = 1;
+ const t2 = t1;
+ const t3 = a1;
+ const t4 = p.a2;
+ const t5 = A.a3;
+ const t6 = p.A.a3;
+
+ foo() {
+ int i;
+ i = t1;
+ i = t2;
+ i = t3;
+ i = t4;
+ }
+ '''
+ });
+
+ testChecker('infer statics with method invocations', {
+ '/a.dart': '''
+ m3(String a, String b, [a1,a2]) {}
+ ''',
+ '/main.dart': '''
+ import 'a.dart';
+ class T {
+ static final T foo = m1(m2(m3('', '')));
+ static T m1(String m) { return null; }
+ static String m2(e) { return ''; }
+ }
+
+
+ '''
+ });
+
+ testChecker('downwards inference: miscellaneous', {
+ '/main.dart': '''
+ typedef (T x);
+ class A<T> {
+ Function2<T> x;
+ A(this.x);
+ }
+ void main() {
+ { // Variables, nested literals
+ var x = "hello";
+ var y = 3;
+ void f(List<Map<int, String>> l) {};
+ f(/*info:InferredTypeLiteral*/[{y: x}]);
+ }
+ {
+ int f(int x) {};
+ A<int> a = /*info:InferredTypeAllocation*/new A(f);
+ }
+ }
+ '''
+ });
+
+ group('downwards inference on instance creations', () {
+ String info = 'info:InferredTypeAllocation';
+ String code = '''
+ class A<S, T> {
+ S x;
+ T y;
+ A(this.x, this.y);
+ A.named(this.x, this.y);
+ }
+
+ class B<S, T> extends A<T, S> {
+ B(S y, T x) : super(x, y);
+ B.named(S y, T x) : super.named(x, y);
+ }
+
+ class C<S> extends B<S, S> {
+ C(S a) : super(a, a);
+ C.named(S a) : super.named(a, a);
+ }
+
+ class D<S, T> extends B<T, int> {
+ D(T a) : super(a, 3);
+ D.named(T a) : super.named(a, 3);
+ }
+
+ class E<S, T> extends A<C<S>, T> {
+ E(T a) : super(null, a);
+ }
+
+ class F<S, T> extends A<S, T> {
+ F(S x, T y, {List<S> a, List<T> b}) : super(x, y);
+ F.named(S x, T y, [S a, T b]) : super(a, b);
+ }
+
+ void main() {
+ {
+ A<int, String> a0 = /*$info*/new A(3, "hello");
+ A<int, String> a1 = /*$info*/new A.named(3, "hello");
+ A<int, String> a2 = new A<int, String>(3, "hello");
+ A<int, String> a3 = new A<int, String>.named(3, "hello");
+ A<int, String> a4 = /*severe:StaticTypeError*/new A<int, dynamic>(3, "hello");
+ A<int, String> a5 = /*severe:StaticTypeError*/new A<dynamic, dynamic>.named(3, "hello");
+ }
+ {
+ A<int, String> a0 = /*severe:StaticTypeError*/new A("hello", 3);
+ A<int, String> a1 = /*severe:StaticTypeError*/new A.named("hello", 3);
+ }
+ {
+ A<int, String> a0 = /*$info*/new B("hello", 3);
+ A<int, String> a1 = /*$info*/new B.named("hello", 3);
+ A<int, String> a2 = new B<String, int>("hello", 3);
+ A<int, String> a3 = new B<String, int>.named("hello", 3);
+ A<int, String> a4 = /*severe:StaticTypeError*/new B<String, dynamic>("hello", 3);
+ A<int, String> a5 = /*severe:StaticTypeError*/new B<dynamic, dynamic>.named("hello", 3);
+ }
+ {
+ A<int, String> a0 = /*severe:StaticTypeError*/new B(3, "hello");
+ A<int, String> a1 = /*severe:StaticTypeError*/new B.named(3, "hello");
+ }
+ {
+ A<int, int> a0 = /*$info*/new C(3);
+ A<int, int> a1 = /*$info*/new C.named(3);
+ A<int, int> a2 = new C<int>(3);
+ A<int, int> a3 = new C<int>.named(3);
+ A<int, int> a4 = /*severe:StaticTypeError*/new C<dynamic>(3);
+ A<int, int> a5 = /*severe:StaticTypeError*/new C<dynamic>.named(3);
+ }
+ {
+ A<int, int> a0 = /*severe:StaticTypeError*/new C("hello");
+ A<int, int> a1 = /*severe:StaticTypeError*/new C.named("hello");
+ }
+ {
+ A<int, String> a0 = /*$info*/new D("hello");
+ A<int, String> a1 = /*$info*/new D.named("hello");
+ A<int, String> a2 = new D<int, String>("hello");
+ A<int, String> a3 = new D<String, String>.named("hello");
+ A<int, String> a4 = /*severe:StaticTypeError*/new D<num, dynamic>("hello");
+ A<int, String> a5 = /*severe:StaticTypeError*/new D<dynamic, dynamic>.named("hello");
+ }
+ {
+ A<int, String> a0 = /*severe:StaticTypeError*/new D(3);
+ A<int, String> a1 = /*severe:StaticTypeError*/new D.named(3);
+ }
+ { // Currently we only allow variable constraints. Test that we reject.
+ A<C<int>, String> a0 = /*severe:StaticTypeError*/new E("hello");
+ }
+ { // Check named and optional arguments
+ A<int, String> a0 = /*$info*/new F(3, "hello", a: [3], b: ["hello"]);
+ A<int, String> a1 = /*severe:StaticTypeError*/new F(3, "hello", a: ["hello"], b:[3]);
+ A<int, String> a2 = /*$info*/new F.named(3, "hello", 3, "hello");
+ A<int, String> a3 = /*$info*/new F.named(3, "hello");
+ A<int, String> a4 = /*severe:StaticTypeError*/new F.named(3, "hello", "hello", 3);
+ A<int, String> a5 = /*severe:StaticTypeError*/new F.named(3, "hello", "hello");
+ }
+ }
+ ''';
+ testChecker('infer downwards', {'/main.dart': code});
+ });
+
+ group('downwards inference on list literals', () {
+ String info = "info:InferredTypeLiteral";
+ String code = '''
+ void foo([List<String> list1 = /*$info*/const [],
+ List<String> list2 = /*severe:StaticTypeError*/const [42]]) {
+ }
+
+ void main() {
+ {
+ List<int> l0 = /*$info*/[];
+ List<int> l1 = /*$info*/[3];
+ List<int> l2 = /*severe:StaticTypeError*/["hello"];
+ List<int> l3 = /*severe:StaticTypeError*/["hello", 3];
+ }
+ {
+ List<dynamic> l0 = [];
+ List<dynamic> l1 = [3];
+ List<dynamic> l2 = ["hello"];
+ List<dynamic> l3 = ["hello", 3];
+ }
+ {
+ List<int> l0 = /*severe:StaticTypeError*/<num>[];
+ List<int> l1 = /*severe:StaticTypeError*/<num>[3];
+ List<int> l2 = /*severe:StaticTypeError*/<num>[/*severe:StaticTypeError*/"hello"];
+ List<int> l3 = /*severe:StaticTypeError*/<num>[/*severe:StaticTypeError*/"hello", 3];
+ }
+ {
+ Iterable<int> i0 = /*$info*/[];
+ Iterable<int> i1 = /*$info*/[3];
+ Iterable<int> i2 = /*severe:StaticTypeError*/["hello"];
+ Iterable<int> i3 = /*severe:StaticTypeError*/["hello", 3];
+ }
+ {
+ const List<int> c0 = /*$info*/const [];
+ const List<int> c1 = /*$info*/const [3];
+ const List<int> c2 = /*severe:StaticTypeError*/const ["hello"];
+ const List<int> c3 = /*severe:StaticTypeError*/const ["hello", 3];
+ }
+ }
+ ''';
+ testChecker('infer downwards', {'/main.dart': code});
+ });
+
+ group('downwards inference on function arguments', () {
+ String info = "info:InferredTypeLiteral";
+ String code = '''
+ void f0(List<int> a) {};
+ void f1({List<int> a}) {};
+ void f2(Iterable<int> a) {};
+ void f3(Iterable<Iterable<int>> a) {};
+ void f4({Iterable<Iterable<int>> a}) {};
+ void main() {
+ f0(/*$info*/[]);
+ f0(/*$info*/[3]);
+ f0(/*severe:StaticTypeError*/["hello"]);
+ f0(/*severe:StaticTypeError*/["hello", 3]);
+
+ f1(a: /*$info*/[]);
+ f1(a: /*$info*/[3]);
+ f1(a: /*severe:StaticTypeError*/["hello"]);
+ f1(a: /*severe:StaticTypeError*/["hello", 3]);
+
+ f2(/*$info*/[]);
+ f2(/*$info*/[3]);
+ f2(/*severe:StaticTypeError*/["hello"]);
+ f2(/*severe:StaticTypeError*/["hello", 3]);
+
+ f3(/*$info*/[]);
+ f3(/*$info*/[[3]]);
+ f3(/*severe:StaticTypeError*/[["hello"]]);
+ f3(/*severe:StaticTypeError*/[["hello"], [3]]);
+
+ f4(a: /*$info*/[]);
+ f4(a: /*$info*/[[3]]);
+ f4(a: /*severe:StaticTypeError*/[["hello"]]);
+ f4(a: /*severe:StaticTypeError*/[["hello"], [3]]);
+ }
+ ''';
+ testChecker('infer downwards', {'/main.dart': code});
+ });
+
+ group('downwards inference on map literals', () {
+ String info = "info:InferredTypeLiteral";
+ String code = '''
+ void foo([Map<int, String> m1 = /*$info*/const {1: "hello"},
+ Map<int, String> m1 = /*severe:StaticTypeError*/const {"hello": "world"}]) {
+ }
+ void main() {
+ {
+ Map<int, String> l0 = /*$info*/{};
+ Map<int, String> l1 = /*$info*/{3: "hello"};
+ Map<int, String> l2 = /*severe:StaticTypeError*/{"hello": "hello"};
+ Map<int, String> l3 = /*severe:StaticTypeError*/{3: 3};
+ Map<int, String> l4 = /*severe:StaticTypeError*/{3:"hello", "hello": 3};
+ }
+ {
+ Map<dynamic, dynamic> l0 = {};
+ Map<dynamic, dynamic> l1 = {3: "hello"};
+ Map<dynamic, dynamic> l2 = {"hello": "hello"};
+ Map<dynamic, dynamic> l3 = {3: 3};
+ Map<dynamic, dynamic> l4 = {3:"hello", "hello": 3};
+ }
+ {
+ Map<dynamic, String> l0 = /*$info*/{};
+ Map<dynamic, String> l1 = /*$info*/{3: "hello"};
+ Map<dynamic, String> l2 = /*$info*/{"hello": "hello"};
+ Map<dynamic, String> l3 = /*severe:StaticTypeError*/{3: 3};
+ Map<dynamic, String> l4 = /*severe:StaticTypeError*/{3:"hello", "hello": 3};
+ }
+ {
+ Map<int, dynamic> l0 = /*$info*/{};
+ Map<int, dynamic> l1 = /*$info*/{3: "hello"};
+ Map<int, dynamic> l2 = /*severe:StaticTypeError*/{"hello": "hello"};
+ Map<int, dynamic> l3 = /*$info*/{3: 3};
+ Map<int, dynamic> l3 = /*severe:StaticTypeError*/{3:"hello", "hello": 3};
+ }
+ {
+ Map<int, String> l0 = /*severe:StaticTypeError*/<num, dynamic>{};
+ Map<int, String> l1 = /*severe:StaticTypeError*/<num, dynamic>{3: "hello"};
+ Map<int, String> l3 = /*severe:StaticTypeError*/<num, dynamic>{3: 3};
+ }
+ {
+ const Map<int, String> l0 = /*$info*/const {};
+ const Map<int, String> l1 = /*$info*/const {3: "hello"};
+ const Map<int, String> l2 = /*severe:StaticTypeError*/const {"hello": "hello"};
+ const Map<int, String> l3 = /*severe:StaticTypeError*/const {3: 3};
+ const Map<int, String> l4 = /*severe:StaticTypeError*/const {3:"hello", "hello": 3};
+ }
+ }
+ ''';
+ testChecker('infer downwards', {'/main.dart': code});
+ });
+
+ testChecker('downwards inference on function expressions', {
+ '/main.dart': '''
+ typedef T Function2<S, T>(S x);
+
+ void main () {
+ {
+ Function2<int, String> l0 = (int x) => null;
+ Function2<int, String> l1 = (int x) => "hello";
+ Function2<int, String> l2 = /*severe:StaticTypeError*/(String x) => "hello";
+ Function2<int, String> l3 = /*severe:StaticTypeError*/(int x) => 3;
+ Function2<int, String> l4 = /*warning:UninferredClosure should be severe:StaticTypeError*/(int x) {return 3};
+ }
+ {
+ Function2<int, String> l0 = /*info:InferredTypeClosure*/(x) => null;
+ Function2<int, String> l1 = /*info:InferredTypeClosure*/(x) => "hello";
+ Function2<int, String> l2 = /*severe:StaticTypeError*/(x) => 3;
+ Function2<int, String> l3 = /*warning:UninferredClosure should be severe:StaticTypeError*/(x) {return 3};
+ }
+ {
+ Function2<int, List<String>> l0 = (int x) => null;
+ Function2<int, List<String>> l1 = /*info:InferredTypeClosure*/(int x) => ["hello"];
+ Function2<int, List<String>> l2 = /*severe:StaticTypeError*/(String x) => ["hello"];
+ Function2<int, List<String>> l3 = /*warning:UninferredClosure should be severe:StaticTypeError*/(int x) => [3];
+ Function2<int, List<String>> l4 = /*warning:UninferredClosure should be severe:StaticTypeError*/(int x) {return [3]};
+ }
+ {
+ Function2<int, int> l0 = /*info:InferredTypeClosure*/(x) => x;
+ Function2<int, int> l1 = /*info:InferredTypeClosure*/(x) => /*info:DynamicInvoke should be pass*/x+1;
+ Function2<int, String> l2 = /*info:InferredTypeClosure should be severe:StaticTypeError*/(x) => x;
+ Function2<int, String> l3 = /*info:InferredTypeClosure should be severe:StaticTypeError*/(x) => /*info:DynamicInvoke should be pass*/x.substring(3);
+ Function2<String, String> l4 = /*info:InferredTypeClosure*/(x) => /*info:DynamicInvoke should be pass*/x.substring(3);
+ }
+ }
+ '''
+ });
+
+ testChecker('inferred initializing formal checks default value', {
+ '/main.dart': '''
+ class Foo {
+ var x = 1;
+ Foo([this.x = /*severe:StaticTypeError*/"1"]);
+ }'''
+ });
+
+ group('quasi-generics', () {
+ testChecker('dart:math min/max', {
+ '/main.dart': '''
+ import 'dart:math';
+
+ void printInt(int x) => print(x);
+ void printDouble(double x) => print(x);
+
+ num myMax(num x, num y) => max(x, y);
+
+ main() {
+ // Okay if static types match.
+ printInt(max(1, 2));
+ printInt(min(1, 2));
+ printDouble(max(1.0, 2.0));
+ printDouble(min(1.0, 2.0));
+
+ // No help for user-defined functions from num->num->num.
+ printInt(/*info:DownCastImplicit*/myMax(1, 2));
+ printInt(myMax(1, 2) as int);
+
+ // Mixing int and double means return type is num.
+ printInt(/*info:DownCastImplicit*/max(1, 2.0));
+ printInt(/*info:DownCastImplicit*/min(1, 2.0));
+ printDouble(/*info:DownCastImplicit*/max(1, 2.0));
+ printDouble(/*info:DownCastImplicit*/min(1, 2.0));
+
+ // Types other than int and double are not accepted.
+ printInt(
+ /*info:DownCastImplicit*/min(
+ /*severe:StaticTypeError*/"hi",
+ /*severe:StaticTypeError*/"there"));
+ }
+ '''
+ });
+
+ testChecker('Iterable and Future', {
+ '/main.dart': '''
+ import 'dart:async';
+
+ Future<int> make(int x) => (/*info:InferredTypeAllocation*/new Future(() => x));
+
+ main() {
+ Iterable<Future<int>> list = <int>[1, 2, 3].map(make);
+ Future<List<int>> results = Future.wait(list);
+ Future<String> results2 = results.then((List<int> list)
+ => list.fold('', (String x, int y) => x + y.toString()));
+ }
+ '''
+ });
+ });
+}
diff --git a/packages/analyzer/test/src/task/strong/strong_test_helper.dart b/packages/analyzer/test/src/task/strong/strong_test_helper.dart
new file mode 100644
index 0000000..330801f
--- /dev/null
+++ b/packages/analyzer/test/src/task/strong/strong_test_helper.dart
@@ -0,0 +1,500 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// TODO(jmesserly): this file needs to be refactored, it's a port from
+// package:dev_compiler's tests
+library test.src.task.strong.strong_test_helper;
+
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/memory_file_system.dart';
+import 'package:analyzer/src/context/context.dart' show SdkAnalysisContext;
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart' hide SdkAnalysisContext;
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/task/strong/checker.dart';
+import 'package:analyzer/src/task/strong/rules.dart';
+import 'package:logging/logging.dart'; // TODO(jmesserly): remove
+import 'package:source_span/source_span.dart'; // TODO(jmesserly): remove
+import 'package:unittest/unittest.dart';
+
+/// Run the checker on a program with files contents as indicated in
+/// [testFiles].
+///
+/// This function makes several assumptions to make it easier to describe error
+/// expectations:
+///
+/// * a file named `/main.dart` exists in [testFiles].
+/// * all expected failures are listed in the source code using comments
+/// immediately in front of the AST node that should contain the error.
+/// * errors are formatted as a token `level:Type`, where `level` is the
+/// logging level were the error would be reported at, and `Type` is the
+/// concrete subclass of [StaticInfo] that denotes the error.
+///
+/// For example, to check that an assignment produces a warning about a boxing
+/// conversion, you can describe the test as follows:
+///
+/// testChecker({
+/// '/main.dart': '''
+/// testMethod() {
+/// dynamic x = /*warning:Box*/3;
+/// }
+/// '''
+/// });
+///
+void testChecker(String name, Map<String, String> testFiles) {
+ test(name, () {
+ expect(testFiles.containsKey('/main.dart'), isTrue,
+ reason: '`/main.dart` is missing in testFiles');
+
+ var provider = new MemoryResourceProvider();
+ testFiles.forEach((key, value) {
+ var scheme = 'package:';
+ if (key.startsWith(scheme)) {
+ key = '/packages/${key.substring(scheme.length)}';
+ }
+ provider.newFile(key, value);
+ });
+ var uriResolver = new TestUriResolver(provider);
+ // Enable task model strong mode
+ AnalysisEngine.instance.useTaskModel = true;
+ var context = AnalysisEngine.instance.createAnalysisContext();
+ context.analysisOptions.strongMode = true;
+
+ context.sourceFactory = new SourceFactory([
+ new MockDartSdk(mockSdkSources, reportMissing: true).resolver,
+ uriResolver
+ ]);
+
+ // Run the checker on /main.dart.
+ Source mainSource = uriResolver.resolveAbsolute(new Uri.file('/main.dart'));
+ var initialLibrary =
+ context.resolveCompilationUnit2(mainSource, mainSource);
+
+ var collector = new _ErrorCollector();
+ var checker = new CodeChecker(
+ new TypeRules(context.typeProvider), collector,
+ hints: true);
+
+ // Extract expectations from the comments in the test files, and
+ // check that all errors we emit are included in the expected map.
+ var allLibraries = reachableLibraries(initialLibrary.element.library);
+ for (var lib in allLibraries) {
+ for (var unit in lib.units) {
+ var errors = <AnalysisError>[];
+ collector.errors = errors;
+
+ var source = unit.source;
+ if (source.uri.scheme == 'dart') continue;
+
+ var librarySource = context.getLibrariesContaining(source).single;
+ var resolved = context.resolveCompilationUnit2(source, librarySource);
+ checker.visitCompilationUnit(resolved);
+
+ new _ExpectedErrorVisitor(errors).validate(resolved);
+ }
+ }
+ });
+}
+
+class _ErrorCollector implements AnalysisErrorListener {
+ List<AnalysisError> errors;
+ final bool hints;
+ _ErrorCollector({this.hints: true});
+
+ void onError(AnalysisError error) {
+ // Unless DDC hints are requested, filter them out.
+ var HINT = ErrorSeverity.INFO.ordinal;
+ if (hints || error.errorCode.errorSeverity.ordinal > HINT) {
+ errors.add(error);
+ }
+ }
+}
+
+class TestUriResolver extends ResourceUriResolver {
+ final MemoryResourceProvider provider;
+ TestUriResolver(provider)
+ : provider = provider,
+ super(provider);
+
+ @override
+ Source resolveAbsolute(Uri uri, [Uri actualUri]) {
+ if (uri.scheme == 'package') {
+ return (provider.getResource('/packages/' + uri.path) as File)
+ .createSource(uri);
+ }
+ return super.resolveAbsolute(uri, actualUri);
+ }
+}
+
+class _ExpectedErrorVisitor extends UnifyingAstVisitor {
+ final Set<AnalysisError> _actualErrors;
+ CompilationUnit _unit;
+ String _unitSourceCode;
+
+ _ExpectedErrorVisitor(List<AnalysisError> actualErrors)
+ : _actualErrors = new Set.from(actualErrors);
+
+ validate(CompilationUnit unit) {
+ _unit = unit;
+ // This reads the file. Only safe because tests use MemoryFileSystem.
+ _unitSourceCode = unit.element.source.contents.data;
+
+ // Visit the compilation unit.
+ unit.accept(this);
+
+ if (_actualErrors.isNotEmpty) {
+ var actualMsgs = _actualErrors.map(_formatActualError).join('\n');
+ fail('Unexpected errors reported by checker:\n\n$actualMsgs');
+ }
+ }
+
+ visitNode(AstNode node) {
+ var token = node.beginToken;
+ var comment = token.precedingComments;
+ // Use error marker found in an immediately preceding comment,
+ // and attach it to the outermost expression that starts at that token.
+ if (comment != null) {
+ while (comment.next != null) {
+ comment = comment.next;
+ }
+ if (comment.end == token.offset && node.parent.beginToken != token) {
+ var commentText = '$comment';
+ var start = commentText.lastIndexOf('/*');
+ var end = commentText.lastIndexOf('*/');
+ if (start != -1 && end != -1) {
+ expect(start, lessThan(end));
+ var errors = commentText.substring(start + 2, end).split(',');
+ var expectations =
+ errors.map(_ErrorExpectation.parse).where((x) => x != null);
+
+ for (var e in expectations) _expectError(node, e);
+ }
+ }
+ }
+ return super.visitNode(node);
+ }
+
+ void _expectError(AstNode node, _ErrorExpectation expected) {
+ // See if we can find the expected error in our actual errors
+ for (var actual in _actualErrors) {
+ if (actual.offset == node.offset && actual.length == node.length) {
+ var actualMsg = _formatActualError(actual);
+ expect(_actualErrorLevel(actual), expected.level,
+ reason: 'expected different error code at:\n\n$actualMsg');
+ expect(errorCodeName(actual.errorCode), expected.typeName,
+ reason: 'expected different error type at:\n\n$actualMsg');
+
+ // We found it. Stop the search.
+ _actualErrors.remove(actual);
+ return;
+ }
+ }
+
+ var span = _createSpan(node.offset, node.length);
+ var levelName = expected.level.name.toLowerCase();
+ var msg = span.message(expected.typeName, color: colorOf(levelName));
+ fail('expected error was not reported at:\n\n$levelName: $msg');
+ }
+
+ Level _actualErrorLevel(AnalysisError actual) {
+ return const <ErrorSeverity, Level>{
+ ErrorSeverity.ERROR: Level.SEVERE,
+ ErrorSeverity.WARNING: Level.WARNING,
+ ErrorSeverity.INFO: Level.INFO
+ }[actual.errorCode.errorSeverity];
+ }
+
+ String _formatActualError(AnalysisError actual) {
+ var span = _createSpan(actual.offset, actual.length);
+ var levelName = _actualErrorLevel(actual).name.toLowerCase();
+ var msg = span.message(actual.message, color: colorOf(levelName));
+ return '$levelName: [${errorCodeName(actual.errorCode)}] $msg';
+ }
+
+ SourceSpan _createSpan(int offset, int len) {
+ return createSpanHelper(_unit.lineInfo, offset, offset + len,
+ _unit.element.source, _unitSourceCode);
+ }
+}
+
+SourceLocation locationForOffset(LineInfo lineInfo, Uri uri, int offset) {
+ var loc = lineInfo.getLocation(offset);
+ return new SourceLocation(offset,
+ sourceUrl: uri, line: loc.lineNumber - 1, column: loc.columnNumber - 1);
+}
+
+SourceSpanWithContext createSpanHelper(
+ LineInfo lineInfo, int start, int end, Source source, String content) {
+ var startLoc = locationForOffset(lineInfo, source.uri, start);
+ var endLoc = locationForOffset(lineInfo, source.uri, end);
+
+ var lineStart = startLoc.offset - startLoc.column;
+ // Find the end of the line. This is not exposed directly on LineInfo, but
+ // we can find it pretty easily.
+ // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get
+ // some help from the LineInfo API.
+ int lineEnd = endLoc.offset;
+ int lineNum = lineInfo.getLocation(lineEnd).lineNumber;
+ while (lineEnd < content.length &&
+ lineInfo.getLocation(++lineEnd).lineNumber == lineNum);
+
+ var text = content.substring(start, end);
+ var lineText = content.substring(lineStart, lineEnd);
+ return new SourceSpanWithContext(startLoc, endLoc, text, lineText);
+}
+
+/// Describes an expected message that should be produced by the checker.
+class _ErrorExpectation {
+ final Level level;
+ final String typeName;
+ _ErrorExpectation(this.level, this.typeName);
+
+ static _ErrorExpectation _parse(String descriptor) {
+ var tokens = descriptor.split(':');
+ expect(tokens.length, 2, reason: 'invalid error descriptor');
+ var name = tokens[0].toUpperCase();
+ var typeName = tokens[1];
+
+ var level =
+ Level.LEVELS.firstWhere((l) => l.name == name, orElse: () => null);
+ expect(level, isNotNull,
+ reason: 'invalid level in error descriptor: `${tokens[0]}`');
+ expect(typeName, isNotNull,
+ reason: 'invalid type in error descriptor: ${tokens[1]}');
+ return new _ErrorExpectation(level, typeName);
+ }
+
+ static _ErrorExpectation parse(String descriptor) {
+ descriptor = descriptor.trim();
+ var tokens = descriptor.split(' ');
+ if (tokens.length == 1) return _parse(tokens[0]);
+ expect(tokens.length, 4, reason: 'invalid error descriptor');
+ expect(tokens[1], "should", reason: 'invalid error descriptor');
+ expect(tokens[2], "be", reason: 'invalid error descriptor');
+ if (tokens[0] == "pass") return null;
+ // TODO(leafp) For now, we just use whatever the current expectation is,
+ // eventually we could do more automated reporting here.
+ return _parse(tokens[0]);
+ }
+
+ String toString() => '$level $typeName';
+}
+
+/// Dart SDK which contains a mock implementation of the SDK libraries. May be
+/// used to speed up execution when most of the core libraries is not needed.
+class MockDartSdk implements DartSdk {
+ final Map<Uri, _MockSdkSource> _sources = {};
+ final bool reportMissing;
+ final Map<String, SdkLibrary> _libs = {};
+ final String sdkVersion = '0';
+ List<String> get uris => _sources.keys.map((uri) => '$uri').toList();
+ final AnalysisContext context = new SdkAnalysisContext();
+ DartUriResolver _resolver;
+ DartUriResolver get resolver => _resolver;
+
+ MockDartSdk(Map<String, String> sources, {this.reportMissing}) {
+ sources.forEach((uriString, contents) {
+ var uri = Uri.parse(uriString);
+ _sources[uri] = new _MockSdkSource(uri, contents);
+ _libs[uriString] = new SdkLibraryImpl(uri.path)
+ ..setDart2JsLibrary()
+ ..setVmLibrary();
+ });
+ _resolver = new DartUriResolver(this);
+ context.sourceFactory = new SourceFactory([_resolver]);
+ }
+
+ List<SdkLibrary> get sdkLibraries => _libs.values.toList();
+ SdkLibrary getSdkLibrary(String dartUri) => _libs[dartUri];
+ Source mapDartUri(String dartUri) => _getSource(Uri.parse(dartUri));
+
+ Source fromEncoding(UriKind kind, Uri uri) {
+ if (kind != UriKind.DART_URI) {
+ throw new UnsupportedError('expected dart: uri kind, got $kind.');
+ }
+ return _getSource(uri);
+ }
+
+ Source _getSource(Uri uri) {
+ var src = _sources[uri];
+ if (src == null) {
+ if (reportMissing) print('warning: missing mock for $uri.');
+ _sources[uri] =
+ src = new _MockSdkSource(uri, 'library dart.${uri.path};');
+ }
+ return src;
+ }
+
+ @override
+ Source fromFileUri(Uri uri) {
+ throw new UnsupportedError('MockDartSdk.fromFileUri');
+ }
+}
+
+class _MockSdkSource implements Source {
+ /// Absolute URI which this source can be imported from.
+ final Uri uri;
+ final String _contents;
+
+ _MockSdkSource(this.uri, this._contents);
+
+ bool exists() => true;
+
+ int get hashCode => uri.hashCode;
+
+ final int modificationStamp = 1;
+
+ TimestampedData<String> get contents =>
+ new TimestampedData(modificationStamp, _contents);
+
+ String get encoding => "${uriKind.encoding}$uri";
+
+ Source get source => this;
+
+ String get fullName => shortName;
+
+ String get shortName => uri.path;
+
+ UriKind get uriKind => UriKind.DART_URI;
+
+ bool get isInSystemLibrary => true;
+
+ Source resolveRelative(Uri relativeUri) =>
+ throw new UnsupportedError('not expecting relative urls in dart: mocks');
+
+ Uri resolveRelativeUri(Uri relativeUri) =>
+ throw new UnsupportedError('not expecting relative urls in dart: mocks');
+}
+
+/// Sample mock SDK sources.
+final Map<String, String> mockSdkSources = {
+ // The list of types below is derived from:
+ // * types we use via our smoke queries, including HtmlElement and
+ // types from `_typeHandlers` (deserialize.dart)
+ // * types that are used internally by the resolver (see
+ // _initializeFrom in resolver.dart).
+ 'dart:core': '''
+ library dart.core;
+
+ void print(Object o) {}
+
+ class Object {
+ int get hashCode {}
+ Type get runtimeType {}
+ String toString(){}
+ bool ==(other){}
+ }
+ class Function {}
+ class StackTrace {}
+ class Symbol {}
+ class Type {}
+
+ class String {
+ String operator +(String other) {}
+ }
+ class bool {}
+ class num {
+ num operator +(num other) {}
+ }
+ class int extends num {
+ bool operator<(num other) {}
+ int operator-() {}
+ }
+ class double extends num {}
+ class DateTime {}
+ class Null {}
+
+ class Deprecated {
+ final String expires;
+ const Deprecated(this.expires);
+ }
+ const Object deprecated = const Deprecated("next release");
+ class _Override { const _Override(); }
+ const Object override = const _Override();
+ class _Proxy { const _Proxy(); }
+ const Object proxy = const _Proxy();
+
+ class Iterable<E> {
+ fold(initialValue, combine(previousValue, E element)) {}
+ Iterable map(f(E element)) {}
+ }
+ class List<E> implements Iterable<E> {
+ List([int length]);
+ List.filled(int length, E fill);
+ }
+ class Map<K, V> {
+ Iterable<K> get keys {}
+ }
+ ''',
+ 'dart:async': '''
+ class Future<T> {
+ Future(computation()) {}
+ Future.value(T t) {}
+ Future then(onValue(T value)) {}
+ static Future<List> wait(Iterable<Future> futures) {}
+ }
+ class Stream<T> {}
+ ''',
+ 'dart:html': '''
+ library dart.html;
+ class HtmlElement {}
+ ''',
+ 'dart:math': '''
+ library dart.math;
+ class Random {
+ bool nextBool() {}
+ }
+ num min(num x, num y) {}
+ num max(num x, num y) {}
+ ''',
+};
+
+/// Returns all libraries transitively imported or exported from [start].
+List<LibraryElement> reachableLibraries(LibraryElement start) {
+ var results = <LibraryElement>[];
+ var seen = new Set();
+ void find(LibraryElement lib) {
+ if (seen.contains(lib)) return;
+ seen.add(lib);
+ results.add(lib);
+ lib.importedLibraries.forEach(find);
+ lib.exportedLibraries.forEach(find);
+ }
+ find(start);
+ return results;
+}
+
+String errorCodeName(ErrorCode errorCode) {
+ var name = errorCode.name;
+ final prefix = 'dev_compiler.';
+ if (name.startsWith(prefix)) {
+ return name.substring(prefix.length);
+ } else {
+ // TODO(jmesserly): this is for backwards compat, but not sure it's very
+ // useful to log this.
+ return 'AnalyzerMessage';
+ }
+}
+
+/// Returns an ANSII color escape sequence corresponding to [levelName]. Colors
+/// are defined for: severe, error, warning, or info. Returns null if the level
+/// name is not recognized.
+String colorOf(String levelName) {
+ levelName = levelName.toLowerCase();
+ if (levelName == 'shout' || levelName == 'severe' || levelName == 'error') {
+ return _RED_COLOR;
+ }
+ if (levelName == 'warning') return _MAGENTA_COLOR;
+ if (levelName == 'info') return _CYAN_COLOR;
+ return null;
+}
+
+const String _RED_COLOR = '\u001b[31m';
+const String _MAGENTA_COLOR = '\u001b[35m';
+const String _CYAN_COLOR = '\u001b[36m';
+const String GREEN_COLOR = '\u001b[32m';
+const String NO_COLOR = '\u001b[0m';
diff --git a/packages/analyzer/tool/generate_files b/packages/analyzer/tool/generate_files
new file mode 100755
index 0000000..4d0b220
--- /dev/null
+++ b/packages/analyzer/tool/generate_files
@@ -0,0 +1,50 @@
+#!/bin/bash
+# Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+#
+# This script executes code generation tools found in the analyzer
+# "tool" directory.
+
+set -e
+
+function follow_links() {
+ file="$1"
+ while [ -h "$file" ]; do
+ # On Mac OS, readlink -f doesn't work.
+ file="$(readlink "$file")"
+ done
+ echo "$file"
+}
+
+# Unlike $0, $BASH_SOURCE points to the absolute path of this file.
+PROG_NAME="$(follow_links "$BASH_SOURCE")"
+
+SCRIPT_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
+
+ROOT_DIR="$(cd "${SCRIPT_DIR}/../../.." ; pwd -P)"
+
+BIN_DIR="${ROOT_DIR}/sdk/bin"
+
+if [ -z "$DART_CONFIGURATION" ];
+then
+ DART_CONFIGURATION="ReleaseIA32"
+fi
+
+if [[ `uname` == 'Darwin' ]];
+then
+ BUILD_DIR="${ROOT_DIR}/xcodebuild/$DART_CONFIGURATION"
+else
+ BUILD_DIR="${ROOT_DIR}/out/$DART_CONFIGURATION"
+fi
+
+PKG_DIR="${BUILD_DIR}/packages"
+
+DART="${BIN_DIR}/dart"
+
+declare -a VM_OPTIONS
+VM_OPTIONS+=("--checked")
+VM_OPTIONS+=("--package-root=${PKG_DIR}")
+
+cd "${SCRIPT_DIR}"
+"${DART}" "${VM_OPTIONS[@]}" "task_dependency_graph/generate.dart"
diff --git a/packages/analyzer/tool/task_dependency_graph.dart b/packages/analyzer/tool/task_dependency_graph.dart
deleted file mode 100644
index 5da2976..0000000
--- a/packages/analyzer/tool/task_dependency_graph.dart
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/**
- * This file contains code to output a description of tasks and their
- * dependencies in ".dot" format. Prior to running, the user should run "pub
- * get" in the analyzer directory to ensure that a "packages" folder exists.
- *
- * The ".dot" file is output to standard out. To convert it to a pdf, store it
- * in a file (e.g. "tasks.dot"), and post-process it with
- * "dot tasks.dart -Tpdf -O".
- *
- * TODO(paulberry):
- * - Add general.dart and html.dart for completeness.
- * - Use Graphviz's "record" feature to produce more compact output
- * (http://www.graphviz.org/content/node-shapes#record)
- * - Produce a warning if a result descriptor is found which isn't the output
- * of exactly one task.
- * - Convert this tool to use package_config to find the package map.
- */
-library task_dependency_graph;
-
-import 'dart:io' hide File;
-
-import 'package:analyzer/analyzer.dart';
-import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/file_system/physical_file_system.dart';
-import 'package:analyzer/src/generated/element.dart';
-import 'package:analyzer/src/generated/engine.dart';
-import 'package:analyzer/src/generated/java_io.dart';
-import 'package:analyzer/src/generated/sdk.dart';
-import 'package:analyzer/src/generated/sdk_io.dart';
-import 'package:analyzer/src/generated/source.dart';
-import 'package:analyzer/src/generated/source_io.dart';
-import 'package:path/path.dart' as path;
-
-main() {
- new Driver().run();
-}
-
-typedef void ResultDescriptorFinderCallback(PropertyAccessorElement element);
-
-class Driver {
- PhysicalResourceProvider resourceProvider;
- AnalysisContext context;
- InterfaceType resultDescriptorType;
- String rootDir;
-
- void findResultDescriptors(
- AstNode node, void callback(String descriptorName)) {
- Set<PropertyAccessorElement> resultDescriptors =
- new Set<PropertyAccessorElement>();
- node.accept(new ResultDescriptorFinder(
- resultDescriptorType, resultDescriptors.add));
- for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
- callback(resultDescriptor.name);
- }
- }
-
- /**
- * Find the root directory of the analyzer package by proceeding
- * upward to the 'tool' dir, and then going up one more directory.
- */
- String findRoot(String pathname) {
- while (path.basename(pathname) != 'tool') {
- String parent = path.dirname(pathname);
- if (parent.length >= pathname.length) {
- throw new Exception("Can't find root directory");
- }
- pathname = parent;
- }
- return path.dirname(pathname);
- }
-
- CompilationUnit getUnit(Source source) =>
- context.resolveCompilationUnit2(source, source);
-
- void run() {
- rootDir = findRoot(Platform.script.toFilePath(windows: Platform.isWindows));
- resourceProvider = PhysicalResourceProvider.INSTANCE;
- DartSdk sdk = DirectoryBasedDartSdk.defaultSdk;
- context = AnalysisEngine.instance.createAnalysisContext();
- JavaFile packagesDir = new JavaFile(path.join(rootDir, 'packages'));
- List<UriResolver> uriResolvers = [
- new DartUriResolver(sdk),
- new PackageUriResolver(<JavaFile>[packagesDir]),
- new FileUriResolver()
- ];
- context.sourceFactory = new SourceFactory(uriResolvers);
- Source taskSource =
- setupSource(path.join('lib', 'src', 'task', 'dart.dart'));
- Source modelSource = setupSource(path.join('lib', 'task', 'model.dart'));
- CompilationUnitElement modelElement = getUnit(modelSource).element;
- InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type;
- DartType dynamicType = context.typeProvider.dynamicType;
- resultDescriptorType = modelElement
- .getType('ResultDescriptor')
- .type
- .substitute4([dynamicType]);
- CompilationUnit taskUnit = getUnit(taskSource);
- CompilationUnitElement taskUnitElement = taskUnit.element;
- print('digraph G {');
- Set<String> results = new Set<String>();
- for (ClassElement cls in taskUnitElement.types) {
- if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) {
- String task = cls.name;
- // TODO(paulberry): node is deprecated. What am I supposed to do
- // instead?
- findResultDescriptors(cls.getMethod('buildInputs').node,
- (String input) {
- results.add(input);
- print(' $input -> $task');
- });
- findResultDescriptors(cls.getField('DESCRIPTOR').node, (String output) {
- results.add(output);
- print(' $task -> $output');
- });
- }
- }
- for (String result in results) {
- print(' $result [shape=box]');
- }
- print('}');
- }
-
- Source setupSource(String filename) {
- String filePath = path.join(rootDir, filename);
- File file = resourceProvider.getResource(filePath);
- Source source = file.createSource();
- Uri restoredUri = context.sourceFactory.restoreUri(source);
- if (restoredUri != null) {
- source = file.createSource(restoredUri);
- }
- ChangeSet changeSet = new ChangeSet();
- changeSet.addedSource(source);
- context.applyChanges(changeSet);
- return source;
- }
-}
-
-class ResultDescriptorFinder extends GeneralizingAstVisitor {
- final InterfaceType resultDescriptorType;
- final ResultDescriptorFinderCallback callback;
-
- ResultDescriptorFinder(this.resultDescriptorType, this.callback);
-
- @override
- visitIdentifier(Identifier node) {
- Element element = node.staticElement;
- if (element is PropertyAccessorElement &&
- element.isGetter &&
- element.returnType.isSubtypeOf(resultDescriptorType)) {
- callback(element);
- }
- }
-}
diff --git a/packages/analyzer/tool/task_dependency_graph/check_test.dart b/packages/analyzer/tool/task_dependency_graph/check_test.dart
new file mode 100644
index 0000000..a2d1118
--- /dev/null
+++ b/packages/analyzer/tool/task_dependency_graph/check_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library task_dependency_graph.check_test;
+
+import 'dart:io';
+
+import 'package:path/path.dart';
+
+import 'generate.dart';
+
+/**
+ * Check that the target file has been code generated. If it hasn't tell the
+ * user to run generate.dart.
+ */
+main() {
+ String script = Platform.script.toFilePath(windows: Platform.isWindows);
+ Driver driver = new Driver();
+ if (!driver.checkFile()) {
+ print('${driver.file.absolute} does not have expected contents.');
+ print('Please regenerate using:');
+ String executable = Platform.executable;
+ String packageRoot = '';
+ if (Platform.packageRoot.isNotEmpty) {
+ packageRoot = ' --package-root=${Platform.packageRoot}';
+ }
+ String generateScript = join(dirname(script), 'generate.dart');
+ print(' $executable$packageRoot $generateScript');
+ exit(1);
+ } else {
+ print('Generated file is up to date.');
+ }
+}
diff --git a/packages/analyzer/tool/task_dependency_graph/generate.dart b/packages/analyzer/tool/task_dependency_graph/generate.dart
new file mode 100644
index 0000000..235c2b9
--- /dev/null
+++ b/packages/analyzer/tool/task_dependency_graph/generate.dart
@@ -0,0 +1,346 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * This file contains code to output a description of tasks and their
+ * dependencies in ".dot" format. Prior to running, the user should run "pub
+ * get" in the analyzer directory to ensure that a "packages" folder exists.
+ *
+ * TODO(paulberry):
+ * - Add general.dart and html.dart for completeness.
+ * - Use Graphviz's "record" feature to produce more compact output
+ * (http://www.graphviz.org/content/node-shapes#record)
+ * - Produce a warning if a result descriptor is found which isn't the output
+ * of exactly one task.
+ * - Convert this tool to use package_config to find the package map.
+ */
+library task_dependency_graph.generate;
+
+import 'dart:io' hide File;
+import 'dart:io' as io;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/java_io.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/sdk_io.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/source_io.dart';
+import 'package:path/path.dart' as path;
+
+/**
+ * Generate the target .dot file.
+ */
+main() {
+ new Driver().generateFile();
+}
+
+typedef void GetterFinderCallback(PropertyAccessorElement element);
+
+class Driver {
+ PhysicalResourceProvider resourceProvider;
+ AnalysisContext context;
+ InterfaceType resultDescriptorType;
+ InterfaceType listOfResultDescriptorType;
+ ClassElement enginePluginClass;
+ CompilationUnitElement taskUnitElement;
+ InterfaceType extensionPointIdType;
+ final String rootDir;
+
+ Driver()
+ : rootDir =
+ findRoot(Platform.script.toFilePath(windows: Platform.isWindows));
+
+ /**
+ * Get an [io.File] object corresponding to the file in which the generated
+ * graph should be output.
+ */
+ io.File get file => new io.File(
+ path.join(rootDir, 'tool', 'task_dependency_graph', 'tasks.dot'));
+
+ /**
+ * Determine if the output [file] contains the expected contents.
+ */
+ bool checkFile() {
+ String expectedContents = generateFileContents();
+ String actualContents = file.readAsStringSync();
+ // Normalize Windows line endings to Unix line endings so that the
+ // comparison doesn't fail on Windows.
+ actualContents = actualContents.replaceAll('\r\n', '\n');
+ return expectedContents == actualContents;
+ }
+
+ /**
+ * Starting at [node], find all calls to registerExtension() which refer to
+ * the given [extensionIdVariable], and execute [callback] for the associated
+ * result descriptors.
+ */
+ void findExtensions(AstNode node, TopLevelVariableElement extensionIdVariable,
+ void callback(descriptorName)) {
+ Set<PropertyAccessorElement> resultDescriptors =
+ new Set<PropertyAccessorElement>();
+ node.accept(new ExtensionFinder(
+ resultDescriptorType, extensionIdVariable, resultDescriptors.add));
+ for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
+ callback(resultDescriptor.name);
+ }
+ }
+
+ /**
+ * Starting at [node], find all references to a getter of type
+ * `List<ResultDescriptor>`, and execute [callback] on the getter names.
+ */
+ void findResultDescriptorLists(
+ AstNode node, void callback(String descriptorListName)) {
+ Set<PropertyAccessorElement> resultDescriptorLists =
+ new Set<PropertyAccessorElement>();
+ node.accept(new GetterFinder(
+ listOfResultDescriptorType, resultDescriptorLists.add));
+ for (PropertyAccessorElement resultDescriptorList
+ in resultDescriptorLists) {
+ // We only care about result descriptor lists associated with getters in
+ // the engine plugin class.
+ if (resultDescriptorList.enclosingElement != enginePluginClass) {
+ continue;
+ }
+ callback(resultDescriptorList.name);
+ }
+ }
+
+ void findResultDescriptors(
+ AstNode node, void callback(String descriptorName)) {
+ Set<PropertyAccessorElement> resultDescriptors =
+ new Set<PropertyAccessorElement>();
+ node.accept(new GetterFinder(resultDescriptorType, resultDescriptors.add));
+ for (PropertyAccessorElement resultDescriptor in resultDescriptors) {
+ callback(resultDescriptor.name);
+ }
+ }
+
+ /**
+ * Generate the task dependency graph and write it to the output [file].
+ */
+ void generateFile() {
+ String fileContents = generateFileContents();
+ file.writeAsStringSync(fileContents);
+ }
+
+ /**
+ * Generate the task dependency graph and return it as a [String].
+ */
+ String generateFileContents() {
+ List<String> lines = <String>[];
+ resourceProvider = PhysicalResourceProvider.INSTANCE;
+ DartSdk sdk = DirectoryBasedDartSdk.defaultSdk;
+ context = AnalysisEngine.instance.createAnalysisContext();
+ String packageRootPath;
+ if (Platform.packageRoot.isNotEmpty) {
+ packageRootPath = Platform.packageRoot;
+ } else {
+ packageRootPath = path.join(rootDir, 'packages');
+ }
+ JavaFile packagesDir = new JavaFile(packageRootPath);
+ List<UriResolver> uriResolvers = [
+ new DartUriResolver(sdk),
+ new PackageUriResolver(<JavaFile>[packagesDir]),
+ new FileUriResolver()
+ ];
+ context.sourceFactory = new SourceFactory(uriResolvers);
+ Source dartDartSource =
+ setupSource(path.join('lib', 'src', 'task', 'dart.dart'));
+ Source taskSource = setupSource(path.join('lib', 'plugin', 'task.dart'));
+ Source modelSource = setupSource(path.join('lib', 'task', 'model.dart'));
+ Source enginePluginSource =
+ setupSource(path.join('lib', 'src', 'plugin', 'engine_plugin.dart'));
+ CompilationUnitElement modelElement = getUnit(modelSource).element;
+ InterfaceType analysisTaskType = modelElement.getType('AnalysisTask').type;
+ DartType dynamicType = context.typeProvider.dynamicType;
+ resultDescriptorType = modelElement
+ .getType('ResultDescriptor')
+ .type
+ .substitute4([dynamicType]);
+ listOfResultDescriptorType =
+ context.typeProvider.listType.substitute4([resultDescriptorType]);
+ CompilationUnitElement enginePluginUnitElement =
+ getUnit(enginePluginSource).element;
+ enginePluginClass = enginePluginUnitElement.getType('EnginePlugin');
+ extensionPointIdType =
+ enginePluginUnitElement.getType('ExtensionPointId').type;
+ CompilationUnit dartDartUnit = getUnit(dartDartSource);
+ CompilationUnitElement dartDartUnitElement = dartDartUnit.element;
+ CompilationUnit taskUnit = getUnit(taskSource);
+ taskUnitElement = taskUnit.element;
+ Set<String> results = new Set<String>();
+ Set<String> resultLists = new Set<String>();
+ for (ClassElement cls in dartDartUnitElement.types) {
+ if (!cls.isAbstract && cls.type.isSubtypeOf(analysisTaskType)) {
+ String task = cls.name;
+ AstNode buildInputsAst = cls.getMethod('buildInputs').computeNode();
+ findResultDescriptors(buildInputsAst, (String input) {
+ results.add(input);
+ lines.add(' $input -> $task');
+ });
+ findResultDescriptorLists(buildInputsAst, (String input) {
+ resultLists.add(input);
+ lines.add(' $input -> $task');
+ });
+ findResultDescriptors(cls.getField('DESCRIPTOR').computeNode(), (String out) {
+ results.add(out);
+ lines.add(' $task -> $out');
+ });
+ }
+ }
+ AstNode enginePluginAst = enginePluginUnitElement.computeNode();
+ for (String resultList in resultLists) {
+ lines.add(' $resultList [shape=hexagon]');
+ TopLevelVariableElement extensionIdVariable = _getExtensionId(resultList);
+ findExtensions(enginePluginAst, extensionIdVariable, (String extension) {
+ results.add(extension);
+ lines.add(' $extension -> $resultList');
+ });
+ }
+ for (String result in results) {
+ lines.add(' $result [shape=box]');
+ }
+ lines.sort();
+ return '''
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// To regenerate the file, use the script
+// "pkg/analyzer/tool/task_dependency_graph/generate.dart".
+//
+// To render this graph using Graphviz (www.graphviz.org) use the command:
+// "dot tasks.dot -Tpdf -O".
+digraph G {
+${lines.join('\n')}
+}
+''';
+ }
+
+ CompilationUnit getUnit(Source source) =>
+ context.resolveCompilationUnit2(source, source);
+
+ Source setupSource(String filename) {
+ String filePath = path.join(rootDir, filename);
+ File file = resourceProvider.getResource(filePath);
+ Source source = file.createSource();
+ Uri restoredUri = context.sourceFactory.restoreUri(source);
+ if (restoredUri != null) {
+ source = file.createSource(restoredUri);
+ }
+ ChangeSet changeSet = new ChangeSet();
+ changeSet.addedSource(source);
+ context.applyChanges(changeSet);
+ return source;
+ }
+
+ /**
+ * Find the result list getter having name [resultListGetterName] in the
+ * [EnginePlugin] class, and use the [ExtensionPointId] annotation on that
+ * getter to find the associated [TopLevelVariableElement] which can be used
+ * to register extensions for that getter.
+ */
+ TopLevelVariableElement _getExtensionId(String resultListGetterName) {
+ PropertyAccessorElement getter =
+ enginePluginClass.getGetter(resultListGetterName);
+ for (ElementAnnotation annotation in getter.metadata) {
+ DartObjectImpl annotationValue = annotation.constantValue;
+ if (annotationValue.type.isSubtypeOf(extensionPointIdType)) {
+ String extensionPointId =
+ annotationValue.fields['extensionPointId'].toStringValue();
+ for (TopLevelVariableElement variable
+ in taskUnitElement.topLevelVariables) {
+ if (variable.name == extensionPointId) {
+ return variable;
+ }
+ }
+ }
+ }
+ throw new Exception(
+ 'Could not find extension ID corresponding to $resultListGetterName');
+ }
+
+ /**
+ * Find the root directory of the analyzer package by proceeding
+ * upward to the 'tool' dir, and then going up one more directory.
+ */
+ static String findRoot(String pathname) {
+ while (path.basename(pathname) != 'tool') {
+ String parent = path.dirname(pathname);
+ if (parent.length >= pathname.length) {
+ throw new Exception("Can't find root directory");
+ }
+ pathname = parent;
+ }
+ return path.dirname(pathname);
+ }
+}
+
+/**
+ * Visitor that finds calls that register extension points. Specifically, we
+ * look for calls of the form `method(extensionIdVariable, resultDescriptor)`,
+ * where `resultDescriptor` has type [resultDescriptorType], and we pass the
+ * corresponding result descriptor names to [callback].
+ */
+class ExtensionFinder extends GeneralizingAstVisitor {
+ final InterfaceType resultDescriptorType;
+ final TopLevelVariableElement extensionIdVariable;
+ final GetterFinderCallback callback;
+
+ ExtensionFinder(
+ this.resultDescriptorType, this.extensionIdVariable, this.callback);
+
+ @override
+ visitIdentifier(Identifier node) {
+ Element element = node.staticElement;
+ if (element is PropertyAccessorElement &&
+ element.isGetter &&
+ element.variable == extensionIdVariable) {
+ AstNode parent = node.parent;
+ if (parent is ArgumentList &&
+ parent.arguments.length == 2 &&
+ parent.arguments[0] == node) {
+ Expression extension = parent.arguments[1];
+ if (extension is Identifier) {
+ Element element = extension.staticElement;
+ if (element is PropertyAccessorElement &&
+ element.isGetter &&
+ element.returnType.isSubtypeOf(resultDescriptorType)) {
+ callback(element);
+ return;
+ }
+ }
+ }
+ throw new Exception('Could not decode extension setup: $parent');
+ }
+ }
+}
+
+/**
+ * Visitor that finds references to getters having a specific type (or a
+ * subtype of that type)
+ */
+class GetterFinder extends GeneralizingAstVisitor {
+ final InterfaceType type;
+ final GetterFinderCallback callback;
+
+ GetterFinder(this.type, this.callback);
+
+ @override
+ visitIdentifier(Identifier node) {
+ Element element = node.staticElement;
+ if (element is PropertyAccessorElement &&
+ element.isGetter &&
+ element.returnType.isSubtypeOf(type)) {
+ callback(element);
+ }
+ }
+}
diff --git a/packages/analyzer/tool/task_dependency_graph/tasks.dot b/packages/analyzer/tool/task_dependency_graph/tasks.dot
new file mode 100644
index 0000000..42c5f69
--- /dev/null
+++ b/packages/analyzer/tool/task_dependency_graph/tasks.dot
@@ -0,0 +1,245 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+// This file has been automatically generated. Please do not edit it manually.
+// To regenerate the file, use the script
+// "pkg/analyzer/tool/task_dependency_graph/generate.dart".
+//
+// To render this graph using Graphviz (www.graphviz.org) use the command:
+// "dot tasks.dot -Tpdf -O".
+digraph G {
+ BUILD_DIRECTIVES_ERRORS -> LibraryUnitErrorsTask
+ BUILD_DIRECTIVES_ERRORS [shape=box]
+ BUILD_LIBRARY_ERRORS -> LibraryUnitErrorsTask
+ BUILD_LIBRARY_ERRORS [shape=box]
+ BuildCompilationUnitElementTask -> COMPILATION_UNIT_CONSTANTS
+ BuildCompilationUnitElementTask -> COMPILATION_UNIT_ELEMENT
+ BuildCompilationUnitElementTask -> RESOLVED_UNIT1
+ BuildDirectiveElementsTask -> BUILD_DIRECTIVES_ERRORS
+ BuildDirectiveElementsTask -> LIBRARY_ELEMENT2
+ BuildEnumMemberElementsTask -> RESOLVED_UNIT2
+ BuildExportNamespaceTask -> LIBRARY_ELEMENT4
+ BuildLibraryElementTask -> BUILD_LIBRARY_ERRORS
+ BuildLibraryElementTask -> IS_LAUNCHABLE
+ BuildLibraryElementTask -> LIBRARY_ELEMENT1
+ BuildPublicNamespaceTask -> LIBRARY_ELEMENT3
+ BuildSourceExportClosureTask -> EXPORT_SOURCE_CLOSURE
+ BuildSourceImportExportClosureTask -> IMPORT_EXPORT_SOURCE_CLOSURE
+ BuildSourceImportExportClosureTask -> IS_CLIENT
+ BuildTypeProviderTask -> TYPE_PROVIDER
+ COMPILATION_UNIT_CONSTANTS -> EvaluateUnitConstantsTask
+ COMPILATION_UNIT_CONSTANTS [shape=box]
+ COMPILATION_UNIT_ELEMENT [shape=box]
+ CONSTANT_DEPENDENCIES -> ComputeConstantValueTask
+ CONSTANT_DEPENDENCIES [shape=box]
+ CONSTANT_VALUE -> ComputeConstantValueTask
+ CONSTANT_VALUE -> EvaluateUnitConstantsTask
+ CONSTANT_VALUE [shape=box]
+ CONTAINING_LIBRARIES -> DartErrorsTask
+ CONTAINING_LIBRARIES [shape=box]
+ CONTENT -> ScanDartTask
+ CONTENT [shape=box]
+ ComputeConstantDependenciesTask -> CONSTANT_DEPENDENCIES
+ ComputeConstantValueTask -> CONSTANT_VALUE
+ ComputeInferableStaticVariableDependenciesTask -> INFERABLE_STATIC_VARIABLE_DEPENDENCIES
+ ComputeLibraryCycleTask -> LIBRARY_CYCLE
+ ComputeLibraryCycleTask -> LIBRARY_CYCLE_DEPENDENCIES
+ ComputeLibraryCycleTask -> LIBRARY_CYCLE_UNITS
+ ContainingLibrariesTask -> CONTAINING_LIBRARIES
+ DART_ERRORS -> LibraryErrorsReadyTask
+ DART_ERRORS [shape=box]
+ DART_SCRIPTS -> ScanDartTask
+ DART_SCRIPTS [shape=box]
+ DartErrorsTask -> DART_ERRORS
+ EXPLICITLY_IMPORTED_LIBRARIES [shape=box]
+ EXPORTED_LIBRARIES -> BuildDirectiveElementsTask
+ EXPORTED_LIBRARIES [shape=box]
+ EXPORT_SOURCE_CLOSURE -> BuildExportNamespaceTask
+ EXPORT_SOURCE_CLOSURE [shape=box]
+ EvaluateUnitConstantsTask -> RESOLVED_UNIT10
+ GatherUsedImportedElementsTask -> USED_IMPORTED_ELEMENTS
+ GatherUsedLocalElementsTask -> USED_LOCAL_ELEMENTS
+ GenerateHintsTask -> HINTS
+ GenerateLintsTask -> LINTS
+ HINTS -> LibraryUnitErrorsTask
+ HINTS [shape=box]
+ IMPORTED_LIBRARIES -> BuildDirectiveElementsTask
+ IMPORTED_LIBRARIES -> ResolveUnitTypeNamesTask
+ IMPORTED_LIBRARIES [shape=box]
+ IMPORT_EXPORT_SOURCE_CLOSURE -> ComputeLibraryCycleTask
+ IMPORT_EXPORT_SOURCE_CLOSURE -> PartiallyResolveUnitReferencesTask
+ IMPORT_EXPORT_SOURCE_CLOSURE -> ResolveLibraryReferencesTask
+ IMPORT_EXPORT_SOURCE_CLOSURE -> StrongModeVerifyUnitTask
+ IMPORT_EXPORT_SOURCE_CLOSURE -> VerifyUnitTask
+ IMPORT_EXPORT_SOURCE_CLOSURE [shape=box]
+ INCLUDED_PARTS -> BuildLibraryElementTask
+ INCLUDED_PARTS [shape=box]
+ INFERABLE_STATIC_VARIABLES_IN_UNIT -> InferStaticVariableTypesInUnitTask
+ INFERABLE_STATIC_VARIABLES_IN_UNIT [shape=box]
+ INFERABLE_STATIC_VARIABLE_DEPENDENCIES -> InferStaticVariableTypeTask
+ INFERABLE_STATIC_VARIABLE_DEPENDENCIES [shape=box]
+ INFERRED_STATIC_VARIABLE -> InferStaticVariableTypeTask
+ INFERRED_STATIC_VARIABLE -> InferStaticVariableTypesInUnitTask
+ INFERRED_STATIC_VARIABLE [shape=box]
+ IS_CLIENT [shape=box]
+ IS_LAUNCHABLE [shape=box]
+ InferInstanceMembersInUnitTask -> RESOLVED_UNIT8
+ InferStaticVariableTypeTask -> INFERRED_STATIC_VARIABLE
+ InferStaticVariableTypesInUnitTask -> RESOLVED_UNIT6
+ LIBRARY_CYCLE [shape=box]
+ LIBRARY_CYCLE_DEPENDENCIES -> InferInstanceMembersInUnitTask
+ LIBRARY_CYCLE_DEPENDENCIES -> InferStaticVariableTypeTask
+ LIBRARY_CYCLE_DEPENDENCIES -> PartiallyResolveUnitReferencesTask
+ LIBRARY_CYCLE_DEPENDENCIES -> ResolveInstanceFieldsInUnitTask
+ LIBRARY_CYCLE_DEPENDENCIES [shape=box]
+ LIBRARY_CYCLE_UNITS -> InferInstanceMembersInUnitTask
+ LIBRARY_CYCLE_UNITS -> ResolveInstanceFieldsInUnitTask
+ LIBRARY_CYCLE_UNITS -> ResolveUnitTask
+ LIBRARY_CYCLE_UNITS [shape=box]
+ LIBRARY_ELEMENT -> EvaluateUnitConstantsTask
+ LIBRARY_ELEMENT [shape=box]
+ LIBRARY_ELEMENT1 -> BuildDirectiveElementsTask
+ LIBRARY_ELEMENT1 -> ResolveVariableReferencesTask
+ LIBRARY_ELEMENT1 [shape=box]
+ LIBRARY_ELEMENT2 -> BuildPublicNamespaceTask
+ LIBRARY_ELEMENT2 -> BuildSourceExportClosureTask
+ LIBRARY_ELEMENT2 -> BuildSourceImportExportClosureTask
+ LIBRARY_ELEMENT2 -> ComputeLibraryCycleTask
+ LIBRARY_ELEMENT2 [shape=box]
+ LIBRARY_ELEMENT3 -> BuildExportNamespaceTask
+ LIBRARY_ELEMENT3 -> BuildTypeProviderTask
+ LIBRARY_ELEMENT3 [shape=box]
+ LIBRARY_ELEMENT4 -> ResolveLibraryTypeNamesTask
+ LIBRARY_ELEMENT4 -> ResolveUnitTypeNamesTask
+ LIBRARY_ELEMENT4 [shape=box]
+ LIBRARY_ELEMENT5 -> PartiallyResolveUnitReferencesTask
+ LIBRARY_ELEMENT5 -> ResolveInstanceFieldsInUnitTask
+ LIBRARY_ELEMENT5 -> ResolveLibraryReferencesTask
+ LIBRARY_ELEMENT5 -> ResolveUnitTask
+ LIBRARY_ELEMENT5 [shape=box]
+ LIBRARY_ERRORS_READY [shape=box]
+ LIBRARY_UNIT_ERRORS -> dartErrorsForUnit
+ LIBRARY_UNIT_ERRORS [shape=box]
+ LINE_INFO -> ParseDartTask
+ LINE_INFO [shape=box]
+ LINTS -> LibraryUnitErrorsTask
+ LINTS [shape=box]
+ LibraryErrorsReadyTask -> LIBRARY_ERRORS_READY
+ LibraryUnitErrorsTask -> LIBRARY_UNIT_ERRORS
+ MODIFICATION_TIME -> ParseDartTask
+ MODIFICATION_TIME [shape=box]
+ PARSED_UNIT -> BuildCompilationUnitElementTask
+ PARSED_UNIT [shape=box]
+ PARSE_ERRORS -> dartErrorsForSource
+ PARSE_ERRORS [shape=box]
+ ParseDartTask -> EXPLICITLY_IMPORTED_LIBRARIES
+ ParseDartTask -> EXPORTED_LIBRARIES
+ ParseDartTask -> IMPORTED_LIBRARIES
+ ParseDartTask -> INCLUDED_PARTS
+ ParseDartTask -> PARSED_UNIT
+ ParseDartTask -> PARSE_ERRORS
+ ParseDartTask -> SOURCE_KIND
+ ParseDartTask -> UNITS
+ PartiallyResolveUnitReferencesTask -> INFERABLE_STATIC_VARIABLES_IN_UNIT
+ PartiallyResolveUnitReferencesTask -> RESOLVED_UNIT5
+ REFERENCED_NAMES [shape=box]
+ RESOLVED_UNIT -> GenerateHintsTask
+ RESOLVED_UNIT -> GenerateLintsTask
+ RESOLVED_UNIT -> VerifyUnitTask
+ RESOLVED_UNIT [shape=box]
+ RESOLVED_UNIT1 -> BuildDirectiveElementsTask
+ RESOLVED_UNIT1 -> BuildEnumMemberElementsTask
+ RESOLVED_UNIT1 -> BuildLibraryElementTask
+ RESOLVED_UNIT1 [shape=box]
+ RESOLVED_UNIT10 -> StrongModeVerifyUnitTask
+ RESOLVED_UNIT10 [shape=box]
+ RESOLVED_UNIT2 -> ResolveUnitTypeNamesTask
+ RESOLVED_UNIT2 [shape=box]
+ RESOLVED_UNIT3 -> ResolveLibraryTypeNamesTask
+ RESOLVED_UNIT3 -> ResolveVariableReferencesTask
+ RESOLVED_UNIT3 [shape=box]
+ RESOLVED_UNIT4 -> PartiallyResolveUnitReferencesTask
+ RESOLVED_UNIT4 [shape=box]
+ RESOLVED_UNIT5 -> ComputeInferableStaticVariableDependenciesTask
+ RESOLVED_UNIT5 -> InferStaticVariableTypeTask
+ RESOLVED_UNIT5 -> InferStaticVariableTypesInUnitTask
+ RESOLVED_UNIT5 [shape=box]
+ RESOLVED_UNIT6 -> ResolveInstanceFieldsInUnitTask
+ RESOLVED_UNIT6 [shape=box]
+ RESOLVED_UNIT7 -> InferInstanceMembersInUnitTask
+ RESOLVED_UNIT7 [shape=box]
+ RESOLVED_UNIT8 -> InferInstanceMembersInUnitTask
+ RESOLVED_UNIT8 -> InferStaticVariableTypeTask
+ RESOLVED_UNIT8 -> PartiallyResolveUnitReferencesTask
+ RESOLVED_UNIT8 -> ResolveInstanceFieldsInUnitTask
+ RESOLVED_UNIT8 -> ResolveUnitTask
+ RESOLVED_UNIT8 [shape=box]
+ RESOLVED_UNIT9 -> ComputeConstantDependenciesTask
+ RESOLVED_UNIT9 -> EvaluateUnitConstantsTask
+ RESOLVED_UNIT9 -> GatherUsedImportedElementsTask
+ RESOLVED_UNIT9 -> GatherUsedLocalElementsTask
+ RESOLVED_UNIT9 -> ResolveLibraryReferencesTask
+ RESOLVED_UNIT9 [shape=box]
+ RESOLVE_TYPE_NAMES_ERRORS -> LibraryUnitErrorsTask
+ RESOLVE_TYPE_NAMES_ERRORS [shape=box]
+ RESOLVE_UNIT_ERRORS -> LibraryUnitErrorsTask
+ RESOLVE_UNIT_ERRORS [shape=box]
+ ResolveInstanceFieldsInUnitTask -> RESOLVED_UNIT7
+ ResolveLibraryReferencesTask -> LIBRARY_ELEMENT
+ ResolveLibraryReferencesTask -> REFERENCED_NAMES
+ ResolveLibraryTypeNamesTask -> LIBRARY_ELEMENT5
+ ResolveUnitTask -> RESOLVED_UNIT9
+ ResolveUnitTask -> RESOLVE_UNIT_ERRORS
+ ResolveUnitTypeNamesTask -> RESOLVED_UNIT3
+ ResolveUnitTypeNamesTask -> RESOLVE_TYPE_NAMES_ERRORS
+ ResolveVariableReferencesTask -> RESOLVED_UNIT4
+ ResolveVariableReferencesTask -> VARIABLE_REFERENCE_ERRORS
+ SCAN_ERRORS -> dartErrorsForSource
+ SCAN_ERRORS [shape=box]
+ SOURCE_KIND -> BuildDirectiveElementsTask
+ SOURCE_KIND [shape=box]
+ STRONG_MODE_ERRORS -> LibraryUnitErrorsTask
+ STRONG_MODE_ERRORS [shape=box]
+ ScanDartTask -> LINE_INFO
+ ScanDartTask -> SCAN_ERRORS
+ ScanDartTask -> TOKEN_STREAM
+ StrongModeVerifyUnitTask -> RESOLVED_UNIT
+ StrongModeVerifyUnitTask -> STRONG_MODE_ERRORS
+ TOKEN_STREAM -> ParseDartTask
+ TOKEN_STREAM [shape=box]
+ TYPE_PROVIDER -> BuildEnumMemberElementsTask
+ TYPE_PROVIDER -> ComputeConstantDependenciesTask
+ TYPE_PROVIDER -> ComputeConstantValueTask
+ TYPE_PROVIDER -> GenerateHintsTask
+ TYPE_PROVIDER -> InferInstanceMembersInUnitTask
+ TYPE_PROVIDER -> InferStaticVariableTypeTask
+ TYPE_PROVIDER -> PartiallyResolveUnitReferencesTask
+ TYPE_PROVIDER -> ResolveInstanceFieldsInUnitTask
+ TYPE_PROVIDER -> ResolveUnitTask
+ TYPE_PROVIDER -> ResolveUnitTypeNamesTask
+ TYPE_PROVIDER -> ResolveVariableReferencesTask
+ TYPE_PROVIDER -> StrongModeVerifyUnitTask
+ TYPE_PROVIDER -> VerifyUnitTask
+ TYPE_PROVIDER [shape=box]
+ UNITS -> GenerateHintsTask
+ UNITS -> LibraryErrorsReadyTask
+ UNITS -> ResolveLibraryReferencesTask
+ UNITS -> ResolveLibraryTypeNamesTask
+ UNITS -> StrongModeVerifyUnitTask
+ UNITS -> VerifyUnitTask
+ UNITS [shape=box]
+ USED_IMPORTED_ELEMENTS -> GenerateHintsTask
+ USED_IMPORTED_ELEMENTS [shape=box]
+ USED_LOCAL_ELEMENTS -> GenerateHintsTask
+ USED_LOCAL_ELEMENTS [shape=box]
+ VARIABLE_REFERENCE_ERRORS -> LibraryUnitErrorsTask
+ VARIABLE_REFERENCE_ERRORS [shape=box]
+ VERIFY_ERRORS -> LibraryUnitErrorsTask
+ VERIFY_ERRORS [shape=box]
+ VerifyUnitTask -> VERIFY_ERRORS
+ dartErrorsForSource -> DartErrorsTask
+ dartErrorsForSource [shape=hexagon]
+ dartErrorsForUnit -> DartErrorsTask
+ dartErrorsForUnit [shape=hexagon]
+}
diff --git a/packages/async/CHANGELOG.md b/packages/async/CHANGELOG.md
index 39d204b..5801872 100644
--- a/packages/async/CHANGELOG.md
+++ b/packages/async/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.4.0
+
+- Added `AsyncMemoizer.future`, which allows the result to be accessed before
+ `runOnce()` is called.
+
+- Added `RestartableTimer`, a non-periodic timer that can be reset over and
+ over.
+
## 1.3.0
- Added `StreamCompleter` class for creating a stream now and providing its
diff --git a/packages/async/lib/async.dart b/packages/async/lib/async.dart
index 71a1849..9da3552 100644
--- a/packages/async/lib/async.dart
+++ b/packages/async/lib/async.dart
@@ -6,6 +6,7 @@
export "result.dart";
export "src/async_memoizer.dart";
+export "src/cancelable_operation.dart";
export "src/delegate/event_sink.dart";
export "src/delegate/future.dart";
export "src/delegate/sink.dart";
@@ -13,6 +14,7 @@
export "src/delegate/stream_sink.dart";
export "src/delegate/stream_subscription.dart";
export "src/future_group.dart";
+export "src/restartable_timer.dart";
export "src/result_future.dart";
export "src/stream_completer.dart";
export "src/stream_group.dart";
diff --git a/packages/async/lib/src/async_memoizer.dart b/packages/async/lib/src/async_memoizer.dart
index 68a6c80..41c3dae 100644
--- a/packages/async/lib/src/async_memoizer.dart
+++ b/packages/async/lib/src/async_memoizer.dart
@@ -31,17 +31,18 @@
class AsyncMemoizer<T> {
/// The future containing the method's result.
///
- /// This will be `null` if [run] hasn't been called yet.
- Future<T> _future;
+ /// This can be accessed at any time, and will fire once [runOnce] is called.
+ Future<T> get future => _completer.future;
+ final _completer = new Completer();
/// Whether [run] has been called yet.
- bool get hasRun => _future != null;
+ bool get hasRun => _completer.isCompleted;
/// Runs the function, [computation], if it hasn't been run before.
///
/// If [run] has already been called, this returns the original result.
Future<T> runOnce(computation()) {
- if (_future == null) _future = new Future.sync(computation);
- return _future;
+ if (!hasRun) _completer.complete(new Future.sync(computation));
+ return future;
}
}
diff --git a/packages/async/lib/src/cancelable_operation.dart b/packages/async/lib/src/cancelable_operation.dart
new file mode 100644
index 0000000..a7fe6a8
--- /dev/null
+++ b/packages/async/lib/src/cancelable_operation.dart
@@ -0,0 +1,148 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library async.cancelable_operation;
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+
+/// An asynchronuos operation that can be cancelled.
+///
+/// The value of this operation is exposed as [value]. When this operation is
+/// cancelled, [value] won't complete either successfully or with an error. If
+/// [value] has already completed, cancelling the operation does nothing.
+class CancelableOperation<T> {
+ /// The completer that produced this operation.
+ ///
+ /// This is canceled when [cancel] is called.
+ final CancelableCompleter<T> _completer;
+
+ CancelableOperation._(this._completer);
+
+ /// Creates a [CancelableOperation] wrapping [inner].
+ ///
+ /// When this operation is canceled, [onCancel] will be called and any value
+ /// or error produced by [inner] will be discarded. The callback may return a
+ /// Future to indicate that asynchronous work has to be done to cancel the
+ /// future; this Future will be returned by [cancel].
+ ///
+ /// [onCancel] will be called synchronously when the operation is canceled.
+ /// It's guaranteed to only be called once.
+ factory CancelableOperation.fromFuture(Future<T> inner, {onCancel()}) {
+ var completer = new CancelableCompleter<T>(onCancel: onCancel);
+ completer.complete(inner);
+ return completer.operation;
+ }
+
+ /// The value returned by the operation.
+ Future<T> get value => _completer._inner.future;
+
+ /// Creates a [Stream] containing the result of this operation.
+ ///
+ /// This is like `value.asStream()`, but if a subscription to the stream is
+ /// canceled, this is as well.
+ Stream<T> asStream() {
+ var controller = new StreamController<T>(
+ sync: true, onCancel: _completer._cancel);
+
+ value.then((value) {
+ controller.add(value);
+ controller.close();
+ }, onError: (error, stackTrace) {
+ controller.addError(error, stackTrace);
+ controller.close();
+ });
+ return controller.stream;
+ }
+
+ /// Cancels this operation.
+ ///
+ /// This returns the [Future] returned by the [CancelableCompleter]'s
+ /// `onCancel` callback. Unlike [Stream.cancel], it never returns `null`.
+ Future cancel() => _completer._cancel();
+}
+
+/// A completer for a [CancelableOperation].
+class CancelableCompleter<T> {
+ /// The completer for the wrapped future.
+ final Completer<T> _inner;
+
+ /// The callback to call if the future is canceled.
+ final ZoneCallback _onCancel;
+
+ /// Creates a new completer for a [CancelableOperation].
+ ///
+ /// When the future operation canceled, as long as the completer hasn't yet
+ /// completed, [onCancel] is called. The callback may return a [Future]; if
+ /// so, that [Future] is returned by [CancelableOperation.cancel].
+ ///
+ /// [onCancel] will be called synchronously when the operation is canceled.
+ /// It's guaranteed to only be called once.
+ CancelableCompleter({onCancel()})
+ : _onCancel = onCancel,
+ _inner = new Completer<T>() {
+ _operation = new CancelableOperation<T>._(this);
+ }
+
+ /// The operation controlled by this completer.
+ CancelableOperation<T> get operation => _operation;
+ CancelableOperation<T> _operation;
+
+ /// Whether the completer has completed.
+ bool get isCompleted => _isCompleted;
+ bool _isCompleted = false;
+
+ /// Whether the completer was canceled before being completed.
+ bool get isCanceled => _isCanceled;
+ bool _isCanceled = false;
+
+ /// The memoizer for [_cancel].
+ final _cancelMemo = new AsyncMemoizer();
+
+ /// Completes [operation] to [value].
+ ///
+ /// If [value] is a [Future], this will complete to the result of that
+ /// [Future] once it completes.
+ void complete([value]) {
+ if (_isCompleted) throw new StateError("Operation already completed");
+ _isCompleted = true;
+
+ if (value is! Future) {
+ if (_isCanceled) return;
+ _inner.complete(value);
+ return;
+ }
+
+ if (_isCanceled) {
+ // Make sure errors from [value] aren't top-leveled.
+ value.catchError((_) {});
+ return;
+ }
+
+ value.then((result) {
+ if (_isCanceled) return;
+ _inner.complete(result);
+ }, onError: (error, stackTrace) {
+ if (_isCanceled) return;
+ _inner.completeError(error, stackTrace);
+ });
+ }
+
+ /// Completes [operation] to [error].
+ void completeError(Object error, [StackTrace stackTrace]) {
+ if (_isCompleted) throw new StateError("Operation already completed");
+ _isCompleted = true;
+
+ if (_isCanceled) return;
+ _inner.completeError(error, stackTrace);
+ }
+
+ /// Cancel the completer.
+ Future _cancel() => _cancelMemo.runOnce(() {
+ if (_inner.isCompleted) return null;
+ _isCanceled = true;
+ if (_onCancel != null) return _onCancel();
+ });
+}
diff --git a/packages/async/lib/src/restartable_timer.dart b/packages/async/lib/src/restartable_timer.dart
new file mode 100644
index 0000000..05196d2
--- /dev/null
+++ b/packages/async/lib/src/restartable_timer.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library async.restartable_timer;
+
+import 'dart:async';
+
+/// A non-periodic timer that can be restarted any number of times.
+///
+/// Once restarted (via [reset]), the timer counts down from its original
+/// duration again.
+class RestartableTimer implements Timer {
+ /// The duration of the timer.
+ final Duration _duration;
+
+ /// The callback to call when the timer fires.
+ final ZoneCallback _callback;
+
+ /// The timer for the current or most recent countdown.
+ ///
+ /// This timer is canceled and overwritten every time this [RestartableTimer]
+ /// is reset.
+ Timer _timer;
+
+ /// Creates a new timer.
+ ///
+ /// The [callback] function is invoked after the given [duration]. Unlike a
+ /// normal non-periodic [Timer], [callback] may be called more than once.
+ RestartableTimer(this._duration, this._callback) {
+ _timer = new Timer(_duration, _callback);
+ }
+
+ bool get isActive => _timer.isActive;
+
+ /// Restarts the timer so that it counts down from its original duration
+ /// again.
+ ///
+ /// This restarts the timer even if it has already fired or has been canceled.
+ void reset() {
+ _timer.cancel();
+ _timer = new Timer(_duration, _callback);
+ }
+
+ void cancel() {
+ _timer.cancel();
+ }
+}
diff --git a/packages/async/lib/src/stream_queue.dart b/packages/async/lib/src/stream_queue.dart
index 36d03ef..09b3a75 100644
--- a/packages/async/lib/src/stream_queue.dart
+++ b/packages/async/lib/src/stream_queue.dart
@@ -60,15 +60,17 @@
///
/// When you need no further events the `StreamQueue` should be closed
/// using [cancel]. This releases the underlying stream subscription.
-class StreamQueue<T> {
+abstract class StreamQueue<T> {
// This class maintains two queues: one of events and one of requests.
// The active request (the one in front of the queue) is called with
- // the current event queue when it becomes active.
+ // the current event queue when it becomes active, every time a
+ // new event arrives, and when the event source closes.
//
- // If the request returns true, it's complete and will be removed from the
+ // If the request returns `true`, it's complete and will be removed from the
// request queue.
- // If the request returns false, it needs more events, and will be called
- // again when new events are available.
+ // If the request returns `false`, it needs more events, and will be called
+ // again when new events are available. It may trigger a call itself by
+ // calling [_updateRequests].
// The request can remove events that it uses, or keep them in the event
// queue until it has all that it needs.
//
@@ -77,16 +79,7 @@
// potentially a request that takes either five or zero events, determined
// by the content of the fifth event.
- /// Source of events.
- final Stream _sourceStream;
-
- /// Subscription on [_sourceStream] while listening for events.
- ///
- /// Set to subscription when listening, and set to `null` when the
- /// subscription is done (and [_isDone] is set to true).
- StreamSubscription _subscription;
-
- /// Whether we have listened on [_sourceStream] and the subscription is done.
+ /// Whether the event source is done.
bool _isDone = false;
/// Whether a closing operation has been performed on the stream queue.
@@ -103,8 +96,9 @@
final Queue<_EventRequest> _requestQueue = new Queue();
/// Create a `StreamQueue` of the events of [source].
- StreamQueue(Stream source)
- : _sourceStream = source;
+ factory StreamQueue(Stream source) = _StreamQueue<T>;
+
+ StreamQueue._();
/// Asks if the stream has any more events.
///
@@ -115,6 +109,8 @@
///
/// Can be used before using [next] to avoid getting an error in the
/// future returned by `next` in the case where there are no more events.
+ /// Another alternative is to use `take(1)` which returns either zero or
+ /// one events.
Future<bool> get hasNext {
if (!_isClosed) {
var hasNextRequest = new _HasNextRequest();
@@ -216,15 +212,15 @@
throw _failClosed();
}
- /// Cancels the underlying stream subscription.
+ /// Cancels the underlying event source.
///
/// If [immediate] is `false` (the default), the cancel operation waits until
/// all previously requested events have been processed, then it cancels the
/// subscription providing the events.
///
- /// If [immediate] is `true`, the subscription is instead canceled
- /// immediately. Any pending events complete with a 'closed'-event, as though
- /// the stream had closed by itself.
+ /// If [immediate] is `true`, the source is instead canceled
+ /// immediately. Any pending events are completed as though the underlying
+ /// stream had closed.
///
/// The returned future completes with the result of calling
/// `cancel`.
@@ -242,13 +238,84 @@
return request.future;
}
- if (_isDone) return new Future.value();
- if (_subscription == null) _subscription = _sourceStream.listen(null);
- var future = _subscription.cancel();
- _onDone();
- return future;
+ if (_isDone && _eventQueue.isEmpty) return new Future.value();
+ return _cancel();
}
+ // ------------------------------------------------------------------
+ // Methods that may be called from the request implementations to
+ // control the even stream.
+
+ /// Matches events with requests.
+ ///
+ /// Called after receiving an event or when the event source closes.
+ ///
+ /// May be called by requests which have returned `false` (saying they
+ /// are not yet done) so they can be checked again before any new
+ /// events arrive.
+ /// Any request returing `false` from `update` when `isDone` is `true`
+ /// *must* call `_updateRequests` when they are ready to continue
+ /// (since no further events will trigger the call).
+ void _updateRequests() {
+ while (_requestQueue.isNotEmpty) {
+ if (_requestQueue.first.update(_eventQueue, _isDone)) {
+ _requestQueue.removeFirst();
+ } else {
+ return;
+ }
+ }
+
+ if (!_isDone) {
+ _pause();
+ }
+ }
+
+ /// Extracts a stream from the event source and makes this stream queue
+ /// unusable.
+ ///
+ /// Can only be used by the very last request (the stream queue must
+ /// be closed by that request).
+ /// Only used by [rest].
+ Stream _extractStream();
+
+ /// Requests that the event source pauses events.
+ ///
+ /// This is called automatically when the request queue is empty.
+ ///
+ /// The event source is restarted by the next call to [_ensureListening].
+ void _pause();
+
+ /// Ensures that we are listening on events from the event source.
+ ///
+ /// Starts listening for the first time or resumes after a [_pause].
+ ///
+ /// Is called automatically if a request requires more events.
+ void _ensureListening();
+
+ /// Cancels the underlying event source.
+ Future _cancel();
+
+ // ------------------------------------------------------------------
+ // Methods called by the event source to add events or say that it's
+ // done.
+
+ /// Called when the event source adds a new data or error event.
+ /// Always calls [_updateRequests] after adding.
+ void _addResult(Result result) {
+ _eventQueue.add(result);
+ _updateRequests();
+ }
+
+ /// Called when the event source is done.
+ /// Always calls [_updateRequests] after adding.
+ void _close() {
+ _isDone = true;
+ _updateRequests();
+ }
+
+ // ------------------------------------------------------------------
+ // Internal helper methods.
+
/// Returns an error for when a request is made after cancel.
///
/// Returns a [StateError] with a message saying that either
@@ -257,99 +324,92 @@
return new StateError("Already cancelled");
}
- // Callbacks receiving the events of the source stream.
-
- void _onData(T data) {
- _eventQueue.add(new Result.value(data));
- _checkQueues();
- }
-
- void _onError(error, StackTrace stack) {
- _eventQueue.add(new Result.error(error, stack));
- _checkQueues();
- }
-
- void _onDone() {
- _subscription = null;
- _isDone = true;
- _closeAllRequests();
- }
-
- // Request queue management.
-
/// Adds a new request to the queue.
+ ///
+ /// If the request queue is empty and the request can be completed
+ /// immediately, it skips the queue.
void _addRequest(_EventRequest request) {
- if (_isDone) {
- assert(_requestQueue.isEmpty);
- if (!request.addEvents(_eventQueue)) {
- request.close(_eventQueue);
- }
- return;
- }
if (_requestQueue.isEmpty) {
- if (request.addEvents(_eventQueue)) return;
+ if (request.update(_eventQueue, _isDone)) return;
_ensureListening();
}
_requestQueue.add(request);
}
+}
- /// Ensures that we are listening on events from [_sourceStream].
+
+/// The default implementation of [StreamQueue].
+///
+/// This queue gets its events from a stream which is listened
+/// to when a request needs events.
+class _StreamQueue<T> extends StreamQueue<T> {
+ /// Source of events.
+ final Stream _sourceStream;
+
+ /// Subscription on [_sourceStream] while listening for events.
///
- /// Resumes subscription on [_sourceStream], or creates it if necessary.
+ /// Set to subscription when listening, and set to `null` when the
+ /// subscription is done (and [_isDone] is set to true).
+ StreamSubscription _subscription;
+
+ _StreamQueue(this._sourceStream) : super._();
+
+ Future _cancel() {
+ if (_isDone) return null;
+ if (_subscription == null) _subscription = _sourceStream.listen(null);
+ var future = _subscription.cancel();
+ _close();
+ return future;
+ }
+
void _ensureListening() {
assert(!_isDone);
if (_subscription == null) {
_subscription =
- _sourceStream.listen(_onData, onError: _onError, onDone: _onDone);
+ _sourceStream.listen(
+ (data) {
+ _addResult(new Result.value(data));
+ },
+ onError: (error, StackTrace stackTrace) {
+ _addResult(new Result.error(error, stackTrace));
+ },
+ onDone: () {
+ _subscription = null;
+ this._close();
+ });
} else {
_subscription.resume();
}
}
- /// Removes all requests and closes them.
- ///
- /// Used when the source stream is done.
- /// After this, no further requests will be added to the queue,
- /// requests are immediately served entirely by events already in the event
- /// queue, if any.
- void _closeAllRequests() {
- assert(_isDone);
- while (_requestQueue.isNotEmpty) {
- var request = _requestQueue.removeFirst();
- if (!request.addEvents(_eventQueue)) {
- request.close(_eventQueue);
- }
- }
+ void _pause() {
+ _subscription.pause();
}
- /// Matches events with requests.
- ///
- /// Called after receiving an event.
- void _checkQueues() {
- while (_requestQueue.isNotEmpty) {
- if (_requestQueue.first.addEvents(_eventQueue)) {
- _requestQueue.removeFirst();
- } else {
- return;
- }
- }
- if (!_isDone) {
- _subscription.pause();
- }
- }
-
- /// Extracts the subscription and makes this stream queue unusable.
- ///
- /// Can only be used by the very last request.
- StreamSubscription _dispose() {
+ Stream<T> _extractStream() {
assert(_isClosed);
+ if (_isDone) {
+ return new Stream<T>.empty();
+ }
+
+ if (_subscription == null) {
+ return _sourceStream;
+ }
+
var subscription = _subscription;
_subscription = null;
_isDone = true;
- return subscription;
+
+ var wasPaused = subscription.isPaused;
+ var result = new SubscriptionStream<T>(subscription);
+ // Resume after creating stream because that pauses the subscription too.
+ // This way there won't be a short resumption in the middle.
+ if (wasPaused) subscription.resume();
+ return result;
}
}
+
/// Request object that receives events when they arrive, until fulfilled.
///
/// Each request that cannot be fulfilled immediately is represented by
@@ -367,7 +427,7 @@
abstract class _EventRequest {
/// Handle available events.
///
- /// The available events are provided as a queue. The `addEvents` function
+ /// The available events are provided as a queue. The `update` function
/// should only remove events from the front of the event queue, e.g.,
/// using [removeFirst].
///
@@ -382,22 +442,10 @@
/// This method is called when a request reaches the front of the request
/// queue, and if it returns `false`, it's called again every time a new event
/// becomes available, or when the stream closes.
- bool addEvents(Queue<Result> events);
-
- /// Complete the request.
- ///
- /// This is called when the source stream is done before the request
- /// had a chance to receive all its events. That is, after a call
- /// to [addEvents] has returned `false`.
- /// If there are any unused events available, they are in the [events] queue.
- /// No further events will become available.
- ///
- /// The queue should only remove events from the front of the event queue,
- /// e.g., using [removeFirst].
- ///
- /// If the request kept events in the queue after an [addEvents] call,
- /// this is the last chance to use them.
- void close(Queue<Result> events);
+ /// If the function returns `false` when the stream has already closed
+ /// ([isDone] is true), then the request must call
+ /// [StreamQueue._updateRequests] itself when it's ready to continue.
+ bool update(Queue<Result> events, bool isDone);
}
/// Request for a [StreamQueue.next] call.
@@ -412,16 +460,18 @@
Future<T> get future => _completer.future;
- bool addEvents(Queue<Result> events) {
- if (events.isEmpty) return false;
- events.removeFirst().complete(_completer);
- return true;
- }
-
- void close(Queue<Result> events) {
- var errorFuture =
- new Future.sync(() => throw new StateError("No elements"));
- _completer.complete(errorFuture);
+ bool update(Queue<Result> events, bool isDone) {
+ if (events.isNotEmpty) {
+ events.removeFirst().complete(_completer);
+ return true;
+ }
+ if (isDone) {
+ var errorFuture =
+ new Future.sync(() => throw new StateError("No elements"));
+ _completer.complete(errorFuture);
+ return true;
+ }
+ return false;
}
}
@@ -443,22 +493,22 @@
/// The future completed when the correct number of events have been skipped.
Future get future => _completer.future;
- bool addEvents(Queue<Result> events) {
+ bool update(Queue<Result> events, bool isDone) {
while (_eventsToSkip > 0) {
- if (events.isEmpty) return false;
+ if (events.isEmpty) {
+ if (isDone) break;
+ return false;
+ }
_eventsToSkip--;
+
var event = events.removeFirst();
if (event.isError) {
event.complete(_completer);
return true;
}
}
- _completer.complete(0);
- return true;
- }
-
- void close(Queue<Result> events) {
_completer.complete(_eventsToSkip);
+ return true;
}
}
@@ -481,9 +531,13 @@
/// The future completed when the correct number of events have been captured.
Future get future => _completer.future;
- bool addEvents(Queue<Result> events) {
+ bool update(Queue<Result> events, bool isDone) {
while (_list.length < _eventsToTake) {
- if (events.isEmpty) return false;
+ if (events.isEmpty) {
+ if (isDone) break;
+ return false;
+ }
+
var result = events.removeFirst();
if (result.isError) {
result.complete(_completer);
@@ -494,10 +548,6 @@
_completer.complete(_list);
return true;
}
-
- void close(Queue<Result> events) {
- _completer.complete(_list);
- }
}
/// Request for a [StreamQueue.cancel] call.
@@ -520,22 +570,14 @@
/// The future completed when the cancel request is completed.
Future get future => _completer.future;
- bool addEvents(Queue<Result> events) {
- _shutdown();
- return true;
- }
-
- void close(_) {
- _shutdown();
- }
-
- void _shutdown() {
+ bool update(Queue<Result> events, bool isDone) {
if (_streamQueue._isDone) {
_completer.complete();
} else {
_streamQueue._ensureListening();
- _completer.complete(_streamQueue._dispose().cancel());
+ _completer.complete(_streamQueue._extractStream().listen(null).cancel());
}
+ return true;
}
}
@@ -559,21 +601,12 @@
/// The stream which will contain the remaining events of [_streamQueue].
Stream<T> get stream => _completer.stream;
- bool addEvents(Queue<Result> events) {
- _completeStream(events);
- return true;
- }
-
- void close(Queue<Result> events) {
- _completeStream(events);
- }
-
- void _completeStream(Queue<Result> events) {
+ bool update(Queue<Result> events, bool isDone) {
if (events.isEmpty) {
if (_streamQueue._isDone) {
_completer.setEmpty();
} else {
- _completer.setSourceStream(_getRestStream());
+ _completer.setSourceStream(_streamQueue._extractStream());
}
} else {
// There are prefetched events which needs to be added before the
@@ -582,26 +615,11 @@
for (var event in events) {
event.addTo(controller);
}
- controller.addStream(_getRestStream(), cancelOnError: false)
+ controller.addStream(_streamQueue._extractStream(), cancelOnError: false)
.whenComplete(controller.close);
_completer.setSourceStream(controller.stream);
}
- }
-
- /// Create a stream from the rest of [_streamQueue]'s subscription.
- Stream _getRestStream() {
- if (_streamQueue._isDone) {
- var controller = new StreamController<T>()..close();
- return controller.stream;
- // TODO(lrn). Use the following when 1.11 is released.
- // return new Stream<T>.empty();
- }
- if (_streamQueue._subscription == null) {
- return _streamQueue._sourceStream;
- }
- var subscription = _streamQueue._dispose();
- subscription.resume();
- return new SubscriptionStream<T>(subscription);
+ return true;
}
}
@@ -616,15 +634,15 @@
Future<bool> get future => _completer.future;
- bool addEvents(Queue<Result> events) {
+ bool update(Queue<Result> events, bool isDone) {
if (events.isNotEmpty) {
_completer.complete(true);
return true;
}
+ if (isDone) {
+ _completer.complete(false);
+ return true;
+ }
return false;
}
-
- void close(_) {
- _completer.complete(false);
- }
}
diff --git a/packages/async/pubspec.yaml b/packages/async/pubspec.yaml
index 84b8de5..91d752d 100644
--- a/packages/async/pubspec.yaml
+++ b/packages/async/pubspec.yaml
@@ -1,9 +1,10 @@
name: async
-version: 1.3.0
+version: 1.4.0
author: Dart Team <misc@dartlang.org>
description: Utility functions and classes related to the 'dart:async' library.
homepage: https://www.github.com/dart-lang/async
dev_dependencies:
+ fake_async: "^0.1.2"
stack_trace: "^1.0.0"
test: "^0.12.0"
environment:
diff --git a/packages/async/test/async_memoizer_test.dart b/packages/async/test/async_memoizer_test.dart
index 3289aa9..b8ca38a 100644
--- a/packages/async/test/async_memoizer_test.dart
+++ b/packages/async/test/async_memoizer_test.dart
@@ -21,16 +21,19 @@
});
test("forwards the return value from the function", () async {
+ expect(cache.future, completion(equals("value")));
expect(cache.runOnce(() => "value"), completion(equals("value")));
expect(cache.runOnce(() {}), completion(equals("value")));
});
test("forwards the return value from an async function", () async {
+ expect(cache.future, completion(equals("value")));
expect(cache.runOnce(() async => "value"), completion(equals("value")));
expect(cache.runOnce(() {}), completion(equals("value")));
});
test("forwards the error from an async function", () async {
+ expect(cache.future, throwsA("error"));
expect(cache.runOnce(() async => throw "error"), throwsA("error"));
expect(cache.runOnce(() {}), throwsA("error"));
});
diff --git a/packages/async/test/cancelable_operation_test.dart b/packages/async/test/cancelable_operation_test.dart
new file mode 100644
index 0000000..189c073
--- /dev/null
+++ b/packages/async/test/cancelable_operation_test.dart
@@ -0,0 +1,197 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:test/test.dart';
+
+import 'utils.dart';
+
+void main() {
+ group("without being canceled", () {
+ var completer;
+ setUp(() {
+ completer = new CancelableCompleter(
+ onCancel: expectAsync(() {}, count: 0));
+ });
+
+ test("sends values to the future", () {
+ expect(completer.operation.value, completion(equals(1)));
+ expect(completer.isCompleted, isFalse);
+ completer.complete(1);
+ expect(completer.isCompleted, isTrue);
+ });
+
+ test("sends errors to the future", () {
+ expect(completer.operation.value, throwsA("error"));
+ expect(completer.isCompleted, isFalse);
+ completer.completeError("error");
+ expect(completer.isCompleted, isTrue);
+ });
+
+ test("sends values in a future to the future", () {
+ expect(completer.operation.value, completion(equals(1)));
+ expect(completer.isCompleted, isFalse);
+ completer.complete(new Future.value(1));
+ expect(completer.isCompleted, isTrue);
+ });
+
+ test("sends errors in a future to the future", () {
+ expect(completer.operation.value, throwsA("error"));
+ expect(completer.isCompleted, isFalse);
+ completer.complete(new Future.error("error"));
+ expect(completer.isCompleted, isTrue);
+ });
+
+ group("throws a StateError if completed", () {
+ test("successfully twice", () {
+ completer.complete(1);
+ expect(() => completer.complete(1), throwsStateError);
+ });
+
+ test("successfully then unsuccessfully", () {
+ completer.complete(1);
+ expect(() => completer.completeError("error"), throwsStateError);
+ });
+
+ test("unsuccessfully twice", () {
+ expect(completer.operation.value, throwsA("error"));
+ completer.completeError("error");
+ expect(() => completer.completeError("error"), throwsStateError);
+ });
+
+ test("successfully then with a future", () {
+ completer.complete(1);
+ expect(() => completer.complete(new Completer().future),
+ throwsStateError);
+ });
+
+ test("with a future then successfully", () {
+ completer.complete(new Completer().future);
+ expect(() => completer.complete(1), throwsStateError);
+ });
+
+ test("with a future twice", () {
+ completer.complete(new Completer().future);
+ expect(() => completer.complete(new Completer().future),
+ throwsStateError);
+ });
+ });
+
+ group("CancelableOperation.fromFuture", () {
+ test("forwards values", () {
+ var operation = new CancelableOperation.fromFuture(new Future.value(1));
+ expect(operation.value, completion(equals(1)));
+ });
+
+ test("forwards errors", () {
+ var operation = new CancelableOperation.fromFuture(
+ new Future.error("error"));
+ expect(operation.value, throwsA("error"));
+ });
+ });
+ });
+
+ group("when canceled", () {
+ test("causes the future never to fire", () async {
+ var completer = new CancelableCompleter();
+ completer.operation.value.whenComplete(expectAsync(() {}, count: 0));
+ completer.operation.cancel();
+
+ // Give the future plenty of time to fire if it's going to.
+ await flushMicrotasks();
+ completer.complete();
+ await flushMicrotasks();
+ });
+
+ test("fires onCancel", () {
+ var canceled = false;
+ var completer;
+ completer = new CancelableCompleter(onCancel: expectAsync(() {
+ expect(completer.isCanceled, isTrue);
+ canceled = true;
+ }));
+
+ expect(canceled, isFalse);
+ expect(completer.isCanceled, isFalse);
+ expect(completer.isCompleted, isFalse);
+ completer.operation.cancel();
+ expect(canceled, isTrue);
+ expect(completer.isCanceled, isTrue);
+ expect(completer.isCompleted, isFalse);
+ });
+
+ test("returns the onCancel future each time cancel is called", () {
+ var completer = new CancelableCompleter(onCancel: expectAsync(() {
+ return new Future.value(1);
+ }));
+ expect(completer.operation.cancel(), completion(equals(1)));
+ expect(completer.operation.cancel(), completion(equals(1)));
+ expect(completer.operation.cancel(), completion(equals(1)));
+ });
+
+ test("returns a future even if onCancel doesn't", () {
+ var completer = new CancelableCompleter(onCancel: expectAsync(() {}));
+ expect(completer.operation.cancel(), completes);
+ });
+
+ test("doesn't call onCancel if the completer has completed", () {
+ var completer = new CancelableCompleter(
+ onCancel: expectAsync(() {}, count: 0));
+ completer.complete(1);
+ expect(completer.operation.value, completion(equals(1)));
+ expect(completer.operation.cancel(), completes);
+ });
+
+ test("does call onCancel if the completer has completed to an unfired "
+ "Future", () {
+ var completer = new CancelableCompleter(onCancel: expectAsync(() {}));
+ completer.complete(new Completer().future);
+ expect(completer.operation.cancel(), completes);
+ });
+
+ test("doesn't call onCancel if the completer has completed to a fired "
+ "Future", () async {
+ var completer = new CancelableCompleter(
+ onCancel: expectAsync(() {}, count: 0));
+ completer.complete(new Future.value(1));
+ await completer.operation.value;
+ expect(completer.operation.cancel(), completes);
+ });
+
+ test("can be completed once after being canceled", () async {
+ var completer = new CancelableCompleter();
+ completer.operation.value.whenComplete(expectAsync(() {}, count: 0));
+ await completer.operation.cancel();
+ completer.complete(1);
+ expect(() => completer.complete(1), throwsStateError);
+ });
+ });
+
+ group("asStream()", () {
+ test("emits a value and then closes", () {
+ var completer = new CancelableCompleter();
+ expect(completer.operation.asStream().toList(), completion(equals([1])));
+ completer.complete(1);
+ });
+
+ test("emits an error and then closes", () {
+ var completer = new CancelableCompleter();
+ var queue = new StreamQueue(completer.operation.asStream());
+ expect(queue.next, throwsA("error"));
+ expect(queue.hasNext, completion(isFalse));
+ completer.completeError("error");
+ });
+
+ test("cancels the completer when the subscription is canceled", () {
+ var completer = new CancelableCompleter(onCancel: expectAsync(() {}));
+ var sub = completer.operation.asStream()
+ .listen(expectAsync((_) {}, count: 0));
+ completer.operation.value.whenComplete(expectAsync(() {}, count: 0));
+ sub.cancel();
+ expect(completer.isCanceled, isTrue);
+ });
+ });
+}
diff --git a/packages/async/test/restartable_timer_test.dart b/packages/async/test/restartable_timer_test.dart
new file mode 100644
index 0000000..6732b81
--- /dev/null
+++ b/packages/async/test/restartable_timer_test.dart
@@ -0,0 +1,110 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:fake_async/fake_async.dart';
+import 'package:test/test.dart';
+
+main() {
+ test("runs the callback once the duration has elapsed", () {
+ new FakeAsync().run((async) {
+ var fired = false;
+ var timer = new RestartableTimer(new Duration(seconds: 5), () {
+ fired = true;
+ });
+
+ async.elapse(new Duration(seconds: 4));
+ expect(fired, isFalse);
+
+ async.elapse(new Duration(seconds: 1));
+ expect(fired, isTrue);
+ });
+ });
+
+ test("doesn't run the callback if the timer is canceled", () {
+ new FakeAsync().run((async) {
+ var fired = false;
+ var timer = new RestartableTimer(new Duration(seconds: 5), () {
+ fired = true;
+ });
+
+ async.elapse(new Duration(seconds: 4));
+ expect(fired, isFalse);
+ timer.cancel();
+
+ async.elapse(new Duration(seconds: 4));
+ expect(fired, isFalse);
+ });
+ });
+
+ test("resets the duration if the timer is reset before it fires", () {
+ new FakeAsync().run((async) {
+ var fired = false;
+ var timer = new RestartableTimer(new Duration(seconds: 5), () {
+ fired = true;
+ });
+
+ async.elapse(new Duration(seconds: 4));
+ expect(fired, isFalse);
+ timer.reset();
+
+ async.elapse(new Duration(seconds: 4));
+ expect(fired, isFalse);
+
+ async.elapse(new Duration(seconds: 1));
+ expect(fired, isTrue);
+ });
+ });
+
+ test("re-runs the callback if the timer is reset after firing", () {
+ new FakeAsync().run((async) {
+ var fired = 0;
+ var timer = new RestartableTimer(new Duration(seconds: 5), () {
+ fired++;
+ });
+
+ async.elapse(new Duration(seconds: 5));
+ expect(fired, equals(1));
+ timer.reset();
+
+ async.elapse(new Duration(seconds: 5));
+ expect(fired, equals(2));
+ timer.reset();
+
+ async.elapse(new Duration(seconds: 5));
+ expect(fired, equals(3));
+ });
+ });
+
+ test("runs the callback if the timer is reset after being canceled", () {
+ new FakeAsync().run((async) {
+ var fired = false;
+ var timer = new RestartableTimer(new Duration(seconds: 5), () {
+ fired = true;
+ });
+
+ async.elapse(new Duration(seconds: 4));
+ expect(fired, isFalse);
+ timer.cancel();
+
+ async.elapse(new Duration(seconds: 4));
+ expect(fired, isFalse);
+ timer.reset();
+
+ async.elapse(new Duration(seconds: 5));
+ expect(fired, isTrue);
+ });
+ });
+
+ test("only runs the callback once if the timer isn't reset", () {
+ new FakeAsync().run((async) {
+ var timer = new RestartableTimer(
+ new Duration(seconds: 5),
+ expectAsync(() {}, count: 1));
+ async.elapse(new Duration(seconds: 10));
+ });
+ });
+}
diff --git a/packages/async/test/subscription_stream_test.dart b/packages/async/test/subscription_stream_test.dart
index 03f0ddd..0d1870b 100644
--- a/packages/async/test/subscription_stream_test.dart
+++ b/packages/async/test/subscription_stream_test.dart
@@ -73,8 +73,11 @@
for (var sourceCancels in [false, true]) {
group("${sourceCancels ? "yes" : "no"}:", () {
var subscriptionStream;
+ var onCancel; // Completes if source stream is canceled before done.
setUp(() {
- var source = createErrorStream();
+ var cancelCompleter = new Completer();
+ var source = createErrorStream(cancelCompleter);
+ onCancel = cancelCompleter.future;
var sourceSubscription = source.listen(null,
cancelOnError: sourceCancels);
subscriptionStream = new SubscriptionStream<int>(sourceSubscription);
@@ -89,9 +92,12 @@
cancelOnError: false);
var expected = [1, 2, "To err is divine!"];
if (sourceCancels) {
- var timeout = done.future.timeout(const Duration(milliseconds: 5),
- onTimeout: () => true);
- expect(await timeout, true);
+ await onCancel;
+ // And [done] won't complete at all.
+ bool isDone = false;
+ done.future.then((_) { isDone = true; });
+ await new Future.delayed(const Duration(milliseconds: 5));
+ expect(isDone, false);
} else {
expected.add(4);
await done.future;
@@ -155,20 +161,25 @@
yield 4;
}
-Stream<int> createErrorStream() {
- StreamController controller = new StreamController<int>();
- () async {
- controller.add(1);
+Stream<int> createErrorStream([Completer onCancel]) async* {
+ bool canceled = true;
+ try {
+ yield 1;
await flushMicrotasks();
- controller.add(2);
+ yield 2;
await flushMicrotasks();
- controller.addError("To err is divine!");
+ yield* new Future.error("To err is divine!").asStream();
await flushMicrotasks();
- controller.add(4);
+ yield 4;
await flushMicrotasks();
- controller.close();
- }();
- return controller.stream;
+ canceled = false;
+ } finally {
+ // Completes before the "done", but should be after all events.
+ if (canceled && onCancel != null) {
+ await flushMicrotasks();
+ onCancel.complete();
+ }
+ }
}
Stream<int> createLongStream() async* {
diff --git a/packages/charted/lib/charts/behaviors/axis_label_tooltip.dart b/packages/charted/lib/charts/behaviors/axis_label_tooltip.dart
index 6361a4e..092db6b 100644
--- a/packages/charted/lib/charts/behaviors/axis_label_tooltip.dart
+++ b/packages/charted/lib/charts/behaviors/axis_label_tooltip.dart
@@ -38,10 +38,9 @@
void _subscribe() {
var elements = _area.host.querySelectorAll(_AXIS_SELECTOR);
_disposer.dispose();
- _disposer.addAll(
- elements.map((x) => x.onMouseOver.listen(_handleMouseOver)));
- _disposer.addAll(
- elements.map((x) => x.onMouseOut.listen(_handleMouseOut)));
+ _disposer
+ .addAll(elements.map((x) => x.onMouseOver.listen(_handleMouseOver)));
+ _disposer.addAll(elements.map((x) => x.onMouseOut.listen(_handleMouseOut)));
}
void _handleMouseOver(MouseEvent e) {
@@ -51,10 +50,8 @@
ensureRenderAreaRect();
_tooltipRoot.text = target.dataset['detail'];
- var position = computeTooltipPosition(
- target.getBoundingClientRect(),
- _tooltipRoot.getBoundingClientRect(),
- _renderAreaRect);
+ var position = computeTooltipPosition(target.getBoundingClientRect(),
+ _tooltipRoot.getBoundingClientRect(), _renderAreaRect);
_tooltipRoot.style
..left = '${position.x}px'
@@ -94,12 +91,13 @@
_renderAreaRect = new math.Rectangle(
_hostAreaRect.left + layout.chartArea.x + layout.renderArea.x,
_hostAreaRect.top + layout.chartArea.y + layout.renderArea.y,
- layout.renderArea.width, layout.renderArea.height);
+ layout.renderArea.width,
+ layout.renderArea.height);
}
/// Computes the ideal tooltip position based on orientation.
- math.Point computeTooltipPosition(math.Rectangle label,
- math.Rectangle tooltip, math.Rectangle renderArea) {
+ math.Point computeTooltipPosition(
+ math.Rectangle label, math.Rectangle tooltip, math.Rectangle renderArea) {
var x = label.left + (label.width - tooltip.width) / 2,
y = label.top + (label.height - tooltip.height) / 2;
diff --git a/packages/charted/lib/charts/behaviors/chart_tooltip.dart b/packages/charted/lib/charts/behaviors/chart_tooltip.dart
index f588d80..083d0f8 100644
--- a/packages/charted/lib/charts/behaviors/chart_tooltip.dart
+++ b/packages/charted/lib/charts/behaviors/chart_tooltip.dart
@@ -27,8 +27,8 @@
/// Constructs the tooltip.
/// If [showDimensionValue] is set, displays the dimension value as title.
/// If [showMeasureTotal] is set, displays the total value.
- ChartTooltip({
- this.showSelectedMeasure: false,
+ ChartTooltip(
+ {this.showSelectedMeasure: false,
this.showDimensionValue: false,
this.showMeasureTotal: false,
this.orientation: ORIENTATION_RIGHT});
@@ -38,8 +38,8 @@
_area = area;
_state = area.state;
_disposer.addAll([
- area.onValueMouseOver.listen(show),
- area.onValueMouseOut.listen(hide)
+ area.onValueMouseOver.listen(show),
+ area.onValueMouseOut.listen(hide)
]);
// Tooltip requires host to be position: relative.
@@ -92,8 +92,8 @@
var activeMeasures = [];
if (showSelectedMeasure) {
activeMeasures.addAll(_state.selection);
- activeMeasures.add(_state.preview != null ? _state.preview :
- _state.hovered.first);
+ activeMeasures
+ .add(_state.preview != null ? _state.preview : _state.hovered.first);
if (activeMeasures.isEmpty) {
for (var series in _area.config.series) {
activeMeasures.addAll(series.measures);
@@ -105,35 +105,33 @@
var data = (showSelectedMeasure) ? activeMeasures : e.series.measures;
// Create the tooltip items base on the number of measures in the series.
- var items = _tooltipRoot.selectAll('.tooltip-item').
- data(data);
+ var items = _tooltipRoot.selectAll('.tooltip-item').data(data);
items.enter.append('div')
- ..classed('tooltip-item')
- ..classedWithCallback('active', (d, i, c) =>
- !showSelectedMeasure && (d == e.column));
+ ..classed('tooltip-item')
+ ..classedWithCallback(
+ 'active', (d, i, c) => !showSelectedMeasure && (d == e.column));
// Display the label for the currently active series.
var tooltipItems = _tooltipRoot.selectAll('.tooltip-item');
tooltipItems.append('div')
- ..classed('tooltip-item-label')
- ..textWithCallback((d, i, c) => _area.data.columns.
- elementAt((showSelectedMeasure) ? d :
- e.series.measures.elementAt(i)).label);
+ ..classed('tooltip-item-label')
+ ..textWithCallback((d, i, c) => _area.data.columns
+ .elementAt((showSelectedMeasure) ? d : e.series.measures.elementAt(i))
+ .label);
// Display the value of the currently active series
tooltipItems.append('div')
..classed('tooltip-item-value')
- ..styleWithCallback('color', (d, i, c) =>
- _area.theme.getColorForKey(d))
+ ..styleWithCallback('color', (d, i, c) => _area.theme.getColorForKey(d))
..textWithCallback((d, i, c) {
- var formatter = _getFormatterForColumn(d),
- value = _area.data.rows.elementAt(e.row).elementAt(d);
- return (formatter != null) ? formatter(value) : value.toString();
- });
+ var formatter = _getFormatterForColumn(d),
+ value = _area.data.rows.elementAt(e.row).elementAt(d);
+ return (formatter != null) ? formatter(value) : value.toString();
+ });
math.Point position = computeTooltipPosition(
new math.Point(e.chartX, e.chartY),
- _tooltipRoot.first.getBoundingClientRect());
+ _tooltipRoot.first.getBoundingClientRect());
// Set position of the tooltip and display it.
_tooltipRoot
@@ -143,13 +141,10 @@
}
static String switchPositionDirection(String direction) =>
- direction == ORIENTATION_LEFT
- ? ORIENTATION_RIGHT
- : ORIENTATION_LEFT;
+ direction == ORIENTATION_LEFT ? ORIENTATION_RIGHT : ORIENTATION_LEFT;
/// Computes the ideal tooltip position based on orientation.
- math.Point computeTooltipPosition(
- math.Point coord, math.Rectangle rect) {
+ math.Point computeTooltipPosition(math.Point coord, math.Rectangle rect) {
var x, y, direction;
direction = _area.config.isRTL && _area.config.switchAxesForRTL
? switchPositionDirection(orientation)
@@ -198,4 +193,3 @@
_tooltipRoot.style('opacity', '0');
}
}
-
diff --git a/packages/charted/lib/charts/behaviors/hovercard.dart b/packages/charted/lib/charts/behaviors/hovercard.dart
index f01d2ac..ff62aa7 100644
--- a/packages/charted/lib/charts/behaviors/hovercard.dart
+++ b/packages/charted/lib/charts/behaviors/hovercard.dart
@@ -58,8 +58,14 @@
bool _showDimensionTitle;
Iterable<int> _columnsToShow;
- Iterable placementOrder =
- const['orientation', 'top', 'right', 'bottom', 'left', 'orientation'];
+ Iterable placementOrder = const [
+ 'orientation',
+ 'top',
+ 'right',
+ 'bottom',
+ 'left',
+ 'orientation'
+ ];
int offset = 20;
ChartArea _area;
@@ -68,8 +74,8 @@
Element _hovercardRoot;
- Hovercard({
- bool isMouseTracking,
+ Hovercard(
+ {bool isMouseTracking,
bool isMultiValue: false,
bool showDimensionTitle: false,
List columnsToShow: const [],
@@ -91,8 +97,8 @@
// Subscribe to events.
if (_isMouseTracking) {
_disposer.addAll([
- _area.onValueMouseOver.listen(_handleMouseOver),
- _area.onValueMouseOut.listen(_handleMouseOut)
+ _area.onValueMouseOver.listen(_handleMouseOver),
+ _area.onValueMouseOut.listen(_handleMouseOut)
]);
} else {
_disposer.add(_state.changes.listen(_handleStateChange));
@@ -206,12 +212,12 @@
var rowData = area.data.rows.elementAt(row),
measurePosition = 0,
isNegative = false,
- dimensionPosition = dimensionScale.scale(
- rowData.elementAt(dimensionCol)) + dimensionCenterOffset;
+ dimensionPosition = dimensionScale
+ .scale(rowData.elementAt(dimensionCol)) +
+ dimensionCenterOffset;
if (_isMultiValue) {
- var max = SMALL_INT_MIN,
- min = SMALL_INT_MAX;
+ var max = SMALL_INT_MIN, min = SMALL_INT_MAX;
area.config.series.forEach((ChartSeries series) {
CartesianRenderer renderer = series.renderer;
Extent extent = renderer.extentForRow(rowData);
@@ -227,24 +233,26 @@
}
if (area.config.isLeftAxisPrimary) {
- _positionAtPoint(measurePosition, dimensionPosition,
- 0, dimensionOffset, isNegative, true);
+ _positionAtPoint(measurePosition, dimensionPosition, 0, dimensionOffset,
+ isNegative, true);
} else {
- _positionAtPoint(dimensionPosition, measurePosition,
- dimensionOffset, 0, isNegative, false);
+ _positionAtPoint(dimensionPosition, measurePosition, dimensionOffset, 0,
+ isNegative, false);
}
}
- void _positionAtPoint(num x, num y,
- num xBand, num yBand, bool negative, [bool isLeftPrimary = false]) {
+ void _positionAtPoint(num x, num y, num xBand, num yBand, bool negative,
+ [bool isLeftPrimary = false]) {
var rect = _hovercardRoot.getBoundingClientRect(),
width = rect.width,
height = rect.height,
- scaleToHostY =
- (_area.theme.padding != null ? _area.theme.padding.top : 0) +
+ scaleToHostY = (_area.theme.padding != null
+ ? _area.theme.padding.top
+ : 0) +
(_area.layout.renderArea.y),
- scaleToHostX =
- (_area.theme.padding != null ? _area.theme.padding.start: 0) +
+ scaleToHostX = (_area.theme.padding != null
+ ? _area.theme.padding.start
+ : 0) +
(_area.layout.renderArea.x),
renderAreaHeight = _area.layout.renderArea.height,
renderAreaWidth = _area.layout.renderArea.width;
@@ -280,8 +288,10 @@
// Check if the popup is contained in the RenderArea.
// If not, try other placements.
- if (top > 0 && left > 0 &&
- top + height < renderAreaHeight && left + width < renderAreaWidth) {
+ if (top > 0 &&
+ left > 0 &&
+ top + height < renderAreaHeight &&
+ left + width < renderAreaWidth) {
break;
}
}
@@ -300,7 +310,7 @@
element.append(titleElement);
}
- var measureVals = _getMeasuresData(column, row);
+ var measureVals = _getMeasuresData(column, row);
measureVals.forEach((ChartLegendItem item) {
var labelElement = new Element.div()
..className = 'hovercard-measure-label'
@@ -349,7 +359,7 @@
ChartLegendItem _createHovercardItem(int column, int row) {
var rowData = _area.data.rows.elementAt(row),
- columns = _area.data.columns,
+ columns = _area.data.columns,
spec = columns.elementAt(column),
colorKey = _area.useRowColoring ? row : column,
formatter = _getFormatterForColumn(column),
@@ -370,16 +380,17 @@
} else {
var count = (_area as CartesianArea).useTwoDimensionAxes ? 2 : 1,
dimensions = _area.config.dimensions.take(count);
- return dimensions.map(
- (int c) =>
- _getFormatterForColumn(c)(rowData.elementAt(c))).join(', ');
+ return dimensions
+ .map((int c) => _getFormatterForColumn(c)(rowData.elementAt(c)))
+ .join(', ');
}
}
// TODO: Move this to a common place?
Scale _getScaleForColumn(int column) {
var series = _area.config.series.firstWhere(
- (ChartSeries x) => x.measures.contains(column), orElse: () => null);
+ (ChartSeries x) => x.measures.contains(column),
+ orElse: () => null);
return series != null
? (_area as CartesianArea).measureScales(series).first
: null;
diff --git a/packages/charted/lib/charts/behaviors/line_marker.dart b/packages/charted/lib/charts/behaviors/line_marker.dart
index f6abd22..3166f3c 100644
--- a/packages/charted/lib/charts/behaviors/line_marker.dart
+++ b/packages/charted/lib/charts/behaviors/line_marker.dart
@@ -11,7 +11,7 @@
/// A behavior that draws marking lines on the chart.
class LineMarker implements ChartBehavior {
/// Position of the line markers.
- final Map<int,dynamic> positions;
+ final Map<int, dynamic> positions;
/// If true, the markers are drawn above the series
final bool drawAboveSeries;
@@ -37,8 +37,7 @@
_area = area;
_parent = drawAboveSeries ? upper : lower;
_isLeftAxisPrimary = _area.config.isLeftAxisPrimary;
- _axesChangeSubscription =
- _area.onChartAxesUpdated.listen((_) => _update());
+ _axesChangeSubscription = _area.onChartAxesUpdated.listen((_) => _update());
_update();
}
@@ -53,14 +52,14 @@
assert(_isDimension(column));
int index;
- for(index = 0;
- _area.config.dimensions.elementAt(index) != column; ++index);
+ for (index = 0;
+ _area.config.dimensions.elementAt(index) != column;
+ ++index);
assert(index == 0 || index == 1 && _area.useTwoDimensionAxes);
var dimensionAtBottom =
- index == 1 && _isLeftAxisPrimary ||
- index == 0 && !_isLeftAxisPrimary,
+ index == 1 && _isLeftAxisPrimary || index == 0 && !_isLeftAxisPrimary,
scale = _area.dimensionScales.elementAt(index),
scaled = scale.scale(positions[column]),
theme = _area.theme.getDimensionAxisTheme(),
@@ -71,8 +70,7 @@
top = initial ? bottom : renderAreaRect.y;
if (scale is OrdinalScale) {
- var band = scale.rangeBand,
- bandPadding = theme.axisBandInnerPadding;
+ var band = scale.rangeBand, bandPadding = theme.axisBandInnerPadding;
scaled = scaled - band * bandPadding + _area.theme.defaultStrokeWidth;
band = band + 2 * (band * bandPadding - _area.theme.defaultStrokeWidth);
return dimensionAtBottom
@@ -89,10 +87,9 @@
throw new UnimplementedError('Measure axis markers');
}
- String _getMarkerPath(int column, bool initial) =>
- _isDimension(column)
- ? _pathForDimension(column, initial)
- : _pathForMeasure(column, initial);
+ String _getMarkerPath(int column, bool initial) => _isDimension(column)
+ ? _pathForDimension(column, initial)
+ : _pathForMeasure(column, initial);
void _update() {
if (!_area.isReady) return;
@@ -104,11 +101,11 @@
});
if (animate) {
- _markers.transition()
+ _markers
+ .transition()
.attrWithCallback('d', (d, i, e) => _getMarkerPath(d, false));
}
_markers.exit.remove();
}
}
-
diff --git a/packages/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart b/packages/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart
index cc55c2d..74e93ca 100644
--- a/packages/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart
+++ b/packages/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart
@@ -11,7 +11,7 @@
class BarChartRenderer extends CartesianRendererBase {
static const RADIUS = 2;
- final Iterable<int> dimensionsUsingBand = const[0];
+ final Iterable<int> dimensionsUsingBand = const [0];
final bool alwaysAnimate;
@override
@@ -38,12 +38,12 @@
dimensionScale = area.dimensionScales.first;
var rows = new List()
- ..addAll(area.data.rows.map((e) =>
- new List.generate(
- measuresCount, (i) => e[series.measures.elementAt(i)])));
+ ..addAll(area.data.rows.map((e) => new List.generate(
+ measuresCount, (i) => e[series.measures.elementAt(i)])));
- var dimensionVals = area.data.rows.map(
- (row) => row.elementAt(area.config.dimensions.first)).toList();
+ var dimensionVals = area.data.rows
+ .map((row) => row.elementAt(area.config.dimensions.first))
+ .toList();
var bars = new OrdinalScale()
..domain = new Range(series.measures.length).toList()
@@ -56,32 +56,37 @@
groups.enter.append('g')
..classed('bar-rdr-rowgroup')
- ..attrWithCallback('transform', (d, i, c) => verticalBars ?
- 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' :
- 'translate(0, ${dimensionScale.scale(dimensionVals[i])})');
+ ..attrWithCallback(
+ 'transform',
+ (d, i, c) => verticalBars
+ ? 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)'
+ : 'translate(0, ${dimensionScale.scale(dimensionVals[i])})');
groups.attrWithCallback('data-row', (d, i, e) => i);
groups.exit.remove();
if (animateBarGroups) {
groups.transition()
- ..attrWithCallback('transform', (d, i, c) => verticalBars ?
- 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' :
- 'translate(0, ${dimensionScale.scale(dimensionVals[i])})')
+ ..attrWithCallback(
+ 'transform',
+ (d, i, c) => verticalBars
+ ? 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)'
+ : 'translate(0, ${dimensionScale.scale(dimensionVals[i])})')
..duration(theme.transitionDurationMilliseconds);
}
// TODO: Test interactions between stroke width and bar width.
var barWidth = bars.rangeBand.abs() -
- theme.defaultSeparatorWidth - theme.defaultStrokeWidth,
+ theme.defaultSeparatorWidth -
+ theme.defaultStrokeWidth,
strokeWidth = theme.defaultStrokeWidth,
strokeWidthOffset = strokeWidth ~/ 2;
// Create and update the bars
// Avoids animation on first render unless alwaysAnimate is set to true.
- var bar = groups.selectAll('.bar-rdr-bar').dataWithCallback(
- (d, i, c) => rows[i]),
+ var bar =
+ groups.selectAll('.bar-rdr-bar').dataWithCallback((d, i, c) => rows[i]),
scaled0 = measureScale.scale(0).round();
var getBarLength = (d) {
@@ -105,42 +110,43 @@
return fn(
bars.scale(i).toInt() + strokeWidthOffset,
animate ? rect.height : getBarPos(d),
- barWidth, animate ? 0 : getBarLength(d), RADIUS);
+ barWidth,
+ animate ? 0 : getBarLength(d),
+ RADIUS);
} else {
- var fn = d > 0 ? rightRoundedRect : leftRoundedRect;
- return fn(
- getBarPos(d), bars.scale(i).toInt() + strokeWidthOffset,
+ var fn = d > 0 ? rightRoundedRect : leftRoundedRect;
+ return fn(getBarPos(d), bars.scale(i).toInt() + strokeWidthOffset,
animate ? 0 : getBarLength(d), barWidth, RADIUS);
}
};
bar.enter.appendWithCallback((d, i, e) {
- var rect = Namespace.createChildElement('path', e),
- measure = series.measures.elementAt(i),
- row = int.parse(e.dataset['row']),
- color = colorForValue(measure, row),
- filter = filterForValue(measure, row),
- style = stylesForValue(measure, row);
+ var rect = Namespace.createChildElement('path', e),
+ measure = series.measures.elementAt(i),
+ row = int.parse(e.dataset['row']),
+ color = colorForValue(measure, row),
+ filter = filterForValue(measure, row),
+ style = stylesForValue(measure, row);
- if (!isNullOrEmpty(style)) {
- rect.classes.addAll(style);
- }
- rect.classes.add('bar-rdr-bar');
+ if (!isNullOrEmpty(style)) {
+ rect.classes.addAll(style);
+ }
+ rect.classes.add('bar-rdr-bar');
- rect.attributes
- ..['d'] = buildPath(d, i, animateBarGroups)
- ..['stroke-width'] = '${strokeWidth}px'
- ..['fill'] = color
- ..['stroke'] = color;
+ rect.attributes
+ ..['d'] = buildPath(d, i, animateBarGroups)
+ ..['stroke-width'] = '${strokeWidth}px'
+ ..['fill'] = color
+ ..['stroke'] = color;
- if (!isNullOrEmpty(filter)) {
- rect.attributes['filter'] = filter;
- }
- if (!animateBarGroups) {
- rect.attributes['data-column'] = '$measure';
- }
- return rect;
- })
+ if (!isNullOrEmpty(filter)) {
+ rect.attributes['filter'] = filter;
+ }
+ if (!animateBarGroups) {
+ rect.attributes['data-column'] = '$measure';
+ }
+ return rect;
+ })
..on('click', (d, i, e) => _event(mouseClickController, d, i, e))
..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e))
..on('mouseout', (d, i, e) => _event(mouseOutController, d, i, e));
@@ -167,8 +173,7 @@
});
bar.transition()
- ..attrWithCallback('d',
- (d, i, e) => buildPath(d, i, false));
+ ..attrWithCallback('d', (d, i, e) => buildPath(d, i, false));
}
bar.exit.remove();
@@ -184,8 +189,9 @@
double get bandInnerPadding {
assert(series != null && area != null);
var measuresCount = series.measures.length;
- return measuresCount > 2 ? 1 - (measuresCount / (measuresCount + 1)) :
- area.theme.getDimensionAxisTheme().axisBandInnerPadding;
+ return measuresCount > 2
+ ? 1 - (measuresCount / (measuresCount + 1))
+ : area.theme.getDimensionAxisTheme().axisBandInnerPadding;
}
@override
@@ -199,12 +205,12 @@
var groups = host.querySelectorAll('.bar-rdr-rowgroup');
if (groups == null || groups.isEmpty) return;
- for(int i = 0, len = groups.length; i < len; ++i) {
+ for (int i = 0, len = groups.length; i < len; ++i) {
var group = groups.elementAt(i),
bars = group.querySelectorAll('.bar-rdr-bar'),
row = int.parse(group.dataset['row']);
- for(int j = 0, barsCount = bars.length; j < barsCount; ++j) {
+ for (int j = 0, barsCount = bars.length; j < barsCount; ++j) {
var bar = bars.elementAt(j),
column = int.parse(bar.dataset['column']),
color = colorForValue(column, row),
@@ -228,8 +234,7 @@
if (controller == null) return;
var rowStr = e.parent.dataset['row'];
var row = rowStr != null ? int.parse(rowStr) : null;
- controller.add(
- new DefaultChartEventImpl(scope.event, area,
- series, row, series.measures.elementAt(index), data));
+ controller.add(new DefaultChartEventImpl(scope.event, area, series, row,
+ series.measures.elementAt(index), data));
}
}
diff --git a/packages/charted/lib/charts/cartesian_renderers/bubble_chart_renderer.dart b/packages/charted/lib/charts/cartesian_renderers/bubble_chart_renderer.dart
index 7c285cc..b05dd81 100644
--- a/packages/charted/lib/charts/cartesian_renderers/bubble_chart_renderer.dart
+++ b/packages/charted/lib/charts/cartesian_renderers/bubble_chart_renderer.dart
@@ -1,15 +1,15 @@
-/*
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file or at
- * https://developers.google.com/open-source/licenses/bsd
- */
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
part of charted.charts;
class BubbleChartRenderer extends CartesianRendererBase {
- final Iterable<int> dimensionsUsingBand = const[];
+ final Iterable<int> dimensionsUsingBand = const [];
final double maxBubbleRadius;
final bool alwaysAnimate;
@@ -20,13 +20,9 @@
@override
final String name = "bubble-rdr";
- BubbleChartRenderer({
- this.maxBubbleRadius: 20.0,
- this.alwaysAnimate: false});
+ BubbleChartRenderer({this.maxBubbleRadius: 20.0, this.alwaysAnimate: false});
- /*
- * BubbleChart needs two dimension axes.
- */
+ /// BubbleChart needs two dimension axes.
@override
bool prepare(ChartArea area, ChartSeries series) {
_ensureAreaAndSeries(area, series);
@@ -50,7 +46,7 @@
yDimensionScale = area.dimensionScales.last,
theme = area.theme,
bubbleRadiusFactor =
- maxBubbleRadius / min([geometry.width, geometry.height]);
+ maxBubbleRadius / min([geometry.width, geometry.height]);
String color(i) => theme.getColorForKey(series.measures.elementAt(i));
@@ -79,8 +75,8 @@
});
group.exit.remove();
- var measures = group.selectAll('.bubble').dataWithCallback(
- (d, i, e) => columns[i]);
+ var measures =
+ group.selectAll('.bubble').dataWithCallback((d, i, e) => columns[i]);
measures.enter.append('circle')..classed('bubble');
measures.each((d, i, e) {
@@ -111,9 +107,7 @@
@override
Extent get extent {
assert(series != null && area != null);
- var rows = area.data.rows,
- max = rows[0][series.measures.first],
- min = max;
+ var rows = area.data.rows, max = rows[0][series.measures.first], min = max;
rows.forEach((row) {
series.measures.forEach((idx) {
@@ -129,21 +123,19 @@
var groups = host.querySelectorAll('.bar-rdr-rowgroup');
if (groups == null || groups.isEmpty) return;
- for(int i = 0, len = groups.length; i < len; ++i) {
+ for (int i = 0, len = groups.length; i < len; ++i) {
var group = groups.elementAt(i),
bars = group.querySelectorAll('.bar-rdr-bar'),
row = int.parse(group.dataset['row']);
- for(int j = 0, barsCount = bars.length; j < barsCount; ++j) {
+ for (int j = 0, barsCount = bars.length; j < barsCount; ++j) {
var bar = bars.elementAt(j),
column = int.parse(bar.dataset['column']),
color = colorForValue(column, row);
bar.classes.removeAll(ChartState.VALUE_CLASS_NAMES);
bar.classes.addAll(stylesForValue(column, row));
- bar.style
- ..setProperty('fill', color)
- ..setProperty('stroke', color);
+ bar.style..setProperty('fill', color)..setProperty('stroke', color);
}
}
}
@@ -152,7 +144,7 @@
if (controller == null) return;
var rowStr = e.parent.dataset['row'];
var row = rowStr != null ? int.parse(rowStr) : null;
- controller.add(
- new DefaultChartEventImpl(_scope.event, area, series, row, index, data));
+ controller.add(new DefaultChartEventImpl(
+ _scope.event, area, series, row, index, data));
}
}
diff --git a/packages/charted/lib/charts/cartesian_renderers/cartesian_base_renderer.dart b/packages/charted/lib/charts/cartesian_renderers/cartesian_base_renderer.dart
index 08d8917..96873bd 100644
--- a/packages/charted/lib/charts/cartesian_renderers/cartesian_base_renderer.dart
+++ b/packages/charted/lib/charts/cartesian_renderers/cartesian_base_renderer.dart
@@ -1,10 +1,10 @@
-/**
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file or at
- * https://developers.google.com/open-source/licenses/bsd
- */
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
part of charted.charts;
@@ -20,9 +20,9 @@
List<int> _columnStateCache;
List<Iterable<String>> _columnStylesCache;
- final _valueColorCache = new Map<int,String>();
- final _valueFilterCache = new Map<int,String>();
- final _valueStylesCache = new Map<int,Iterable<String>>();
+ final _valueColorCache = new Map<int, String>();
+ final _valueFilterCache = new Map<int, String>();
+ final _valueStylesCache = new Map<int, Iterable<String>>();
Element host;
Selection root;
@@ -103,13 +103,10 @@
@override
Extent extentForRow(Iterable row) {
assert(series != null && area != null);
- var measures = series.measures,
- max = SMALL_INT_MIN,
- min = SMALL_INT_MAX;
+ var measures = series.measures, max = SMALL_INT_MIN, min = SMALL_INT_MAX;
- for (int i = 0, len = measures.length; i < len; ++i) {
- var measure = measures.elementAt(i),
- value = row.elementAt(measure);
+ for (int i = 0, len = measures.length; i < len; ++i) {
+ var measure = measures.elementAt(i), value = row.elementAt(measure);
if (value != null && value.isFinite) {
if (value > max) {
max = value;
@@ -183,14 +180,13 @@
Iterable<String> stylesForColumn(int column) {
if (_columnStylesCache[column] == null) {
if (state == null || area.useRowColoring) {
- _columnStylesCache[column] = const[];
+ _columnStylesCache[column] = const [];
} else {
- var styles = [],
- flags = _columnStateCache[column];
+ var styles = [], flags = _columnStateCache[column];
if (flags & ChartState.COL_SELECTED != 0) {
styles.add(ChartState.COL_SELECTED_CLASS);
- } else if (flags & ChartState.COL_UNSELECTED != 0){
+ } else if (flags & ChartState.COL_UNSELECTED != 0) {
styles.add(ChartState.COL_UNSELECTED_CLASS);
}
@@ -226,7 +222,7 @@
var hash = hash2(column, row);
if (_valueStylesCache[hash] == null) {
if (state == null) {
- _valueStylesCache[hash] = const[];
+ _valueStylesCache[hash] = const [];
} else {
var styles = stylesForColumn(column).toList();
if (state.highlights.isNotEmpty) {
@@ -268,8 +264,8 @@
var flags = _columnStateCache[column];
if (state.highlights.isNotEmpty) {
flags |= (state.highlights.any((x) => x.last == row)
- ? ChartState.VAL_HIGHLIGHTED
- : ChartState.VAL_UNHIGHLIGHTED);
+ ? ChartState.VAL_HIGHLIGHTED
+ : ChartState.VAL_UNHIGHLIGHTED);
}
if (state.hovered != null && state.hovered.last == row) {
flags |= ChartState.VAL_HOVERED;
diff --git a/packages/charted/lib/charts/cartesian_renderers/line_chart_renderer.dart b/packages/charted/lib/charts/cartesian_renderers/line_chart_renderer.dart
index 0584024..e67fcaa 100644
--- a/packages/charted/lib/charts/cartesian_renderers/line_chart_renderer.dart
+++ b/packages/charted/lib/charts/cartesian_renderers/line_chart_renderer.dart
@@ -1,15 +1,15 @@
-/*
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file or at
- * https://developers.google.com/open-source/licenses/bsd
- */
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
part of charted.charts;
class LineChartRenderer extends CartesianRendererBase {
- final Iterable<int> dimensionsUsingBand = const[];
+ final Iterable<int> dimensionsUsingBand = const [];
final SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
final bool alwaysAnimate;
@@ -29,16 +29,14 @@
@override
final String name = "line-rdr";
- LineChartRenderer({
- this.alwaysAnimate: false,
+ LineChartRenderer(
+ {this.alwaysAnimate: false,
this.trackDataPoints: true,
this.trackOnDimensionAxis: false,
- this.quantitativeScaleProximity: 5 });
+ this.quantitativeScaleProximity: 5});
- /*
- * Returns false if the number of dimension axes on the area is 0.
- * Otherwise, the first dimension scale is used to render the chart.
- */
+ // Returns false if the number of dimension axes on the area is 0.
+ // Otherwise, the first dimension scale is used to render the chart.
@override
bool prepare(ChartArea area, ChartSeries series) {
_ensureAreaAndSeries(area, series);
@@ -62,8 +60,9 @@
// We only support one dimension axes, so we always use the
// first dimension.
- var x = area.data.rows.map(
- (row) => row.elementAt(area.config.dimensions.first)).toList();
+ var x = area.data.rows
+ .map((row) => row.elementAt(area.config.dimensions.first))
+ .toList();
var rangeBandOffset =
dimensionScale is OrdinalScale ? dimensionScale.rangeBand / 2 : 0;
@@ -152,15 +151,15 @@
linePoints
..each((d, i, e) {
- var color = colorForColumn(d);
- e.attributes
- ..['r'] = '4'
- ..['stroke'] = color
- ..['fill'] = color
- ..['data-column'] = '$d';
+ var color = colorForColumn(d);
+ e.attributes
+ ..['r'] = '4'
+ ..['stroke'] = color
+ ..['fill'] = color
+ ..['data-column'] = '$d';
})
..on('click', _mouseClickHandler)
- ..on('mousemove', _mouseOverHandler) // Ensure that we update values
+ ..on('mousemove', _mouseOverHandler) // Ensure that we update values
..on('mouseover', _mouseOverHandler)
..on('mouseout', _mouseOutHandler);
@@ -178,8 +177,7 @@
var x = _xPositions[row],
measureVal = area.data.rows.elementAt(row).elementAt(d);
if (measureVal != null && measureVal.isFinite) {
- var color = colorForColumn(d),
- filter = filterForColumn(d);
+ var color = colorForColumn(d), filter = filterForColumn(d);
e.attributes
..['cx'] = '$x'
..['cy'] = '${yScale.scale(measureVal)}'
@@ -208,7 +206,7 @@
..style('visibility', 'hidden');
}
- int _getNearestRowIndex(double x) {
+ int _getNearestRowIndex(num x) {
var lastSmallerValue = 0;
var chartX = x - area.layout.renderArea.x;
for (var i = 0; i < _xPositions.length; i++) {
@@ -216,8 +214,9 @@
if (pos < chartX) {
lastSmallerValue = pos;
} else {
- return i == 0 ? 0 :
- (chartX - lastSmallerValue <= pos - chartX) ? i - 1 : i;
+ return i == 0
+ ? 0
+ : (chartX - lastSmallerValue <= pos - chartX) ? i - 1 : i;
}
}
return _xPositions.length - 1;
diff --git a/packages/charted/lib/charts/cartesian_renderers/stackedbar_chart_renderer.dart b/packages/charted/lib/charts/cartesian_renderers/stackedbar_chart_renderer.dart
index 396b804..05623ba 100644
--- a/packages/charted/lib/charts/cartesian_renderers/stackedbar_chart_renderer.dart
+++ b/packages/charted/lib/charts/cartesian_renderers/stackedbar_chart_renderer.dart
@@ -11,7 +11,7 @@
class StackedBarChartRenderer extends CartesianRendererBase {
static const RADIUS = 2;
- final Iterable<int> dimensionsUsingBand = const[0];
+ final Iterable<int> dimensionsUsingBand = const [0];
final bool alwaysAnimate;
@override
@@ -41,28 +41,32 @@
dimensionScale = area.dimensionScales.first;
var rows = new List()
- ..addAll(area.data.rows.map((e) =>
- new List.generate(measuresCount,
- (i) => e.elementAt(series.measures.elementAt(_reverseIdx(i))))));
+ ..addAll(area.data.rows.map((e) => new List.generate(measuresCount,
+ (i) => e.elementAt(series.measures.elementAt(_reverseIdx(i))))));
- var dimensionVals = area.data.rows.map(
- (row) => row.elementAt(area.config.dimensions.first)).toList();
+ var dimensionVals = area.data.rows
+ .map((row) => row.elementAt(area.config.dimensions.first))
+ .toList();
var groups = root.selectAll('.stack-rdr-rowgroup').data(rows);
var animateBarGroups = alwaysAnimate || !groups.isEmpty;
groups.enter.append('g')
..classed('stack-rdr-rowgroup')
- ..attrWithCallback('transform', (d, i, c) => verticalBars ?
- 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' :
- 'translate(0, ${dimensionScale.scale(dimensionVals[i])})');
+ ..attrWithCallback(
+ 'transform',
+ (d, i, c) => verticalBars
+ ? 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)'
+ : 'translate(0, ${dimensionScale.scale(dimensionVals[i])})');
groups.attrWithCallback('data-row', (d, i, e) => i);
groups.exit.remove();
if (animateBarGroups) {
groups.transition()
- ..attrWithCallback('transform', (d, i, c) => verticalBars ?
- 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' :
- 'translate(0, ${dimensionScale.scale(dimensionVals[i])})')
+ ..attrWithCallback(
+ 'transform',
+ (d, i, c) => verticalBars
+ ? 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)'
+ : 'translate(0, ${dimensionScale.scale(dimensionVals[i])})')
..duration(theme.transitionDurationMilliseconds);
}
@@ -83,15 +87,13 @@
prevOffsetVal[prevOffsetVal.length - 1] = offsetVal;
}
});
- }
-
+ }
var barWidth = dimensionScale.rangeBand - theme.defaultStrokeWidth;
// Calculate height of each segment in the bar.
// Uses prevAllZeroHeight and prevOffset to track previous segments
- var prevAllZeroHeight = true,
- prevOffset = 0;
+ var prevAllZeroHeight = true, prevOffset = 0;
var getBarLength = (d, i) {
if (!verticalBars) return measureScale.scale(d).round();
var retval = rect.height - measureScale.scale(d).round();
@@ -121,8 +123,7 @@
// Initial "y" position of a bar that is being created.
// Only used when animateBarGroups is set to true.
- var ic = 10000000,
- order = 0;
+ var ic = 10000000, order = 0;
var getInitialBarPos = (i) {
var tempY;
if (i <= ic && i > 0) {
@@ -163,14 +164,15 @@
var buildPath = (d, int i, Element e, bool animate, int roundIdx) {
var position = animate ? getInitialBarPos(i) : getBarPos(d, i),
length = animate ? 0 : getBarLength(d, i),
- radius = series.measures.elementAt(_reverseIdx(i)) == roundIdx ? RADIUS : 0,
+ radius =
+ series.measures.elementAt(_reverseIdx(i)) == roundIdx ? RADIUS : 0,
path = (length != 0)
? verticalBars
? topRoundedRect(0, position, barWidth, length, radius)
: rightRoundedRect(position, 0, length, barWidth, radius)
: '';
- e.attributes['data-offset'] = verticalBars ?
- position.toString() : (position + length).toString();
+ e.attributes['data-offset'] =
+ verticalBars ? position.toString() : (position + length).toString();
return path;
};
@@ -189,8 +191,8 @@
rect.classes.add('stack-rdr-bar');
rect.attributes
- ..['d'] = buildPath (d == null ? 0 : d, i, rect, animateBarGroups,
- roundIndex)
+ ..['d'] =
+ buildPath(d == null ? 0 : d, i, rect, animateBarGroups, roundIndex)
..['stroke-width'] = '${theme.defaultStrokeWidth}px'
..['fill'] = color
..['stroke'] = color;
@@ -235,7 +237,7 @@
var row = int.parse(e.parent.dataset['row']),
roundIndex = _lastMeasureWithData[row];
return buildPath(d == null ? 0 : d, i, e, false, roundIndex);
- });
+ });
}
bar.exit.remove();
@@ -285,12 +287,12 @@
var groups = host.querySelectorAll('.stack-rdr-rowgroup');
if (groups == null || groups.isEmpty) return;
- for(int i = 0, len = groups.length; i < len; ++i) {
+ for (int i = 0, len = groups.length; i < len; ++i) {
var group = groups.elementAt(i),
bars = group.querySelectorAll('.stack-rdr-bar'),
row = int.parse(group.dataset['row']);
- for(int j = 0, barsCount = bars.length; j < barsCount; ++j) {
+ for (int j = 0, barsCount = bars.length; j < barsCount; ++j) {
var bar = bars.elementAt(j),
column = int.parse(bar.dataset['column']),
color = colorForValue(column, row),
@@ -314,8 +316,7 @@
if (controller == null) return;
var rowStr = e.parent.dataset['row'];
var row = rowStr != null ? int.parse(rowStr) : null;
- controller.add(new DefaultChartEventImpl(
- scope.event, area, series, row,
+ controller.add(new DefaultChartEventImpl(scope.event, area, series, row,
series.measures.elementAt(_reverseIdx(index)), data));
}
diff --git a/packages/charted/lib/charts/chart_area.dart b/packages/charted/lib/charts/chart_area.dart
index f6c9d2f..e115f64 100644
--- a/packages/charted/lib/charts/chart_area.dart
+++ b/packages/charted/lib/charts/chart_area.dart
@@ -57,16 +57,13 @@
/// change. When not set, [draw] must be called to update the chart.
/// - When [useTwoDimensionAxes] is set, the chart uses both 'x' and 'y'
/// axes as dimensions.
- factory CartesianArea(
- dynamic host,
- ChartData data,
- ChartConfig config, {
- bool autoUpdate: false,
- bool useTwoDimensionAxes: false,
- bool useRowColoring: false,
- ChartState state }) =>
- new DefaultCartesianAreaImpl(host, data, config, autoUpdate,
- useTwoDimensionAxes, useRowColoring, state);
+ factory CartesianArea(dynamic host, ChartData data, ChartConfig config,
+ {bool autoUpdate: false,
+ bool useTwoDimensionAxes: false,
+ bool useRowColoring: false,
+ ChartState state}) =>
+ new DefaultCartesianAreaImpl(host, data, config, autoUpdate,
+ useTwoDimensionAxes, useRowColoring, state);
}
///
@@ -84,13 +81,9 @@
/// Layout area always uses row coloring.
bool get useRowColoring => true;
- factory LayoutArea(
- dynamic host,
- ChartData data,
- ChartConfig config, {
- bool autoUpdate: false,
- ChartState state }) =>
- new DefaultLayoutAreaImpl(host, data, config, autoUpdate, state);
+ factory LayoutArea(dynamic host, ChartData data, ChartConfig config,
+ {bool autoUpdate: false, ChartState state}) =>
+ new DefaultLayoutAreaImpl(host, data, config, autoUpdate, state);
}
///
diff --git a/packages/charted/lib/charts/chart_config.dart b/packages/charted/lib/charts/chart_config.dart
index 9a65883..73ce850 100644
--- a/packages/charted/lib/charts/chart_config.dart
+++ b/packages/charted/lib/charts/chart_config.dart
@@ -66,8 +66,8 @@
bool switchAxesForRTL;
/// Factory method to create an instance of the default implementation
- factory ChartConfig(
- Iterable<ChartSeries> series, Iterable<int> dimensions) = DefaultChartConfigImpl;
+ factory ChartConfig(Iterable<ChartSeries> series,
+ Iterable<int> dimensions) = DefaultChartConfigImpl;
}
///
diff --git a/packages/charted/lib/charts/chart_data.dart b/packages/charted/lib/charts/chart_data.dart
index f6304c0..de58dbc 100644
--- a/packages/charted/lib/charts/chart_data.dart
+++ b/packages/charted/lib/charts/chart_data.dart
@@ -20,8 +20,8 @@
Iterable<Iterable> get rows;
/// Create a new instance of [ChartData]'s internal implementation
- factory ChartData(
- Iterable<ChartColumnSpec> columns, Iterable<Iterable> rows) = DefaultChartDataImpl;
+ factory ChartData(Iterable<ChartColumnSpec> columns,
+ Iterable<Iterable> rows) = DefaultChartDataImpl;
}
///
@@ -37,7 +37,6 @@
ChartData transform(ChartData source);
}
-
///
/// Implementation of [ChangeRecord], that is used to notify when rows get added
/// or removed to ChartData
@@ -74,9 +73,9 @@
static const String TYPE_STRING = 'string';
static const String TYPE_TIMESTAMP = 'timestamp';
- static const List ORDINAL_SCALES = const [ TYPE_STRING ];
- static const List LINEAR_SCALES = const [ TYPE_NUMBER ];
- static const List TIME_SCALES = const [ TYPE_DATE, TYPE_TIMESTAMP ];
+ static const List ORDINAL_SCALES = const [TYPE_STRING];
+ static const List LINEAR_SCALES = const [TYPE_NUMBER];
+ static const List TIME_SCALES = const [TYPE_DATE, TYPE_TIMESTAMP];
/// Formatter for values that belong to this column
final FormatFunction formatter;
@@ -102,20 +101,20 @@
Scale createDefaultScale() {
if (useOrdinalScale == true) {
return new OrdinalScale();
- }
- else if (LINEAR_SCALES.contains(type)) {
+ } else if (LINEAR_SCALES.contains(type)) {
return new LinearScale();
- }
- else if (TIME_SCALES.contains(type)) {
+ } else if (TIME_SCALES.contains(type)) {
return new TimeScale();
}
return null;
}
- ChartColumnSpec({this.label, String type : TYPE_NUMBER,
- this.formatter, bool useOrdinalScale})
- : useOrdinalScale =
- useOrdinalScale == true ||
+ ChartColumnSpec(
+ {this.label,
+ String type: TYPE_NUMBER,
+ this.formatter,
+ bool useOrdinalScale})
+ : useOrdinalScale = useOrdinalScale == true ||
useOrdinalScale == null && ORDINAL_SCALES.contains(type),
type = type;
}
diff --git a/packages/charted/lib/charts/chart_legend.dart b/packages/charted/lib/charts/chart_legend.dart
index a55a918..8cc69d0 100644
--- a/packages/charted/lib/charts/chart_legend.dart
+++ b/packages/charted/lib/charts/chart_legend.dart
@@ -24,8 +24,8 @@
/// Factory to create an instance of the default implementation.
factory ChartLegend(Element host,
- {maxItems: 0, title: '', showValues: false}) =>
- new DefaultChartLegendImpl(host, maxItems, showValues, title);
+ {maxItems: 0, title: '', showValues: false}) =>
+ new DefaultChartLegendImpl(host, maxItems, showValues, title);
}
///
@@ -52,6 +52,11 @@
/// List of series that this column is part of
Iterable<ChartSeries> series;
- ChartLegendItem({this.index, this.color,
- this.label, this.description, this.series, this.value});
+ ChartLegendItem(
+ {this.index,
+ this.color,
+ this.label,
+ this.description,
+ this.series,
+ this.value});
}
diff --git a/packages/charted/lib/charts/chart_series.dart b/packages/charted/lib/charts/chart_series.dart
index 8582fe4..4d0fbd9 100644
--- a/packages/charted/lib/charts/chart_series.dart
+++ b/packages/charted/lib/charts/chart_series.dart
@@ -54,12 +54,12 @@
/// Factory function to create an instance of internal implementation of
/// [ChartSeries].
- factory ChartSeries(String name, Iterable<int> measures,
- ChartRenderer renderer, { Iterable<String> measureAxisIds : null }) =>
- new DefaultChartSeriesImpl(name, measures, renderer, measureAxisIds);
+ factory ChartSeries(
+ String name, Iterable<int> measures, ChartRenderer renderer,
+ {Iterable<String> measureAxisIds: null}) =>
+ new DefaultChartSeriesImpl(name, measures, renderer, measureAxisIds);
}
-
///
/// Implementation of [ChangeRecord] that is used to notify changes to
/// [ChartSeries]. Currently, only changes to measures and measureAxisIds
@@ -70,4 +70,4 @@
final ChartSeries series;
const ChartSeriesChangeRecord(this.series);
-}
\ No newline at end of file
+}
diff --git a/packages/charted/lib/charts/chart_state.dart b/packages/charted/lib/charts/chart_state.dart
index d807677..7909ada 100644
--- a/packages/charted/lib/charts/chart_state.dart
+++ b/packages/charted/lib/charts/chart_state.dart
@@ -13,38 +13,50 @@
/// Selection and visibility
///
abstract class ChartState implements ChangeNotifier {
- static int COL_SELECTED = 0x001;
- static int COL_UNSELECTED = 0x002;
- static int COL_PREVIEW = 0x004;
- static int COL_HIDDEN = 0x008;
- static int COL_HIGHLIGHTED = 0x010;
- static int COL_UNHIGHLIGHTED = 0x020;
- static int COL_HOVERED = 0x040;
- static int VAL_HIGHLIGHTED = 0x080;
- static int VAL_UNHIGHLIGHTED = 0x100;
- static int VAL_HOVERED = 0x200;
+ static int COL_SELECTED = 0x001;
+ static int COL_UNSELECTED = 0x002;
+ static int COL_PREVIEW = 0x004;
+ static int COL_HIDDEN = 0x008;
+ static int COL_HIGHLIGHTED = 0x010;
+ static int COL_UNHIGHLIGHTED = 0x020;
+ static int COL_HOVERED = 0x040;
+ static int VAL_HIGHLIGHTED = 0x080;
+ static int VAL_UNHIGHLIGHTED = 0x100;
+ static int VAL_HOVERED = 0x200;
- static const COL_SELECTED_CLASS = 'col-selected';
- static const COL_UNSELECTED_CLASS = 'col-unselected';
- static const COL_PREVIEW_CLASS = 'col-previewed';
- static const COL_HIDDEN_CLASS = 'col-hidden';
- static const COL_HIGHLIGHTED_CLASS = 'col-highlighted';
- static const COL_UNHIGHLIGHTED_CLASS = 'col-unhighlighted';
- static const COL_HOVERED_CLASS = 'col-hovered';
- static const VAL_HIGHLIGHTED_CLASS = 'row-highlighted';
- static const VAL_UNHIGHLIGHTED_CLASS = 'row-unhighlighted';
- static const VAL_HOVERED_CLASS = 'row-hovered';
+ static const COL_SELECTED_CLASS = 'col-selected';
+ static const COL_UNSELECTED_CLASS = 'col-unselected';
+ static const COL_PREVIEW_CLASS = 'col-previewed';
+ static const COL_HIDDEN_CLASS = 'col-hidden';
+ static const COL_HIGHLIGHTED_CLASS = 'col-highlighted';
+ static const COL_UNHIGHLIGHTED_CLASS = 'col-unhighlighted';
+ static const COL_HOVERED_CLASS = 'col-hovered';
+ static const VAL_HIGHLIGHTED_CLASS = 'row-highlighted';
+ static const VAL_UNHIGHLIGHTED_CLASS = 'row-unhighlighted';
+ static const VAL_HOVERED_CLASS = 'row-hovered';
- static const COLUMN_CLASS_NAMES = const[
- COL_SELECTED_CLASS, COL_UNSELECTED_CLASS, COL_PREVIEW_CLASS,
- COL_HIGHLIGHTED_CLASS, COL_UNHIGHLIGHTED_CLASS, COL_HIDDEN_CLASS,
- COL_HOVERED_CLASS];
+ static const COLUMN_CLASS_NAMES = const [
+ COL_SELECTED_CLASS,
+ COL_UNSELECTED_CLASS,
+ COL_PREVIEW_CLASS,
+ COL_HIGHLIGHTED_CLASS,
+ COL_UNHIGHLIGHTED_CLASS,
+ COL_HIDDEN_CLASS,
+ COL_HOVERED_CLASS
+ ];
- static const VALUE_CLASS_NAMES = const[
- COL_SELECTED_CLASS, COL_UNSELECTED_CLASS, COL_PREVIEW_CLASS,
- COL_HIGHLIGHTED_CLASS, COL_UNHIGHLIGHTED_CLASS, COL_HIDDEN_CLASS,
- COL_HOVERED_CLASS, VAL_HIGHLIGHTED_CLASS, VAL_UNHIGHLIGHTED_CLASS,
- VAL_HOVERED_CLASS];
+ static const VALUE_CLASS_NAMES = const [
+ COL_SELECTED_CLASS,
+ COL_UNSELECTED_CLASS,
+ COL_PREVIEW_CLASS,
+ COL_HIGHLIGHTED_CLASS,
+ COL_UNHIGHLIGHTED_CLASS,
+ COL_HIDDEN_CLASS,
+ COL_HOVERED_CLASS,
+ VAL_HIGHLIGHTED_CLASS,
+ VAL_UNHIGHLIGHTED_CLASS,
+ VAL_HOVERED_CLASS
+ ];
/// List of selected items.
/// - Contains a column on CartesianArea if useRowColoring is false.
@@ -64,11 +76,11 @@
/// Currently highlighted value, if any, represented as column and row.
/// Highlight is result of a click on certain value.
- Iterable<Pair<int,int>> highlights;
+ Iterable<Pair<int, int>> highlights;
/// Currently hovered value, if any, represented as column and row.
/// Hover is result of mouse moving over a certian value in chart.
- Pair<int,int> hovered;
+ Pair<int, int> hovered;
/// Ensure that a row or column is visible.
bool unhide(int id);
@@ -126,8 +138,8 @@
/// values in [ChartData].
///
class ChartHighlightChangeRecord implements ChangeRecord {
- final Pair<int,int> remove;
- final Pair<int,int> add;
+ final Pair<int, int> remove;
+ final Pair<int, int> add;
const ChartHighlightChangeRecord({this.add, this.remove});
}
@@ -136,7 +148,7 @@
/// values in [ChartData].
///
class ChartHoverChangeRecord implements ChangeRecord {
- final Pair<int,int> hovered;
+ final Pair<int, int> hovered;
const ChartHoverChangeRecord(this.hovered);
}
diff --git a/packages/charted/lib/charts/chart_theme.dart b/packages/charted/lib/charts/chart_theme.dart
index bea1f72..1515f87 100644
--- a/packages/charted/lib/charts/chart_theme.dart
+++ b/packages/charted/lib/charts/chart_theme.dart
@@ -50,7 +50,7 @@
String get defaultFont;
/// Easing function for the transition
- EasingFunction get transitionEasingType => Transition.defaultEasingType;
+ EasingFunction get transitionEasingType => Transition.defaultEasingType;
/// Easing mode for the transition
EasingModeFunction get transitionEasingMode => Transition.defaultEasingMode;
diff --git a/packages/charted/lib/charts/data_transformers/aggregation.dart b/packages/charted/lib/charts/data_transformers/aggregation.dart
index ffde9ee..1efe921 100644
--- a/packages/charted/lib/charts/data_transformers/aggregation.dart
+++ b/packages/charted/lib/charts/data_transformers/aggregation.dart
@@ -1,35 +1,31 @@
-/*
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file or at
- * https://developers.google.com/open-source/licenses/bsd
- */
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
part of charted.charts;
-/**
- * Function callback to filter items in the input
- */
+///Function callback to filter items in the input
typedef bool AggregationFilterFunc(var item);
typedef dynamic FieldAccessor(dynamic item, dynamic key);
-
-// TODO(midoringo, prsd): Consider splitting each aggregation type into its own
-// strategy object for readability, maintainability, and scalability.
-/**
- * Given list of items, dimensions and facts, compute
- * aggregates (COUNT, SUM, MIN, MAX) for facts at each dimension level.
- */
+/// Given list of items, dimensions and facts, compute
+/// aggregates (COUNT, SUM, MIN, MAX) for facts at each dimension level.
class AggregationModel {
-
// Number of aggregations we collect on each fact
int _aggregationTypesCount = 0;
// Currently supported list of aggregations.
- static final List<String> supportedAggregationTypes =
- ['sum', 'min', 'max', 'valid'];
+ static final List<String> supportedAggregationTypes = [
+ 'sum',
+ 'min',
+ 'max',
+ 'valid'
+ ];
// Computed aggregation types.
List<String> computedAggregationTypes;
@@ -103,18 +99,15 @@
FieldAccessor dimensionAccessor;
FieldAccessor factsAccessor;
- /**
- * Create a new [AggregationModel] from a [collection] of items,
- * list of [dimensions] on which the items are grouped and a list of [facts]
- * on which aggregations are computed.
- */
- AggregationModel(List collection, List dimensions,
- List facts,
- { List<String> aggregationTypes,
- this.walkThroughMap: false,
- this.comparators,
- this.dimensionAccessor,
- this.factsAccessor}) {
+ /// Create a new [AggregationModel] from a [collection] of items,
+ /// list of [dimensions] on which the items are grouped and a list of [facts]
+ /// on which aggregations are computed.
+ AggregationModel(List collection, List dimensions, List facts,
+ {List<String> aggregationTypes,
+ this.walkThroughMap: false,
+ this.comparators,
+ this.dimensionAccessor,
+ this.factsAccessor}) {
_init(collection, dimensions, facts, aggregationTypes);
}
@@ -127,17 +120,15 @@
void _timeItEnd() {
_timeItWatch.stop();
_logger.info('[aggregations/$_timeItName] '
- '${_timeItWatch.elapsed.inMilliseconds}ms/${_rows.length}r');
+ '${_timeItWatch.elapsed.inMilliseconds}ms/${_rows.length}r');
}
List get factFields => _factFields;
List get dimensionFields => _dimFields;
- /**
- * Initialize the view
- */
- void _init(List collection, List dimensions,
- List facts, List<String> aggregationTypes) {
+ /// Initialize the view
+ void _init(List collection, List dimensions, List facts,
+ List<String> aggregationTypes) {
if (collection == null) {
throw new ArgumentError('Data cannot be empty or null');
}
@@ -183,7 +174,7 @@
_aggregationTypesCount = aggregationTypes.length;
for (int i = 0; i < _aggregationTypesCount; i++) {
- switch(aggregationTypes[i]) {
+ switch (aggregationTypes[i]) {
case 'sum':
_offsetSum = i;
break;
@@ -204,9 +195,7 @@
_preprocess();
}
- /**
- * Re-calculate aggregations based on new dimensions.
- */
+ /// Re-calculate aggregations based on new dimensions.
void groupBy(List dimensions, [AggregationFilterFunc filter = null]) {
if (dimensions == null) {
dimensions = [];
@@ -217,13 +206,13 @@
_dimPrefixLength = 0;
while (_dimPrefixLength < _dimFields.length &&
- _dimPrefixLength < savedDimFields.length &&
- savedDimFields[_dimPrefixLength] == _dimFields[_dimPrefixLength]) {
+ _dimPrefixLength < savedDimFields.length &&
+ savedDimFields[_dimPrefixLength] == _dimFields[_dimPrefixLength]) {
++_dimPrefixLength;
}
_createBuffers();
- _preprocess(groupBy:true);
+ _preprocess(groupBy: true);
// For groupBy, compute immediately.
compute(filter);
@@ -232,12 +221,11 @@
_updateCachedEntities();
}
- /**
- * Create buffers.
- * This method is called when the object is being created and when
- * a groupBy is called to change the dimensions on which
- * aggregations are computed.
- */
+ /// Create buffers.
+ ///
+ /// This method is called when the object is being created and when
+ /// a groupBy is called to change the dimensions on which
+ /// aggregations are computed.
void _createBuffers() {
// Create both when object is created and groupBy is called
_dimEnumCache = new Int32List(_dimFields.length * _rows.length);
@@ -259,13 +247,11 @@
(i) => i < _dimPrefixLength ? oldDimToInt[i] : new Map<dynamic, int>());
}
- /**
- * Check cache entries
- * When data is grouped by a new dimensions, entities that were
- * created prior to the groupBy should be cleared and removed from cache
- * if they aren't valid anymore.
- * Update the entities that are valid after the groupBy.
- */
+ /// Check cache entries
+ /// When data is grouped by a new dimensions, entities that were
+ /// created prior to the groupBy should be cleared and removed from cache
+ /// if they aren't valid anymore.
+ /// Update the entities that are valid after the groupBy.
void _updateCachedEntities() {
List keys = new List.from(_entityCache.keys, growable: false);
keys.forEach((key) {
@@ -284,9 +270,7 @@
}
final Map<String, List> _parsedKeys = {};
- /**
- * Get value from a map-like object
- */
+ /// Get value from a map-like object
dynamic _fetch(var item, String key) {
if (walkThroughMap && key.contains('.')) {
return walk(item, key, _parsedKeys);
@@ -295,14 +279,11 @@
}
}
- /*
- * Preprocess Data
- * - Enumerate dimension values
- * - Create sort orders for dimension values
- * - Cache facts in lists
- */
+ /// Preprocess Data
+ /// - Enumerate dimension values
+ /// - Create sort orders for dimension values
+ /// - Cache facts in lists
void _preprocess({bool groupBy: false}) {
-
_timeItStart('preprocess');
// Enumerate dimensions...
@@ -316,8 +297,8 @@
int rowCount = _rows.length;
for (int ri = 0, factsDataOffset = 0, dimDataOffset = 0;
- ri < rowCount; ++ri, factsDataOffset += factsCount,
- dimDataOffset += dimensionsCount) {
+ ri < rowCount;
+ ++ri, factsDataOffset += factsCount, dimDataOffset += dimensionsCount) {
var item = _rows[ri];
// Cache the fact values in the big buffer, but only
@@ -325,7 +306,7 @@
// after initialization)
if (!groupBy) {
for (int fi = 0; fi < factsCount; fi++) {
- var value = factsAccessor(item,_factFields[fi]);
+ var value = factsAccessor(item, _factFields[fi]);
_factsCache[factsDataOffset + fi] =
(value == null) ? double.NAN : value.toDouble();
}
@@ -358,8 +339,9 @@
// When a comparator is not specified, our implementation of the
// comparator tries to gracefully handle null values.
dimensionVals.sort(
- comparators != null && comparators.containsKey(_dimFields[i]) ?
- comparators[_dimFields[i]] : _defaultDimComparator);
+ comparators != null && comparators.containsKey(_dimFields[i])
+ ? comparators[_dimFields[i]]
+ : _defaultDimComparator);
for (int si = 0; si < retval.length; ++si) {
retval[_dimToIntMap[i][dimensionVals[si]]] = si;
@@ -383,14 +365,12 @@
// Ensures that null dimension values don't cause an issue with sorting
_defaultDimComparator(Comparable left, Comparable right) =>
- (left == null && right == null) ? 0 :
- (left == null) ? -1 :
- (right == null) ? 1 : left.compareTo(right);
+ (left == null && right == null)
+ ? 0
+ : (left == null) ? -1 : (right == null) ? 1 : left.compareTo(right);
- /*
- * Given item indices in rows, compare them based
- * on the sort orders created while preprocessing data.
- */
+ /// Given item indices in rows, compare them based
+ /// on the sort orders created while pre-processing data.
_comparator(int one, int two) {
if (one == two) {
return 0;
@@ -401,7 +381,7 @@
for (int i = 0; i < _dimFields.length; ++i) {
int diff = _dimSortOrders[i][_dimEnumCache[offsetOne + i]] -
- _dimSortOrders[i][_dimEnumCache[offsetTwo + i]];
+ _dimSortOrders[i][_dimEnumCache[offsetTwo + i]];
if (diff != 0) {
return diff;
}
@@ -409,18 +389,17 @@
return 0;
}
- /**
- * Compute aggregations
- * If [filter] is not null, it would be used to filter out items that
- * should not be included in the aggregates.
- */
+ /// Compute aggregations
+ /// If [filter] is not null, it would be used to filter out items that
+ /// should not be included in the aggregates.
void compute([AggregationFilterFunc filter = null]) {
_timeItStart('compute');
_dimToAggrMap = new Map<String, int>();
_aggregations = new Float64List(AGGREGATIONS_BUFFER_LENGTH);
- _filterResults = filter == null ?
- null : new List<int>.filled((_rows.length ~/ SMI_BITS) + 1, 0);
+ _filterResults = filter == null
+ ? null
+ : new List<int>.filled((_rows.length ~/ SMI_BITS) + 1, 0);
int rowCount = _rows.length;
int dimensionCount = _dimFields.length;
@@ -439,10 +418,9 @@
_dimToAggrMap[''] = 0;
_aggregations[_offsetSortedIndex] = 0.0;
-
for (int ri = 0, index = 0, dimensionDataOffset = 0, factsDataOffset = 0;
- ri < rowCount; ++ri, reset = false) {
-
+ ri < rowCount;
+ ++ri, reset = false) {
// If filter is not null, check if this row must be included in
// the aggregations and mark it accordingly.
index = _sorted[ri];
@@ -472,8 +450,8 @@
currentDim[ci + 1] = currentBufferPos;
// Save location to aggregations position in the buffer
- _dimToAggrMap[new List.generate(di + 1,
- (i) => currentDim[2 * i]).join(':')] = currentBufferPos;
+ _dimToAggrMap[new List.generate(di + 1, (i) => currentDim[2 * i])
+ .join(':')] = currentBufferPos;
// Store items start position
_aggregations[currentBufferPos + _offsetSortedIndex] = ri.toDouble();
@@ -485,8 +463,8 @@
isNewDimension = true;
}
- _updateAggregationsAt(currentDim[ci + 1],
- factsDataOffset, isNewDimension);
+ _updateAggregationsAt(
+ currentDim[ci + 1], factsDataOffset, isNewDimension);
isNewDimension = false;
}
}
@@ -494,20 +472,17 @@
_timeItEnd();
}
- /**
- * Helper function that does the actual aggregations.
- * This function is called once per row per dimension.
- */
- _updateAggregationsAt(int aggrDataOffset,
- int factsDataOffset, bool isNewDimension) {
+ /// Helper function that does the actual aggregations.
+ /// This function is called once per row per dimension.
+ _updateAggregationsAt(
+ int aggrDataOffset, int factsDataOffset, bool isNewDimension) {
// Update count.
_aggregations[aggrDataOffset + _offsetFilteredCount] += 1;
// Update aggregation for each of the facts.
for (int fi = 0, bufferFactOffset = aggrDataOffset;
- fi < _factFields.length;
- bufferFactOffset += _aggregationTypesCount, ++fi) {
-
+ fi < _factFields.length;
+ bufferFactOffset += _aggregationTypesCount, ++fi) {
double factValue = _factsCache[factsDataOffset + fi];
if (factValue.isNaN) {
continue;
@@ -519,14 +494,16 @@
}
// Min
- if (_offsetMin != null && (isNewDimension || factValue <
- _aggregations[bufferFactOffset + _offsetMin])) {
+ if (_offsetMin != null &&
+ (isNewDimension ||
+ factValue < _aggregations[bufferFactOffset + _offsetMin])) {
_aggregations[bufferFactOffset + _offsetMin] = factValue;
}
// Max
- if (_offsetMax != null && (isNewDimension || factValue >
- _aggregations[bufferFactOffset + _offsetMax])) {
+ if (_offsetMax != null &&
+ (isNewDimension ||
+ factValue > _aggregations[bufferFactOffset + _offsetMax])) {
_aggregations[bufferFactOffset + _offsetMax] = factValue;
}
@@ -537,51 +514,41 @@
}
}
- /*
- * TODO(prsd):
- * 1. Implementation of updates and posting updates to entities.
- * patchEntity and addToEntity must add listeners on AggregationItems
- * and any changes must be propagated to entities.
- * 2. Updates (add/remove/update) should do their best to update the
- * aggregations and then maybe do a delayed recomputation (sort etc;)
- */
+ // TODO(prsd):
+ // 1. Implementation of updates and posting updates to entities.
+ // patchEntity and addToEntity must add listeners on AggregationItems
+ // and any changes must be propagated to entities.
+ // 2. Updates (add/remove/update) should do their best to update the
+ // aggregations and then maybe do a delayed recomputation (sort etc;)
- /**
- * Update an item.
- * If aggregates were already computed, they are updated to reflect the
- * new value and any observers are notified.
- */
+ /// Update an item.
+ /// If aggregates were already computed, they are updated to reflect the
+ /// new value and any observers are notified.
void updateItem(dynamic item, String field) {
throw new UnimplementedError();
}
- /**
- * Add a new item.
- * If aggregates were already computed, they are updated to reflect
- * values on the new item.
- */
+ /// Add a new item.
+ /// If aggregates were already computed, they are updated to reflect
+ /// values on the new item.
void addItem(dynamic item) {
throw new UnimplementedError();
}
- /**
- * Remove an existing item.
- * If aggregates were already computed, they are updated to reflect
- * facts on the removed item.
- */
+ /// Remove an existing item.
+ /// If aggregates were already computed, they are updated to reflect
+ /// facts on the removed item.
void removeItem(dynamic item) {
throw new UnimplementedError();
}
- /**
- * Return an [AggregationItem] that represents facts for dimension
- * represented by [dimension] Only one instance of an entity is created
- * per dimension (even if this function is called multiple times)
- *
- * Callers of this method can observe the returned entity for updates to
- * aggregations caused by changes to filter or done through add, remove
- * or modify of items in the collection.
- */
+ /// Return an [AggregationItem] that represents facts for dimension
+ /// represented by [dimension] Only one instance of an entity is created
+ /// per dimension (even if this function is called multiple times)
+ ///
+ /// Callers of this method can observe the returned entity for updates to
+ /// aggregations caused by changes to filter or done through add, remove
+ /// or modify of items in the collection.
AggregationItem facts(List dimension) {
List<int> enumeratedList = new List<int>();
for (int i = 0; i < dimension.length; ++i) {
@@ -599,9 +566,7 @@
return item;
}
- /**
- * Return a list of values that are present for a dimension field.
- */
+ /// Return a list of values that are present for a dimension field.
List valuesForDimension(dynamic dimensionFieldName) {
int di = _dimFields.indexOf(dimensionFieldName);
if (di < 0) {
@@ -609,22 +574,21 @@
}
List values = new List.from(_dimToIntMap[di].keys);
values.sort(
- comparators != null && comparators.containsKey(dimensionFieldName) ?
- comparators[dimensionFieldName] : _defaultDimComparator);
+ comparators != null && comparators.containsKey(dimensionFieldName)
+ ? comparators[dimensionFieldName]
+ : _defaultDimComparator);
return values;
}
}
-/**
- * Parse a path for nested map-like objects.
- * Caches the parsed key in the passed map.
- *
- * Takes map keys of the format:
- * "list(key=val;val=m).another(state=good).state"
- * and outputs:
- * ["list", {"key": "val", "val": "m"},
- * "another", {"state": "good"}, "state"]
- */
+/// Parse a path for nested map-like objects.
+/// Caches the parsed key in the passed map.
+///
+/// Takes map keys of the format:
+/// "list(key=val;val=m).another(state=good).state"
+/// and outputs:
+/// ["list", {"key": "val", "val": "m"},
+/// "another", {"state": "good"}, "state"]
List _parseKey(String key, Map parsedKeysCache) {
List parts = parsedKeysCache == null ? null : parsedKeysCache[key];
if (parts == null && key != null) {
@@ -678,30 +642,28 @@
return parts;
}
-/**
- * Walk a map-like structure that could have list in the path.
- *
- * Example:
- * Map testMap = {
- * "first": "firstval",
- * "list": [
- * { "val": "m",
- * "key": "val",
- * "another": [
- * { 'state': 'good' },
- * { 'state': 'bad' }
- * ]
- * },
- * { "val": "m", "key": "invalid" },
- * { "val": "o" }
- * ]
- * };
- *
- * For the above map:
- * walk(testMap, "list(key=val;val=m).another(state=good).state");
- * outputs:
- * good
- */
+/// Walk a map-like structure that could have list in the path.
+///
+/// Example:
+/// Map testMap = {
+/// "first": "firstval",
+/// "list": [
+/// { "val": "m",
+/// "key": "val",
+/// "another": [
+/// { 'state': 'good' },
+/// { 'state': 'bad' }
+/// ]
+/// },
+/// { "val": "m", "key": "invalid" },
+/// { "val": "o" }
+/// ]
+/// };
+///
+/// For the above map:
+/// walk(testMap, "list(key=val;val=m).another(state=good).state");
+/// outputs:
+/// good
dynamic walk(initial, String key, Map parsedKeyCache) {
List parts = _parseKey(key, parsedKeyCache);
return parts.fold(initial, (current, part) {
@@ -712,7 +674,7 @@
bool match = true;
part.forEach((key, val) {
if ((key.contains('.') &&
- walk(part, key, parsedKeyCache).toString() != val) ||
+ walk(part, key, parsedKeyCache).toString() != val) ||
part[key] != val) {
match = false;
}
diff --git a/packages/charted/lib/charts/data_transformers/aggregation_item.dart b/packages/charted/lib/charts/data_transformers/aggregation_item.dart
index 17c6413..1c9f564 100644
--- a/packages/charted/lib/charts/data_transformers/aggregation_item.dart
+++ b/packages/charted/lib/charts/data_transformers/aggregation_item.dart
@@ -1,59 +1,44 @@
-/*
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file or at
- * https://developers.google.com/open-source/licenses/bsd
- */
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
part of charted.charts;
-/**
- * AggregationItem is created by [AggregationModel] to make access to facts
- * observable. Users must use AggregationItem.isValid before trying to access
- * the aggregations.
- */
+/// AggregationItem is created by [AggregationModel] to make access to facts
+/// observable. Users must use AggregationItem.isValid before trying to access
+/// the aggregations.
abstract class AggregationItem extends ChangeNotifier {
- /**
- * List of dimension fields in effect
- */
+ /// List of dimension fields in effect
List<String> dimensions;
- /**
- * Check if this entity is valid.
- * Currently the only case where an entity becomes invalid
- * is when a groupBy is called on the model.
- */
+ /// Check if this entity is valid.
+ /// Currently the only case where an entity becomes invalid
+ /// is when a groupBy is called on the model.
bool get isValid;
- /**
- * Fetch the fact from AggregationModel and return it
- * Currently takes keys in the form of "sum(spend)", where sum is
- * the aggregation type and spend is fact's field name.
- *
- * Currently, "sum", "count", "min", "max", "avg", "valid" and "avgOfValid"
- * are supported as the operators.
- */
+ /// Fetch the fact from AggregationModel and return it
+ /// Currently takes keys in the form of "sum(spend)", where sum is
+ /// the aggregation type and spend is fact's field name.
+ ///
+ /// Currently, "sum", "count", "min", "max", "avg", "valid" and "avgOfValid"
+ /// are supported as the operators.
+ operator [](String key);
- operator[](String key);
-
- /**
- * Check if we support a given key.
- */
+ /// Check if we support a given key.
bool containsKey(String key);
- /**
- * List of valid field names for this entity.
- * It's the combined list of accessors for individual items, items in
- * the next dimension and all possible facts defined on the view.
- */
+ /// List of valid field names for this entity.
+ /// It's the combined list of accessors for individual items, items in
+ /// the next dimension and all possible facts defined on the view.
Iterable<String> get fieldNames;
}
-/*
- * Implementation of AggregationItem
- * Instances of _AggregationItemImpl are created only by AggregationModel
- */
+/// Implementation of AggregationItem
+/// Instances of _AggregationItemImpl are created only by AggregationModel
class _AggregationItemImpl extends ChangeNotifier implements AggregationItem {
static final List<String> derivedAggregationTypes = ['count', 'avg'];
@@ -64,9 +49,7 @@
int _factsOffset;
- /*
- * Currently entities are created only when they have valid aggregations
- */
+ /// Currently entities are created only when they have valid aggregations
_AggregationItemImpl(this.model, this.dimensions, this._key) {
if (model == null) {
throw new ArgumentError('Model cannot be null');
@@ -79,23 +62,19 @@
_factsOffset = model._dimToAggrMap[_key];
}
- /**
- * _dimToAggrMap got updated on the model, update ourselves accordingly
- */
+ /// _dimToAggrMap got updated on the model, update ourselves accordingly
void update() {
_factsOffset = model._dimToAggrMap[_key];
}
- /*
- * Mark this entity as invalid.
- */
+ /// Mark this entity as invalid.
void clear() {
_factsOffset = null;
}
bool get isValid => _factsOffset != null;
- dynamic operator[](String key) {
+ dynamic operator [](String key) {
if (!isValid) {
throw new StateError('Entity is not valid anymore');
}
@@ -116,11 +95,10 @@
// Try parsing int if every element in factFields is int.
if (model._factFields.every((e) => e is int)) {
- factIndex = model._factFields.indexOf(int.parse(factName,
- onError: (e) {
- throw new ArgumentError('Type of factFields are int but factName' +
- 'contains non int value');
- }));
+ factIndex = model._factFields.indexOf(int.parse(factName, onError: (e) {
+ throw new ArgumentError('Type of factFields are int but factName' +
+ 'contains non int value');
+ }));
}
if (factIndex == -1) {
throw new ArgumentError('Model not configured for ${factName}');
@@ -128,7 +106,8 @@
int offset = _factsOffset + factIndex * model._aggregationTypesCount;
// No items for the corresponding fact, so return null.
- if (aggrFunc != 'count' && aggrFunc != 'avg' &&
+ if (aggrFunc != 'count' &&
+ aggrFunc != 'avg' &&
model._aggregations[offset + model._offsetCnt].toInt() == 0) {
return null;
}
@@ -136,13 +115,13 @@
if (aggrFuncIndex != -1) {
return model._aggregations[offset + aggrFuncIndex];
} else if (aggrFunc == 'count') {
- return model._aggregations[_factsOffset +
- model._offsetFilteredCount].toInt();
+ return model._aggregations[_factsOffset + model._offsetFilteredCount]
+ .toInt();
} else if (aggrFunc == 'avg') {
return model._aggregations[offset + model._offsetSum] /
- model._aggregations[_factsOffset + model._offsetFilteredCount].
- toInt();
- } else if (aggrFunc == 'avgOfValid') {
+ model._aggregations[_factsOffset + model._offsetFilteredCount]
+ .toInt();
+ } else if (aggrFunc == 'avgOfValid') {
return model._aggregations[offset + model._offsetSum] /
model._aggregations[offset + model._offsetCnt].toInt();
}
@@ -198,9 +177,7 @@
return model._itemFieldNamesCache;
}
- /*
- * TODO(prsd): Implementation of [Observable]
- */
+ // TODO(prsd): Implementation of [Observable]
Stream<List<ChangeRecord>> get changes {
throw new UnimplementedError();
}
@@ -218,12 +195,12 @@
int _count;
int _endOfRows;
- _AggregationItemsIterator(this.model, List<String> this.dimensions,
- String this.key) {
+ _AggregationItemsIterator(
+ this.model, List<String> this.dimensions, String this.key) {
int offset = model._dimToAggrMap[key];
if (offset != null) {
- int factsEndOffset = offset +
- model._factFields.length * model._aggregationTypesCount;
+ int factsEndOffset =
+ offset + model._factFields.length * model._aggregationTypesCount;
_start = model._aggregations[factsEndOffset].toInt();
_count = model._aggregations[factsEndOffset + 1].toInt();
_endOfRows = model._rows.length;
@@ -241,15 +218,14 @@
return false;
}
- /*
- * If model had a filter applied, then check if _current points to a
- * filtered-in row, else skip till we find one.
- * Also, make sure (even if something else went wrong) we don't go
- * beyond the number of items in the model.
- */
+ // If model had a filter applied, then check if _current points to a
+ // filtered-in row, else skip till we find one.
+ // Also, make sure (even if something else went wrong) we don't go
+ // beyond the number of items in the model.
if (this.model._filterResults != null) {
while ((this.model._filterResults[_current ~/ AggregationModel.SMI_BITS] &
- (1 << _current % AggregationModel.SMI_BITS)) == 0 &&
+ (1 << _current % AggregationModel.SMI_BITS)) ==
+ 0 &&
_current <= _endOfRows) {
++_current;
}
diff --git a/packages/charted/lib/charts/data_transformers/aggregation_transformer.dart b/packages/charted/lib/charts/data_transformers/aggregation_transformer.dart
index 37db0b1..7cd8028 100644
--- a/packages/charted/lib/charts/data_transformers/aggregation_transformer.dart
+++ b/packages/charted/lib/charts/data_transformers/aggregation_transformer.dart
@@ -1,28 +1,25 @@
-/*
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file or at
- * https://developers.google.com/open-source/licenses/bsd
- */
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
part of charted.charts;
-/**
- * Transforms the ChartData base on the specified dimension columns and facts
- * columns indices. The values in the facts columns will be aggregated by the
- * tree hierarchy generated by the dimension columns. Expand and Collapse
- * methods may be called to display different levels of aggregation.
- *
- * The output ChartData produced by transform() will contain only columns in the
- * original ChartData that were specified in dimensions or facts column indices.
- * The output column will be re-ordered first by the indices specified in the
- * dimension column indices then by the facts column indices. The data in the
- * cells of each row will also follow this rule.
- */
+/// Transforms the ChartData based on the specified dimension columns and facts
+/// columns indices. The values in the facts columns will be aggregated by the
+/// tree hierarchy generated by the dimension columns. Expand and Collapse
+/// methods may be called to display different levels of aggregation.
+///
+/// The output ChartData produced by transform() will contain only columns in the
+/// original ChartData that were specified in dimensions or facts column indices.
+/// The output column will be re-ordered first by the indices specified in the
+/// dimension column indices then by the facts column indices. The data in the
+/// cells of each row will also follow this rule.
class AggregationTransformer extends ChangeNotifier
implements ChartDataTransform, ChartData {
-
static const String AGGREGATION_TYPE_SUM = 'sum';
static const String AGGREGATION_TYPE_MIN = 'min';
static const String AGGREGATION_TYPE_MAX = 'max';
@@ -40,16 +37,13 @@
FieldAccessor _indexFieldAccessor = (List row, int index) => row[index];
ChartData _data;
- AggregationTransformer(this._dimensionColumnIndices,
- this._factsColumnIndices,
+ AggregationTransformer(this._dimensionColumnIndices, this._factsColumnIndices,
[String aggregationType = AGGREGATION_TYPE_SUM]) {
_aggregationType = aggregationType;
}
- /**
- * Transforms the ChartData base on the specified dimension columns and facts
- * columns, aggregation type and currently expanded dimensions.
- */
+ /// Transforms the ChartData base on the specified dimension columns and facts
+ /// columns, aggregation type and currently expanded dimensions.
ChartData transform(ChartData data) {
assert(data.columns.length > max(_dimensionColumnIndices));
assert(data.columns.length > max(_factsColumnIndices));
@@ -59,11 +53,11 @@
return this;
}
- /** Registers listeners if data.rows or data.columns are Observable. */
+ /// Registers listeners if data.rows or data.columns are Observable.
_registerListeners() {
_dataSubscriptions.dispose();
- if(_data is Observable) {
+ if (_data is Observable) {
var observable = (_data as Observable);
_dataSubscriptions.add(observable.changes.listen((records) {
_transform();
@@ -76,13 +70,12 @@
}
}
- /**
- * Performs the filter transform with _data. This is called on transform and
- * onChange if the input ChartData is Observable.
- */
+ /// Performs the filter transform with _data. This is called on transform and
+ /// onChange if the input ChartData is Observable.
_transform() {
- _model = new AggregationModel(_data.rows, _dimensionColumnIndices,
- _factsColumnIndices, aggregationTypes: [_aggregationType],
+ _model = new AggregationModel(
+ _data.rows, _dimensionColumnIndices, _factsColumnIndices,
+ aggregationTypes: [_aggregationType],
dimensionAccessor: _indexFieldAccessor,
factsAccessor: _indexFieldAccessor);
_model.compute();
@@ -105,17 +98,15 @@
rows.addAll(transformedRows);
// Process columns.
- columns = new List<ChartColumnSpec>.generate(_selectedColumns.length, (index) =>
- _data.columns.elementAt(_selectedColumns[index]));
+ columns = new List<ChartColumnSpec>.generate(_selectedColumns.length,
+ (index) => _data.columns.elementAt(_selectedColumns[index]));
}
- /**
- * Fills the aggregatedRows List with data base on the set of expanded values
- * recursively. Currently when a dimension is expanded, rows are
- * generated for its children but not for itself. If we want to change the
- * logic to include itself, just move the expand check around the else clause
- * and always write a row of data whether it's expanded or not.
- */
+ /// Fills the aggregatedRows List with data base on the set of expanded values
+ /// recursively. Currently when a dimension is expanded, rows are
+ /// generated for its children but not for itself. If we want to change the
+ /// logic to include itself, just move the expand check around the else clause
+ /// and always write a row of data whether it's expanded or not.
_generateAggregatedRow(List<Iterable> aggregatedRows, List dimensionValues) {
var entity = _model.facts(dimensionValues);
var dimensionLevel = dimensionValues.length - 1;
@@ -125,7 +116,6 @@
if (!_isExpanded(dimensionValues) ||
dimensionValues.length == _dimensionColumnIndices.length) {
aggregatedRows.add(new List.generate(_selectedColumns.length, (index) {
-
// Dimension column.
if (index < _dimensionColumnIndices.length) {
if (index < dimensionLevel) {
@@ -152,10 +142,8 @@
}
}
- /**
- * Expands a specific dimension and optionally expands all of its parent
- * dimensions.
- */
+ /// Expands a specific dimension and optionally expands all of its parent
+ /// dimensions.
void expand(List dimension, [bool expandParent = true]) {
_expandAllDimension = false;
_expandedSet.add(dimension);
@@ -168,10 +156,8 @@
}
}
- /**
- * Collapses a specific dimension and optionally collapse all of its
- * Children dimensions.
- */
+ /// Collapses a specific dimension and optionally collapse all of its
+ /// Children dimensions.
void collapse(List dimension, [bool collapseChildren = true]) {
_expandAllDimension = false;
if (collapseChildren) {
@@ -189,7 +175,7 @@
}
}
- /** Expands all dimensions. */
+ /// Expands all dimensions.
void expandAll() {
if (_model != null) {
for (var value in _model.valuesForDimension(_dimensionColumnIndices[0])) {
@@ -209,13 +195,13 @@
}
}
- /** Collapses all dimensions. */
+ /// Collapses all dimensions.
void collapseAll() {
_expandAllDimension = false;
_expandedSet.clear();
}
- /** Tests if specific dimension is expanded. */
+ /// Tests if specific dimension is expanded.
bool _isExpanded(List dimension) {
Function eq = const ListEquality().equals;
return _expandedSet.any((e) => eq(e, dimension));
diff --git a/packages/charted/lib/charts/data_transformers/filter_transformer.dart b/packages/charted/lib/charts/data_transformers/filter_transformer.dart
index 393c453..bc12597 100644
--- a/packages/charted/lib/charts/data_transformers/filter_transformer.dart
+++ b/packages/charted/lib/charts/data_transformers/filter_transformer.dart
@@ -1,21 +1,19 @@
-/*
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file or at
- * https://developers.google.com/open-source/licenses/bsd
- */
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
part of charted.charts;
typedef bool FilterFunction(dynamic value);
-/**
- * Transforms the ChartData base on the specified FilterDefinitions. Each row
- * of data will be tested by passing the value at target column to the filter
- * function. If filter function returns false, the row will be filtered out.
- * This transformer does not modify the column part of the input ChartData.
- */
+/// Transforms the ChartData base on the specified FilterDefinitions. Each row
+/// of data will be tested by passing the value at target column to the filter
+/// function. If filter function returns false, the row will be filtered out.
+/// This transformer does not modify the column part of the input ChartData.
class FilterTransformer extends ChangeNotifier
implements ChartDataTransform, ChartData {
final SubscriptionsDisposer _dataSubscriptions = new SubscriptionsDisposer();
@@ -26,12 +24,10 @@
FilterTransformer(this.filterFunctions);
- /**
- * Transforms the input data with the list of [FilterDefinition] specified in
- * the constructor. If the rows and columns are ObservableList in the data,
- * changes in rows and columns in input data will trigger transform to be
- * performed again to update the output rows and columns.
- */
+ /// Transforms the input data with the list of [FilterDefinition] specified in
+ /// the constructor. If the rows and columns are ObservableList in the data,
+ /// changes in rows and columns in input data will trigger transform to be
+ /// performed again to update the output rows and columns.
ChartData transform(ChartData data) {
_data = data;
_registerListeners();
@@ -39,11 +35,11 @@
return this;
}
- /** Registers listeners if data.rows or data.columns are Observable. */
+ /// Registers listeners if data.rows or data.columns are Observable.
_registerListeners() {
_dataSubscriptions.dispose();
- if(_data is Observable) {
+ if (_data is Observable) {
var observable = (_data as Observable);
_dataSubscriptions.add(observable.changes.listen((records) {
_transform();
@@ -56,18 +52,16 @@
}
}
- /**
- * Performs the filter transform with _data. This is called on transform and
- * onChange if the input ChartData is Observable.
- */
+ /// Performs the filter transform with _data. This is called on transform and
+ /// onChange if the input ChartData is Observable.
_transform() {
columns = _data.columns;
rows.clear();
for (var row in _data.rows) {
// Add the row if each value in target column passes the filter function.
- if (filterFunctions.every((e) =>
- e.filterFunc(row.elementAt(e.targetColumn)))) {
+ if (filterFunctions
+ .every((e) => e.filterFunc(row.elementAt(e.targetColumn)))) {
rows.add(row);
}
}
diff --git a/packages/charted/lib/charts/data_transformers/transpose_transformer.dart b/packages/charted/lib/charts/data_transformers/transpose_transformer.dart
index 2ed8e84..fcab1a0 100644
--- a/packages/charted/lib/charts/data_transformers/transpose_transformer.dart
+++ b/packages/charted/lib/charts/data_transformers/transpose_transformer.dart
@@ -1,24 +1,22 @@
-/*
- * Copyright 2014 Google Inc. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style
- * license that can be found in the LICENSE file or at
- * https://developers.google.com/open-source/licenses/bsd
- */
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
part of charted.charts;
-/**
- * Transforms the ChartData by transposing the columns and rows. A label column
- * index in the original data will need to be specified (default to 0), all
- * values in the specified label column will be used as the label for the
- * transformed data, all the labels in the original Chart data columns will be
- * populated in the label column as values of that column.
- *
- * All values in the data except for the data in the label column must have the
- * same type; All columns except for the label column must have the same
- * formatter if a formatter exist for columns.
- */
+/// Transforms the ChartData by transposing the columns and rows. A label column
+/// index in the original data will need to be specified (default to 0), all
+/// values in the specified label column will be used as the label for the
+/// transformed data, all the labels in the original Chart data columns will be
+/// populated in the label column as values of that column.
+///
+/// All values in the data except for the data in the label column must have the
+/// same type; All columns except for the label column must have the same
+/// formatter if a formatter exist for columns.
class TransposeTransformer extends ChangeNotifier
implements ChartDataTransform, ChartData {
final SubscriptionsDisposer _dataSubscriptions = new SubscriptionsDisposer();
@@ -32,12 +30,10 @@
TransposeTransformer([this._labelColumn = 0]);
- /**
- * Transforms the input data with the specified label column in the
- * constructor. If the ChartData is Observable, changes fired by the input
- * data will trigger transform to be performed again to update the output rows
- * and columns.
- */
+ /// Transforms the input data with the specified label column in the
+ /// constructor. If the ChartData is Observable, changes fired by the input
+ /// data will trigger transform to be performed again to update the output rows
+ /// and columns.
ChartData transform(ChartData data) {
_data = data;
_registerListeners();
@@ -45,11 +41,11 @@
return this;
}
- /** Registers listeners if input ChartData is Observable. */
+ /// Registers listeners if input ChartData is Observable.
_registerListeners() {
_dataSubscriptions.dispose();
- if(_data is Observable) {
+ if (_data is Observable) {
var observable = (_data as Observable);
_dataSubscriptions.add(observable.changes.listen((records) {
_transform();
@@ -62,10 +58,8 @@
}
}
- /**
- * Performs the transpose transform with _data. This is called on transform
- * and on changes if ChartData is Observable.
- */
+ /// Performs the transpose transform with _data. This is called on transform
+ /// and on changes if ChartData is Observable.
_transform() {
// Assert all columns are of the same type and formatter, excluding the
// label column.
@@ -88,7 +82,8 @@
columns.clear();
rows.clear();
- rows.addAll(new List<Iterable>.generate(_data.columns.length - 1, (i) => []));
+ rows.addAll(
+ new List<Iterable>.generate(_data.columns.length - 1, (i) => []));
// Populate the transposed rows' data, excluding the label column, visit
// each value in the original data once.
@@ -108,18 +103,19 @@
// column that is used as the new label.
for (var i = 0; i < rows.length; i++) {
var columnOffset = (i < _labelColumn) ? 0 : 1;
- (rows.elementAt(i) as List).insert(_labelColumn,
- _data.columns.elementAt(i + columnOffset).label);
-
+ (rows.elementAt(i) as List).insert(
+ _labelColumn, _data.columns.elementAt(i + columnOffset).label);
}
// Construct new ColumnSpaces base on the label column.
for (var label in columnLabels) {
- columns.add(new ChartColumnSpec(type: type, label: label,
- formatter: formatter));
+ columns.add(
+ new ChartColumnSpec(type: type, label: label, formatter: formatter));
}
- columns.insert(_labelColumn,
- new ChartColumnSpec(type: ChartColumnSpec.TYPE_STRING, label:
- _data.columns.elementAt(_labelColumn).label));
+ columns.insert(
+ _labelColumn,
+ new ChartColumnSpec(
+ type: ChartColumnSpec.TYPE_STRING,
+ label: _data.columns.elementAt(_labelColumn).label));
}
}
diff --git a/packages/charted/lib/charts/layout_renderers/layout_base_renderer.dart b/packages/charted/lib/charts/layout_renderers/layout_base_renderer.dart
index 2ebac9c..35bfbcc 100644
--- a/packages/charted/lib/charts/layout_renderers/layout_base_renderer.dart
+++ b/packages/charted/lib/charts/layout_renderers/layout_base_renderer.dart
@@ -117,14 +117,13 @@
}
}
- Iterable<String> stylesForValue(int row, { bool isTail: false }) {
- if (isTail == true) return const[];
+ Iterable<String> stylesForValue(int row, {bool isTail: false}) {
+ if (isTail == true) return const [];
if (_valueStylesCache[row] == null) {
if (state == null) {
- _valueStylesCache[row] = const[];
+ _valueStylesCache[row] = const [];
} else {
- var styles = [],
- flags = _valueStateCache[row];
+ var styles = [], flags = _valueStateCache[row];
if (flags & ChartState.VAL_HIGHLIGHTED != 0) {
styles.add(ChartState.VAL_HIGHLIGHTED_CLASS);
diff --git a/packages/charted/lib/charts/layout_renderers/pie_chart_renderer.dart b/packages/charted/lib/charts/layout_renderers/pie_chart_renderer.dart
index 03941d6..4409469 100644
--- a/packages/charted/lib/charts/layout_renderers/pie_chart_renderer.dart
+++ b/packages/charted/lib/charts/layout_renderers/pie_chart_renderer.dart
@@ -13,7 +13,7 @@
static const STATS_VALUE = 'value-only';
static const STATS_VALUE_PERCENTAGE = 'value-percentage';
- final Iterable<int> dimensionsUsingBand = const[];
+ final Iterable<int> dimensionsUsingBand = const [];
final String statsMode;
final num innerRadiusRatio;
final int maxSliceCount;
@@ -29,8 +29,8 @@
Iterable otherRow;
- PieChartRenderer({
- num innerRadiusRatio: 0,
+ PieChartRenderer(
+ {num innerRadiusRatio: 0,
bool showLabels,
this.sortDataByValue: true,
this.statsMode: STATS_PERCENTAGE,
@@ -49,8 +49,8 @@
}
@override
- Iterable<ChartLegendItem> layout(
- Element element, {Future schedulePostRender}) {
+ Iterable<ChartLegendItem> layout(Element element,
+ {Future schedulePostRender}) {
_ensureReadyToDraw(element);
var radius = math.min(rect.width, rect.height) / 2;
@@ -81,8 +81,7 @@
var displayed = indices.take(maxSliceCount).toList();
var otherItemsValue = 0;
for (int i = displayed.length; i < indices.length; ++i) {
- var index = indices.elementAt(i),
- row = area.data.rows.elementAt(index);
+ var index = indices.elementAt(i), row = area.data.rows.elementAt(index);
otherItemsValue += row == null || row.elementAt(measure) == null
? 0
: row.elementAt(measure);
@@ -126,8 +125,7 @@
..['stroke'] = '#ffffff';
e.append(
- Namespace.createChildElement('text', e)
- ..classes.add('pie-label'));
+ Namespace.createChildElement('text', e)..classes.add('pie-label'));
})
..on('click', (d, i, e) => _event(mouseClickController, d, i, e))
..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e))
@@ -138,13 +136,16 @@
_legend.clear();
var items = new List.generate(data.length, (i) {
SvgArcData d = data.elementAt(i);
- Iterable row = d.data == SMALL_INT_MAX
- ? otherRow
- : area.data.rows.elementAt(d.data);
+ Iterable row =
+ d.data == SMALL_INT_MAX ? otherRow : area.data.rows.elementAt(d.data);
- return new ChartLegendItem(index: d.data, color: colorForData(d.data, i),
- label: row.elementAt(dimension), series: [series],
- value: '${(((d.endAngle - d.startAngle) * 50) / math.PI).toStringAsFixed(2)}%');
+ return new ChartLegendItem(
+ index: d.data,
+ color: colorForData(d.data, i),
+ label: row.elementAt(dimension),
+ series: [series],
+ value:
+ '${(((d.endAngle - d.startAngle) * 50) / math.PI).toStringAsFixed(2)}%');
});
return _legend..addAll(area.config.isRTL ? items.reversed : items);
}
@@ -176,7 +177,7 @@
void _event(StreamController controller, data, int index, Element e) {
// Currently, events are not supported on "Other" pie
if (controller == null || data.data == SMALL_INT_MAX) return;
- controller.add(new DefaultChartEventImpl(
- scope.event, area, series, data.data, series.measures.first, data.value));
+ controller.add(new DefaultChartEventImpl(scope.event, area, series,
+ data.data, series.measures.first, data.value));
}
}
diff --git a/packages/charted/lib/charts/src/cartesian_area_impl.dart b/packages/charted/lib/charts/src/cartesian_area_impl.dart
index 2769f7e..8138873 100644
--- a/packages/charted/lib/charts/src/cartesian_area_impl.dart
+++ b/packages/charted/lib/charts/src/cartesian_area_impl.dart
@@ -13,22 +13,22 @@
/// which contain two dimension axes.
class DefaultCartesianAreaImpl implements CartesianArea {
/// Default identifiers used by the measure axes
- static const MEASURE_AXIS_IDS = const['_default'];
+ static const MEASURE_AXIS_IDS = const ['_default'];
/// Orientations used by measure axes. First, when "x" axis is the primary
/// and the only dimension. Second, when "y" axis is the primary and the only
/// dimension.
- static const MEASURE_AXIS_ORIENTATIONS = const[
- const[ORIENTATION_LEFT, ORIENTATION_RIGHT],
- const[ORIENTATION_BOTTOM, ORIENTATION_TOP]
+ static const MEASURE_AXIS_ORIENTATIONS = const [
+ const [ORIENTATION_LEFT, ORIENTATION_RIGHT],
+ const [ORIENTATION_BOTTOM, ORIENTATION_TOP]
];
/// Orientations used by the dimension axes. First, when "x" is the
/// primary dimension and the last one for cases where "y" axis is primary
/// dimension.
- static const DIMENSION_AXIS_ORIENTATIONS = const[
- const[ORIENTATION_BOTTOM, ORIENTATION_LEFT],
- const[ORIENTATION_LEFT, ORIENTATION_BOTTOM]
+ static const DIMENSION_AXIS_ORIENTATIONS = const [
+ const [ORIENTATION_BOTTOM, ORIENTATION_LEFT],
+ const [ORIENTATION_LEFT, ORIENTATION_BOTTOM]
];
/// Mapping of measure axis Id to it's axis.
@@ -99,7 +99,8 @@
bool autoUpdate,
this.useTwoDimensionAxes,
this.useRowColoring,
- this.state) : _autoUpdate = autoUpdate {
+ this.state)
+ : _autoUpdate = autoUpdate {
assert(host != null);
assert(isNotInline(host));
@@ -194,9 +195,9 @@
DefaultChartAxisImpl _getMeasureAxis(String axisId) {
_measureAxes.putIfAbsent(axisId, () {
var axisConf = config.getMeasureAxis(axisId),
- axis = axisConf != null ?
- new DefaultChartAxisImpl.withAxisConfig(this, axisConf) :
- new DefaultChartAxisImpl(this);
+ axis = axisConf != null
+ ? new DefaultChartAxisImpl.withAxisConfig(this, axisConf)
+ : new DefaultChartAxisImpl(this);
return axis;
});
return _measureAxes[axisId];
@@ -207,9 +208,9 @@
DefaultChartAxisImpl _getDimensionAxis(int column) {
_dimensionAxes.putIfAbsent(column, () {
var axisConf = config.getDimensionAxis(column),
- axis = axisConf != null ?
- new DefaultChartAxisImpl.withAxisConfig(this, axisConf) :
- new DefaultChartAxisImpl(this);
+ axis = axisConf != null
+ ? new DefaultChartAxisImpl.withAxisConfig(this, axisConf)
+ : new DefaultChartAxisImpl(this);
return axis;
});
return _dimensionAxes[column];
@@ -237,8 +238,7 @@
/// Computes the size of chart and if changed from the previous time
/// size was computed, sets attributes on svg element
Rect _computeChartSize() {
- int width = host.clientWidth,
- height = host.clientHeight;
+ int width = host.clientWidth, height = host.clientHeight;
if (config.minimumSize != null) {
width = max([width, config.minimumSize.width]);
@@ -247,7 +247,9 @@
AbsoluteRect padding = theme.padding;
num paddingLeft = config.isRTL ? padding.end : padding.start;
- Rect current = new Rect(paddingLeft, padding.top,
+ Rect current = new Rect(
+ paddingLeft,
+ padding.top,
width - (padding.start + padding.end),
height - (padding.top + padding.bottom));
if (layout.chartArea == null || layout.chartArea != current) {
@@ -264,7 +266,7 @@
}
@override
- draw({bool preRender:false, Future schedulePostRender}) {
+ draw({bool preRender: false, Future schedulePostRender}) {
assert(data != null && config != null);
assert(config.series != null && config.series.isNotEmpty);
@@ -275,9 +277,9 @@
_svg = _scope.append('svg:svg')..classed('chart-canvas');
if (!isNullOrEmpty(theme.filters)) {
var element = _svg.first,
- defs = Namespace.createChildElement('defs', element)
- ..append(new SvgElement.svg(
- theme.filters, treeSanitizer: new NullTreeSanitizer()));
+ defs = Namespace.createChildElement('defs', element)
+ ..append(new SvgElement.svg(theme.filters,
+ treeSanitizer: new NullTreeSanitizer()));
_svg.first.append(defs);
}
@@ -286,17 +288,18 @@
upperBehaviorPane = _svg.append('g')..classed('upper-render-pane');
if (_behaviors.isNotEmpty) {
- _behaviors.forEach(
- (b) => b.init(this, upperBehaviorPane, lowerBehaviorPane));
+ _behaviors
+ .forEach((b) => b.init(this, upperBehaviorPane, lowerBehaviorPane));
}
}
// Compute chart sizes and filter out unsupported series
_computeChartSize();
- var series = config.series.where((s) =>
- _isSeriesValid(s) && s.renderer.prepare(this, s)),
- selection = visualization.selectAll('.series-group').
- data(series, (x) => x.hashCode),
+ var series = config.series
+ .where((s) => _isSeriesValid(s) && s.renderer.prepare(this, s)),
+ selection = visualization
+ .selectAll('.series-group')
+ .data(series, (x) => x.hashCode),
axesDomainCompleter = new Completer();
// Wait till the axes are rendered before rendering series.
@@ -314,7 +317,7 @@
info.check();
group.attributes['transform'] = transform;
(s.renderer as CartesianRenderer)
- .draw(group, schedulePostRender:schedulePostRender);
+ .draw(group, schedulePostRender: schedulePostRender);
});
// A series that was rendered earlier isn't there anymore, remove it
@@ -350,16 +353,15 @@
/// Initialize the axes - required even if the axes are not being displayed.
_initAxes({bool preRender: false}) {
- Map measureAxisUsers = <String,Iterable<ChartSeries>>{};
+ Map measureAxisUsers = <String, Iterable<ChartSeries>>{};
// Create necessary measures axes.
// If measure axes were not configured on the series, default is used.
_series.forEach((ChartSeries s) {
- var measureAxisIds = isNullOrEmpty(s.measureAxisIds)
- ? MEASURE_AXIS_IDS
- : s.measureAxisIds;
+ var measureAxisIds =
+ isNullOrEmpty(s.measureAxisIds) ? MEASURE_AXIS_IDS : s.measureAxisIds;
measureAxisIds.forEach((axisId) {
- _getMeasureAxis(axisId); // Creates axis if required
+ _getMeasureAxis(axisId); // Creates axis if required
var users = measureAxisUsers[axisId];
if (users == null) {
measureAxisUsers[axisId] = [s];
@@ -402,18 +404,18 @@
// Configure dimension axes.
int dimensionAxesCount = useTwoDimensionAxes ? 2 : 1;
config.dimensions.take(dimensionAxesCount).forEach((int column) {
- var axis = _getDimensionAxis(column),
- sampleColumnSpec = data.columns.elementAt(column),
- values = data.rows.map((row) => row.elementAt(column)),
- domain;
+ var axis = _getDimensionAxis(column),
+ sampleColumnSpec = data.columns.elementAt(column),
+ values = data.rows.map((row) => row.elementAt(column)),
+ domain;
- if (sampleColumnSpec.useOrdinalScale) {
- domain = values.map((e) => e.toString()).toList();
- } else {
- var extent = new Extent.items(values);
- domain = [extent.min, extent.max];
- }
- axis.initAxisDomain(column, true, domain);
+ if (sampleColumnSpec.useOrdinalScale) {
+ domain = values.map((e) => e.toString()).toList();
+ } else {
+ var extent = new Extent.items(values);
+ domain = [extent.min, extent.max];
+ }
+ axis.initAxisDomain(column, true, domain);
});
// See if any dimensions need "band" on the axis.
@@ -421,23 +423,22 @@
List<bool> usingBands = [false, false];
_series.forEach((ChartSeries s) =>
(s.renderer as CartesianRenderer).dimensionsUsingBand.forEach((x) {
- if (x <= 1 && !(usingBands[x])) {
- usingBands[x] = true;
- dimensionsUsingBands.add(config.dimensions.elementAt(x));
- }
- }));
+ if (x <= 1 && !(usingBands[x])) {
+ usingBands[x] = true;
+ dimensionsUsingBands.add(config.dimensions.elementAt(x));
+ }
+ }));
// List of measure and dimension axes that are displayed
- assert(
- isNullOrEmpty(config.displayedMeasureAxes) ||
+ assert(isNullOrEmpty(config.displayedMeasureAxes) ||
config.displayedMeasureAxes.length < 2);
var measureAxesCount = dimensionAxesCount == 1 ? 2 : 0,
displayedMeasureAxes = (isNullOrEmpty(config.displayedMeasureAxes)
- ? _measureAxes.keys.take(measureAxesCount)
- : config.displayedMeasureAxes.take(measureAxesCount)).
- toList(growable: false),
+ ? _measureAxes.keys.take(measureAxesCount)
+ : config.displayedMeasureAxes.take(measureAxesCount))
+ .toList(growable: false),
displayedDimensionAxes =
- config.dimensions.take(dimensionAxesCount).toList(growable: false);
+ config.dimensions.take(dimensionAxesCount).toList(growable: false);
// Compute size of the dimension axes
if (config.renderDimensionAxes != false) {
@@ -481,8 +482,9 @@
// Draw the visible measure axes, if any.
if (displayedMeasureAxes.isNotEmpty) {
- var axisGroups = visualization.
- selectAll('.measure-axis-group').data(displayedMeasureAxes);
+ var axisGroups = visualization
+ .selectAll('.measure-axis-group')
+ .data(displayedMeasureAxes);
// Update measure axis (add/remove/update)
axisGroups.enter.append('svg:g');
axisGroups.each((axisId, index, group) {
@@ -494,8 +496,9 @@
// Draw the dimension axes, unless asked not to.
if (config.renderDimensionAxes != false) {
- var dimAxisGroups = visualization.
- selectAll('.dimension-axis-group').data(displayedDimensionAxes);
+ var dimAxisGroups = visualization
+ .selectAll('.dimension-axis-group')
+ .data(displayedDimensionAxes);
// Update dimension axes (add/remove/update)
dimAxisGroups.enter.append('svg:g');
dimAxisGroups.each((column, index, group) {
@@ -505,15 +508,18 @@
dimAxisGroups.exit.remove();
} else {
// Initialize scale on invisible axis
- var dimensionAxisOrientations = config.isLeftAxisPrimary ?
- DIMENSION_AXIS_ORIENTATIONS.last : DIMENSION_AXIS_ORIENTATIONS.first;
+ var dimensionAxisOrientations = config.isLeftAxisPrimary
+ ? DIMENSION_AXIS_ORIENTATIONS.last
+ : DIMENSION_AXIS_ORIENTATIONS.first;
for (int i = 0; i < dimensionAxesCount; ++i) {
var column = config.dimensions.elementAt(i),
axis = _dimensionAxes[column],
orientation = dimensionAxisOrientations[i];
- axis.initAxisScale(orientation == ORIENTATION_LEFT ?
- [layout.renderArea.height, 0] : [0, layout.renderArea.width]);
- };
+ axis.initAxisScale(orientation == ORIENTATION_LEFT
+ ? [layout.renderArea.height, 0]
+ : [0, layout.renderArea.width]);
+ }
+ ;
}
}
@@ -535,21 +541,17 @@
renderAreaWidth = layout.chartArea.width -
(left.width + layout.axes[ORIENTATION_RIGHT].width);
- layout.renderArea = new Rect(
- left.width, top.height, renderAreaWidth, renderAreaHeight);
+ layout.renderArea =
+ new Rect(left.width, top.height, renderAreaWidth, renderAreaHeight);
layout._axes
- ..[ORIENTATION_TOP] =
- new Rect(left.width, 0, renderAreaWidth, top.height)
- ..[ORIENTATION_RIGHT] =
- new Rect(left.width + renderAreaWidth, top.y,
- right.width, renderAreaHeight)
- ..[ORIENTATION_BOTTOM] =
- new Rect(left.width, top.height + renderAreaHeight,
- renderAreaWidth, bottom.height)
+ ..[ORIENTATION_TOP] = new Rect(left.width, 0, renderAreaWidth, top.height)
+ ..[ORIENTATION_RIGHT] = new Rect(
+ left.width + renderAreaWidth, top.y, right.width, renderAreaHeight)
+ ..[ORIENTATION_BOTTOM] = new Rect(left.width,
+ top.height + renderAreaHeight, renderAreaWidth, bottom.height)
..[ORIENTATION_LEFT] =
- new Rect(
- left.width, top.height, left.width, renderAreaHeight);
+ new Rect(left.width, top.height, left.width, renderAreaHeight);
}
// Updates the legend, if configuration changed since the last
@@ -562,14 +564,15 @@
List seriesByColumn =
new List.generate(data.columns.length, (_) => new List());
- _series.forEach((s) =>
- s.measures.forEach((m) => seriesByColumn[m].add(s)));
+ _series.forEach((s) => s.measures.forEach((m) => seriesByColumn[m].add(s)));
seriesByColumn.asMap().forEach((int i, List s) {
if (s.length == 0) return;
legend.add(new ChartLegendItem(
- index:i, label:data.columns.elementAt(i).label, series:s,
- color:theme.getColorForKey(i)));
+ index: i,
+ label: data.columns.elementAt(i).label,
+ series: s,
+ color: theme.getColorForKey(i)));
});
_config.legend.update(legend, this);
@@ -578,28 +581,23 @@
@override
Stream<ChartEvent> get onMouseUp =>
- host.onMouseUp
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ host.onMouseUp.map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
- Stream<ChartEvent> get onMouseDown =>
- host.onMouseDown
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ Stream<ChartEvent> get onMouseDown => host.onMouseDown
+ .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
- Stream<ChartEvent> get onMouseOver =>
- host.onMouseOver
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ Stream<ChartEvent> get onMouseOver => host.onMouseOver
+ .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
Stream<ChartEvent> get onMouseOut =>
- host.onMouseOut
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ host.onMouseOut.map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
- Stream<ChartEvent> get onMouseMove =>
- host.onMouseMove
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ Stream<ChartEvent> get onMouseMove => host.onMouseMove
+ .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
Stream<ChartEvent> get onValueClick {
@@ -654,11 +652,11 @@
class _ChartAreaLayout implements ChartAreaLayout {
final _axes = <String, Rect>{
- ORIENTATION_LEFT: const Rect(),
- ORIENTATION_RIGHT: const Rect(),
- ORIENTATION_TOP: const Rect(),
- ORIENTATION_BOTTOM: const Rect()
- };
+ ORIENTATION_LEFT: const Rect(),
+ ORIENTATION_RIGHT: const Rect(),
+ ORIENTATION_TOP: const Rect(),
+ ORIENTATION_BOTTOM: const Rect()
+ };
UnmodifiableMapView<String, Rect> _axesView;
@@ -713,7 +711,8 @@
if (state != null) {
var current = state.hovered;
if (current != null &&
- current.first == e.column && current.last == e.row) {
+ current.first == e.column &&
+ current.last == e.row) {
state.hovered = null;
}
}
@@ -725,7 +724,7 @@
check() {
if (_renderer != _series.renderer) {
dispose();
- if (_series.renderer is ChartRendererBehaviorSource){
+ if (_series.renderer is ChartRendererBehaviorSource) {
_disposer.addAll([
_series.renderer.onValueClick.listen(_click),
_series.renderer.onValueMouseOver.listen(_mouseOver),
@@ -736,5 +735,8 @@
_renderer = _series.renderer;
}
- dispose() => _disposer.dispose();
+ dispose() {
+ _renderer?.dispose();
+ _disposer.dispose();
+ }
}
diff --git a/packages/charted/lib/charts/src/chart_axis_impl.dart b/packages/charted/lib/charts/src/chart_axis_impl.dart
index 3f5d12d..345d537 100644
--- a/packages/charted/lib/charts/src/chart_axis_impl.dart
+++ b/packages/charted/lib/charts/src/chart_axis_impl.dart
@@ -55,16 +55,16 @@
if (scale is OrdinalScale) {
var usingBands = _area.dimensionsUsingBands.contains(_column),
innerPadding = usingBands ? _theme.axisBandInnerPadding : 1.0,
- outerPadding = usingBands ?
- _theme.axisBandOuterPadding : _theme.axisOuterPadding;
+ outerPadding =
+ usingBands ? _theme.axisBandOuterPadding : _theme.axisOuterPadding;
// This is because when left axis is primary the first data row should
// appear on top of the y-axis instead of on bottom.
if (_area.config.isLeftAxisPrimary) {
range = range.toList().reversed;
}
- (scale as OrdinalScale).
- rangeRoundBands(range, innerPadding, outerPadding);
+ (scale as OrdinalScale)
+ .rangeRoundBands(range, innerPadding, outerPadding);
} else {
scale.range = range;
scale.ticksCount = _theme.axisTickCount;
@@ -97,8 +97,9 @@
var width = textMetrics.getLongestTextWidth(formattedTicks).ceil();
if (width > _theme.verticalAxisWidth) {
width = _theme.verticalAxisWidth;
- shortenedTicks = formattedTicks.map(
- (x) => textMetrics.ellipsizeText(x, width)).toList();
+ shortenedTicks = formattedTicks
+ .map((x) => textMetrics.ellipsizeText(x, width))
+ .toList();
}
if (_theme.verticalAxisAutoResize) {
size.width =
@@ -115,7 +116,7 @@
var rect = _area.layout.axes[_orientation],
renderAreaRect = _area.layout.renderArea,
- range = _isVertical ? [rect.height, 0] : [0, rect.width],
+ range = _isVertical ? [rect.height, 0] : [0, rect.width],
innerTickSize = _theme.axisTickSize <= ChartAxisTheme.FILL_RENDER_AREA
? 0 - (_isVertical ? renderAreaRect.width : renderAreaRect.height)
: _theme.axisTickSize,
@@ -126,24 +127,25 @@
element.attributes['transform'] = 'translate(${rect.x}, ${rect.y})';
if (!_isVertical) {
- _axisTicksPlacement =
- new RotateHorizontalAxisTicks(rect,
- _theme.ticksFont, _theme.axisTickSize + _theme.axisTickPadding);
+ _axisTicksPlacement = new RotateHorizontalAxisTicks(
+ rect, _theme.ticksFont, _theme.axisTickSize + _theme.axisTickPadding);
}
initAxisScale(range);
- var axis = new SvgAxis(orientation: _orientation,
- innerTickSize: innerTickSize, outerTickSize: 0,
+ var axis = new SvgAxis(
+ orientation: _orientation,
+ innerTickSize: innerTickSize,
+ outerTickSize: 0,
tickPadding: _theme.axisTickPadding,
- tickFormat: _columnSpec.formatter, tickValues: tickValues,
+ tickFormat: _columnSpec.formatter,
+ tickValues: tickValues,
scale: scale);
axis.create(element, scope,
axisTicksBuilder: _axisTicksPlacement, isRTL: _area.config.isRTL);
}
- void clear() {
- }
+ void clear() {}
// Scale passed through configuration takes precedence
Scale get scale =>
@@ -175,8 +177,7 @@
RotateHorizontalAxisTicks(this.rect, this.ticksFont, this.tickLineLength);
void init(SvgAxis axis) {
- assert(
- axis.orientation == ORIENTATION_BOTTOM ||
+ assert(axis.orientation == ORIENTATION_BOTTOM ||
axis.orientation == ORIENTATION_TOP);
assert(ticksFont != null);
ticks = axis.tickValues;
@@ -190,16 +191,16 @@
// Check if we need rotation
if (0.90 * allowedWidth < maxLabelWidth) {
- var rectHeight = tickLineLength > 0
- ? rect.height - tickLineLength
- : rect.height;
+ var rectHeight =
+ tickLineLength > 0 ? rect.height - tickLineLength : rect.height;
rotation = 45;
// Check if we have enough space to render full chart
allowedWidth = (1.4142 * (rectHeight)) - (textMetrics.fontSize / 1.4142);
if (maxLabelWidth > allowedWidth) {
- shortenedTicks = formattedTicks.map(
- (x) => textMetrics.ellipsizeText(x, allowedWidth)).toList();
+ shortenedTicks = formattedTicks
+ .map((x) => textMetrics.ellipsizeText(x, allowedWidth))
+ .toList();
}
}
}
diff --git a/packages/charted/lib/charts/src/chart_config_impl.dart b/packages/charted/lib/charts/src/chart_config_impl.dart
index e77057c..640d23f 100644
--- a/packages/charted/lib/charts/src/chart_config_impl.dart
+++ b/packages/charted/lib/charts/src/chart_config_impl.dart
@@ -9,8 +9,8 @@
part of charted.charts;
class DefaultChartConfigImpl extends ChangeNotifier implements ChartConfig {
- final Map<String,ChartAxisConfig> _measureAxisRegistry = {};
- final Map<int,ChartAxisConfig> _dimensionAxisRegistry = {};
+ final Map<String, ChartAxisConfig> _measureAxisRegistry = {};
+ final Map<int, ChartAxisConfig> _dimensionAxisRegistry = {};
final SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
bool _isRTL = false;
@@ -39,7 +39,8 @@
@override
bool switchAxesForRTL = true;
- DefaultChartConfigImpl(Iterable<ChartSeries> series, Iterable<int> dimensions) {
+ DefaultChartConfigImpl(
+ Iterable<ChartSeries> series, Iterable<int> dimensions) {
this.series = series;
this.dimensions = dimensions;
}
@@ -55,8 +56,10 @@
// Monitor each series for changes on them
values.forEach((item) {
if (item is Observable) {
- _disposer.add(item.changes.listen(
- (_) => notifyChange(const ChartConfigChangeRecord())), item);
+ _disposer.add(
+ item.changes
+ .listen((_) => notifyChange(const ChartConfigChangeRecord())),
+ item);
}
});
@@ -69,8 +72,8 @@
record.removed.forEach((value) => _disposer.unsubscribe(value));
for (int i = 0; i < record.addedCount; i++) {
var added = observable[i + record.index];
- _disposer.add(added.changes.listen(
- (_) => notifyChange(const ChartConfigChangeRecord())));
+ _disposer.add(added.changes
+ .listen((_) => notifyChange(const ChartConfigChangeRecord())));
}
});
notifyChange(const ChartConfigChangeRecord());
@@ -93,9 +96,9 @@
if (values == null || values.isEmpty) return;
if (_dimensions is ObservableList) {
- _dimensionsSubscription =
- (_dimensions as ObservableList).listChanges.listen(
- (_) => notifyChange(const ChartConfigChangeRecord()));
+ _dimensionsSubscription = (_dimensions as ObservableList)
+ .listChanges
+ .listen((_) => notifyChange(const ChartConfigChangeRecord()));
}
}
diff --git a/packages/charted/lib/charts/src/chart_data_impl.dart b/packages/charted/lib/charts/src/chart_data_impl.dart
index 9a62f1d..dff6f5c 100644
--- a/packages/charted/lib/charts/src/chart_data_impl.dart
+++ b/packages/charted/lib/charts/src/chart_data_impl.dart
@@ -15,7 +15,8 @@
bool _hasObservableRows = false;
SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
- DefaultChartDataImpl(Iterable<ChartColumnSpec> columns, Iterable<Iterable> rows) {
+ DefaultChartDataImpl(
+ Iterable<ChartColumnSpec> columns, Iterable<Iterable> rows) {
this.columns = columns;
this.rows = rows;
}
@@ -36,17 +37,18 @@
_rows = value;
if (_rows is ObservableList) {
- _disposer.add(
- (_rows as ObservableList).listChanges.listen(rowsChanged));
+ _disposer.add((_rows as ObservableList).listChanges.listen(rowsChanged));
}
if (_rows.every((row) => row is ObservableList)) {
_hasObservableRows = true;
for (int i = 0; i < _rows.length; i++) {
var row = _rows.elementAt(i);
- _disposer.add(row.listChanges.listen((changes)
- => _valuesChanged(i, changes)), row);
- };
+ _disposer.add(
+ row.listChanges.listen((changes) => _valuesChanged(i, changes)),
+ row);
+ }
+ ;
} else if (_rows is Observable) {
logger.info('List of rows is Observable, but not rows themselves!');
}
@@ -62,16 +64,17 @@
changes.forEach((ListChangeRecord change) {
change.removed.forEach((item) => _disposer.unsubscribe(item));
- for(int i = 0; i < change.addedCount; i++) {
- var index = change.index + i,
- row = _rows.elementAt(index);
+ for (int i = 0; i < change.addedCount; i++) {
+ var index = change.index + i, row = _rows.elementAt(index);
if (row is! ObservableList) {
logger.severe('A non-observable row was added! '
'Changes on this row will not be monitored');
} else {
- _disposer.add(row.listChanges.listen((changes)
- => _valuesChanged(index, changes)), row);
+ _disposer.add(
+ row.listChanges
+ .listen((changes) => _valuesChanged(index, changes)),
+ row);
}
}
});
@@ -98,7 +101,7 @@
}
}
- var totalLength = 1; // 1 for the leading '|'.
+ var totalLength = 1; // 1 for the leading '|'.
for (var length in cellDataLength) {
// 3 for the leading and trailing ' ' padding and trailing '|'.
totalLength += length + 3;
diff --git a/packages/charted/lib/charts/src/chart_events_impl.dart b/packages/charted/lib/charts/src/chart_events_impl.dart
index 6e2c258..9bb8d1e 100644
--- a/packages/charted/lib/charts/src/chart_events_impl.dart
+++ b/packages/charted/lib/charts/src/chart_events_impl.dart
@@ -36,9 +36,8 @@
DefaultChartEventImpl(this.source, this.area,
[this.series, this.row, this.column, this.value]) {
var hostRect = area.host.getBoundingClientRect(),
- left = area.config.isRTL
- ? area.theme.padding.end
- : area.theme.padding.start;
+ left =
+ area.config.isRTL ? area.theme.padding.end : area.theme.padding.start;
if (source != null) {
chartX = source.client.x - hostRect.left - left;
chartY = source.client.y - hostRect.top - area.theme.padding.top;
diff --git a/packages/charted/lib/charts/src/chart_legend_impl.dart b/packages/charted/lib/charts/src/chart_legend_impl.dart
index 37778e1..dfefbc4 100644
--- a/packages/charted/lib/charts/src/chart_legend_impl.dart
+++ b/packages/charted/lib/charts/src/chart_legend_impl.dart
@@ -100,7 +100,7 @@
var tooltip = _root.select('.chart-legend-more-tooltip');
if (tooltip.isEmpty) {
tooltip = _root.select('.chart-legend-more').append('div')
- ..classed('chart-legend-more-tooltip');
+ ..classed('chart-legend-more-tooltip');
}
tooltip.style('opacity', '1');
@@ -110,8 +110,8 @@
/// Creates legend items starting at the given index.
void _createLegendItems() {
var state = _area.state,
- rows = _root.selectAll(
- '.chart-legend-row').data(_items, (x) => x.hashCode),
+ rows =
+ _root.selectAll('.chart-legend-row').data(_items, (x) => x.hashCode),
isFirstRender = rows.length == 0;
var enter = rows.enter.appendWithCallback((d, i, e) {
@@ -120,8 +120,10 @@
..className = 'chart-legend-color',
label = Namespace.createChildElement('div', e)
..className = 'chart-legend-label',
- value = showValues ? (Namespace.createChildElement('div', e)
- ..className = 'chart-legend-value') : null;
+ value = showValues
+ ? (Namespace.createChildElement('div', e)
+ ..className = 'chart-legend-value')
+ : null;
var rowStyles = ['chart-legend-row'].toList();
@@ -136,8 +138,8 @@
rowStyles.add('chart-legend-selected');
}
}
- rowStyles.addAll(
- d.series.map((ChartSeries x) => 'type-${x.renderer.name}'));
+ rowStyles
+ .addAll(d.series.map((ChartSeries x) => 'type-${x.renderer.name}'));
color.style.setProperty('background-color', d.color);
row.append(color);
@@ -186,17 +188,17 @@
enter
..on('mouseover', (d, i, e) => state.preview = d.index)
..on('mouseout', (d, i, e) {
- if (state.preview == d.index) {
- state.preview = null;
- }
- })
+ if (state.preview == d.index) {
+ state.preview = null;
+ }
+ })
..on('click', (d, i, e) {
- if (state.isSelected(d.index)) {
- state.unselect(d.index);
- } else {
- state.select(d.index);
- }
- });
+ if (state.isSelected(d.index)) {
+ state.unselect(d.index);
+ } else {
+ state.select(d.index);
+ }
+ });
}
rows.exit.remove();
diff --git a/packages/charted/lib/charts/src/chart_state_impl.dart b/packages/charted/lib/charts/src/chart_state_impl.dart
index 71c4afa..44f5d74 100644
--- a/packages/charted/lib/charts/src/chart_state_impl.dart
+++ b/packages/charted/lib/charts/src/chart_state_impl.dart
@@ -26,21 +26,22 @@
LinkedHashSet<int> hidden = new LinkedHashSet<int>();
LinkedHashSet<int> selection = new LinkedHashSet<int>();
- LinkedHashSet<Pair<int,int>> highlights = new LinkedHashSet<Pair<int,int>>();
+ LinkedHashSet<Pair<int, int>> highlights =
+ new LinkedHashSet<Pair<int, int>>();
- Pair<int,int> _hovered;
+ Pair<int, int> _hovered;
int _preview;
- DefaultChartStateImpl({
- this.supportColumnSelection: true,
- this.supportColumnPreview: true,
- this.supportValueHighlight: true,
- this.supportValueHover: true,
- this.isMultiSelect: false,
- this.isMultiHighlight: false,
- this.isSelectOrHighlight: true});
+ DefaultChartStateImpl(
+ {this.supportColumnSelection: true,
+ this.supportColumnPreview: true,
+ this.supportValueHighlight: true,
+ this.supportValueHover: true,
+ this.isMultiSelect: false,
+ this.isMultiHighlight: false,
+ this.isSelectOrHighlight: true});
- set hovered(Pair<int,int> value) {
+ set hovered(Pair<int, int> value) {
if (!this.supportValueHover) return null;
if (value != _hovered) {
_hovered = value;
@@ -48,7 +49,8 @@
}
return value;
}
- Pair<int,int> get hovered => _hovered;
+
+ Pair<int, int> get hovered => _hovered;
set preview(int value) {
if (!this.supportColumnPreview) return null;
@@ -58,12 +60,13 @@
}
return value;
}
+
int get preview => _preview;
bool unhide(int id) {
if (hidden.contains(id)) {
hidden.remove(id);
- notifyChange(new ChartVisibilityChangeRecord(unhide:id));
+ notifyChange(new ChartVisibilityChangeRecord(unhide: id));
}
return true;
}
@@ -71,7 +74,7 @@
bool hide(int id) {
if (!hidden.contains(id)) {
hidden.add(id);
- notifyChange(new ChartVisibilityChangeRecord(hide:id));
+ notifyChange(new ChartVisibilityChangeRecord(hide: id));
}
return false;
}
@@ -88,7 +91,7 @@
highlights.clear();
}
selection.add(id);
- notifyChange(new ChartSelectionChangeRecord(add:id));
+ notifyChange(new ChartSelectionChangeRecord(add: id));
}
return true;
}
@@ -96,7 +99,7 @@
bool unselect(int id) {
if (selection.contains(id)) {
selection.remove(id);
- notifyChange(new ChartSelectionChangeRecord(remove:id));
+ notifyChange(new ChartSelectionChangeRecord(remove: id));
}
return false;
}
diff --git a/packages/charted/lib/charts/src/layout_area_impl.dart b/packages/charted/lib/charts/src/layout_area_impl.dart
index cc4de21..d4bbaef 100644
--- a/packages/charted/lib/charts/src/layout_area_impl.dart
+++ b/packages/charted/lib/charts/src/layout_area_impl.dart
@@ -60,12 +60,8 @@
StreamController<ChartEvent> _valueMouseOutController;
StreamController<ChartEvent> _valueMouseClickController;
- DefaultLayoutAreaImpl(
- this.host,
- ChartData data,
- ChartConfig config,
- this._autoUpdate,
- this.state) {
+ DefaultLayoutAreaImpl(this.host, ChartData data, ChartConfig config,
+ this._autoUpdate, this.state) {
assert(host != null);
assert(isNotInline(host));
@@ -139,8 +135,7 @@
/// Computes the size of chart and if changed from the previous time
/// size was computed, sets attributes on svg element
Rect _computeChartSize() {
- int width = host.clientWidth,
- height = host.clientHeight;
+ int width = host.clientWidth, height = host.clientHeight;
if (config.minimumSize != null) {
width = max([width, config.minimumSize.width]);
@@ -149,7 +144,9 @@
AbsoluteRect padding = theme.padding;
num paddingLeft = config.isRTL ? padding.end : padding.start;
- Rect current = new Rect(paddingLeft, padding.top,
+ Rect current = new Rect(
+ paddingLeft,
+ padding.top,
width - (padding.start + padding.end),
height - (padding.top + padding.bottom));
if (layout.chartArea == null || layout.chartArea != current) {
@@ -170,7 +167,7 @@
}
@override
- draw({bool preRender:false, Future schedulePostRender}) {
+ draw({bool preRender: false, Future schedulePostRender}) {
assert(data != null && config != null);
assert(config.series != null && config.series.isNotEmpty);
@@ -185,15 +182,15 @@
upperBehaviorPane = _svg.append('g')..classed('upper-render-pane');
if (_behaviors.isNotEmpty) {
- _behaviors.forEach(
- (b) => b.init(this, upperBehaviorPane, lowerBehaviorPane));
+ _behaviors
+ .forEach((b) => b.init(this, upperBehaviorPane, lowerBehaviorPane));
}
}
// Compute chart sizes and filter out unsupported series
_computeChartSize();
- var series = config.series.firstWhere(
- (s) => s.renderer.prepare(this, s), orElse: () => null),
+ var series = config.series
+ .firstWhere((s) => s.renderer.prepare(this, s), orElse: () => null),
group = visualization.first.querySelector('.series-group');
// We need atleast one matching series.
@@ -202,7 +199,7 @@
// Create a group for rendering, if it was not already done.
if (group == null) {
group = Namespace.createChildElement('g', visualization.first)
- ..classes.add('series-group');
+ ..classes.add('series-group');
visualization.first.append(group);
}
@@ -250,7 +247,7 @@
}
Iterable<ChartLegendItem> legend =
- _renderer.layout(group, schedulePostRender:schedulePostRender);
+ _renderer.layout(group, schedulePostRender: schedulePostRender);
// Notify on the stream that the chart has been updated.
isReady = true;
@@ -259,33 +256,30 @@
_series = series;
// Updates the legend if required.
- _config.legend.update(legend, this);
+ if (_config.legend != null) {
+ _config.legend.update(legend, this);
+ }
}
@override
Stream<ChartEvent> get onMouseUp =>
- host.onMouseUp
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ host.onMouseUp.map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
- Stream<ChartEvent> get onMouseDown =>
- host.onMouseDown
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ Stream<ChartEvent> get onMouseDown => host.onMouseDown
+ .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
- Stream<ChartEvent> get onMouseOver =>
- host.onMouseOver
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ Stream<ChartEvent> get onMouseOver => host.onMouseOver
+ .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
Stream<ChartEvent> get onMouseOut =>
- host.onMouseOut
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ host.onMouseOut.map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
- Stream<ChartEvent> get onMouseMove =>
- host.onMouseMove
- .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
+ Stream<ChartEvent> get onMouseMove => host.onMouseMove
+ .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
@override
Stream<ChartEvent> get onValueClick {
diff --git a/packages/charted/lib/charts/themes/quantum_theme.dart b/packages/charted/lib/charts/themes/quantum_theme.dart
index a8cf063..127e78a 100644
--- a/packages/charted/lib/charts/themes/quantum_theme.dart
+++ b/packages/charted/lib/charts/themes/quantum_theme.dart
@@ -9,33 +9,32 @@
part of charted.charts;
class QuantumChartTheme extends ChartTheme {
- static const List OTHER_COLORS =
- const['#EEEEEE', '#BDBDBD', '#9E9E9E'];
+ static const List OTHER_COLORS = const ['#EEEEEE', '#BDBDBD', '#9E9E9E'];
- static const List<List<String>> COLORS = const[
- const [ '#C5D9FB', '#4184F3', '#2955C5' ],
- const [ '#F3C6C2', '#DB4437', '#A52714' ],
- const [ '#FBE7B1', '#F4B400', '#EF9200' ],
- const [ '#B6E0CC', '#0F9D58', '#0A7F42' ],
- const [ '#E0BDE6', '#AA46BB', '#691A99' ],
- const [ '#B1EAF1', '#00ABC0', '#00828E' ],
- const [ '#FFCBBB', '#FF6F42', '#E54918' ],
- const [ '#EFF3C2', '#9D9C23', '#817616' ],
- const [ '#C4C9E8', '#5B6ABF', '#3848AA' ],
- const [ '#F7BACF', '#EF6191', '#E81D62' ],
- const [ '#B1DEDA', '#00786A', '#004C3F' ],
- const [ '#F38EB0', '#C1175A', '#870D4E' ],
+ static const List<List<String>> COLORS = const [
+ const ['#C5D9FB', '#4184F3', '#2955C5'],
+ const ['#F3C6C2', '#DB4437', '#A52714'],
+ const ['#FBE7B1', '#F4B400', '#EF9200'],
+ const ['#B6E0CC', '#0F9D58', '#0A7F42'],
+ const ['#E0BDE6', '#AA46BB', '#691A99'],
+ const ['#B1EAF1', '#00ABC0', '#00828E'],
+ const ['#FFCBBB', '#FF6F42', '#E54918'],
+ const ['#EFF3C2', '#9D9C23', '#817616'],
+ const ['#C4C9E8', '#5B6ABF', '#3848AA'],
+ const ['#F7BACF', '#EF6191', '#E81D62'],
+ const ['#B1DEDA', '#00786A', '#004C3F'],
+ const ['#F38EB0', '#C1175A', '#870D4E'],
];
- static const List<List<String>> COLORS_ASSIST = const[
- const [ '#C5D9FB', '#4184F3', '#2955C5' ],
- const [ '#F3C6C2', '#DB4437', '#A52714' ],
- const [ '#FBE7B1', '#F4B400', '#EF9200' ],
- const [ '#B6E0CC', '#0F9D58', '#0A7F42' ],
- const [ '#E0BDE6', '#AA46BB', '#691A99' ],
- const [ '#B1EAF1', '#00ABC0', '#00828E' ],
- const [ '#FFCBBB', '#FF6F42', '#E54918' ],
- const [ '#EFF3C2', '#9D9C23', '#817616' ]
+ static const List<List<String>> COLORS_ASSIST = const [
+ const ['#C5D9FB', '#4184F3', '#2955C5'],
+ const ['#F3C6C2', '#DB4437', '#A52714'],
+ const ['#FBE7B1', '#F4B400', '#EF9200'],
+ const ['#B6E0CC', '#0F9D58', '#0A7F42'],
+ const ['#E0BDE6', '#AA46BB', '#691A99'],
+ const ['#B1EAF1', '#00ABC0', '#00828E'],
+ const ['#FFCBBB', '#FF6F42', '#E54918'],
+ const ['#EFF3C2', '#9D9C23', '#817616']
];
final OrdinalScale _scale = new OrdinalScale()..range = COLORS;
@@ -64,19 +63,15 @@
}
@override
- String getFilterForState(int state) =>
- state & ChartState.COL_PREVIEW != 0 ||
+ String getFilterForState(int state) => state & ChartState.COL_PREVIEW != 0 ||
state & ChartState.VAL_HOVERED != 0 ||
state & ChartState.COL_SELECTED != 0 ||
- state & ChartState.VAL_HIGHLIGHTED != 0
- ? 'url(#drop-shadow)'
- : '';
+ state & ChartState.VAL_HIGHLIGHTED != 0 ? 'url(#drop-shadow)' : '';
@override
- String getOtherColor([int state = 0]) =>
- OTHER_COLORS is Iterable
- ? colorForState(OTHER_COLORS, state)
- : OTHER_COLORS;
+ String getOtherColor([int state = 0]) => OTHER_COLORS is Iterable
+ ? colorForState(OTHER_COLORS, state)
+ : OTHER_COLORS;
@override
ChartAxisTheme getMeasureAxisTheme([Scale _]) =>
diff --git a/packages/charted/lib/core/interpolators.dart b/packages/charted/lib/core/interpolators.dart
index 0626a6f..9ad174a 100644
--- a/packages/charted/lib/core/interpolators.dart
+++ b/packages/charted/lib/core/interpolators.dart
@@ -7,12 +7,12 @@
//
/// A collection of interpolator generators and easing functions.
-///
+///
/// Interpolators provide intermediate state when transitioning from one
/// frame to another in an animation.
-///
-/// Easing functions indicate progress of an animation to interpolators.
-///
+///
+/// Easing functions indicate progress of an animation to interpolators.
+///
/// Currently provides interpolator for various types, including basic types
/// like numbers, colors, strings, transforms and for iterables.
library charted.core.interpolators;
diff --git a/packages/charted/lib/core/interpolators/easing.dart b/packages/charted/lib/core/interpolators/easing.dart
index 2b4bdec..e80bbd6 100644
--- a/packages/charted/lib/core/interpolators/easing.dart
+++ b/packages/charted/lib/core/interpolators/easing.dart
@@ -8,21 +8,21 @@
part of charted.core.interpolators;
-const String EASE_TYPE_LINEAR = 'linear';
-const String EASE_TYPE_POLY = 'poly';
-const String EASE_TYPE_QUAD = 'quad';
-const String EASE_TYPE_CUBIC = 'cubic';
-const String EASE_TYPE_SIN = 'sin';
-const String EASE_TYPE_EXP = 'exp';
-const String EASE_TYPE_CIRCLE = 'circle';
-const String EASE_TYPE_ELASTIC = 'elastic';
-const String EASE_TYPE_BACK = 'back';
-const String EASE_TYPE_BOUNCE = 'bounce';
+const String EASE_TYPE_LINEAR = 'linear';
+const String EASE_TYPE_POLY = 'poly';
+const String EASE_TYPE_QUAD = 'quad';
+const String EASE_TYPE_CUBIC = 'cubic';
+const String EASE_TYPE_SIN = 'sin';
+const String EASE_TYPE_EXP = 'exp';
+const String EASE_TYPE_CIRCLE = 'circle';
+const String EASE_TYPE_ELASTIC = 'elastic';
+const String EASE_TYPE_BACK = 'back';
+const String EASE_TYPE_BOUNCE = 'bounce';
-const String EASE_MODE_IN = 'in';
-const String EASE_MODE_OUT = 'out';
-const String EASE_MODE_IN_OUT = 'in-out';
-const String EASE_MODE_OUT_IN = 'out-in';
+const String EASE_MODE_IN = 'in';
+const String EASE_MODE_OUT = 'out';
+const String EASE_MODE_IN_OUT = 'in-out';
+const String EASE_MODE_OUT_IN = 'out-in';
/// [EasingFunction] manipulates progression of an animation. The returned
/// value is passed to an [Interpolator] to generate intermediate state.
@@ -33,8 +33,8 @@
typedef EasingFunction EasingModeFunction(EasingFunction fn);
/// Creates an easing function based on type and mode.
-EasingFunction easingFunctionByName(
- String type, [String mode = EASE_MODE_IN, List params]) {
+EasingFunction easingFunctionByName(String type,
+ [String mode = EASE_MODE_IN, List params]) {
const Map easingTypes = const {
EASE_TYPE_LINEAR: identityFunction,
EASE_TYPE_POLY: easePoly,
@@ -47,21 +47,21 @@
EASE_TYPE_BACK: easeBack,
EASE_TYPE_BOUNCE: easeBounce
};
-
+
const Map easingModes = const {
EASE_MODE_IN: identityFunction,
EASE_MODE_OUT: reverseEasingFn,
EASE_MODE_IN_OUT: reflectEasingFn,
EASE_MODE_OUT_IN: reflectReverseEasingFn
};
-
+
const Map customEasingFunctions = const {
- '$EASE_TYPE_CUBIC-$EASE_MODE_IN_OUT': easeCubicInOut
+ '$EASE_TYPE_CUBIC-$EASE_MODE_IN_OUT': easeCubicInOut
};
assert(easingTypes.containsKey(type));
assert(easingModes.containsKey(mode));
-
+
EasingFunction fn;
if (customEasingFunctions.containsKey('$type-$mode')) {
fn = Function.apply(customEasingFunctions['$type-$mode'], params);
@@ -72,18 +72,15 @@
return clampEasingFn(fn);
}
-
/// Clamps transition progress to stay between 0.0 and 1.0
EasingFunction clampEasingFn(EasingFunction f) =>
(t) => t <= 0 ? 0 : t >= 1 ? 1 : f(t);
-
//
// Implementation of easing modes.
//
-EasingFunction reverseEasingFn(EasingFunction f) =>
- (t) => 1 - f(1 - t);
+EasingFunction reverseEasingFn(EasingFunction f) => (t) => 1 - f(1 - t);
EasingFunction reflectEasingFn(EasingFunction f) =>
(t) => .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
@@ -91,7 +88,6 @@
EasingFunction reflectReverseEasingFn(EasingFunction f) =>
reflectEasingFn(reverseEasingFn(f));
-
//
// Implementation of easing function generators.
//
@@ -100,38 +96,33 @@
EasingFunction easeElastic([a = 1, p = 0.45]) {
var s = p / 2 * math.PI * math.asin(1 / a);
- return (t) => 1 + a * math.pow(2, -10 * t) *
- math.sin((t - s) * 2 * math.PI / p);
+ return (t) =>
+ 1 + a * math.pow(2, -10 * t) * math.sin((t - s) * 2 * math.PI / p);
}
-EasingFunction easeBack([s = 1.70158]) =>
- (num t) => t * t * ((s + 1) * t - s);
+EasingFunction easeBack([s = 1.70158]) => (num t) => t * t * ((s + 1) * t - s);
EasingFunction easeQuad() => (num t) => t * t;
EasingFunction easeCubic() => (num t) => t * t * t;
-EasingFunction easeCubicInOut() =>
- (num t) {
+EasingFunction easeCubicInOut() => (num t) {
if (t <= 0) return 0;
if (t >= 1) return 1;
- var t2 = t * t,
- t3 = t2 * t;
+ var t2 = t * t, t3 = t2 * t;
return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
};
-EasingFunction easeSin() =>
- (num t) => 1 - math.cos(t * math.PI / 2);
+EasingFunction easeSin() => (num t) => 1 - math.cos(t * math.PI / 2);
-EasingFunction easeExp() =>
- (num t) => math.pow(2, 10 * (t - 1));
+EasingFunction easeExp() => (num t) => math.pow(2, 10 * (t - 1));
-EasingFunction easeCircle() =>
- (num t) => 1 - math.sqrt(1 - t * t);
+EasingFunction easeCircle() => (num t) => 1 - math.sqrt(1 - t * t);
-EasingFunction easeBounce() =>
- (num t) => t < 1 / 2.75 ?
- 7.5625 * t * t : t < 2 / 2.75 ?
- 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ?
- 7.5625 * (t -= 2.25 / 2.75) * t + .9375
- : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
+EasingFunction easeBounce() => (num t) => t < 1 / 2.75
+ ? 7.5625 * t * t
+ : t < 2 / 2.75
+ ? 7.5625 * (t -= 1.5 / 2.75) * t + .75
+ : t < 2.5 / 2.75
+ ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375
+ : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
diff --git a/packages/charted/lib/core/interpolators/interpolators.dart b/packages/charted/lib/core/interpolators/interpolators.dart
index 0de80ee..56dc9df 100644
--- a/packages/charted/lib/core/interpolators/interpolators.dart
+++ b/packages/charted/lib/core/interpolators/interpolators.dart
@@ -19,7 +19,7 @@
/// List of registered interpolators - [createInterpolatorFromRegistry]
/// iterates through this list from backwards and the first non-null
/// interpolate function is returned to the caller.
-List<InterpolatorGenerator> _interpolators = [ createInterpolatorByType ];
+List<InterpolatorGenerator> _interpolators = [createInterpolatorByType];
/// Returns a default interpolator between values [a] and [b]. Unless
/// more interpolators are added, one of the internal implementations are
@@ -33,18 +33,21 @@
}
/// Creates an interpolator based on the type of [a] and [b].
-///
+///
/// Usage note: Use this method only when type of [a] and [b] are not known.
/// When used, this function will prevent tree shaking of all built-in
/// interpolators.
-Interpolator createInterpolatorByType(a, b) =>
- (a is List && b is List) ? createListInterpolator(a, b) :
- (a is Map && b is Map) ? createMapInterpolator(a, b) :
- (a is String && b is String) ? createStringInterpolator(a, b) :
- (a is num && b is num) ? createNumberInterpolator(a, b) :
- (a is Color && b is Color) ? createRgbColorInterpolator(a, b) :
- (t) => (t <= 0.5) ? a : b;
-
+Interpolator createInterpolatorByType(a, b) => (a is List && b is List)
+ ? createListInterpolator(a, b)
+ : (a is Map && b is Map)
+ ? createMapInterpolator(a, b)
+ : (a is String && b is String)
+ ? createStringInterpolator(a, b)
+ : (a is num && b is num)
+ ? createNumberInterpolator(a, b)
+ : (a is Color && b is Color)
+ ? createRgbColorInterpolator(a, b)
+ : (t) => (t <= 0.5) ? a : b;
//
// Implementations of InterpolatorGenerator
@@ -62,7 +65,6 @@
return (t) => (a + b * t).round();
}
-
/// Generate an interpolator between two strings [a] and [b].
///
/// The interpolator will interpolate all the number pairs in both strings
@@ -87,9 +89,8 @@
return createHslColorInterpolator(
new Color.fromHslString(a), new Color.fromHslString(b));
}
-
- var numberRegEx =
- new RegExp(r'[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?'),
+
+ var numberRegEx = new RegExp(r'[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?'),
numMatchesInA = numberRegEx.allMatches(a),
numMatchesInB = numberRegEx.allMatches(b),
stringParts = [],
@@ -111,13 +112,13 @@
int numberLength = math.min(numberPartsInA.length, numberPartsInB.length);
int maxLength = math.max(numberPartsInA.length, numberPartsInB.length);
for (var i = 0; i < numberLength; i++) {
- interpolators.add(createNumberInterpolator(num.parse(numberPartsInA[i]),
- num.parse(numberPartsInB[i])));
+ interpolators.add(createNumberInterpolator(
+ num.parse(numberPartsInA[i]), num.parse(numberPartsInB[i])));
}
if (numberPartsInA.length < numberPartsInB.length) {
for (var i = numberLength; i < maxLength; i++) {
- interpolators.add(createNumberInterpolator(num.parse(numberPartsInB[i]),
- num.parse(numberPartsInB[i])));
+ interpolators.add(createNumberInterpolator(
+ num.parse(numberPartsInB[i]), num.parse(numberPartsInB[i])));
}
}
@@ -136,29 +137,19 @@
/// Generate an interpolator for RGB values.
Interpolator createRgbColorInterpolator(Color a, Color b) {
if (a == null || b == null) return (t) => b;
- var ar = a.r,
- ag = a.g,
- ab = a.b,
- br = b.r - ar,
- bg = b.g - ag,
- bb = b.b - ab;
+ var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
- return (t) => new Color.fromRgba((ar + br * t).round(),
- (ag + bg * t).round(), (ab + bb * t).round(), 1.0).toRgbaString();
+ return (t) => new Color.fromRgba((ar + br * t).round(), (ag + bg * t).round(),
+ (ab + bb * t).round(), 1.0).toRgbaString();
}
/// Generate an interpolator using HSL color system converted to Hex string.
Interpolator createHslColorInterpolator(Color a, Color b) {
if (a == null || b == null) return (t) => b;
- var ah = a.h,
- as = a.s,
- al = a.l,
- bh = b.h - ah,
- bs = b.s - as,
- bl = b.l - al;
+ var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
- return (t) => new Color.fromHsla((ah + bh * t).round(),
- (as + bs * t).round(), (al + bl * t).round(), 1.0).toHslaString();
+ return (t) => new Color.fromHsla((ah + bh * t).round(), (as + bs * t).round(),
+ (al + bl * t).round(), 1.0).toHslaString();
}
/// Generates an interpolator to interpolate each element between lists
@@ -218,25 +209,21 @@
var numRegExStr = r'[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?',
numberRegEx = new RegExp(numRegExStr),
translateRegEx =
- new RegExp(r'translate\(' + '$numRegExStr,$numRegExStr' + r'\)'),
+ new RegExp(r'translate\(' + '$numRegExStr,$numRegExStr' + r'\)'),
scaleRegEx =
- new RegExp(r'scale\(' + numRegExStr + r',' + numRegExStr + r'\)'),
+ new RegExp(r'scale\(' + numRegExStr + r',' + numRegExStr + r'\)'),
rotateRegEx = new RegExp(r'rotate\(' + numRegExStr + r'(deg)?\)'),
skewRegEx = new RegExp(r'skewX\(' + numRegExStr + r'(deg)?\)'),
-
translateA = translateRegEx.firstMatch(a),
scaleA = scaleRegEx.firstMatch(a),
rotateA = rotateRegEx.firstMatch(a),
skewA = skewRegEx.firstMatch(a),
-
translateB = translateRegEx.firstMatch(b),
scaleB = scaleRegEx.firstMatch(b),
rotateB = rotateRegEx.firstMatch(b),
skewB = skewRegEx.firstMatch(b);
- var numSetA = [],
- numSetB = [],
- tempStr, match;
+ var numSetA = [], numSetB = [], tempStr, match;
// translate
if (translateA != null) {
@@ -246,7 +233,7 @@
numSetA.add(num.parse(m.group(0)));
}
} else {
- numSetA.addAll(const[0, 0]);
+ numSetA.addAll(const [0, 0]);
}
if (translateB != null) {
@@ -256,7 +243,7 @@
numSetB.add(num.parse(m.group(0)));
}
} else {
- numSetB.addAll(const[0, 0]);
+ numSetB.addAll(const [0, 0]);
}
// scale
@@ -267,7 +254,7 @@
numSetA.add(num.parse(m.group(0)));
}
} else {
- numSetA.addAll(const[1, 1]);
+ numSetA.addAll(const [1, 1]);
}
if (scaleB != null) {
@@ -277,7 +264,7 @@
numSetB.add(num.parse(m.group(0)));
}
} else {
- numSetB.addAll(const[1, 1]);
+ numSetB.addAll(const [1, 1]);
}
// rotate
@@ -324,11 +311,10 @@
}
return (t) {
- return
- 'translate(${createNumberInterpolator(numSetA[0], numSetB[0])(t)},'
- '${createNumberInterpolator(numSetA[1], numSetB[1])(t)})'
+ return 'translate(${createNumberInterpolator(numSetA[0], numSetB[0])(t)},'
+ '${createNumberInterpolator(numSetA[1], numSetB[1])(t)})'
'scale(${createNumberInterpolator(numSetA[2], numSetB[2])(t)},'
- '${createNumberInterpolator(numSetA[3], numSetB[3])(t)})'
+ '${createNumberInterpolator(numSetA[3], numSetB[3])(t)})'
'rotate(${createNumberInterpolator(numSetA[4], numSetB[4])(t)})'
'skewX(${createNumberInterpolator(numSetA[5], numSetB[5])(t)})';
};
@@ -340,12 +326,9 @@
if (a == null || b == null) return (t) => b;
assert(a.length == b.length && a.length == 3);
- var sqrt2 = math.SQRT2,
- param2 = 2,
- param4 = 4;
+ var sqrt2 = math.SQRT2, param2 = 2, param4 = 4;
- var ux0 = a[0], uy0 = a[1], w0 = a[2],
- ux1 = b[0], uy1 = b[1], w1 = b[2];
+ var ux0 = a[0], uy0 = a[1], w0 = a[2], ux1 = b[0], uy1 = b[1], w1 = b[2];
var dx = ux1 - ux0,
dy = uy1 - uy0,
@@ -364,18 +347,10 @@
// General case.
var coshr0 = cosh(r0),
u = w0 / (param2 * d1) * (coshr0 * tanh(sqrt2 * s + r0) - sinh(r0));
- return [
- ux0 + u * dx,
- uy0 + u * dy,
- w0 * coshr0 / cosh(sqrt2 * s + r0)
- ];
+ return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(sqrt2 * s + r0)];
}
// Special case for u0 ~= u1.
- return [
- ux0 + t * dx,
- uy0 + t * dy,
- w0 * math.exp(sqrt2 * s)
- ];
+ return [ux0 + t * dx, uy0 + t * dy, w0 * math.exp(sqrt2 * s)];
};
}
diff --git a/packages/charted/lib/core/scales.dart b/packages/charted/lib/core/scales.dart
index 4acd744..e4a9948 100644
--- a/packages/charted/lib/core/scales.dart
+++ b/packages/charted/lib/core/scales.dart
@@ -106,7 +106,7 @@
void rangeRoundBands(Iterable range, [double padding, double outerPadding]);
}
-class RoundingFunctions extends Pair<RoundFunction,RoundFunction> {
+class RoundingFunctions extends Pair<RoundFunction, RoundFunction> {
RoundingFunctions(RoundFunction floor, RoundFunction ceil)
: super(floor, ceil);
@@ -123,10 +123,9 @@
/// Namespacing container for utilities used by scales.
abstract class ScaleUtils {
/// Utility to return extent of sorted [values].
- static Extent extent(Iterable values) =>
- values.first < values.last
- ? new Extent(values.first, values.last)
- : new Extent(values.last, values.first);
+ static Extent extent(Iterable values) => values.first < values.last
+ ? new Extent(values.first, values.last)
+ : new Extent(values.last, values.first);
/// Extends [values] to round numbers based on the given pair of
/// floor and ceil functions. [functions] is a pair of rounding function
@@ -156,8 +155,8 @@
/// @param range The range of the scale.
/// @param uninterpolator The uninterpolator for domain values.
/// @param interpolator The interpolator for range values.
- static Function bilinearScale(List domain, List range,
- Function uninterpolator, Function interpolator) {
+ static Function bilinearScale(
+ List domain, List range, Function uninterpolator, Function interpolator) {
var u = uninterpolator(domain[0], domain[1]),
i = interpolator(range[0], range[1]);
return (x) => i(u(x));
@@ -170,12 +169,9 @@
/// @param range The range of the scale.
/// @param uninterpolator The uninterpolator for domain values.
/// @param interpolator The interpolator for range values.
- static Function polylinearScale(List domain, List range,
- Function uninterpolator, Function interpolator) {
- var u = [],
- i = [],
- j = 0,
- k = math.min(domain.length, range.length) - 1;
+ static Function polylinearScale(
+ List domain, List range, Function uninterpolator, Function interpolator) {
+ var u = [], i = [], j = 0, k = math.min(domain.length, range.length) - 1;
// Handle descending domains.
if (domain[k] < domain[0]) {
diff --git a/packages/charted/lib/core/scales/linear_scale.dart b/packages/charted/lib/core/scales/linear_scale.dart
index a0de8fb..4c983dc 100644
--- a/packages/charted/lib/core/scales/linear_scale.dart
+++ b/packages/charted/lib/core/scales/linear_scale.dart
@@ -40,15 +40,15 @@
_domain, ScaleUtils.niceStep(_linearTickRange().step));
}
- Function linear = math.min(_domain.length, _range.length) > 2 ?
- ScaleUtils.polylinearScale : ScaleUtils.bilinearScale;
+ Function linear = math.min(_domain.length, _range.length) > 2
+ ? ScaleUtils.polylinearScale
+ : ScaleUtils.bilinearScale;
Function uninterpolator = clamp ? uninterpolateClamp : uninterpolateNumber;
InterpolatorGenerator interpolator =
_rounded ? createRoundedNumberInterpolator : createNumberInterpolator;
- _invert =
- linear(_range, _domain, uninterpolator, createNumberInterpolator);
+ _invert = linear(_range, _domain, uninterpolator, createNumberInterpolator);
_scale = linear(_domain, _range, uninterpolator, interpolator);
}
@@ -135,19 +135,19 @@
if (extent == null) {
extent = ScaleUtils.extent(_domain);
}
- var span = extent.max - extent.min,
- step =
- math.pow(10, (math.log(span / _ticksCount) / math.LN10).floor()),
- err = _ticksCount / span * step;
+ var span = extent.max - extent.min;
+ if (span == 0) {
+ span = 1.0; // [span / _ticksCount] should never be equal zero.
+ }
+ var step = math.pow(10, (math.log(span / _ticksCount) / math.LN10).floor());
+ var err = _ticksCount / span * step;
// Filter ticks to get closer to the desired count.
if (err <= .15) {
step *= 10;
- }
- else if (err <= .35) {
+ } else if (err <= .35) {
step *= 5;
- }
- else if (err <= .75) {
+ } else if (err <= .75) {
step *= 2;
}
diff --git a/packages/charted/lib/core/scales/log_scale.dart b/packages/charted/lib/core/scales/log_scale.dart
index 20fc3a4..ae9d5b0 100644
--- a/packages/charted/lib/core/scales/log_scale.dart
+++ b/packages/charted/lib/core/scales/log_scale.dart
@@ -21,9 +21,7 @@
static const defaultBase = 10;
static const defaultDomain = const [1, 10];
static final negativeNumbersRoundFunctionsPair =
- new RoundingFunctions(
- (x) => -((-x).floor()),
- (x) => -((-x).ceil()));
+ new RoundingFunctions((x) => -((-x).floor()), (x) => -((-x).ceil()));
final LinearScale _linear;
@@ -43,8 +41,9 @@
_nice = source._nice,
_ticksCount = source._ticksCount;
- num _log(x) => (_positive ?
- math.log(x < 0 ? 0 : x) : -math.log(x > 0 ? 0 : -x)) / math.log(base);
+ num _log(x) =>
+ (_positive ? math.log(x < 0 ? 0 : x) : -math.log(x > 0 ? 0 : -x)) /
+ math.log(base);
num _pow(x) => _positive ? math.pow(base, x) : -math.pow(base, -x);
diff --git a/packages/charted/lib/core/scales/ordinal_scale.dart b/packages/charted/lib/core/scales/ordinal_scale.dart
index 6c135a2..718966a 100644
--- a/packages/charted/lib/core/scales/ordinal_scale.dart
+++ b/packages/charted/lib/core/scales/ordinal_scale.dart
@@ -80,15 +80,15 @@
@override
void rangeBands(Iterable range,
- [double padding = 0.0, double outerPadding]) =>
- _setRangeBands(this, range, padding,
- outerPadding == null ? padding : outerPadding);
+ [double padding = 0.0, double outerPadding]) =>
+ _setRangeBands(
+ this, range, padding, outerPadding == null ? padding : outerPadding);
@override
void rangeRoundBands(Iterable range,
- [double padding = 0.0, double outerPadding]) =>
- _setRangeRoundBands(this, range, padding,
- outerPadding == null ? padding : outerPadding);
+ [double padding = 0.0, double outerPadding]) =>
+ _setRangeRoundBands(
+ this, range, padding, outerPadding == null ? padding : outerPadding);
@override
num get rangeBand => _rangeBand;
@@ -121,9 +121,9 @@
stop = range.last,
step = (stop - start) / (s.domain.length - 1 + padding);
- s._range = s._steps(s.domain.length < 2
- ? (start + stop) / 2
- : start + step * padding / 2, step);
+ s._range = s._steps(
+ s.domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2,
+ step);
s._rangeBand = 0;
s._rangeExtent = new Extent(start, stop);
};
@@ -132,8 +132,8 @@
}
}
- static void _setRangeBands(_OrdinalScale scale,
- Iterable range, double padding, double outerPadding) {
+ static void _setRangeBands(_OrdinalScale scale, Iterable range,
+ double padding, double outerPadding) {
scale._reset = (_OrdinalScale s) {
var start = range.first,
stop = range.last,
@@ -143,13 +143,13 @@
s._rangeBand = step * (1 - padding);
s._rangeExtent = new Extent(start, stop);
};
- if (scale.domain.isNotEmpty){
+ if (scale.domain.isNotEmpty) {
scale._reset(scale);
}
}
- static void _setRangeRoundBands(_OrdinalScale scale,
- Iterable range, double padding, double outerPadding) {
+ static void _setRangeRoundBands(_OrdinalScale scale, Iterable range,
+ double padding, double outerPadding) {
scale._reset = (_OrdinalScale s) {
var start = range.first,
stop = range.last,
diff --git a/packages/charted/lib/core/scales/time_scale.dart b/packages/charted/lib/core/scales/time_scale.dart
index 8fba314..d1b439b 100644
--- a/packages/charted/lib/core/scales/time_scale.dart
+++ b/packages/charted/lib/core/scales/time_scale.dart
@@ -11,19 +11,19 @@
/// TimeScale is a linear scale that operates on time.
class TimeScale extends LinearScale {
static const _scaleSteps = const [
- 1e3, // 1-second
- 5e3, // 5-second
- 15e3, // 15-second
- 3e4, // 30-second
- 6e4, // 1-minute
- 3e5, // 5-minute
- 9e5, // 15-minute
- 18e5, // 30-minute
- 36e5, // 1-hour
- 108e5, // 3-hour
- 216e5, // 6-hour
- 432e5, // 12-hour
- 864e5, // 1-day
+ 1e3, // 1-second
+ 5e3, // 5-second
+ 15e3, // 15-second
+ 3e4, // 30-second
+ 6e4, // 1-minute
+ 3e5, // 5-minute
+ 9e5, // 15-minute
+ 18e5, // 30-minute
+ 36e5, // 1-hour
+ 108e5, // 3-hour
+ 216e5, // 6-hour
+ 432e5, // 12-hour
+ 864e5, // 1-day
1728e5, // 2-day
6048e5, // 1-week
2592e6, // 1-month
@@ -40,27 +40,27 @@
[TimeInterval.minute, 5],
[TimeInterval.minute, 15],
[TimeInterval.minute, 30],
- [TimeInterval.hour, 1],
- [TimeInterval.hour, 3],
- [TimeInterval.hour, 6],
- [TimeInterval.hour, 12],
- [TimeInterval.day, 1],
- [TimeInterval.day, 2],
- [TimeInterval.week, 1],
- [TimeInterval.month, 1],
- [TimeInterval.month, 3],
- [TimeInterval.year, 1]
+ [TimeInterval.hour, 1],
+ [TimeInterval.hour, 3],
+ [TimeInterval.hour, 6],
+ [TimeInterval.hour, 12],
+ [TimeInterval.day, 1],
+ [TimeInterval.day, 2],
+ [TimeInterval.week, 1],
+ [TimeInterval.month, 1],
+ [TimeInterval.month, 3],
+ [TimeInterval.year, 1]
];
static TimeFormatFunction _scaleLocalFormat = new TimeFormat().multi([
- [".%L", (DateTime d) => d.millisecond > 0],
- [":%S", (DateTime d) => d.second > 0],
- ["%I:%M", (DateTime d) => d.minute > 0],
- ["%I %p", (DateTime d) => d.hour > 0],
- ["%a %d", (DateTime d) => (d.weekday % 7) > 0 && d.day != 1],
- ["%b %d", (DateTime d) => d.day != 1],
- ["%B", (DateTime d) => d.month > 1],
- ["%Y", (d) => true]
+ [".%L", (DateTime d) => d.millisecond > 0],
+ [":%S", (DateTime d) => d.second > 0],
+ ["%I:%M", (DateTime d) => d.minute > 0],
+ ["%I %p", (DateTime d) => d.hour > 0],
+ ["%a %d", (DateTime d) => (d.weekday % 7) > 0 && d.day != 1],
+ ["%b %d", (DateTime d) => d.day != 1],
+ ["%B", (DateTime d) => d.month > 1],
+ ["%Y", (d) => true]
]);
TimeScale();
@@ -72,8 +72,8 @@
@override
set domain(Iterable value) {
- super.domain = value.map(
- (d) => d is DateTime ? d.millisecondsSinceEpoch : d).toList();
+ super.domain =
+ value.map((d) => d is DateTime ? d.millisecondsSinceEpoch : d).toList();
}
@override
@@ -83,16 +83,19 @@
TimeScale clone() => new TimeScale._clone(this);
List _getTickMethod(Extent extent, int count) {
- var target = (extent.max - extent.min) / count,
+ var target = (extent.max - extent.min) / count,
i = ScaleUtils.bisect(_scaleSteps, target);
return i == _scaleSteps.length
- ? [ TimeInterval.year, _linearTickRange(
- new Extent(extent.min / 31536e6, extent.max / 31536e6)).step ]
+ ? [
+ TimeInterval.year,
+ _linearTickRange(
+ new Extent(extent.min / 31536e6, extent.max / 31536e6)).step
+ ]
: i == 0
- ? [ new ScaleMilliSeconds(), _linearTickRange(extent).step ]
- : _scaleLocalMethods[
- target / _scaleSteps[i - 1] < _scaleSteps[i] / target ? i - 1 : i];
+ ? [new ScaleMilliSeconds(), _linearTickRange(extent).step]
+ : _scaleLocalMethods[target / _scaleSteps[i - 1] <
+ _scaleSteps[i] / target ? i - 1 : i];
}
List niceInterval(int ticksCount, [int skip = 1]) {
@@ -107,30 +110,29 @@
bool skipped(var date) {
if (date is DateTime) date = date.millisecondsSinceEpoch;
- return (interval as TimeInterval)
- .range(date, date + 1, skip).length == 0;
+ return (interval as TimeInterval).range(date, date + 1, skip).length == 0;
}
if (skip > 1) {
- domain = ScaleUtils.nice(domain, new RoundingFunctions(
- (date) {
- while (skipped(date = (interval as TimeInterval).floor(date))) {
- date = new DateTime.fromMillisecondsSinceEpoch(
- date.millisecondsSinceEpoch - 1);
- }
- return date.millisecondsSinceEpoch;
- },
- (date) {
- while (skipped(date = (interval as TimeInterval).ceil(date))) {
- date = new DateTime.fromMillisecondsSinceEpoch(
- date.millisecondsSinceEpoch + 1);
- }
- return date.millisecondsSinceEpoch;
- }
- ));
+ domain = ScaleUtils.nice(
+ domain,
+ new RoundingFunctions((date) {
+ while (skipped(date = (interval as TimeInterval).floor(date))) {
+ date = new DateTime.fromMillisecondsSinceEpoch(
+ date.millisecondsSinceEpoch - 1);
+ }
+ return date.millisecondsSinceEpoch;
+ }, (date) {
+ while (skipped(date = (interval as TimeInterval).ceil(date))) {
+ date = new DateTime.fromMillisecondsSinceEpoch(
+ date.millisecondsSinceEpoch + 1);
+ }
+ return date.millisecondsSinceEpoch;
+ }));
} else {
domain = ScaleUtils.nice(
- domain, new RoundingFunctions(
+ domain,
+ new RoundingFunctions(
(date) => interval.floor(date).millisecondsSinceEpoch,
(date) => interval.ceil(date).millisecondsSinceEpoch));
}
@@ -163,9 +165,10 @@
class ScaleMilliSeconds implements TimeInterval {
DateTime _toDateTime(x) {
- assert (x is int || x is DateTime);
+ assert(x is int || x is DateTime);
return x is num ? new DateTime.fromMillisecondsSinceEpoch(x) : x;
}
+
DateTime floor(dynamic val) => _toDateTime(val);
DateTime ceil(dynamic val) => _toDateTime(val);
DateTime round(dynamic val) => _toDateTime(val);
@@ -179,7 +182,8 @@
List range(var t0, var t1, int step) {
int start = t0 is DateTime ? t0.millisecondsSinceEpoch : t0,
stop = t1 is DateTime ? t1.millisecondsSinceEpoch : t1;
- return new Range((start / step).ceil() * step, stop, step).map(
- (d) => new DateTime.fromMillisecondsSinceEpoch(d)).toList();
+ return new Range((start / step).ceil() * step, stop, step)
+ .map((d) => new DateTime.fromMillisecondsSinceEpoch(d))
+ .toList();
}
}
diff --git a/packages/charted/lib/core/text_metrics.dart b/packages/charted/lib/core/text_metrics.dart
index 5933594..9fb7d06 100644
--- a/packages/charted/lib/core/text_metrics.dart
+++ b/packages/charted/lib/core/text_metrics.dart
@@ -57,17 +57,17 @@
double getTextWidth(String text, {String fontStyle}) {
assert(text.length <= MAX_STRING_LENGTH);
setFontStyle(fontStyle);
- return context.measureText(text).width;
+ return context.measureText(text).width.toDouble();
}
/// Gets length of the longest string in the given [strings].
/// Optionally, uses [fontStyle] instead of using the default style.
double getLongestTextWidth(Iterable<String> strings, {String fontStyle}) {
setFontStyle(fontStyle);
- double maxWidth = 0.0;
+ num maxWidth = 0.0;
for (int i = 0; i < strings.length; ++i) {
assert(strings.elementAt(i).length <= MAX_STRING_LENGTH);
- double width = context.measureText(strings.elementAt(i)).width;
+ double width = context.measureText(strings.elementAt(i)).width.toDouble();
if (width > maxWidth) {
maxWidth = width;
}
@@ -79,14 +79,16 @@
/// Truncates given [text] to fit in [width]. Adds an ellipsis to the
/// returned string, if it needed to be truncated.
/// Optionally, uses [fontStyle] instead of using the default style.
- String ellipsizeText(String text, double width, {String fontStyle}) {
+ String ellipsizeText(String text, num width, {String fontStyle}) {
assert(text.length <= MAX_STRING_LENGTH);
setFontStyle(fontStyle);
- double computedWidth = context.measureText(text).width;
+ double computedWidth = context.measureText(text).width.toDouble();
if (computedWidth > width) {
var indices = graphemeBreakIndices(text);
var position = 0,
- min = 0, max = indices.length - 1, mid,
+ min = 0,
+ max = indices.length - 1,
+ mid,
ellipsis = context.measureText('…').width;
width = width - ellipsis;
while (max >= min) {
@@ -108,6 +110,5 @@
/// if it had to be truncated.
/// Calling this method may force a layout on the document. For better
/// performance, use [TextMetrics.ellipsizeText].
- static ellipsizeTextElement() {
- }
+ static ellipsizeTextElement() {}
}
diff --git a/packages/charted/lib/core/text_metrics/segmentation.dart b/packages/charted/lib/core/text_metrics/segmentation.dart
index b1c6c31..058ac38 100644
--- a/packages/charted/lib/core/text_metrics/segmentation.dart
+++ b/packages/charted/lib/core/text_metrics/segmentation.dart
@@ -18,19 +18,151 @@
// Code table based on:
// http://www.unicode.org/Public/7.0.0/ucd/auxiliary/GraphemeBreakTest.html
// GRAPHEME_BREAK_TABLE[prevType * TYPE_COUNT + curType] == 1 means break.
-const GRAPHEME_BREAK_TABLE = const[
- 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
- 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
- 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
- 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1,
- 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
- 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1,
- 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0
+const GRAPHEME_BREAK_TABLE = const [
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0
];
/// Get type of a given char code.
@@ -41,13 +173,12 @@
while (max >= min) {
int mid = (max + min) ~/ 2;
int idx = mid * 3;
- if (CODE_POINT_BLOCKS[idx] <= rune && rune <= CODE_POINT_BLOCKS[idx+1]) {
- return CODE_POINT_BLOCKS[idx+2]; // Return the found character type
+ if (CODE_POINT_BLOCKS[idx] <= rune && rune <= CODE_POINT_BLOCKS[idx + 1]) {
+ return CODE_POINT_BLOCKS[idx + 2]; // Return the found character type
}
if (CODE_POINT_BLOCKS[idx] > rune) {
max = mid - 1;
- }
- else if (CODE_POINT_BLOCKS[idx+1] < rune) {
+ } else if (CODE_POINT_BLOCKS[idx + 1] < rune) {
min = max + 1;
}
}
diff --git a/packages/charted/lib/core/text_metrics/segmentation_data.dart b/packages/charted/lib/core/text_metrics/segmentation_data.dart
index 3145759..66883c2 100644
--- a/packages/charted/lib/core/text_metrics/segmentation_data.dart
+++ b/packages/charted/lib/core/text_metrics/segmentation_data.dart
@@ -15,1189 +15,3557 @@
/// Each line in the following list represents a code range.
/// Start, End, Type.
-const CODE_POINT_BLOCKS = const[
- 0, 9, 3,
- 10, 10, 2,
- 11, 12, 3,
- 13, 13, 1,
- 14, 31, 3,
- 127, 159, 3,
- 173, 173, 3,
- 768, 879, 4,
- 1155, 1159, 4,
- 1160, 1161, 4,
- 1425, 1469, 4,
- 1471, 1471, 4,
- 1473, 1474, 4,
- 1476, 1477, 4,
- 1479, 1479, 4,
- 1536, 1541, 3,
- 1552, 1562, 4,
- 1564, 1564, 3,
- 1611, 1631, 4,
- 1648, 1648, 4,
- 1750, 1756, 4,
- 1757, 1757, 3,
- 1759, 1764, 4,
- 1767, 1768, 4,
- 1770, 1773, 4,
- 1807, 1807, 3,
- 1809, 1809, 4,
- 1840, 1866, 4,
- 1958, 1968, 4,
- 2027, 2035, 4,
- 2070, 2073, 4,
- 2075, 2083, 4,
- 2085, 2087, 4,
- 2089, 2093, 4,
- 2137, 2139, 4,
- 2276, 2306, 4,
- 2307, 2307, 5,
- 2362, 2362, 4,
- 2363, 2363, 5,
- 2364, 2364, 4,
- 2366, 2368, 5,
- 2369, 2376, 4,
- 2377, 2380, 5,
- 2381, 2381, 4,
- 2382, 2383, 5,
- 2385, 2391, 4,
- 2402, 2403, 4,
- 2433, 2433, 4,
- 2434, 2435, 5,
- 2492, 2492, 4,
- 2494, 2494, 4,
- 2495, 2496, 5,
- 2497, 2500, 4,
- 2503, 2504, 5,
- 2507, 2508, 5,
- 2509, 2509, 4,
- 2519, 2519, 4,
- 2530, 2531, 4,
- 2561, 2562, 4,
- 2563, 2563, 5,
- 2620, 2620, 4,
- 2622, 2624, 5,
- 2625, 2626, 4,
- 2631, 2632, 4,
- 2635, 2637, 4,
- 2641, 2641, 4,
- 2672, 2673, 4,
- 2677, 2677, 4,
- 2689, 2690, 4,
- 2691, 2691, 5,
- 2748, 2748, 4,
- 2750, 2752, 5,
- 2753, 2757, 4,
- 2759, 2760, 4,
- 2761, 2761, 5,
- 2763, 2764, 5,
- 2765, 2765, 4,
- 2786, 2787, 4,
- 2817, 2817, 4,
- 2818, 2819, 5,
- 2876, 2876, 4,
- 2878, 2878, 4,
- 2879, 2879, 4,
- 2880, 2880, 5,
- 2881, 2884, 4,
- 2887, 2888, 5,
- 2891, 2892, 5,
- 2893, 2893, 4,
- 2902, 2902, 4,
- 2903, 2903, 4,
- 2914, 2915, 4,
- 2946, 2946, 4,
- 3006, 3006, 4,
- 3007, 3007, 5,
- 3008, 3008, 4,
- 3009, 3010, 5,
- 3014, 3016, 5,
- 3018, 3020, 5,
- 3021, 3021, 4,
- 3031, 3031, 4,
- 3072, 3072, 4,
- 3073, 3075, 5,
- 3134, 3136, 4,
- 3137, 3140, 5,
- 3142, 3144, 4,
- 3146, 3149, 4,
- 3157, 3158, 4,
- 3170, 3171, 4,
- 3201, 3201, 4,
- 3202, 3203, 5,
- 3260, 3260, 4,
- 3262, 3262, 5,
- 3263, 3263, 4,
- 3264, 3265, 5,
- 3266, 3266, 4,
- 3267, 3268, 5,
- 3270, 3270, 4,
- 3271, 3272, 5,
- 3274, 3275, 5,
- 3276, 3277, 4,
- 3285, 3286, 4,
- 3298, 3299, 4,
- 3329, 3329, 4,
- 3330, 3331, 5,
- 3390, 3390, 4,
- 3391, 3392, 5,
- 3393, 3396, 4,
- 3398, 3400, 5,
- 3402, 3404, 5,
- 3405, 3405, 4,
- 3415, 3415, 4,
- 3426, 3427, 4,
- 3458, 3459, 5,
- 3530, 3530, 4,
- 3535, 3535, 4,
- 3536, 3537, 5,
- 3538, 3540, 4,
- 3542, 3542, 4,
- 3544, 3550, 5,
- 3551, 3551, 4,
- 3570, 3571, 5,
- 3633, 3633, 4,
- 3635, 3635, 5,
- 3636, 3642, 4,
- 3655, 3662, 4,
- 3761, 3761, 4,
- 3763, 3763, 5,
- 3764, 3769, 4,
- 3771, 3772, 4,
- 3784, 3789, 4,
- 3864, 3865, 4,
- 3893, 3893, 4,
- 3895, 3895, 4,
- 3897, 3897, 4,
- 3902, 3903, 5,
- 3953, 3966, 4,
- 3967, 3967, 5,
- 3968, 3972, 4,
- 3974, 3975, 4,
- 3981, 3991, 4,
- 3993, 4028, 4,
- 4038, 4038, 4,
- 4127, 4127, 4,
- 4141, 4144, 4,
- 4142, 4142, 4,
- 4145, 4145, 5,
- 4146, 4151, 4,
- 4153, 4154, 4,
- 4155, 4156, 5,
- 4157, 4158, 4,
- 4182, 4183, 5,
- 4184, 4185, 4,
- 4190, 4192, 4,
- 4209, 4212, 4,
- 4226, 4226, 4,
- 4228, 4228, 5,
- 4229, 4230, 4,
- 4237, 4237, 4,
- 4253, 4253, 4,
- 4259, 4259, 4,
- 4352, 4352, 5,
- 4352, 4447, 6,
- 4352, 4352, 4,
- 4352, 4352, 5,
- 4360, 4360, 5,
- 4363, 4363, 3,
- 4370, 4370, 5,
- 4375, 4375, 4,
- 4376, 4376, 5,
- 4387, 4387, 4,
- 4387, 4387, 5,
- 4397, 4397, 4,
- 4400, 4400, 4,
- 4403, 4403, 5,
- 4403, 4403, 4,
- 4403, 4403, 4,
- 4404, 4404, 4,
- 4405, 4405, 4,
- 4427, 4427, 5,
- 4427, 4427, 4,
- 4427, 4427, 5,
- 4427, 4427, 4,
- 4427, 4427, 4,
- 4428, 4428, 5,
- 4442, 4442, 4,
- 4443, 4443, 5,
- 4448, 4519, 7,
- 4451, 4451, 5,
- 4451, 4451, 4,
- 4458, 4458, 4,
- 4458, 4458, 5,
- 4458, 4458, 4,
- 4459, 4459, 4,
- 4459, 4459, 5,
- 4520, 4607, 8,
- 4957, 4959, 4,
- 5906, 5908, 4,
- 5938, 5940, 4,
- 5970, 5971, 4,
- 6002, 6003, 4,
- 6068, 6069, 4,
- 6070, 6070, 5,
- 6071, 6077, 4,
- 6078, 6085, 5,
- 6086, 6086, 4,
- 6087, 6088, 5,
- 6089, 6099, 4,
- 6109, 6109, 4,
- 6155, 6157, 4,
- 6158, 6158, 3,
- 6313, 6313, 4,
- 6432, 6434, 4,
- 6435, 6438, 5,
- 6439, 6440, 4,
- 6441, 6443, 5,
- 6448, 6449, 5,
- 6450, 6450, 4,
- 6451, 6456, 5,
- 6457, 6459, 4,
- 6581, 6583, 5,
- 6586, 6586, 5,
- 6679, 6680, 4,
- 6681, 6682, 5,
- 6683, 6683, 4,
- 6741, 6741, 5,
- 6742, 6742, 4,
- 6743, 6743, 5,
- 6744, 6750, 4,
- 6752, 6752, 4,
- 6754, 6754, 4,
- 6757, 6764, 4,
- 6765, 6770, 5,
- 6771, 6780, 4,
- 6783, 6783, 4,
- 6832, 6845, 4,
- 6846, 6846, 4,
- 6912, 6915, 4,
- 6916, 6916, 5,
- 6964, 6964, 4,
- 6965, 6965, 5,
- 6966, 6970, 4,
- 6971, 6971, 5,
- 6972, 6972, 4,
- 6973, 6977, 5,
- 6978, 6978, 4,
- 6979, 6980, 5,
- 7019, 7027, 4,
- 7040, 7041, 4,
- 7042, 7042, 5,
- 7073, 7073, 5,
- 7074, 7077, 4,
- 7078, 7079, 5,
- 7080, 7081, 4,
- 7082, 7082, 5,
- 7083, 7085, 4,
- 7142, 7142, 4,
- 7143, 7143, 5,
- 7144, 7145, 4,
- 7146, 7148, 5,
- 7149, 7149, 4,
- 7150, 7150, 5,
- 7151, 7153, 4,
- 7154, 7155, 5,
- 7204, 7211, 5,
- 7212, 7219, 4,
- 7220, 7221, 5,
- 7222, 7223, 4,
- 7376, 7378, 4,
- 7380, 7392, 4,
- 7393, 7393, 5,
- 7394, 7400, 4,
- 7405, 7405, 4,
- 7410, 7411, 5,
- 7412, 7412, 4,
- 7416, 7417, 4,
- 7446, 7446, 4,
- 7446, 7446, 5,
- 7446, 7446, 5,
- 7616, 7669, 4,
- 7676, 7679, 4,
- 8203, 8203, 3,
- 8204, 8205, 4,
- 8206, 8207, 3,
- 8232, 8232, 3,
- 8233, 8233, 3,
- 8234, 8238, 3,
- 8288, 8292, 3,
- 8293, 8293, 3,
- 8294, 8303, 3,
- 8400, 8412, 4,
- 8413, 8416, 4,
- 8417, 8417, 4,
- 8418, 8420, 4,
- 8421, 8432, 4,
- 11503, 11505, 4,
- 11647, 11647, 4,
- 11744, 11775, 4,
- 12330, 12333, 4,
- 12334, 12335, 4,
- 12441, 12442, 4,
- 42607, 42607, 4,
- 42608, 42610, 4,
- 42612, 42621, 4,
- 42655, 42655, 4,
- 42736, 42737, 4,
- 43010, 43010, 4,
- 43014, 43014, 4,
- 43019, 43019, 4,
- 43043, 43044, 5,
- 43045, 43046, 4,
- 43047, 43047, 5,
- 43136, 43137, 5,
- 43188, 43203, 5,
- 43204, 43204, 4,
- 43232, 43249, 4,
- 43302, 43309, 4,
- 43335, 43345, 4,
- 43346, 43347, 5,
- 43360, 43388, 6,
- 43392, 43394, 4,
- 43395, 43395, 5,
- 43443, 43443, 4,
- 43444, 43445, 5,
- 43446, 43449, 4,
- 43450, 43451, 5,
- 43452, 43452, 4,
- 43453, 43456, 5,
- 43493, 43493, 4,
- 43561, 43566, 4,
- 43567, 43568, 5,
- 43569, 43570, 4,
- 43571, 43572, 5,
- 43573, 43574, 4,
- 43587, 43587, 4,
- 43596, 43596, 4,
- 43597, 43597, 5,
- 43644, 43644, 4,
- 43696, 43696, 4,
- 43698, 43700, 4,
- 43703, 43704, 4,
- 43710, 43711, 4,
- 43713, 43713, 4,
- 43755, 43755, 5,
- 43756, 43757, 4,
- 43758, 43759, 5,
- 43765, 43765, 5,
- 43766, 43766, 4,
- 44003, 44004, 5,
- 44005, 44005, 4,
- 44006, 44007, 5,
- 44008, 44008, 4,
- 44009, 44010, 5,
- 44012, 44012, 5,
- 44013, 44013, 4,
- 44032, 44032, 9,
- 44033, 44059, 10,
- 44060, 44060, 9,
- 44061, 44087, 10,
- 44088, 44088, 9,
- 44089, 44115, 10,
- 44116, 44116, 9,
- 44117, 44143, 10,
- 44144, 44144, 9,
- 44145, 44171, 10,
- 44172, 44172, 9,
- 44173, 44199, 10,
- 44200, 44200, 9,
- 44201, 44227, 10,
- 44228, 44228, 9,
- 44229, 44255, 10,
- 44256, 44256, 9,
- 44257, 44283, 10,
- 44284, 44284, 9,
- 44285, 44311, 10,
- 44312, 44312, 9,
- 44313, 44339, 10,
- 44340, 44340, 9,
- 44341, 44367, 10,
- 44368, 44368, 9,
- 44369, 44395, 10,
- 44396, 44396, 9,
- 44397, 44423, 10,
- 44424, 44424, 9,
- 44425, 44451, 10,
- 44452, 44452, 9,
- 44453, 44479, 10,
- 44480, 44480, 9,
- 44481, 44507, 10,
- 44508, 44508, 9,
- 44509, 44535, 10,
- 44536, 44536, 9,
- 44537, 44563, 10,
- 44564, 44564, 9,
- 44565, 44591, 10,
- 44592, 44592, 9,
- 44593, 44619, 10,
- 44620, 44620, 9,
- 44621, 44647, 10,
- 44648, 44648, 9,
- 44649, 44675, 10,
- 44676, 44676, 9,
- 44677, 44703, 10,
- 44704, 44704, 9,
- 44705, 44731, 10,
- 44732, 44732, 9,
- 44733, 44759, 10,
- 44760, 44760, 9,
- 44761, 44787, 10,
- 44788, 44788, 9,
- 44789, 44815, 10,
- 44816, 44816, 9,
- 44817, 44843, 10,
- 44844, 44844, 9,
- 44845, 44871, 10,
- 44872, 44872, 9,
- 44873, 44899, 10,
- 44900, 44900, 9,
- 44901, 44927, 10,
- 44928, 44928, 9,
- 44929, 44955, 10,
- 44956, 44956, 9,
- 44957, 44983, 10,
- 44984, 44984, 9,
- 44985, 45011, 10,
- 45012, 45012, 9,
- 45013, 45039, 10,
- 45040, 45040, 9,
- 45041, 45067, 10,
- 45068, 45068, 9,
- 45069, 45095, 10,
- 45096, 45096, 9,
- 45097, 45123, 10,
- 45124, 45124, 9,
- 45125, 45151, 10,
- 45152, 45152, 9,
- 45153, 45179, 10,
- 45180, 45180, 9,
- 45181, 45207, 10,
- 45208, 45208, 9,
- 45209, 45235, 10,
- 45236, 45236, 9,
- 45237, 45263, 10,
- 45264, 45264, 9,
- 45265, 45291, 10,
- 45292, 45292, 9,
- 45293, 45319, 10,
- 45320, 45320, 9,
- 45321, 45347, 10,
- 45348, 45348, 9,
- 45349, 45375, 10,
- 45376, 45376, 9,
- 45377, 45403, 10,
- 45404, 45404, 9,
- 45405, 45431, 10,
- 45432, 45432, 9,
- 45433, 45459, 10,
- 45460, 45460, 9,
- 45461, 45487, 10,
- 45488, 45488, 9,
- 45489, 45515, 10,
- 45516, 45516, 9,
- 45517, 45543, 10,
- 45544, 45544, 9,
- 45545, 45571, 10,
- 45572, 45572, 9,
- 45573, 45599, 10,
- 45600, 45600, 9,
- 45601, 45627, 10,
- 45628, 45628, 9,
- 45629, 45655, 10,
- 45656, 45656, 9,
- 45657, 45683, 10,
- 45684, 45684, 9,
- 45685, 45711, 10,
- 45712, 45712, 9,
- 45713, 45739, 10,
- 45740, 45740, 9,
- 45741, 45767, 10,
- 45768, 45768, 9,
- 45769, 45795, 10,
- 45796, 45796, 9,
- 45797, 45823, 10,
- 45824, 45824, 9,
- 45825, 45851, 10,
- 45852, 45852, 9,
- 45853, 45879, 10,
- 45880, 45880, 9,
- 45881, 45907, 10,
- 45908, 45908, 9,
- 45909, 45935, 10,
- 45936, 45936, 9,
- 45937, 45963, 10,
- 45964, 45964, 9,
- 45965, 45991, 10,
- 45992, 45992, 9,
- 45993, 46019, 10,
- 46020, 46020, 9,
- 46021, 46047, 10,
- 46048, 46048, 9,
- 46049, 46075, 10,
- 46076, 46076, 9,
- 46077, 46103, 10,
- 46104, 46104, 9,
- 46105, 46131, 10,
- 46132, 46132, 9,
- 46133, 46159, 10,
- 46160, 46160, 9,
- 46161, 46187, 10,
- 46188, 46188, 9,
- 46189, 46215, 10,
- 46216, 46216, 9,
- 46217, 46243, 10,
- 46244, 46244, 9,
- 46245, 46271, 10,
- 46272, 46272, 9,
- 46273, 46299, 10,
- 46300, 46300, 9,
- 46301, 46327, 10,
- 46328, 46328, 9,
- 46329, 46355, 10,
- 46356, 46356, 9,
- 46357, 46383, 10,
- 46384, 46384, 9,
- 46385, 46411, 10,
- 46412, 46412, 9,
- 46413, 46439, 10,
- 46440, 46440, 9,
- 46441, 46467, 10,
- 46468, 46468, 9,
- 46469, 46495, 10,
- 46496, 46496, 9,
- 46497, 46523, 10,
- 46524, 46524, 9,
- 46525, 46551, 10,
- 46552, 46552, 9,
- 46553, 46579, 10,
- 46580, 46580, 9,
- 46581, 46607, 10,
- 46608, 46608, 9,
- 46609, 46635, 10,
- 46636, 46636, 9,
- 46637, 46663, 10,
- 46664, 46664, 9,
- 46665, 46691, 10,
- 46692, 46692, 9,
- 46693, 46719, 10,
- 46720, 46720, 9,
- 46721, 46747, 10,
- 46748, 46748, 9,
- 46749, 46775, 10,
- 46776, 46776, 9,
- 46777, 46803, 10,
- 46804, 46804, 9,
- 46805, 46831, 10,
- 46832, 46832, 9,
- 46833, 46859, 10,
- 46860, 46860, 9,
- 46861, 46887, 10,
- 46888, 46888, 9,
- 46889, 46915, 10,
- 46916, 46916, 9,
- 46917, 46943, 10,
- 46944, 46944, 9,
- 46945, 46971, 10,
- 46972, 46972, 9,
- 46973, 46999, 10,
- 47000, 47000, 9,
- 47001, 47027, 10,
- 47028, 47028, 9,
- 47029, 47055, 10,
- 47056, 47056, 9,
- 47057, 47083, 10,
- 47084, 47084, 9,
- 47085, 47111, 10,
- 47112, 47112, 9,
- 47113, 47139, 10,
- 47140, 47140, 9,
- 47141, 47167, 10,
- 47168, 47168, 9,
- 47169, 47195, 10,
- 47196, 47196, 9,
- 47197, 47223, 10,
- 47224, 47224, 9,
- 47225, 47251, 10,
- 47252, 47252, 9,
- 47253, 47279, 10,
- 47280, 47280, 9,
- 47281, 47307, 10,
- 47308, 47308, 9,
- 47309, 47335, 10,
- 47336, 47336, 9,
- 47337, 47363, 10,
- 47364, 47364, 9,
- 47365, 47391, 10,
- 47392, 47392, 9,
- 47393, 47419, 10,
- 47420, 47420, 9,
- 47421, 47447, 10,
- 47448, 47448, 9,
- 47449, 47475, 10,
- 47476, 47476, 9,
- 47477, 47503, 10,
- 47504, 47504, 9,
- 47505, 47531, 10,
- 47532, 47532, 9,
- 47533, 47559, 10,
- 47560, 47560, 9,
- 47561, 47587, 10,
- 47588, 47588, 9,
- 47589, 47615, 10,
- 47616, 47616, 9,
- 47617, 47643, 10,
- 47644, 47644, 9,
- 47645, 47671, 10,
- 47672, 47672, 9,
- 47673, 47699, 10,
- 47700, 47700, 9,
- 47701, 47727, 10,
- 47728, 47728, 9,
- 47729, 47755, 10,
- 47756, 47756, 9,
- 47757, 47783, 10,
- 47784, 47784, 9,
- 47785, 47811, 10,
- 47812, 47812, 9,
- 47813, 47839, 10,
- 47840, 47840, 9,
- 47841, 47867, 10,
- 47868, 47868, 9,
- 47869, 47895, 10,
- 47896, 47896, 9,
- 47897, 47923, 10,
- 47924, 47924, 9,
- 47925, 47951, 10,
- 47952, 47952, 9,
- 47953, 47979, 10,
- 47980, 47980, 9,
- 47981, 48007, 10,
- 48008, 48008, 9,
- 48009, 48035, 10,
- 48036, 48036, 9,
- 48037, 48063, 10,
- 48064, 48064, 9,
- 48065, 48091, 10,
- 48092, 48092, 9,
- 48093, 48119, 10,
- 48120, 48120, 9,
- 48121, 48147, 10,
- 48148, 48148, 9,
- 48149, 48175, 10,
- 48176, 48176, 9,
- 48177, 48203, 10,
- 48204, 48204, 9,
- 48205, 48231, 10,
- 48232, 48232, 9,
- 48233, 48259, 10,
- 48260, 48260, 9,
- 48261, 48287, 10,
- 48288, 48288, 9,
- 48289, 48315, 10,
- 48316, 48316, 9,
- 48317, 48343, 10,
- 48344, 48344, 9,
- 48345, 48371, 10,
- 48372, 48372, 9,
- 48373, 48399, 10,
- 48400, 48400, 9,
- 48401, 48427, 10,
- 48428, 48428, 9,
- 48429, 48455, 10,
- 48456, 48456, 9,
- 48457, 48483, 10,
- 48484, 48484, 9,
- 48485, 48511, 10,
- 48512, 48512, 9,
- 48513, 48539, 10,
- 48540, 48540, 9,
- 48541, 48567, 10,
- 48568, 48568, 9,
- 48569, 48595, 10,
- 48596, 48596, 9,
- 48597, 48623, 10,
- 48624, 48624, 9,
- 48625, 48651, 10,
- 48652, 48652, 9,
- 48653, 48679, 10,
- 48680, 48680, 9,
- 48681, 48707, 10,
- 48708, 48708, 9,
- 48709, 48735, 10,
- 48736, 48736, 9,
- 48737, 48763, 10,
- 48764, 48764, 9,
- 48765, 48791, 10,
- 48792, 48792, 9,
- 48793, 48819, 10,
- 48820, 48820, 9,
- 48821, 48847, 10,
- 48848, 48848, 9,
- 48849, 48875, 10,
- 48876, 48876, 9,
- 48877, 48903, 10,
- 48904, 48904, 9,
- 48905, 48931, 10,
- 48932, 48932, 9,
- 48933, 48959, 10,
- 48960, 48960, 9,
- 48961, 48987, 10,
- 48988, 48988, 9,
- 48989, 49015, 10,
- 49016, 49016, 9,
- 49017, 49043, 10,
- 49044, 49044, 9,
- 49045, 49071, 10,
- 49072, 49072, 9,
- 49073, 49099, 10,
- 49100, 49100, 9,
- 49101, 49127, 10,
- 49128, 49128, 9,
- 49129, 49155, 10,
- 49156, 49156, 9,
- 49157, 49183, 10,
- 49184, 49184, 9,
- 49185, 49211, 10,
- 49212, 49212, 9,
- 49213, 49239, 10,
- 49240, 49240, 9,
- 49241, 49267, 10,
- 49268, 49268, 9,
- 49269, 49295, 10,
- 49296, 49296, 9,
- 49297, 49323, 10,
- 49324, 49324, 9,
- 49325, 49351, 10,
- 49352, 49352, 9,
- 49353, 49379, 10,
- 49380, 49380, 9,
- 49381, 49407, 10,
- 49408, 49408, 9,
- 49409, 49435, 10,
- 49436, 49436, 9,
- 49437, 49463, 10,
- 49464, 49464, 9,
- 49465, 49491, 10,
- 49492, 49492, 9,
- 49493, 49519, 10,
- 49520, 49520, 9,
- 49521, 49547, 10,
- 49548, 49548, 9,
- 49549, 49575, 10,
- 49576, 49576, 9,
- 49577, 49603, 10,
- 49604, 49604, 9,
- 49605, 49631, 10,
- 49632, 49632, 9,
- 49633, 49659, 10,
- 49660, 49660, 9,
- 49661, 49687, 10,
- 49688, 49688, 9,
- 49689, 49715, 10,
- 49716, 49716, 9,
- 49717, 49743, 10,
- 49744, 49744, 9,
- 49745, 49771, 10,
- 49772, 49772, 9,
- 49773, 49799, 10,
- 49800, 49800, 9,
- 49801, 49827, 10,
- 49828, 49828, 9,
- 49829, 49855, 10,
- 49856, 49856, 9,
- 49857, 49883, 10,
- 49884, 49884, 9,
- 49885, 49911, 10,
- 49912, 49912, 9,
- 49913, 49939, 10,
- 49940, 49940, 9,
- 49941, 49967, 10,
- 49968, 49968, 9,
- 49969, 49995, 10,
- 49996, 49996, 9,
- 49997, 50023, 10,
- 50024, 50024, 9,
- 50025, 50051, 10,
- 50052, 50052, 9,
- 50053, 50079, 10,
- 50080, 50080, 9,
- 50081, 50107, 10,
- 50108, 50108, 9,
- 50109, 50135, 10,
- 50136, 50136, 9,
- 50137, 50163, 10,
- 50164, 50164, 9,
- 50165, 50191, 10,
- 50192, 50192, 9,
- 50193, 50219, 10,
- 50220, 50220, 9,
- 50221, 50247, 10,
- 50248, 50248, 9,
- 50249, 50275, 10,
- 50276, 50276, 9,
- 50277, 50303, 10,
- 50304, 50304, 9,
- 50305, 50331, 10,
- 50332, 50332, 9,
- 50333, 50359, 10,
- 50360, 50360, 9,
- 50361, 50387, 10,
- 50388, 50388, 9,
- 50389, 50415, 10,
- 50416, 50416, 9,
- 50417, 50443, 10,
- 50444, 50444, 9,
- 50445, 50471, 10,
- 50472, 50472, 9,
- 50473, 50499, 10,
- 50500, 50500, 9,
- 50501, 50527, 10,
- 50528, 50528, 9,
- 50529, 50555, 10,
- 50556, 50556, 9,
- 50557, 50583, 10,
- 50584, 50584, 9,
- 50585, 50611, 10,
- 50612, 50612, 9,
- 50613, 50639, 10,
- 50640, 50640, 9,
- 50641, 50667, 10,
- 50668, 50668, 9,
- 50669, 50695, 10,
- 50696, 50696, 9,
- 50697, 50723, 10,
- 50724, 50724, 9,
- 50725, 50751, 10,
- 50752, 50752, 9,
- 50753, 50779, 10,
- 50780, 50780, 9,
- 50781, 50807, 10,
- 50808, 50808, 9,
- 50809, 50835, 10,
- 50836, 50836, 9,
- 50837, 50863, 10,
- 50864, 50864, 9,
- 50865, 50891, 10,
- 50892, 50892, 9,
- 50893, 50919, 10,
- 50920, 50920, 9,
- 50921, 50947, 10,
- 50948, 50948, 9,
- 50949, 50975, 10,
- 50976, 50976, 9,
- 50977, 51003, 10,
- 51004, 51004, 9,
- 51005, 51031, 10,
- 51032, 51032, 9,
- 51033, 51059, 10,
- 51060, 51060, 9,
- 51061, 51087, 10,
- 51088, 51088, 9,
- 51089, 51115, 10,
- 51116, 51116, 9,
- 51117, 51143, 10,
- 51144, 51144, 9,
- 51145, 51171, 10,
- 51172, 51172, 9,
- 51173, 51199, 10,
- 51200, 51200, 9,
- 51201, 51227, 10,
- 51228, 51228, 9,
- 51229, 51255, 10,
- 51256, 51256, 9,
- 51257, 51283, 10,
- 51284, 51284, 9,
- 51285, 51311, 10,
- 51312, 51312, 9,
- 51313, 51339, 10,
- 51340, 51340, 9,
- 51341, 51367, 10,
- 51368, 51368, 9,
- 51369, 51395, 10,
- 51396, 51396, 9,
- 51397, 51423, 10,
- 51424, 51424, 9,
- 51425, 51451, 10,
- 51452, 51452, 9,
- 51453, 51479, 10,
- 51480, 51480, 9,
- 51481, 51507, 10,
- 51508, 51508, 9,
- 51509, 51535, 10,
- 51536, 51536, 9,
- 51537, 51563, 10,
- 51564, 51564, 9,
- 51565, 51591, 10,
- 51592, 51592, 9,
- 51593, 51619, 10,
- 51620, 51620, 9,
- 51621, 51647, 10,
- 51648, 51648, 9,
- 51649, 51675, 10,
- 51676, 51676, 9,
- 51677, 51703, 10,
- 51704, 51704, 9,
- 51705, 51731, 10,
- 51732, 51732, 9,
- 51733, 51759, 10,
- 51760, 51760, 9,
- 51761, 51787, 10,
- 51788, 51788, 9,
- 51789, 51815, 10,
- 51816, 51816, 9,
- 51817, 51843, 10,
- 51844, 51844, 9,
- 51845, 51871, 10,
- 51872, 51872, 9,
- 51873, 51899, 10,
- 51900, 51900, 9,
- 51901, 51927, 10,
- 51928, 51928, 9,
- 51929, 51955, 10,
- 51956, 51956, 9,
- 51957, 51983, 10,
- 51984, 51984, 9,
- 51985, 52011, 10,
- 52012, 52012, 9,
- 52013, 52039, 10,
- 52040, 52040, 9,
- 52041, 52067, 10,
- 52068, 52068, 9,
- 52069, 52095, 10,
- 52096, 52096, 9,
- 52097, 52123, 10,
- 52124, 52124, 9,
- 52125, 52151, 10,
- 52152, 52152, 9,
- 52153, 52179, 10,
- 52180, 52180, 9,
- 52181, 52207, 10,
- 52208, 52208, 9,
- 52209, 52235, 10,
- 52236, 52236, 9,
- 52237, 52263, 10,
- 52264, 52264, 9,
- 52265, 52291, 10,
- 52292, 52292, 9,
- 52293, 52319, 10,
- 52320, 52320, 9,
- 52321, 52347, 10,
- 52348, 52348, 9,
- 52349, 52375, 10,
- 52376, 52376, 9,
- 52377, 52403, 10,
- 52404, 52404, 9,
- 52405, 52431, 10,
- 52432, 52432, 9,
- 52433, 52459, 10,
- 52460, 52460, 9,
- 52461, 52487, 10,
- 52488, 52488, 9,
- 52489, 52515, 10,
- 52516, 52516, 9,
- 52517, 52543, 10,
- 52544, 52544, 9,
- 52545, 52571, 10,
- 52572, 52572, 9,
- 52573, 52599, 10,
- 52600, 52600, 9,
- 52601, 52627, 10,
- 52628, 52628, 9,
- 52629, 52655, 10,
- 52656, 52656, 9,
- 52657, 52683, 10,
- 52684, 52684, 9,
- 52685, 52711, 10,
- 52712, 52712, 9,
- 52713, 52739, 10,
- 52740, 52740, 9,
- 52741, 52767, 10,
- 52768, 52768, 9,
- 52769, 52795, 10,
- 52796, 52796, 9,
- 52797, 52823, 10,
- 52824, 52824, 9,
- 52825, 52851, 10,
- 52852, 52852, 9,
- 52853, 52879, 10,
- 52880, 52880, 9,
- 52881, 52907, 10,
- 52908, 52908, 9,
- 52909, 52935, 10,
- 52936, 52936, 9,
- 52937, 52963, 10,
- 52964, 52964, 9,
- 52965, 52991, 10,
- 52992, 52992, 9,
- 52993, 53019, 10,
- 53020, 53020, 9,
- 53021, 53047, 10,
- 53048, 53048, 9,
- 53049, 53075, 10,
- 53076, 53076, 9,
- 53077, 53103, 10,
- 53104, 53104, 9,
- 53105, 53131, 10,
- 53132, 53132, 9,
- 53133, 53159, 10,
- 53160, 53160, 9,
- 53161, 53187, 10,
- 53188, 53188, 9,
- 53189, 53215, 10,
- 53216, 53216, 9,
- 53217, 53243, 10,
- 53244, 53244, 9,
- 53245, 53271, 10,
- 53272, 53272, 9,
- 53273, 53299, 10,
- 53300, 53300, 9,
- 53301, 53327, 10,
- 53328, 53328, 9,
- 53329, 53355, 10,
- 53356, 53356, 9,
- 53357, 53383, 10,
- 53384, 53384, 9,
- 53385, 53411, 10,
- 53412, 53412, 9,
- 53413, 53439, 10,
- 53440, 53440, 9,
- 53441, 53467, 10,
- 53468, 53468, 9,
- 53469, 53495, 10,
- 53496, 53496, 9,
- 53497, 53523, 10,
- 53524, 53524, 9,
- 53525, 53551, 10,
- 53552, 53552, 9,
- 53553, 53579, 10,
- 53580, 53580, 9,
- 53581, 53607, 10,
- 53608, 53608, 9,
- 53609, 53635, 10,
- 53636, 53636, 9,
- 53637, 53663, 10,
- 53664, 53664, 9,
- 53665, 53691, 10,
- 53692, 53692, 9,
- 53693, 53719, 10,
- 53720, 53720, 9,
- 53721, 53747, 10,
- 53748, 53748, 9,
- 53749, 53775, 10,
- 53776, 53776, 9,
- 53777, 53803, 10,
- 53804, 53804, 9,
- 53805, 53831, 10,
- 53832, 53832, 9,
- 53833, 53859, 10,
- 53860, 53860, 9,
- 53861, 53887, 10,
- 53888, 53888, 9,
- 53889, 53915, 10,
- 53916, 53916, 9,
- 53917, 53943, 10,
- 53944, 53944, 9,
- 53945, 53971, 10,
- 53972, 53972, 9,
- 53973, 53999, 10,
- 54000, 54000, 9,
- 54001, 54027, 10,
- 54028, 54028, 9,
- 54029, 54055, 10,
- 54056, 54056, 9,
- 54057, 54083, 10,
- 54084, 54084, 9,
- 54085, 54111, 10,
- 54112, 54112, 9,
- 54113, 54139, 10,
- 54140, 54140, 9,
- 54141, 54167, 10,
- 54168, 54168, 9,
- 54169, 54195, 10,
- 54196, 54196, 9,
- 54197, 54223, 10,
- 54224, 54224, 9,
- 54225, 54251, 10,
- 54252, 54252, 9,
- 54253, 54279, 10,
- 54280, 54280, 9,
- 54281, 54307, 10,
- 54308, 54308, 9,
- 54309, 54335, 10,
- 54336, 54336, 9,
- 54337, 54363, 10,
- 54364, 54364, 9,
- 54365, 54391, 10,
- 54392, 54392, 9,
- 54393, 54419, 10,
- 54420, 54420, 9,
- 54421, 54447, 10,
- 54448, 54448, 9,
- 54449, 54475, 10,
- 54476, 54476, 9,
- 54477, 54503, 10,
- 54504, 54504, 9,
- 54505, 54531, 10,
- 54532, 54532, 9,
- 54533, 54559, 10,
- 54560, 54560, 9,
- 54561, 54587, 10,
- 54588, 54588, 9,
- 54589, 54615, 10,
- 54616, 54616, 9,
- 54617, 54643, 10,
- 54644, 54644, 9,
- 54645, 54671, 10,
- 54672, 54672, 9,
- 54673, 54699, 10,
- 54700, 54700, 9,
- 54701, 54727, 10,
- 54728, 54728, 9,
- 54729, 54755, 10,
- 54756, 54756, 9,
- 54757, 54783, 10,
- 54784, 54784, 9,
- 54785, 54811, 10,
- 54812, 54812, 9,
- 54813, 54839, 10,
- 54840, 54840, 9,
- 54841, 54867, 10,
- 54868, 54868, 9,
- 54869, 54895, 10,
- 54896, 54896, 9,
- 54897, 54923, 10,
- 54924, 54924, 9,
- 54925, 54951, 10,
- 54952, 54952, 9,
- 54953, 54979, 10,
- 54980, 54980, 9,
- 54981, 55007, 10,
- 55008, 55008, 9,
- 55009, 55035, 10,
- 55036, 55036, 9,
- 55037, 55063, 10,
- 55064, 55064, 9,
- 55065, 55091, 10,
- 55092, 55092, 9,
- 55093, 55119, 10,
- 55120, 55120, 9,
- 55121, 55147, 10,
- 55148, 55148, 9,
- 55149, 55175, 10,
- 55176, 55176, 9,
- 55177, 55203, 10,
- 55216, 55238, 7,
- 55243, 55291, 8,
- 55296, 57343, 3,
- 57344, 57344, 3,
- 57344, 57344, 3,
- 64286, 64286, 4,
- 65024, 65039, 4,
- 65056, 65069, 4,
- 65279, 65279, 3,
- 65438, 65439, 4,
- 65520, 65528, 3,
- 65529, 65531, 3
+const CODE_POINT_BLOCKS = const [
+ 0,
+ 9,
+ 3,
+ 10,
+ 10,
+ 2,
+ 11,
+ 12,
+ 3,
+ 13,
+ 13,
+ 1,
+ 14,
+ 31,
+ 3,
+ 127,
+ 159,
+ 3,
+ 173,
+ 173,
+ 3,
+ 768,
+ 879,
+ 4,
+ 1155,
+ 1159,
+ 4,
+ 1160,
+ 1161,
+ 4,
+ 1425,
+ 1469,
+ 4,
+ 1471,
+ 1471,
+ 4,
+ 1473,
+ 1474,
+ 4,
+ 1476,
+ 1477,
+ 4,
+ 1479,
+ 1479,
+ 4,
+ 1536,
+ 1541,
+ 3,
+ 1552,
+ 1562,
+ 4,
+ 1564,
+ 1564,
+ 3,
+ 1611,
+ 1631,
+ 4,
+ 1648,
+ 1648,
+ 4,
+ 1750,
+ 1756,
+ 4,
+ 1757,
+ 1757,
+ 3,
+ 1759,
+ 1764,
+ 4,
+ 1767,
+ 1768,
+ 4,
+ 1770,
+ 1773,
+ 4,
+ 1807,
+ 1807,
+ 3,
+ 1809,
+ 1809,
+ 4,
+ 1840,
+ 1866,
+ 4,
+ 1958,
+ 1968,
+ 4,
+ 2027,
+ 2035,
+ 4,
+ 2070,
+ 2073,
+ 4,
+ 2075,
+ 2083,
+ 4,
+ 2085,
+ 2087,
+ 4,
+ 2089,
+ 2093,
+ 4,
+ 2137,
+ 2139,
+ 4,
+ 2276,
+ 2306,
+ 4,
+ 2307,
+ 2307,
+ 5,
+ 2362,
+ 2362,
+ 4,
+ 2363,
+ 2363,
+ 5,
+ 2364,
+ 2364,
+ 4,
+ 2366,
+ 2368,
+ 5,
+ 2369,
+ 2376,
+ 4,
+ 2377,
+ 2380,
+ 5,
+ 2381,
+ 2381,
+ 4,
+ 2382,
+ 2383,
+ 5,
+ 2385,
+ 2391,
+ 4,
+ 2402,
+ 2403,
+ 4,
+ 2433,
+ 2433,
+ 4,
+ 2434,
+ 2435,
+ 5,
+ 2492,
+ 2492,
+ 4,
+ 2494,
+ 2494,
+ 4,
+ 2495,
+ 2496,
+ 5,
+ 2497,
+ 2500,
+ 4,
+ 2503,
+ 2504,
+ 5,
+ 2507,
+ 2508,
+ 5,
+ 2509,
+ 2509,
+ 4,
+ 2519,
+ 2519,
+ 4,
+ 2530,
+ 2531,
+ 4,
+ 2561,
+ 2562,
+ 4,
+ 2563,
+ 2563,
+ 5,
+ 2620,
+ 2620,
+ 4,
+ 2622,
+ 2624,
+ 5,
+ 2625,
+ 2626,
+ 4,
+ 2631,
+ 2632,
+ 4,
+ 2635,
+ 2637,
+ 4,
+ 2641,
+ 2641,
+ 4,
+ 2672,
+ 2673,
+ 4,
+ 2677,
+ 2677,
+ 4,
+ 2689,
+ 2690,
+ 4,
+ 2691,
+ 2691,
+ 5,
+ 2748,
+ 2748,
+ 4,
+ 2750,
+ 2752,
+ 5,
+ 2753,
+ 2757,
+ 4,
+ 2759,
+ 2760,
+ 4,
+ 2761,
+ 2761,
+ 5,
+ 2763,
+ 2764,
+ 5,
+ 2765,
+ 2765,
+ 4,
+ 2786,
+ 2787,
+ 4,
+ 2817,
+ 2817,
+ 4,
+ 2818,
+ 2819,
+ 5,
+ 2876,
+ 2876,
+ 4,
+ 2878,
+ 2878,
+ 4,
+ 2879,
+ 2879,
+ 4,
+ 2880,
+ 2880,
+ 5,
+ 2881,
+ 2884,
+ 4,
+ 2887,
+ 2888,
+ 5,
+ 2891,
+ 2892,
+ 5,
+ 2893,
+ 2893,
+ 4,
+ 2902,
+ 2902,
+ 4,
+ 2903,
+ 2903,
+ 4,
+ 2914,
+ 2915,
+ 4,
+ 2946,
+ 2946,
+ 4,
+ 3006,
+ 3006,
+ 4,
+ 3007,
+ 3007,
+ 5,
+ 3008,
+ 3008,
+ 4,
+ 3009,
+ 3010,
+ 5,
+ 3014,
+ 3016,
+ 5,
+ 3018,
+ 3020,
+ 5,
+ 3021,
+ 3021,
+ 4,
+ 3031,
+ 3031,
+ 4,
+ 3072,
+ 3072,
+ 4,
+ 3073,
+ 3075,
+ 5,
+ 3134,
+ 3136,
+ 4,
+ 3137,
+ 3140,
+ 5,
+ 3142,
+ 3144,
+ 4,
+ 3146,
+ 3149,
+ 4,
+ 3157,
+ 3158,
+ 4,
+ 3170,
+ 3171,
+ 4,
+ 3201,
+ 3201,
+ 4,
+ 3202,
+ 3203,
+ 5,
+ 3260,
+ 3260,
+ 4,
+ 3262,
+ 3262,
+ 5,
+ 3263,
+ 3263,
+ 4,
+ 3264,
+ 3265,
+ 5,
+ 3266,
+ 3266,
+ 4,
+ 3267,
+ 3268,
+ 5,
+ 3270,
+ 3270,
+ 4,
+ 3271,
+ 3272,
+ 5,
+ 3274,
+ 3275,
+ 5,
+ 3276,
+ 3277,
+ 4,
+ 3285,
+ 3286,
+ 4,
+ 3298,
+ 3299,
+ 4,
+ 3329,
+ 3329,
+ 4,
+ 3330,
+ 3331,
+ 5,
+ 3390,
+ 3390,
+ 4,
+ 3391,
+ 3392,
+ 5,
+ 3393,
+ 3396,
+ 4,
+ 3398,
+ 3400,
+ 5,
+ 3402,
+ 3404,
+ 5,
+ 3405,
+ 3405,
+ 4,
+ 3415,
+ 3415,
+ 4,
+ 3426,
+ 3427,
+ 4,
+ 3458,
+ 3459,
+ 5,
+ 3530,
+ 3530,
+ 4,
+ 3535,
+ 3535,
+ 4,
+ 3536,
+ 3537,
+ 5,
+ 3538,
+ 3540,
+ 4,
+ 3542,
+ 3542,
+ 4,
+ 3544,
+ 3550,
+ 5,
+ 3551,
+ 3551,
+ 4,
+ 3570,
+ 3571,
+ 5,
+ 3633,
+ 3633,
+ 4,
+ 3635,
+ 3635,
+ 5,
+ 3636,
+ 3642,
+ 4,
+ 3655,
+ 3662,
+ 4,
+ 3761,
+ 3761,
+ 4,
+ 3763,
+ 3763,
+ 5,
+ 3764,
+ 3769,
+ 4,
+ 3771,
+ 3772,
+ 4,
+ 3784,
+ 3789,
+ 4,
+ 3864,
+ 3865,
+ 4,
+ 3893,
+ 3893,
+ 4,
+ 3895,
+ 3895,
+ 4,
+ 3897,
+ 3897,
+ 4,
+ 3902,
+ 3903,
+ 5,
+ 3953,
+ 3966,
+ 4,
+ 3967,
+ 3967,
+ 5,
+ 3968,
+ 3972,
+ 4,
+ 3974,
+ 3975,
+ 4,
+ 3981,
+ 3991,
+ 4,
+ 3993,
+ 4028,
+ 4,
+ 4038,
+ 4038,
+ 4,
+ 4127,
+ 4127,
+ 4,
+ 4141,
+ 4144,
+ 4,
+ 4142,
+ 4142,
+ 4,
+ 4145,
+ 4145,
+ 5,
+ 4146,
+ 4151,
+ 4,
+ 4153,
+ 4154,
+ 4,
+ 4155,
+ 4156,
+ 5,
+ 4157,
+ 4158,
+ 4,
+ 4182,
+ 4183,
+ 5,
+ 4184,
+ 4185,
+ 4,
+ 4190,
+ 4192,
+ 4,
+ 4209,
+ 4212,
+ 4,
+ 4226,
+ 4226,
+ 4,
+ 4228,
+ 4228,
+ 5,
+ 4229,
+ 4230,
+ 4,
+ 4237,
+ 4237,
+ 4,
+ 4253,
+ 4253,
+ 4,
+ 4259,
+ 4259,
+ 4,
+ 4352,
+ 4352,
+ 5,
+ 4352,
+ 4447,
+ 6,
+ 4352,
+ 4352,
+ 4,
+ 4352,
+ 4352,
+ 5,
+ 4360,
+ 4360,
+ 5,
+ 4363,
+ 4363,
+ 3,
+ 4370,
+ 4370,
+ 5,
+ 4375,
+ 4375,
+ 4,
+ 4376,
+ 4376,
+ 5,
+ 4387,
+ 4387,
+ 4,
+ 4387,
+ 4387,
+ 5,
+ 4397,
+ 4397,
+ 4,
+ 4400,
+ 4400,
+ 4,
+ 4403,
+ 4403,
+ 5,
+ 4403,
+ 4403,
+ 4,
+ 4403,
+ 4403,
+ 4,
+ 4404,
+ 4404,
+ 4,
+ 4405,
+ 4405,
+ 4,
+ 4427,
+ 4427,
+ 5,
+ 4427,
+ 4427,
+ 4,
+ 4427,
+ 4427,
+ 5,
+ 4427,
+ 4427,
+ 4,
+ 4427,
+ 4427,
+ 4,
+ 4428,
+ 4428,
+ 5,
+ 4442,
+ 4442,
+ 4,
+ 4443,
+ 4443,
+ 5,
+ 4448,
+ 4519,
+ 7,
+ 4451,
+ 4451,
+ 5,
+ 4451,
+ 4451,
+ 4,
+ 4458,
+ 4458,
+ 4,
+ 4458,
+ 4458,
+ 5,
+ 4458,
+ 4458,
+ 4,
+ 4459,
+ 4459,
+ 4,
+ 4459,
+ 4459,
+ 5,
+ 4520,
+ 4607,
+ 8,
+ 4957,
+ 4959,
+ 4,
+ 5906,
+ 5908,
+ 4,
+ 5938,
+ 5940,
+ 4,
+ 5970,
+ 5971,
+ 4,
+ 6002,
+ 6003,
+ 4,
+ 6068,
+ 6069,
+ 4,
+ 6070,
+ 6070,
+ 5,
+ 6071,
+ 6077,
+ 4,
+ 6078,
+ 6085,
+ 5,
+ 6086,
+ 6086,
+ 4,
+ 6087,
+ 6088,
+ 5,
+ 6089,
+ 6099,
+ 4,
+ 6109,
+ 6109,
+ 4,
+ 6155,
+ 6157,
+ 4,
+ 6158,
+ 6158,
+ 3,
+ 6313,
+ 6313,
+ 4,
+ 6432,
+ 6434,
+ 4,
+ 6435,
+ 6438,
+ 5,
+ 6439,
+ 6440,
+ 4,
+ 6441,
+ 6443,
+ 5,
+ 6448,
+ 6449,
+ 5,
+ 6450,
+ 6450,
+ 4,
+ 6451,
+ 6456,
+ 5,
+ 6457,
+ 6459,
+ 4,
+ 6581,
+ 6583,
+ 5,
+ 6586,
+ 6586,
+ 5,
+ 6679,
+ 6680,
+ 4,
+ 6681,
+ 6682,
+ 5,
+ 6683,
+ 6683,
+ 4,
+ 6741,
+ 6741,
+ 5,
+ 6742,
+ 6742,
+ 4,
+ 6743,
+ 6743,
+ 5,
+ 6744,
+ 6750,
+ 4,
+ 6752,
+ 6752,
+ 4,
+ 6754,
+ 6754,
+ 4,
+ 6757,
+ 6764,
+ 4,
+ 6765,
+ 6770,
+ 5,
+ 6771,
+ 6780,
+ 4,
+ 6783,
+ 6783,
+ 4,
+ 6832,
+ 6845,
+ 4,
+ 6846,
+ 6846,
+ 4,
+ 6912,
+ 6915,
+ 4,
+ 6916,
+ 6916,
+ 5,
+ 6964,
+ 6964,
+ 4,
+ 6965,
+ 6965,
+ 5,
+ 6966,
+ 6970,
+ 4,
+ 6971,
+ 6971,
+ 5,
+ 6972,
+ 6972,
+ 4,
+ 6973,
+ 6977,
+ 5,
+ 6978,
+ 6978,
+ 4,
+ 6979,
+ 6980,
+ 5,
+ 7019,
+ 7027,
+ 4,
+ 7040,
+ 7041,
+ 4,
+ 7042,
+ 7042,
+ 5,
+ 7073,
+ 7073,
+ 5,
+ 7074,
+ 7077,
+ 4,
+ 7078,
+ 7079,
+ 5,
+ 7080,
+ 7081,
+ 4,
+ 7082,
+ 7082,
+ 5,
+ 7083,
+ 7085,
+ 4,
+ 7142,
+ 7142,
+ 4,
+ 7143,
+ 7143,
+ 5,
+ 7144,
+ 7145,
+ 4,
+ 7146,
+ 7148,
+ 5,
+ 7149,
+ 7149,
+ 4,
+ 7150,
+ 7150,
+ 5,
+ 7151,
+ 7153,
+ 4,
+ 7154,
+ 7155,
+ 5,
+ 7204,
+ 7211,
+ 5,
+ 7212,
+ 7219,
+ 4,
+ 7220,
+ 7221,
+ 5,
+ 7222,
+ 7223,
+ 4,
+ 7376,
+ 7378,
+ 4,
+ 7380,
+ 7392,
+ 4,
+ 7393,
+ 7393,
+ 5,
+ 7394,
+ 7400,
+ 4,
+ 7405,
+ 7405,
+ 4,
+ 7410,
+ 7411,
+ 5,
+ 7412,
+ 7412,
+ 4,
+ 7416,
+ 7417,
+ 4,
+ 7446,
+ 7446,
+ 4,
+ 7446,
+ 7446,
+ 5,
+ 7446,
+ 7446,
+ 5,
+ 7616,
+ 7669,
+ 4,
+ 7676,
+ 7679,
+ 4,
+ 8203,
+ 8203,
+ 3,
+ 8204,
+ 8205,
+ 4,
+ 8206,
+ 8207,
+ 3,
+ 8232,
+ 8232,
+ 3,
+ 8233,
+ 8233,
+ 3,
+ 8234,
+ 8238,
+ 3,
+ 8288,
+ 8292,
+ 3,
+ 8293,
+ 8293,
+ 3,
+ 8294,
+ 8303,
+ 3,
+ 8400,
+ 8412,
+ 4,
+ 8413,
+ 8416,
+ 4,
+ 8417,
+ 8417,
+ 4,
+ 8418,
+ 8420,
+ 4,
+ 8421,
+ 8432,
+ 4,
+ 11503,
+ 11505,
+ 4,
+ 11647,
+ 11647,
+ 4,
+ 11744,
+ 11775,
+ 4,
+ 12330,
+ 12333,
+ 4,
+ 12334,
+ 12335,
+ 4,
+ 12441,
+ 12442,
+ 4,
+ 42607,
+ 42607,
+ 4,
+ 42608,
+ 42610,
+ 4,
+ 42612,
+ 42621,
+ 4,
+ 42655,
+ 42655,
+ 4,
+ 42736,
+ 42737,
+ 4,
+ 43010,
+ 43010,
+ 4,
+ 43014,
+ 43014,
+ 4,
+ 43019,
+ 43019,
+ 4,
+ 43043,
+ 43044,
+ 5,
+ 43045,
+ 43046,
+ 4,
+ 43047,
+ 43047,
+ 5,
+ 43136,
+ 43137,
+ 5,
+ 43188,
+ 43203,
+ 5,
+ 43204,
+ 43204,
+ 4,
+ 43232,
+ 43249,
+ 4,
+ 43302,
+ 43309,
+ 4,
+ 43335,
+ 43345,
+ 4,
+ 43346,
+ 43347,
+ 5,
+ 43360,
+ 43388,
+ 6,
+ 43392,
+ 43394,
+ 4,
+ 43395,
+ 43395,
+ 5,
+ 43443,
+ 43443,
+ 4,
+ 43444,
+ 43445,
+ 5,
+ 43446,
+ 43449,
+ 4,
+ 43450,
+ 43451,
+ 5,
+ 43452,
+ 43452,
+ 4,
+ 43453,
+ 43456,
+ 5,
+ 43493,
+ 43493,
+ 4,
+ 43561,
+ 43566,
+ 4,
+ 43567,
+ 43568,
+ 5,
+ 43569,
+ 43570,
+ 4,
+ 43571,
+ 43572,
+ 5,
+ 43573,
+ 43574,
+ 4,
+ 43587,
+ 43587,
+ 4,
+ 43596,
+ 43596,
+ 4,
+ 43597,
+ 43597,
+ 5,
+ 43644,
+ 43644,
+ 4,
+ 43696,
+ 43696,
+ 4,
+ 43698,
+ 43700,
+ 4,
+ 43703,
+ 43704,
+ 4,
+ 43710,
+ 43711,
+ 4,
+ 43713,
+ 43713,
+ 4,
+ 43755,
+ 43755,
+ 5,
+ 43756,
+ 43757,
+ 4,
+ 43758,
+ 43759,
+ 5,
+ 43765,
+ 43765,
+ 5,
+ 43766,
+ 43766,
+ 4,
+ 44003,
+ 44004,
+ 5,
+ 44005,
+ 44005,
+ 4,
+ 44006,
+ 44007,
+ 5,
+ 44008,
+ 44008,
+ 4,
+ 44009,
+ 44010,
+ 5,
+ 44012,
+ 44012,
+ 5,
+ 44013,
+ 44013,
+ 4,
+ 44032,
+ 44032,
+ 9,
+ 44033,
+ 44059,
+ 10,
+ 44060,
+ 44060,
+ 9,
+ 44061,
+ 44087,
+ 10,
+ 44088,
+ 44088,
+ 9,
+ 44089,
+ 44115,
+ 10,
+ 44116,
+ 44116,
+ 9,
+ 44117,
+ 44143,
+ 10,
+ 44144,
+ 44144,
+ 9,
+ 44145,
+ 44171,
+ 10,
+ 44172,
+ 44172,
+ 9,
+ 44173,
+ 44199,
+ 10,
+ 44200,
+ 44200,
+ 9,
+ 44201,
+ 44227,
+ 10,
+ 44228,
+ 44228,
+ 9,
+ 44229,
+ 44255,
+ 10,
+ 44256,
+ 44256,
+ 9,
+ 44257,
+ 44283,
+ 10,
+ 44284,
+ 44284,
+ 9,
+ 44285,
+ 44311,
+ 10,
+ 44312,
+ 44312,
+ 9,
+ 44313,
+ 44339,
+ 10,
+ 44340,
+ 44340,
+ 9,
+ 44341,
+ 44367,
+ 10,
+ 44368,
+ 44368,
+ 9,
+ 44369,
+ 44395,
+ 10,
+ 44396,
+ 44396,
+ 9,
+ 44397,
+ 44423,
+ 10,
+ 44424,
+ 44424,
+ 9,
+ 44425,
+ 44451,
+ 10,
+ 44452,
+ 44452,
+ 9,
+ 44453,
+ 44479,
+ 10,
+ 44480,
+ 44480,
+ 9,
+ 44481,
+ 44507,
+ 10,
+ 44508,
+ 44508,
+ 9,
+ 44509,
+ 44535,
+ 10,
+ 44536,
+ 44536,
+ 9,
+ 44537,
+ 44563,
+ 10,
+ 44564,
+ 44564,
+ 9,
+ 44565,
+ 44591,
+ 10,
+ 44592,
+ 44592,
+ 9,
+ 44593,
+ 44619,
+ 10,
+ 44620,
+ 44620,
+ 9,
+ 44621,
+ 44647,
+ 10,
+ 44648,
+ 44648,
+ 9,
+ 44649,
+ 44675,
+ 10,
+ 44676,
+ 44676,
+ 9,
+ 44677,
+ 44703,
+ 10,
+ 44704,
+ 44704,
+ 9,
+ 44705,
+ 44731,
+ 10,
+ 44732,
+ 44732,
+ 9,
+ 44733,
+ 44759,
+ 10,
+ 44760,
+ 44760,
+ 9,
+ 44761,
+ 44787,
+ 10,
+ 44788,
+ 44788,
+ 9,
+ 44789,
+ 44815,
+ 10,
+ 44816,
+ 44816,
+ 9,
+ 44817,
+ 44843,
+ 10,
+ 44844,
+ 44844,
+ 9,
+ 44845,
+ 44871,
+ 10,
+ 44872,
+ 44872,
+ 9,
+ 44873,
+ 44899,
+ 10,
+ 44900,
+ 44900,
+ 9,
+ 44901,
+ 44927,
+ 10,
+ 44928,
+ 44928,
+ 9,
+ 44929,
+ 44955,
+ 10,
+ 44956,
+ 44956,
+ 9,
+ 44957,
+ 44983,
+ 10,
+ 44984,
+ 44984,
+ 9,
+ 44985,
+ 45011,
+ 10,
+ 45012,
+ 45012,
+ 9,
+ 45013,
+ 45039,
+ 10,
+ 45040,
+ 45040,
+ 9,
+ 45041,
+ 45067,
+ 10,
+ 45068,
+ 45068,
+ 9,
+ 45069,
+ 45095,
+ 10,
+ 45096,
+ 45096,
+ 9,
+ 45097,
+ 45123,
+ 10,
+ 45124,
+ 45124,
+ 9,
+ 45125,
+ 45151,
+ 10,
+ 45152,
+ 45152,
+ 9,
+ 45153,
+ 45179,
+ 10,
+ 45180,
+ 45180,
+ 9,
+ 45181,
+ 45207,
+ 10,
+ 45208,
+ 45208,
+ 9,
+ 45209,
+ 45235,
+ 10,
+ 45236,
+ 45236,
+ 9,
+ 45237,
+ 45263,
+ 10,
+ 45264,
+ 45264,
+ 9,
+ 45265,
+ 45291,
+ 10,
+ 45292,
+ 45292,
+ 9,
+ 45293,
+ 45319,
+ 10,
+ 45320,
+ 45320,
+ 9,
+ 45321,
+ 45347,
+ 10,
+ 45348,
+ 45348,
+ 9,
+ 45349,
+ 45375,
+ 10,
+ 45376,
+ 45376,
+ 9,
+ 45377,
+ 45403,
+ 10,
+ 45404,
+ 45404,
+ 9,
+ 45405,
+ 45431,
+ 10,
+ 45432,
+ 45432,
+ 9,
+ 45433,
+ 45459,
+ 10,
+ 45460,
+ 45460,
+ 9,
+ 45461,
+ 45487,
+ 10,
+ 45488,
+ 45488,
+ 9,
+ 45489,
+ 45515,
+ 10,
+ 45516,
+ 45516,
+ 9,
+ 45517,
+ 45543,
+ 10,
+ 45544,
+ 45544,
+ 9,
+ 45545,
+ 45571,
+ 10,
+ 45572,
+ 45572,
+ 9,
+ 45573,
+ 45599,
+ 10,
+ 45600,
+ 45600,
+ 9,
+ 45601,
+ 45627,
+ 10,
+ 45628,
+ 45628,
+ 9,
+ 45629,
+ 45655,
+ 10,
+ 45656,
+ 45656,
+ 9,
+ 45657,
+ 45683,
+ 10,
+ 45684,
+ 45684,
+ 9,
+ 45685,
+ 45711,
+ 10,
+ 45712,
+ 45712,
+ 9,
+ 45713,
+ 45739,
+ 10,
+ 45740,
+ 45740,
+ 9,
+ 45741,
+ 45767,
+ 10,
+ 45768,
+ 45768,
+ 9,
+ 45769,
+ 45795,
+ 10,
+ 45796,
+ 45796,
+ 9,
+ 45797,
+ 45823,
+ 10,
+ 45824,
+ 45824,
+ 9,
+ 45825,
+ 45851,
+ 10,
+ 45852,
+ 45852,
+ 9,
+ 45853,
+ 45879,
+ 10,
+ 45880,
+ 45880,
+ 9,
+ 45881,
+ 45907,
+ 10,
+ 45908,
+ 45908,
+ 9,
+ 45909,
+ 45935,
+ 10,
+ 45936,
+ 45936,
+ 9,
+ 45937,
+ 45963,
+ 10,
+ 45964,
+ 45964,
+ 9,
+ 45965,
+ 45991,
+ 10,
+ 45992,
+ 45992,
+ 9,
+ 45993,
+ 46019,
+ 10,
+ 46020,
+ 46020,
+ 9,
+ 46021,
+ 46047,
+ 10,
+ 46048,
+ 46048,
+ 9,
+ 46049,
+ 46075,
+ 10,
+ 46076,
+ 46076,
+ 9,
+ 46077,
+ 46103,
+ 10,
+ 46104,
+ 46104,
+ 9,
+ 46105,
+ 46131,
+ 10,
+ 46132,
+ 46132,
+ 9,
+ 46133,
+ 46159,
+ 10,
+ 46160,
+ 46160,
+ 9,
+ 46161,
+ 46187,
+ 10,
+ 46188,
+ 46188,
+ 9,
+ 46189,
+ 46215,
+ 10,
+ 46216,
+ 46216,
+ 9,
+ 46217,
+ 46243,
+ 10,
+ 46244,
+ 46244,
+ 9,
+ 46245,
+ 46271,
+ 10,
+ 46272,
+ 46272,
+ 9,
+ 46273,
+ 46299,
+ 10,
+ 46300,
+ 46300,
+ 9,
+ 46301,
+ 46327,
+ 10,
+ 46328,
+ 46328,
+ 9,
+ 46329,
+ 46355,
+ 10,
+ 46356,
+ 46356,
+ 9,
+ 46357,
+ 46383,
+ 10,
+ 46384,
+ 46384,
+ 9,
+ 46385,
+ 46411,
+ 10,
+ 46412,
+ 46412,
+ 9,
+ 46413,
+ 46439,
+ 10,
+ 46440,
+ 46440,
+ 9,
+ 46441,
+ 46467,
+ 10,
+ 46468,
+ 46468,
+ 9,
+ 46469,
+ 46495,
+ 10,
+ 46496,
+ 46496,
+ 9,
+ 46497,
+ 46523,
+ 10,
+ 46524,
+ 46524,
+ 9,
+ 46525,
+ 46551,
+ 10,
+ 46552,
+ 46552,
+ 9,
+ 46553,
+ 46579,
+ 10,
+ 46580,
+ 46580,
+ 9,
+ 46581,
+ 46607,
+ 10,
+ 46608,
+ 46608,
+ 9,
+ 46609,
+ 46635,
+ 10,
+ 46636,
+ 46636,
+ 9,
+ 46637,
+ 46663,
+ 10,
+ 46664,
+ 46664,
+ 9,
+ 46665,
+ 46691,
+ 10,
+ 46692,
+ 46692,
+ 9,
+ 46693,
+ 46719,
+ 10,
+ 46720,
+ 46720,
+ 9,
+ 46721,
+ 46747,
+ 10,
+ 46748,
+ 46748,
+ 9,
+ 46749,
+ 46775,
+ 10,
+ 46776,
+ 46776,
+ 9,
+ 46777,
+ 46803,
+ 10,
+ 46804,
+ 46804,
+ 9,
+ 46805,
+ 46831,
+ 10,
+ 46832,
+ 46832,
+ 9,
+ 46833,
+ 46859,
+ 10,
+ 46860,
+ 46860,
+ 9,
+ 46861,
+ 46887,
+ 10,
+ 46888,
+ 46888,
+ 9,
+ 46889,
+ 46915,
+ 10,
+ 46916,
+ 46916,
+ 9,
+ 46917,
+ 46943,
+ 10,
+ 46944,
+ 46944,
+ 9,
+ 46945,
+ 46971,
+ 10,
+ 46972,
+ 46972,
+ 9,
+ 46973,
+ 46999,
+ 10,
+ 47000,
+ 47000,
+ 9,
+ 47001,
+ 47027,
+ 10,
+ 47028,
+ 47028,
+ 9,
+ 47029,
+ 47055,
+ 10,
+ 47056,
+ 47056,
+ 9,
+ 47057,
+ 47083,
+ 10,
+ 47084,
+ 47084,
+ 9,
+ 47085,
+ 47111,
+ 10,
+ 47112,
+ 47112,
+ 9,
+ 47113,
+ 47139,
+ 10,
+ 47140,
+ 47140,
+ 9,
+ 47141,
+ 47167,
+ 10,
+ 47168,
+ 47168,
+ 9,
+ 47169,
+ 47195,
+ 10,
+ 47196,
+ 47196,
+ 9,
+ 47197,
+ 47223,
+ 10,
+ 47224,
+ 47224,
+ 9,
+ 47225,
+ 47251,
+ 10,
+ 47252,
+ 47252,
+ 9,
+ 47253,
+ 47279,
+ 10,
+ 47280,
+ 47280,
+ 9,
+ 47281,
+ 47307,
+ 10,
+ 47308,
+ 47308,
+ 9,
+ 47309,
+ 47335,
+ 10,
+ 47336,
+ 47336,
+ 9,
+ 47337,
+ 47363,
+ 10,
+ 47364,
+ 47364,
+ 9,
+ 47365,
+ 47391,
+ 10,
+ 47392,
+ 47392,
+ 9,
+ 47393,
+ 47419,
+ 10,
+ 47420,
+ 47420,
+ 9,
+ 47421,
+ 47447,
+ 10,
+ 47448,
+ 47448,
+ 9,
+ 47449,
+ 47475,
+ 10,
+ 47476,
+ 47476,
+ 9,
+ 47477,
+ 47503,
+ 10,
+ 47504,
+ 47504,
+ 9,
+ 47505,
+ 47531,
+ 10,
+ 47532,
+ 47532,
+ 9,
+ 47533,
+ 47559,
+ 10,
+ 47560,
+ 47560,
+ 9,
+ 47561,
+ 47587,
+ 10,
+ 47588,
+ 47588,
+ 9,
+ 47589,
+ 47615,
+ 10,
+ 47616,
+ 47616,
+ 9,
+ 47617,
+ 47643,
+ 10,
+ 47644,
+ 47644,
+ 9,
+ 47645,
+ 47671,
+ 10,
+ 47672,
+ 47672,
+ 9,
+ 47673,
+ 47699,
+ 10,
+ 47700,
+ 47700,
+ 9,
+ 47701,
+ 47727,
+ 10,
+ 47728,
+ 47728,
+ 9,
+ 47729,
+ 47755,
+ 10,
+ 47756,
+ 47756,
+ 9,
+ 47757,
+ 47783,
+ 10,
+ 47784,
+ 47784,
+ 9,
+ 47785,
+ 47811,
+ 10,
+ 47812,
+ 47812,
+ 9,
+ 47813,
+ 47839,
+ 10,
+ 47840,
+ 47840,
+ 9,
+ 47841,
+ 47867,
+ 10,
+ 47868,
+ 47868,
+ 9,
+ 47869,
+ 47895,
+ 10,
+ 47896,
+ 47896,
+ 9,
+ 47897,
+ 47923,
+ 10,
+ 47924,
+ 47924,
+ 9,
+ 47925,
+ 47951,
+ 10,
+ 47952,
+ 47952,
+ 9,
+ 47953,
+ 47979,
+ 10,
+ 47980,
+ 47980,
+ 9,
+ 47981,
+ 48007,
+ 10,
+ 48008,
+ 48008,
+ 9,
+ 48009,
+ 48035,
+ 10,
+ 48036,
+ 48036,
+ 9,
+ 48037,
+ 48063,
+ 10,
+ 48064,
+ 48064,
+ 9,
+ 48065,
+ 48091,
+ 10,
+ 48092,
+ 48092,
+ 9,
+ 48093,
+ 48119,
+ 10,
+ 48120,
+ 48120,
+ 9,
+ 48121,
+ 48147,
+ 10,
+ 48148,
+ 48148,
+ 9,
+ 48149,
+ 48175,
+ 10,
+ 48176,
+ 48176,
+ 9,
+ 48177,
+ 48203,
+ 10,
+ 48204,
+ 48204,
+ 9,
+ 48205,
+ 48231,
+ 10,
+ 48232,
+ 48232,
+ 9,
+ 48233,
+ 48259,
+ 10,
+ 48260,
+ 48260,
+ 9,
+ 48261,
+ 48287,
+ 10,
+ 48288,
+ 48288,
+ 9,
+ 48289,
+ 48315,
+ 10,
+ 48316,
+ 48316,
+ 9,
+ 48317,
+ 48343,
+ 10,
+ 48344,
+ 48344,
+ 9,
+ 48345,
+ 48371,
+ 10,
+ 48372,
+ 48372,
+ 9,
+ 48373,
+ 48399,
+ 10,
+ 48400,
+ 48400,
+ 9,
+ 48401,
+ 48427,
+ 10,
+ 48428,
+ 48428,
+ 9,
+ 48429,
+ 48455,
+ 10,
+ 48456,
+ 48456,
+ 9,
+ 48457,
+ 48483,
+ 10,
+ 48484,
+ 48484,
+ 9,
+ 48485,
+ 48511,
+ 10,
+ 48512,
+ 48512,
+ 9,
+ 48513,
+ 48539,
+ 10,
+ 48540,
+ 48540,
+ 9,
+ 48541,
+ 48567,
+ 10,
+ 48568,
+ 48568,
+ 9,
+ 48569,
+ 48595,
+ 10,
+ 48596,
+ 48596,
+ 9,
+ 48597,
+ 48623,
+ 10,
+ 48624,
+ 48624,
+ 9,
+ 48625,
+ 48651,
+ 10,
+ 48652,
+ 48652,
+ 9,
+ 48653,
+ 48679,
+ 10,
+ 48680,
+ 48680,
+ 9,
+ 48681,
+ 48707,
+ 10,
+ 48708,
+ 48708,
+ 9,
+ 48709,
+ 48735,
+ 10,
+ 48736,
+ 48736,
+ 9,
+ 48737,
+ 48763,
+ 10,
+ 48764,
+ 48764,
+ 9,
+ 48765,
+ 48791,
+ 10,
+ 48792,
+ 48792,
+ 9,
+ 48793,
+ 48819,
+ 10,
+ 48820,
+ 48820,
+ 9,
+ 48821,
+ 48847,
+ 10,
+ 48848,
+ 48848,
+ 9,
+ 48849,
+ 48875,
+ 10,
+ 48876,
+ 48876,
+ 9,
+ 48877,
+ 48903,
+ 10,
+ 48904,
+ 48904,
+ 9,
+ 48905,
+ 48931,
+ 10,
+ 48932,
+ 48932,
+ 9,
+ 48933,
+ 48959,
+ 10,
+ 48960,
+ 48960,
+ 9,
+ 48961,
+ 48987,
+ 10,
+ 48988,
+ 48988,
+ 9,
+ 48989,
+ 49015,
+ 10,
+ 49016,
+ 49016,
+ 9,
+ 49017,
+ 49043,
+ 10,
+ 49044,
+ 49044,
+ 9,
+ 49045,
+ 49071,
+ 10,
+ 49072,
+ 49072,
+ 9,
+ 49073,
+ 49099,
+ 10,
+ 49100,
+ 49100,
+ 9,
+ 49101,
+ 49127,
+ 10,
+ 49128,
+ 49128,
+ 9,
+ 49129,
+ 49155,
+ 10,
+ 49156,
+ 49156,
+ 9,
+ 49157,
+ 49183,
+ 10,
+ 49184,
+ 49184,
+ 9,
+ 49185,
+ 49211,
+ 10,
+ 49212,
+ 49212,
+ 9,
+ 49213,
+ 49239,
+ 10,
+ 49240,
+ 49240,
+ 9,
+ 49241,
+ 49267,
+ 10,
+ 49268,
+ 49268,
+ 9,
+ 49269,
+ 49295,
+ 10,
+ 49296,
+ 49296,
+ 9,
+ 49297,
+ 49323,
+ 10,
+ 49324,
+ 49324,
+ 9,
+ 49325,
+ 49351,
+ 10,
+ 49352,
+ 49352,
+ 9,
+ 49353,
+ 49379,
+ 10,
+ 49380,
+ 49380,
+ 9,
+ 49381,
+ 49407,
+ 10,
+ 49408,
+ 49408,
+ 9,
+ 49409,
+ 49435,
+ 10,
+ 49436,
+ 49436,
+ 9,
+ 49437,
+ 49463,
+ 10,
+ 49464,
+ 49464,
+ 9,
+ 49465,
+ 49491,
+ 10,
+ 49492,
+ 49492,
+ 9,
+ 49493,
+ 49519,
+ 10,
+ 49520,
+ 49520,
+ 9,
+ 49521,
+ 49547,
+ 10,
+ 49548,
+ 49548,
+ 9,
+ 49549,
+ 49575,
+ 10,
+ 49576,
+ 49576,
+ 9,
+ 49577,
+ 49603,
+ 10,
+ 49604,
+ 49604,
+ 9,
+ 49605,
+ 49631,
+ 10,
+ 49632,
+ 49632,
+ 9,
+ 49633,
+ 49659,
+ 10,
+ 49660,
+ 49660,
+ 9,
+ 49661,
+ 49687,
+ 10,
+ 49688,
+ 49688,
+ 9,
+ 49689,
+ 49715,
+ 10,
+ 49716,
+ 49716,
+ 9,
+ 49717,
+ 49743,
+ 10,
+ 49744,
+ 49744,
+ 9,
+ 49745,
+ 49771,
+ 10,
+ 49772,
+ 49772,
+ 9,
+ 49773,
+ 49799,
+ 10,
+ 49800,
+ 49800,
+ 9,
+ 49801,
+ 49827,
+ 10,
+ 49828,
+ 49828,
+ 9,
+ 49829,
+ 49855,
+ 10,
+ 49856,
+ 49856,
+ 9,
+ 49857,
+ 49883,
+ 10,
+ 49884,
+ 49884,
+ 9,
+ 49885,
+ 49911,
+ 10,
+ 49912,
+ 49912,
+ 9,
+ 49913,
+ 49939,
+ 10,
+ 49940,
+ 49940,
+ 9,
+ 49941,
+ 49967,
+ 10,
+ 49968,
+ 49968,
+ 9,
+ 49969,
+ 49995,
+ 10,
+ 49996,
+ 49996,
+ 9,
+ 49997,
+ 50023,
+ 10,
+ 50024,
+ 50024,
+ 9,
+ 50025,
+ 50051,
+ 10,
+ 50052,
+ 50052,
+ 9,
+ 50053,
+ 50079,
+ 10,
+ 50080,
+ 50080,
+ 9,
+ 50081,
+ 50107,
+ 10,
+ 50108,
+ 50108,
+ 9,
+ 50109,
+ 50135,
+ 10,
+ 50136,
+ 50136,
+ 9,
+ 50137,
+ 50163,
+ 10,
+ 50164,
+ 50164,
+ 9,
+ 50165,
+ 50191,
+ 10,
+ 50192,
+ 50192,
+ 9,
+ 50193,
+ 50219,
+ 10,
+ 50220,
+ 50220,
+ 9,
+ 50221,
+ 50247,
+ 10,
+ 50248,
+ 50248,
+ 9,
+ 50249,
+ 50275,
+ 10,
+ 50276,
+ 50276,
+ 9,
+ 50277,
+ 50303,
+ 10,
+ 50304,
+ 50304,
+ 9,
+ 50305,
+ 50331,
+ 10,
+ 50332,
+ 50332,
+ 9,
+ 50333,
+ 50359,
+ 10,
+ 50360,
+ 50360,
+ 9,
+ 50361,
+ 50387,
+ 10,
+ 50388,
+ 50388,
+ 9,
+ 50389,
+ 50415,
+ 10,
+ 50416,
+ 50416,
+ 9,
+ 50417,
+ 50443,
+ 10,
+ 50444,
+ 50444,
+ 9,
+ 50445,
+ 50471,
+ 10,
+ 50472,
+ 50472,
+ 9,
+ 50473,
+ 50499,
+ 10,
+ 50500,
+ 50500,
+ 9,
+ 50501,
+ 50527,
+ 10,
+ 50528,
+ 50528,
+ 9,
+ 50529,
+ 50555,
+ 10,
+ 50556,
+ 50556,
+ 9,
+ 50557,
+ 50583,
+ 10,
+ 50584,
+ 50584,
+ 9,
+ 50585,
+ 50611,
+ 10,
+ 50612,
+ 50612,
+ 9,
+ 50613,
+ 50639,
+ 10,
+ 50640,
+ 50640,
+ 9,
+ 50641,
+ 50667,
+ 10,
+ 50668,
+ 50668,
+ 9,
+ 50669,
+ 50695,
+ 10,
+ 50696,
+ 50696,
+ 9,
+ 50697,
+ 50723,
+ 10,
+ 50724,
+ 50724,
+ 9,
+ 50725,
+ 50751,
+ 10,
+ 50752,
+ 50752,
+ 9,
+ 50753,
+ 50779,
+ 10,
+ 50780,
+ 50780,
+ 9,
+ 50781,
+ 50807,
+ 10,
+ 50808,
+ 50808,
+ 9,
+ 50809,
+ 50835,
+ 10,
+ 50836,
+ 50836,
+ 9,
+ 50837,
+ 50863,
+ 10,
+ 50864,
+ 50864,
+ 9,
+ 50865,
+ 50891,
+ 10,
+ 50892,
+ 50892,
+ 9,
+ 50893,
+ 50919,
+ 10,
+ 50920,
+ 50920,
+ 9,
+ 50921,
+ 50947,
+ 10,
+ 50948,
+ 50948,
+ 9,
+ 50949,
+ 50975,
+ 10,
+ 50976,
+ 50976,
+ 9,
+ 50977,
+ 51003,
+ 10,
+ 51004,
+ 51004,
+ 9,
+ 51005,
+ 51031,
+ 10,
+ 51032,
+ 51032,
+ 9,
+ 51033,
+ 51059,
+ 10,
+ 51060,
+ 51060,
+ 9,
+ 51061,
+ 51087,
+ 10,
+ 51088,
+ 51088,
+ 9,
+ 51089,
+ 51115,
+ 10,
+ 51116,
+ 51116,
+ 9,
+ 51117,
+ 51143,
+ 10,
+ 51144,
+ 51144,
+ 9,
+ 51145,
+ 51171,
+ 10,
+ 51172,
+ 51172,
+ 9,
+ 51173,
+ 51199,
+ 10,
+ 51200,
+ 51200,
+ 9,
+ 51201,
+ 51227,
+ 10,
+ 51228,
+ 51228,
+ 9,
+ 51229,
+ 51255,
+ 10,
+ 51256,
+ 51256,
+ 9,
+ 51257,
+ 51283,
+ 10,
+ 51284,
+ 51284,
+ 9,
+ 51285,
+ 51311,
+ 10,
+ 51312,
+ 51312,
+ 9,
+ 51313,
+ 51339,
+ 10,
+ 51340,
+ 51340,
+ 9,
+ 51341,
+ 51367,
+ 10,
+ 51368,
+ 51368,
+ 9,
+ 51369,
+ 51395,
+ 10,
+ 51396,
+ 51396,
+ 9,
+ 51397,
+ 51423,
+ 10,
+ 51424,
+ 51424,
+ 9,
+ 51425,
+ 51451,
+ 10,
+ 51452,
+ 51452,
+ 9,
+ 51453,
+ 51479,
+ 10,
+ 51480,
+ 51480,
+ 9,
+ 51481,
+ 51507,
+ 10,
+ 51508,
+ 51508,
+ 9,
+ 51509,
+ 51535,
+ 10,
+ 51536,
+ 51536,
+ 9,
+ 51537,
+ 51563,
+ 10,
+ 51564,
+ 51564,
+ 9,
+ 51565,
+ 51591,
+ 10,
+ 51592,
+ 51592,
+ 9,
+ 51593,
+ 51619,
+ 10,
+ 51620,
+ 51620,
+ 9,
+ 51621,
+ 51647,
+ 10,
+ 51648,
+ 51648,
+ 9,
+ 51649,
+ 51675,
+ 10,
+ 51676,
+ 51676,
+ 9,
+ 51677,
+ 51703,
+ 10,
+ 51704,
+ 51704,
+ 9,
+ 51705,
+ 51731,
+ 10,
+ 51732,
+ 51732,
+ 9,
+ 51733,
+ 51759,
+ 10,
+ 51760,
+ 51760,
+ 9,
+ 51761,
+ 51787,
+ 10,
+ 51788,
+ 51788,
+ 9,
+ 51789,
+ 51815,
+ 10,
+ 51816,
+ 51816,
+ 9,
+ 51817,
+ 51843,
+ 10,
+ 51844,
+ 51844,
+ 9,
+ 51845,
+ 51871,
+ 10,
+ 51872,
+ 51872,
+ 9,
+ 51873,
+ 51899,
+ 10,
+ 51900,
+ 51900,
+ 9,
+ 51901,
+ 51927,
+ 10,
+ 51928,
+ 51928,
+ 9,
+ 51929,
+ 51955,
+ 10,
+ 51956,
+ 51956,
+ 9,
+ 51957,
+ 51983,
+ 10,
+ 51984,
+ 51984,
+ 9,
+ 51985,
+ 52011,
+ 10,
+ 52012,
+ 52012,
+ 9,
+ 52013,
+ 52039,
+ 10,
+ 52040,
+ 52040,
+ 9,
+ 52041,
+ 52067,
+ 10,
+ 52068,
+ 52068,
+ 9,
+ 52069,
+ 52095,
+ 10,
+ 52096,
+ 52096,
+ 9,
+ 52097,
+ 52123,
+ 10,
+ 52124,
+ 52124,
+ 9,
+ 52125,
+ 52151,
+ 10,
+ 52152,
+ 52152,
+ 9,
+ 52153,
+ 52179,
+ 10,
+ 52180,
+ 52180,
+ 9,
+ 52181,
+ 52207,
+ 10,
+ 52208,
+ 52208,
+ 9,
+ 52209,
+ 52235,
+ 10,
+ 52236,
+ 52236,
+ 9,
+ 52237,
+ 52263,
+ 10,
+ 52264,
+ 52264,
+ 9,
+ 52265,
+ 52291,
+ 10,
+ 52292,
+ 52292,
+ 9,
+ 52293,
+ 52319,
+ 10,
+ 52320,
+ 52320,
+ 9,
+ 52321,
+ 52347,
+ 10,
+ 52348,
+ 52348,
+ 9,
+ 52349,
+ 52375,
+ 10,
+ 52376,
+ 52376,
+ 9,
+ 52377,
+ 52403,
+ 10,
+ 52404,
+ 52404,
+ 9,
+ 52405,
+ 52431,
+ 10,
+ 52432,
+ 52432,
+ 9,
+ 52433,
+ 52459,
+ 10,
+ 52460,
+ 52460,
+ 9,
+ 52461,
+ 52487,
+ 10,
+ 52488,
+ 52488,
+ 9,
+ 52489,
+ 52515,
+ 10,
+ 52516,
+ 52516,
+ 9,
+ 52517,
+ 52543,
+ 10,
+ 52544,
+ 52544,
+ 9,
+ 52545,
+ 52571,
+ 10,
+ 52572,
+ 52572,
+ 9,
+ 52573,
+ 52599,
+ 10,
+ 52600,
+ 52600,
+ 9,
+ 52601,
+ 52627,
+ 10,
+ 52628,
+ 52628,
+ 9,
+ 52629,
+ 52655,
+ 10,
+ 52656,
+ 52656,
+ 9,
+ 52657,
+ 52683,
+ 10,
+ 52684,
+ 52684,
+ 9,
+ 52685,
+ 52711,
+ 10,
+ 52712,
+ 52712,
+ 9,
+ 52713,
+ 52739,
+ 10,
+ 52740,
+ 52740,
+ 9,
+ 52741,
+ 52767,
+ 10,
+ 52768,
+ 52768,
+ 9,
+ 52769,
+ 52795,
+ 10,
+ 52796,
+ 52796,
+ 9,
+ 52797,
+ 52823,
+ 10,
+ 52824,
+ 52824,
+ 9,
+ 52825,
+ 52851,
+ 10,
+ 52852,
+ 52852,
+ 9,
+ 52853,
+ 52879,
+ 10,
+ 52880,
+ 52880,
+ 9,
+ 52881,
+ 52907,
+ 10,
+ 52908,
+ 52908,
+ 9,
+ 52909,
+ 52935,
+ 10,
+ 52936,
+ 52936,
+ 9,
+ 52937,
+ 52963,
+ 10,
+ 52964,
+ 52964,
+ 9,
+ 52965,
+ 52991,
+ 10,
+ 52992,
+ 52992,
+ 9,
+ 52993,
+ 53019,
+ 10,
+ 53020,
+ 53020,
+ 9,
+ 53021,
+ 53047,
+ 10,
+ 53048,
+ 53048,
+ 9,
+ 53049,
+ 53075,
+ 10,
+ 53076,
+ 53076,
+ 9,
+ 53077,
+ 53103,
+ 10,
+ 53104,
+ 53104,
+ 9,
+ 53105,
+ 53131,
+ 10,
+ 53132,
+ 53132,
+ 9,
+ 53133,
+ 53159,
+ 10,
+ 53160,
+ 53160,
+ 9,
+ 53161,
+ 53187,
+ 10,
+ 53188,
+ 53188,
+ 9,
+ 53189,
+ 53215,
+ 10,
+ 53216,
+ 53216,
+ 9,
+ 53217,
+ 53243,
+ 10,
+ 53244,
+ 53244,
+ 9,
+ 53245,
+ 53271,
+ 10,
+ 53272,
+ 53272,
+ 9,
+ 53273,
+ 53299,
+ 10,
+ 53300,
+ 53300,
+ 9,
+ 53301,
+ 53327,
+ 10,
+ 53328,
+ 53328,
+ 9,
+ 53329,
+ 53355,
+ 10,
+ 53356,
+ 53356,
+ 9,
+ 53357,
+ 53383,
+ 10,
+ 53384,
+ 53384,
+ 9,
+ 53385,
+ 53411,
+ 10,
+ 53412,
+ 53412,
+ 9,
+ 53413,
+ 53439,
+ 10,
+ 53440,
+ 53440,
+ 9,
+ 53441,
+ 53467,
+ 10,
+ 53468,
+ 53468,
+ 9,
+ 53469,
+ 53495,
+ 10,
+ 53496,
+ 53496,
+ 9,
+ 53497,
+ 53523,
+ 10,
+ 53524,
+ 53524,
+ 9,
+ 53525,
+ 53551,
+ 10,
+ 53552,
+ 53552,
+ 9,
+ 53553,
+ 53579,
+ 10,
+ 53580,
+ 53580,
+ 9,
+ 53581,
+ 53607,
+ 10,
+ 53608,
+ 53608,
+ 9,
+ 53609,
+ 53635,
+ 10,
+ 53636,
+ 53636,
+ 9,
+ 53637,
+ 53663,
+ 10,
+ 53664,
+ 53664,
+ 9,
+ 53665,
+ 53691,
+ 10,
+ 53692,
+ 53692,
+ 9,
+ 53693,
+ 53719,
+ 10,
+ 53720,
+ 53720,
+ 9,
+ 53721,
+ 53747,
+ 10,
+ 53748,
+ 53748,
+ 9,
+ 53749,
+ 53775,
+ 10,
+ 53776,
+ 53776,
+ 9,
+ 53777,
+ 53803,
+ 10,
+ 53804,
+ 53804,
+ 9,
+ 53805,
+ 53831,
+ 10,
+ 53832,
+ 53832,
+ 9,
+ 53833,
+ 53859,
+ 10,
+ 53860,
+ 53860,
+ 9,
+ 53861,
+ 53887,
+ 10,
+ 53888,
+ 53888,
+ 9,
+ 53889,
+ 53915,
+ 10,
+ 53916,
+ 53916,
+ 9,
+ 53917,
+ 53943,
+ 10,
+ 53944,
+ 53944,
+ 9,
+ 53945,
+ 53971,
+ 10,
+ 53972,
+ 53972,
+ 9,
+ 53973,
+ 53999,
+ 10,
+ 54000,
+ 54000,
+ 9,
+ 54001,
+ 54027,
+ 10,
+ 54028,
+ 54028,
+ 9,
+ 54029,
+ 54055,
+ 10,
+ 54056,
+ 54056,
+ 9,
+ 54057,
+ 54083,
+ 10,
+ 54084,
+ 54084,
+ 9,
+ 54085,
+ 54111,
+ 10,
+ 54112,
+ 54112,
+ 9,
+ 54113,
+ 54139,
+ 10,
+ 54140,
+ 54140,
+ 9,
+ 54141,
+ 54167,
+ 10,
+ 54168,
+ 54168,
+ 9,
+ 54169,
+ 54195,
+ 10,
+ 54196,
+ 54196,
+ 9,
+ 54197,
+ 54223,
+ 10,
+ 54224,
+ 54224,
+ 9,
+ 54225,
+ 54251,
+ 10,
+ 54252,
+ 54252,
+ 9,
+ 54253,
+ 54279,
+ 10,
+ 54280,
+ 54280,
+ 9,
+ 54281,
+ 54307,
+ 10,
+ 54308,
+ 54308,
+ 9,
+ 54309,
+ 54335,
+ 10,
+ 54336,
+ 54336,
+ 9,
+ 54337,
+ 54363,
+ 10,
+ 54364,
+ 54364,
+ 9,
+ 54365,
+ 54391,
+ 10,
+ 54392,
+ 54392,
+ 9,
+ 54393,
+ 54419,
+ 10,
+ 54420,
+ 54420,
+ 9,
+ 54421,
+ 54447,
+ 10,
+ 54448,
+ 54448,
+ 9,
+ 54449,
+ 54475,
+ 10,
+ 54476,
+ 54476,
+ 9,
+ 54477,
+ 54503,
+ 10,
+ 54504,
+ 54504,
+ 9,
+ 54505,
+ 54531,
+ 10,
+ 54532,
+ 54532,
+ 9,
+ 54533,
+ 54559,
+ 10,
+ 54560,
+ 54560,
+ 9,
+ 54561,
+ 54587,
+ 10,
+ 54588,
+ 54588,
+ 9,
+ 54589,
+ 54615,
+ 10,
+ 54616,
+ 54616,
+ 9,
+ 54617,
+ 54643,
+ 10,
+ 54644,
+ 54644,
+ 9,
+ 54645,
+ 54671,
+ 10,
+ 54672,
+ 54672,
+ 9,
+ 54673,
+ 54699,
+ 10,
+ 54700,
+ 54700,
+ 9,
+ 54701,
+ 54727,
+ 10,
+ 54728,
+ 54728,
+ 9,
+ 54729,
+ 54755,
+ 10,
+ 54756,
+ 54756,
+ 9,
+ 54757,
+ 54783,
+ 10,
+ 54784,
+ 54784,
+ 9,
+ 54785,
+ 54811,
+ 10,
+ 54812,
+ 54812,
+ 9,
+ 54813,
+ 54839,
+ 10,
+ 54840,
+ 54840,
+ 9,
+ 54841,
+ 54867,
+ 10,
+ 54868,
+ 54868,
+ 9,
+ 54869,
+ 54895,
+ 10,
+ 54896,
+ 54896,
+ 9,
+ 54897,
+ 54923,
+ 10,
+ 54924,
+ 54924,
+ 9,
+ 54925,
+ 54951,
+ 10,
+ 54952,
+ 54952,
+ 9,
+ 54953,
+ 54979,
+ 10,
+ 54980,
+ 54980,
+ 9,
+ 54981,
+ 55007,
+ 10,
+ 55008,
+ 55008,
+ 9,
+ 55009,
+ 55035,
+ 10,
+ 55036,
+ 55036,
+ 9,
+ 55037,
+ 55063,
+ 10,
+ 55064,
+ 55064,
+ 9,
+ 55065,
+ 55091,
+ 10,
+ 55092,
+ 55092,
+ 9,
+ 55093,
+ 55119,
+ 10,
+ 55120,
+ 55120,
+ 9,
+ 55121,
+ 55147,
+ 10,
+ 55148,
+ 55148,
+ 9,
+ 55149,
+ 55175,
+ 10,
+ 55176,
+ 55176,
+ 9,
+ 55177,
+ 55203,
+ 10,
+ 55216,
+ 55238,
+ 7,
+ 55243,
+ 55291,
+ 8,
+ 55296,
+ 57343,
+ 3,
+ 57344,
+ 57344,
+ 3,
+ 57344,
+ 57344,
+ 3,
+ 64286,
+ 64286,
+ 4,
+ 65024,
+ 65039,
+ 4,
+ 65056,
+ 65069,
+ 4,
+ 65279,
+ 65279,
+ 3,
+ 65438,
+ 65439,
+ 4,
+ 65520,
+ 65528,
+ 3,
+ 65529,
+ 65531,
+ 3
];
diff --git a/packages/charted/lib/core/text_metrics/segmentation_utils.dart b/packages/charted/lib/core/text_metrics/segmentation_utils.dart
index d3e724c..31d823c 100644
--- a/packages/charted/lib/core/text_metrics/segmentation_utils.dart
+++ b/packages/charted/lib/core/text_metrics/segmentation_utils.dart
@@ -10,7 +10,7 @@
const CODE_CATEGORY_OTHER = 0;
-const CodeUnitCategory = const{
+const CodeUnitCategory = const {
'Other': CODE_CATEGORY_OTHER,
'CR': 1,
'LF': 2,
diff --git a/packages/charted/lib/core/time_interval.dart b/packages/charted/lib/core/time_interval.dart
index 6385312..3f7119e 100644
--- a/packages/charted/lib/core/time_interval.dart
+++ b/packages/charted/lib/core/time_interval.dart
@@ -22,14 +22,13 @@
DateTime floor(dynamic date) {
assert(date is int || date is DateTime);
if (date is int) {
- date = new DateTime.fromMillisecondsSinceEpoch(date) ;
+ date = new DateTime.fromMillisecondsSinceEpoch(date);
}
return _floor(date);
}
DateTime round(dynamic date) {
- DateTime d0 = floor(date),
- d1 = offset(d0, 1);
+ DateTime d0 = floor(date), d1 = offset(d0, 1);
int ms = date is int ? date : date.millisecondsSinceEpoch;
return (ms - d0.millisecondsSinceEpoch < d1.millisecondsSinceEpoch - ms)
? d0
@@ -53,18 +52,15 @@
if (dt > 1) {
while (time.isBefore(t1)) {
if ((_number(time) % dt) == 0) {
- values.add(
- new DateTime.fromMillisecondsSinceEpoch(
- time.millisecondsSinceEpoch));
+ values.add(new DateTime.fromMillisecondsSinceEpoch(
+ time.millisecondsSinceEpoch));
}
time = _step(time, 1);
}
- }
- else {
+ } else {
while (time.isBefore(t1)) {
- values.add(
- new DateTime.fromMillisecondsSinceEpoch(
- time.millisecondsSinceEpoch));
+ values.add(new DateTime.fromMillisecondsSinceEpoch(
+ time.millisecondsSinceEpoch));
time = _step(time, 1);
}
}
@@ -72,63 +68,78 @@
}
static TimeInterval second = new TimeInterval(
- (DateTime date) =>
- new DateTime.fromMillisecondsSinceEpoch(
- (date.millisecondsSinceEpoch ~/ 1000) * 1000),
+ (DateTime date) => new DateTime.fromMillisecondsSinceEpoch(
+ (date.millisecondsSinceEpoch ~/ 1000) * 1000),
(DateTime date, int offset) =>
date = new DateTime.fromMillisecondsSinceEpoch(
date.millisecondsSinceEpoch + offset * 1000),
(DateTime date) => date.second);
static TimeInterval minute = new TimeInterval(
- (DateTime date) =>
- new DateTime.fromMillisecondsSinceEpoch(
- (date.millisecondsSinceEpoch ~/ 60000) * 60000),
+ (DateTime date) => new DateTime.fromMillisecondsSinceEpoch(
+ (date.millisecondsSinceEpoch ~/ 60000) * 60000),
(DateTime date, int offset) =>
date = new DateTime.fromMillisecondsSinceEpoch(
date.millisecondsSinceEpoch + offset * 60000),
(DateTime date) => date.minute);
static TimeInterval hour = new TimeInterval(
- (DateTime date) =>
- new DateTime.fromMillisecondsSinceEpoch(
- (date.millisecondsSinceEpoch ~/ 3600000) * 3600000),
+ (DateTime date) => new DateTime.fromMillisecondsSinceEpoch(
+ (date.millisecondsSinceEpoch ~/ 3600000) * 3600000),
(DateTime date, int offset) =>
date = new DateTime.fromMillisecondsSinceEpoch(
date.millisecondsSinceEpoch + offset * 3600000),
(DateTime date) => date.hour);
static TimeInterval day = new TimeInterval(
- (DateTime date) =>
- new DateTime(date.year, date.month, date.day),
- (DateTime date, int offset) =>
- new DateTime(date.year, date.month, date.day + offset,
- date.hour, date.minute, date.second, date.millisecond),
+ (DateTime date) => new DateTime(date.year, date.month, date.day),
+ (DateTime date, int offset) => new DateTime(
+ date.year,
+ date.month,
+ date.day + offset,
+ date.hour,
+ date.minute,
+ date.second,
+ date.millisecond),
(DateTime date) => date.day - 1);
static TimeInterval week = new TimeInterval(
(DateTime date) =>
new DateTime(date.year, date.month, date.day - (date.weekday % 7)),
- (DateTime date, int offset) =>
- new DateTime(date.year, date.month, date.day + offset * 7,
- date.hour, date.minute, date.second, date.millisecond ),
- (DateTime date) {
- var day = year.floor(date).day;
- return (dayOfYear(date) + day % 7) ~/ 7;
- });
+ (DateTime date, int offset) => new DateTime(
+ date.year,
+ date.month,
+ date.day + offset * 7,
+ date.hour,
+ date.minute,
+ date.second,
+ date.millisecond), (DateTime date) {
+ var day = year.floor(date).day;
+ return (dayOfYear(date) + day % 7) ~/ 7;
+ });
static TimeInterval month = new TimeInterval(
(DateTime date) => new DateTime(date.year, date.month, 1),
- (DateTime date, num offset) =>
- new DateTime(date.year, date.month + offset, date.day,
- date.hour, date.minute, date.second, date.millisecond),
+ (DateTime date, num offset) => new DateTime(
+ date.year,
+ date.month + offset,
+ date.day,
+ date.hour,
+ date.minute,
+ date.second,
+ date.millisecond),
(DateTime date) => date.month - 1);
static TimeInterval year = new TimeInterval(
(DateTime date) => new DateTime(date.year),
- (DateTime date, num offset) =>
- new DateTime(date.year + offset, date.month, date.day,
- date.hour, date.minute, date.second, date.millisecond),
+ (DateTime date, num offset) => new DateTime(
+ date.year + offset,
+ date.month,
+ date.day,
+ date.hour,
+ date.minute,
+ date.second,
+ date.millisecond),
(DateTime date) => date.year);
static int dayOfYear(DateTime date) =>
diff --git a/packages/charted/lib/core/timer.dart b/packages/charted/lib/core/timer.dart
index a166631..93856dd 100644
--- a/packages/charted/lib/core/timer.dart
+++ b/packages/charted/lib/core/timer.dart
@@ -38,10 +38,10 @@
/// Schedule a new [callback] to be called [delay] micro-seconds after
/// [then] micro-seconds since epoch.
- AnimationTimer(this.callback, { int delay: 0, int then: null })
+ AnimationTimer(this.callback, {int delay: 0, int then: null})
: time = then == null
- ? new DateTime.now().millisecondsSinceEpoch + delay
- : then + delay {
+ ? new DateTime.now().millisecondsSinceEpoch + delay
+ : then + delay {
_queue.add(this);
if (!_animationFrameRequested) {
if (_timerScheduled != null) {
@@ -59,7 +59,7 @@
int earliest = null;
AnimationTimer timer = _queue.isEmpty ? null : _queue.first;
- while(timer != null) {
+ while (timer != null) {
bool finished = false;
AnimationTimer ref = timer;
@@ -83,15 +83,13 @@
if (delay == null) {
_animationFrameRequested = false;
- }
- else if (delay > 24) {
+ } else if (delay > 24) {
if (_timerScheduled != null) {
_timerScheduled.cancel();
}
_timerScheduled = new Timer(new Duration(milliseconds: delay), _step);
_animationFrameRequested = false;
- }
- else {
+ } else {
_animationFrameRequested = true;
window.animationFrame.then(_step);
}
diff --git a/packages/charted/lib/core/utils.dart b/packages/charted/lib/core/utils.dart
index febf9e6..abbdb1b 100644
--- a/packages/charted/lib/core/utils.dart
+++ b/packages/charted/lib/core/utils.dart
@@ -26,9 +26,9 @@
part 'utils/rect.dart';
part 'utils/bidi_formatter.dart';
-const String ORIENTATION_LEFT = 'left';
-const String ORIENTATION_RIGHT = 'right';
-const String ORIENTATION_TOP = 'top';
+const String ORIENTATION_LEFT = 'left';
+const String ORIENTATION_RIGHT = 'right';
+const String ORIENTATION_TOP = 'top';
const String ORIENTATION_BOTTOM = 'bottom';
/// Identity function that returns the value passed as it's parameter.
diff --git a/packages/charted/lib/core/utils/bidi_formatter.dart b/packages/charted/lib/core/utils/bidi_formatter.dart
index 8b729ff..85df0c9 100644
--- a/packages/charted/lib/core/utils/bidi_formatter.dart
+++ b/packages/charted/lib/core/utils/bidi_formatter.dart
@@ -13,7 +13,7 @@
/// Fix direction of HTML using <span dir="..."> for RTL when required
fixMarkupDirection(String markup) =>
- _bidiFormatter.wrapWithSpan(markup, isHtml:true);
+ _bidiFormatter.wrapWithSpan(markup, isHtml: true);
/// Fix direction of text using unicode markers for RTL when required
/// This is a simplified version of BidiFormatter.wrapWithUnicode that
diff --git a/packages/charted/lib/core/utils/color.dart b/packages/charted/lib/core/utils/color.dart
index bac12ec..5912a5e 100644
--- a/packages/charted/lib/core/utils/color.dart
+++ b/packages/charted/lib/core/utils/color.dart
@@ -17,9 +17,9 @@
int _b = 0;
// Internal representation of HSL
- int _h = 0; // 0 <= _h <= 360
- int _s = 0; // 0 <= _s <= 100
- int _l = 0; // 0 <= _l <= 100
+ int _h = 0; // 0 <= _h <= 360
+ int _s = 0; // 0 <= _s <= 100
+ int _l = 0; // 0 <= _l <= 100
// Alpha value for this color
double _a = 0.0;
@@ -33,33 +33,33 @@
/// Create an instance using a string representing color in RGB color space.
/// The input string, [value], can be one of the following formats:
- ///
+ ///
/// #RGB
/// #RRGGBB
- ///
+ ///
/// rgb(R, G, B)
/// rgba(R, G, B, A)
- ///
+ ///
/// R, G and B represent intensities of Red, Green and Blue channels and A
/// represents the alpha channel (transparency)
- ///
+ ///
/// When using these formats:
/// 0 <= R,G,B <= 255
/// 0 <= A <= 1.0
factory Color.fromRgbString(String value) =>
- isHexColorString(value) ? _fromHexString(value) : _fromRgbString(value);
-
+ isHexColorString(value) ? _fromHexString(value) : _fromRgbString(value);
+
/// Create an instance from HSL colors.
Color.fromHsla(this._h, this._s, this._l, this._a) : _hasHslColors = true;
/// Create an instance using a string representing color in HSL color space.
/// The input string, [value], can be in one of the following formats:
- ///
+ ///
/// hsl(H, S%, L%)
/// hsla(H, S%, L%, A)
- ///
+ ///
/// H, S and L represent Hue, Saturation and Luminosity respectively.
- ///
+ ///
/// When using these formats:
/// 0 <= H <= 360
/// 0 <= S,L <= 100
@@ -249,12 +249,12 @@
/// Tests if [str] is a color represented by hsl() or hsla()
static bool isHslColorString(String str) => hslaColorRegExp.hasMatch(str);
-
+
/// Create an instance using the passed RGB string.
static Color _fromRgbString(String value) {
- int pos =
- (value.startsWith('rgb(') || value.startsWith('RGB(')) ? 4 :
- (value.startsWith('rgba(') || value.startsWith('RGBA(')) ? 5 : 0;
+ int pos = (value.startsWith('rgb(') || value.startsWith('RGB('))
+ ? 4
+ : (value.startsWith('rgba(') || value.startsWith('RGBA(')) ? 5 : 0;
if (pos != 0) {
final params = value.substring(pos, value.length - 1).split(',');
int r = int.parse(params[0]),
@@ -278,10 +278,9 @@
if (hex.length == 3) {
for (final char in hex) {
final val = int.parse(char, radix: 16);
- rgb = (rgb * 16 + val ) * 16 + val;
+ rgb = (rgb * 16 + val) * 16 + val;
}
- }
- else if (hex.length == 6){
+ } else if (hex.length == 6) {
rgb = int.parse(hex, radix: 16);
}
@@ -291,9 +290,9 @@
/// Create an instance using the passed HSL color string.
static Color _fromHslString(String value) {
- int pos =
- (value.startsWith('hsl(') || value.startsWith('HSL(')) ? 4 :
- (value.startsWith('hsla(') || value.startsWith('HSLA(')) ? 5 : 0;
+ int pos = (value.startsWith('hsl(') || value.startsWith('HSL('))
+ ? 4
+ : (value.startsWith('hsla(') || value.startsWith('HSLA(')) ? 5 : 0;
if (pos != 0) {
final params = value.substring(pos, value.length - 1).split(',');
int h = int.parse(params[0]),
diff --git a/packages/charted/lib/core/utils/lists.dart b/packages/charted/lib/core/utils/lists.dart
index d9633f9..3a5b05c 100644
--- a/packages/charted/lib/core/utils/lists.dart
+++ b/packages/charted/lib/core/utils/lists.dart
@@ -9,30 +9,30 @@
part of charted.core.utils;
/// Returns a sum of all values in the given list of values
-num sum(List values) =>
- values == null || values.isEmpty ?
- 0: values.fold(0.0, (old, next) => old + next);
+num sum(List values) => values == null || values.isEmpty
+ ? 0
+ : values.fold(0.0, (old, next) => old + next);
/// Returns the smallest number in the given list of values
-num min(Iterable values) =>
- values == null || values.isEmpty ?
- null : values.fold(values.elementAt(0), math.min);
+num min(Iterable values) => values == null || values.isEmpty
+ ? null
+ : values.fold(values.elementAt(0), math.min);
/// Returns the largest number in the given list of values
-num max(Iterable values) =>
- values == null || values.isEmpty ?
- null : values.fold(values.elementAt(0), math.max);
+num max(Iterable values) => values == null || values.isEmpty
+ ? null
+ : values.fold(values.elementAt(0), math.max);
/// Represents a constant pair of values
class Pair<T1, T2> {
final T1 first;
final T2 last;
-
+
const Pair(this.first, this.last);
-
- bool operator==(other) =>
+
+ bool operator ==(other) =>
other is Pair && first == other.first && last == other.last;
-
+
int get hashCode => hash2(first, last);
}
@@ -42,10 +42,9 @@
final T max;
factory Extent.items(Iterable<T> items,
- [ Comparator compare = Comparable.compare ]) {
+ [Comparator compare = Comparable.compare]) {
if (items.length == 0) return new Extent(null, null);
- var max = items.first,
- min = items.first;
+ var max = items.first, min = items.first;
for (var value in items) {
if (compare(max, value) < 0) max = value;
if (compare(min, value) > 0) min = value;
@@ -53,7 +52,10 @@
return new Extent(min, max);
}
- const Extent(T min, T max) : min = min, max = max, super(min, max);
+ const Extent(T min, T max)
+ : min = min,
+ max = max,
+ super(min, max);
}
/// Iterable representing a range of values containing the start, stop
@@ -62,13 +64,13 @@
final num start;
final num stop;
final num step;
-
+
factory Range.integers(num start, [num stop, num step = 1]) =>
new Range(start, stop, step, true);
factory Range(num start, [num stop, num step = 1, bool integers = false]) {
List<num> values = <num>[];
-
+
if (stop == null) {
stop = start;
start = 0;
@@ -78,9 +80,7 @@
throw new ArgumentError('Invalid range.');
}
- var k = _integerConversionFactor(step.abs()),
- i = -1,
- j;
+ var k = _integerConversionFactor(step.abs()), i = -1, j;
start *= k;
stop *= k;
@@ -95,10 +95,10 @@
values.add(integers ? j ~/ k : j / k);
}
}
-
+
return new Range._internal(start, stop, step, values);
}
-
+
Range._internal(this.start, this.stop, this.step, List values)
: super(values);
diff --git a/packages/charted/lib/core/utils/namespace.dart b/packages/charted/lib/core/utils/namespace.dart
index 7d1d8d4..00af84c 100644
--- a/packages/charted/lib/core/utils/namespace.dart
+++ b/packages/charted/lib/core/utils/namespace.dart
@@ -13,7 +13,7 @@
/// namespace.
class Namespace {
/// Supported namespace prefixes mapped to their URIs.
- static const Map<String,String> NS_PREFIXES = const {
+ static const Map<String, String> NS_PREFIXES = const {
"svg": "http://www.w3.org/2000/svg",
"xhtml": "http://www.w3.org/1999/xhtml",
"xlink": "http://www.w3.org/1999/xlink",
@@ -30,10 +30,10 @@
return parent.ownerDocument.createElementNS(parent.namespaceUri, tag);
}
Namespace parsed = new Namespace._internal(tag, separatorIndex);
- return parsed.namespaceUri == null ?
- parent.ownerDocument.createElementNS(parent.namespaceUri, tag) :
- parent.ownerDocument.createElementNS(parsed.namespaceUri,
- parsed.localName);
+ return parsed.namespaceUri == null
+ ? parent.ownerDocument.createElementNS(parent.namespaceUri, tag)
+ : parent.ownerDocument
+ .createElementNS(parsed.namespaceUri, parsed.localName);
}
/// Local part of the Element's tag name.
diff --git a/packages/charted/lib/core/utils/object_factory.dart b/packages/charted/lib/core/utils/object_factory.dart
index 7ddab07..465ac18 100644
--- a/packages/charted/lib/core/utils/object_factory.dart
+++ b/packages/charted/lib/core/utils/object_factory.dart
@@ -30,8 +30,7 @@
if (!_components.containsKey(name)) {
throw new ArgumentError('Element $name not found in ComponentFactory');
}
- var creator = _components[name],
- instance = creator();
+ var creator = _components[name], instance = creator();
if (instance == null) {
throw new ArgumentError('Component $name initialization failed.');
}
diff --git a/packages/charted/lib/core/utils/rect.dart b/packages/charted/lib/core/utils/rect.dart
index 628a4b5..3e666ee 100644
--- a/packages/charted/lib/core/utils/rect.dart
+++ b/packages/charted/lib/core/utils/rect.dart
@@ -16,11 +16,12 @@
final num height;
const Rect([this.x = 0, this.y = 0, this.width = 0, this.height = 0]);
- const Rect.size(this.width, this.height) : x = 0, y = 0;
- const Rect.position(this.x, this.y) : width = 0, height = 0;
-
- bool operator==(other) =>
- other is Rect && isSameSizeAs(other) && isSamePositionAs(other);
+ const Rect.size(this.width, this.height)
+ : x = 0,
+ y = 0;
+ const Rect.position(this.x, this.y)
+ : width = 0,
+ height = 0;
bool isSameSizeAs(Rect other) =>
other != null && width == other.width && height == other.height;
@@ -29,10 +30,16 @@
other != null && x == other.x && y == other.y;
bool contains(num otherX, num otherY) =>
- otherX >= x && otherX <= x + width &&
- otherY >= y && otherY <= y + height;
+ otherX >= x && otherX <= x + width && otherY >= y && otherY <= y + height;
String toString() => '$x, $y, $width, $height';
+
+ @override
+ bool operator ==(other) =>
+ other is Rect && isSameSizeAs(other) && isSamePositionAs(other);
+
+ @override
+ int get hashCode => hash4(x, y, width, height);
}
/// Mutable version of [Rect] class.
@@ -55,8 +62,13 @@
const AbsoluteRect(this.top, this.end, this.bottom, this.start);
- bool operator==(other) =>
- other is AbsoluteRect &&
- start == other.start && end == other.end &&
- top == other.top && bottom == other.bottom;
-}
\ No newline at end of file
+ @override
+ bool operator ==(other) => other is AbsoluteRect &&
+ start == other.start &&
+ end == other.end &&
+ top == other.top &&
+ bottom == other.bottom;
+
+ @override
+ int get hashCode => hash4(start, end, top, bottom);
+}
diff --git a/packages/charted/lib/layout/src/hierarchy_layout.dart b/packages/charted/lib/layout/src/hierarchy_layout.dart
index 81cbcbc..9997ab0 100644
--- a/packages/charted/lib/layout/src/hierarchy_layout.dart
+++ b/packages/charted/lib/layout/src/hierarchy_layout.dart
@@ -26,8 +26,8 @@
/// parentColumn and valueColumn which is used to construct the hierarchy.
/// The returned list of nodes contains the hierarchy with root being the
/// first element its children in depth first order.
- List<T> layout(List rows, int parentColumn, int labelColumn,
- int valueColumn) {
+ List<T> layout(
+ List rows, int parentColumn, int labelColumn, int valueColumn) {
List<HierarchyNode> nodeList = [];
for (var row in rows) {
nodeList.add(createNode(row[labelColumn], row[valueColumn], 0));
@@ -38,8 +38,9 @@
if (parentRow == ROOT_ROW_INDEX) continue;
var currentNode = nodeList[i];
var parentNode = nodeList[parentRow];
- (parentNode.children.isEmpty) ? parentNode.children = [currentNode] :
- parentNode.children.add(currentNode);
+ (parentNode.children.isEmpty)
+ ? parentNode.children = [currentNode]
+ : parentNode.children.add(currentNode);
currentNode.parent = parentNode;
currentNode.depth = parentNode.depth + 1;
for (var child in currentNode.children) {
@@ -72,7 +73,6 @@
/// Default sorting method for comparing node a and b.
static int hierarchySort(HierarchyNode a, HierarchyNode b) =>
b.value - a.value;
-
}
abstract class HierarchyNode {
diff --git a/packages/charted/lib/layout/src/pie_layout.dart b/packages/charted/lib/layout/src/pie_layout.dart
index 6d3382a..bb316af 100644
--- a/packages/charted/lib/layout/src/pie_layout.dart
+++ b/packages/charted/lib/layout/src/pie_layout.dart
@@ -43,8 +43,8 @@
* arcs in a pie-chart or donut-chart.
*/
List layout(List data, [int ei, Element e]) {
- var values = new List.generate(data.length,
- (int i) => accessor(data[i], i)),
+ var values =
+ new List.generate(data.length, (int i) => accessor(data[i], i)),
startAngle = startAngleCallback(data, ei, e),
endAngle = endAngleCallback(data, ei, e),
total = sum(values),
@@ -67,12 +67,10 @@
}
/** Sets a constant value to start angle of the layout */
- set startAngle(num value) =>
- startAngleCallback = toCallback(value);
+ set startAngle(num value) => startAngleCallback = toCallback(value);
/** Sets a constant value to end angle of the layout */
- set endAngle(num value) =>
- endAngleCallback = toCallback(value);
+ set endAngle(num value) => endAngleCallback = toCallback(value);
/** Default value accessor */
static num defaultValueAccessor(num d, i) => d;
diff --git a/packages/charted/lib/layout/src/treemap_layout.dart b/packages/charted/lib/layout/src/treemap_layout.dart
index 8f118ba..601d0d4 100644
--- a/packages/charted/lib/layout/src/treemap_layout.dart
+++ b/packages/charted/lib/layout/src/treemap_layout.dart
@@ -20,7 +20,7 @@
static const TREEMAP_LAYOUT_SQUARIFY = 0;
/// Horizontal subdivision.
- static const TREEMAP_LAYOUT_SLICE= 1;
+ static const TREEMAP_LAYOUT_SLICE = 1;
/// Vertical subdivision.
static const TREEMAP_LAYOUT_DICE = 2;
@@ -49,14 +49,14 @@
/// TODO(midoringo): Implement sticky related feature.
get sticky => _sticky;
- set sticky (bool sticky) {
+ set sticky(bool sticky) {
_sticky = sticky;
}
// TODO (midoringo): handle the sticky case.
@override
- List<TreeMapNode> layout(List rows, int parentColumn, int labelColumn,
- int valueColumn) {
+ List<TreeMapNode> layout(
+ List rows, int parentColumn, int labelColumn, int valueColumn) {
var nodes = super.layout(rows, parentColumn, labelColumn, valueColumn);
var root = nodes[0];
root.x = 0;
@@ -71,9 +71,9 @@
@override
TreeMapNode createNode(label, value, depth) {
return new TreeMapNode()
- ..label = label
- ..value = value
- ..depth = depth;
+ ..label = label
+ ..value = value
+ ..depth = depth;
}
void _position(List<TreeMapNode> nodes, num length, MutableRect rect,
@@ -87,8 +87,8 @@
node.x = x;
node.y = y;
node.dy = v;
- x += node.dx = math.min(rect.x + rect.width - x, v > 0 ?
- (node.area / v).round() : 0);
+ x += node.dx = math.min(
+ rect.x + rect.width - x, v > 0 ? (node.area / v).round() : 0);
}
nodes.last.sticky = true;
nodes.last.dx += rect.x + rect.width - x;
@@ -100,8 +100,8 @@
node.x = x;
node.y = y;
node.dx = v;
- y += node.dy = math.min(rect.y + rect.height - y, v > 0 ?
- (node.area / v).round() : 0);
+ y += node.dy = math.min(
+ rect.y + rect.height - y, v > 0 ? (node.area / v).round() : 0);
}
nodes.last.sticky = false;
nodes.last.dy += rect.y + rect.height - y;
@@ -149,8 +149,10 @@
}
pArea *= pArea;
length *= length;
- return (pArea > 0) ? math.max(length * rmax * ratio / pArea,
- pArea / (length * rmin * ratio)) : double.INFINITY;
+ return (pArea > 0)
+ ? math.max(
+ length * rmax * ratio / pArea, pArea / (length * rmin * ratio))
+ : double.INFINITY;
}
/// Recursively compute each nodes (and its children nodes) position and size
@@ -162,12 +164,16 @@
List<TreeMapNode> nodes = [];
var area = 0;
var remaining = new List.from(children);
- var score, n,
- best = double.INFINITY,
- length = (mode == TREEMAP_LAYOUT_SLICE) ? rect.width :
- (mode == TREEMAP_LAYOUT_DICE) ? rect.height :
- (mode == TREEMAP_LAYOUT_SLICE_DICE) ? (node.depth & 1 == 1) ?
- rect.height : rect.width : math.min(rect.width, rect.height);
+ var score,
+ n,
+ best = double.INFINITY,
+ length = (mode == TREEMAP_LAYOUT_SLICE)
+ ? rect.width
+ : (mode == TREEMAP_LAYOUT_DICE)
+ ? rect.height
+ : (mode == TREEMAP_LAYOUT_SLICE_DICE)
+ ? (node.depth & 1 == 1) ? rect.height : rect.width
+ : math.min(rect.width, rect.height);
_scale(remaining, rect.width * rect.height / node.value);
while ((n = remaining.length) > 0) {
var child = remaining[n - 1];
diff --git a/packages/charted/lib/locale/format.dart b/packages/charted/lib/locale/format.dart
index 64b4396..a61c405 100644
--- a/packages/charted/lib/locale/format.dart
+++ b/packages/charted/lib/locale/format.dart
@@ -35,14 +35,30 @@
return locale.numberFormat.format(specifier);
}
-
/*
* Class for computing the SI format prefix for the given value.
*/
class FormatPrefix {
// SI scale units in increments of 1000.
- static const List unitPrefixes = const
- ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
+ static const List unitPrefixes = const [
+ "y",
+ "z",
+ "a",
+ "f",
+ "p",
+ "n",
+ "µ",
+ "m",
+ "",
+ "k",
+ "M",
+ "G",
+ "T",
+ "P",
+ "E",
+ "Z",
+ "Y"
+ ];
Function _scale;
String _symbol;
@@ -58,8 +74,7 @@
// Determining SI scale of the value in increment of 1000.
i = 1 + (1e-12 + math.log(value) / math.LN10).floor();
- i = math.max(-24, math.min(24,
- ((i - 1) / 3).floor() * 3));
+ i = math.max(-24, math.min(24, ((i - 1) / 3).floor() * 3));
i = 8 + (i / 3).floor();
// Sets the scale and symbol of the value.
@@ -74,8 +89,7 @@
/** Returns the value of x rounded to the nth digit. */
_roundToPrecision(num x, num n) {
- return n != 0 ?
- (x * (n = math.pow(10, n))).round() / n : x.round();
+ return n != 0 ? (x * (n = math.pow(10, n))).round() / n : x.round();
}
/** Returns the SI prefix for the value. */
diff --git a/packages/charted/lib/locale/format/number_format.dart b/packages/charted/lib/locale/format/number_format.dart
index d1c97a7..5761e2b 100644
--- a/packages/charted/lib/locale/format/number_format.dart
+++ b/packages/charted/lib/locale/format/number_format.dart
@@ -14,11 +14,11 @@
* specifier with the number properties of the locale.
*/
class NumberFormat {
-
// [[fill]align][sign][symbol][0][width][,][.precision][type]
- static RegExp FORMAT_REGEX =
- new RegExp(r'(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?'
- r'(\.-?\d+)?([a-z%])?', caseSensitive: false);
+ static RegExp FORMAT_REGEX = new RegExp(
+ r'(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?'
+ r'(\.-?\d+)?([a-z%])?',
+ caseSensitive: false);
String localeDecimal;
String localeThousands;
@@ -31,24 +31,23 @@
localeThousands = locale.thousands;
localeGrouping = locale.grouping;
localeCurrency = locale.currency;
- formatGroup = (localeGrouping != null) ? (value) {
- var i = value.length,
- t = [],
- j = 0,
- g = localeGrouping[0];
- while (i > 0 && g > 0) {
- if (i - g >= 0) {
- i = i - g;
- } else {
- g = i;
- i = 0;
- }
- var length = (i + g) < value.length ? (i + g) : value.length;
- t.add(value.substring(i, length));
- g = localeGrouping[j = (j + 1) % localeGrouping.length];
- }
- return t.reversed.join(localeThousands);
- } : (x) => x;
+ formatGroup = (localeGrouping != null)
+ ? (value) {
+ var i = value.length, t = [], j = 0, g = localeGrouping[0];
+ while (i > 0 && g > 0) {
+ if (i - g >= 0) {
+ i = i - g;
+ } else {
+ g = i;
+ i = 0;
+ }
+ var length = (i + g) < value.length ? (i + g) : value.length;
+ t.add(value.substring(i, length));
+ g = localeGrouping[j = (j + 1) % localeGrouping.length];
+ }
+ return t.reversed.join(localeThousands);
+ }
+ : (x) => x;
}
/**
@@ -70,8 +69,8 @@
zfill = match.group(5),
width = match.group(6) != null ? int.parse(match.group(6)) : 0,
comma = match.group(7) != null,
- precision = match.group(8) != null ?
- int.parse(match.group(8).substring(1)) : null,
+ precision =
+ match.group(8) != null ? int.parse(match.group(8).substring(1)) : null,
type = match.group(9),
scale = 1,
prefix = '',
@@ -87,16 +86,35 @@
}
switch (type) {
- case 'n': comma = true; type = 'g'; break;
- case '%': scale = 100; suffix = '%'; type = 'f'; break;
- case 'p': scale = 100; suffix = '%'; type = 'r'; break;
+ case 'n':
+ comma = true;
+ type = 'g';
+ break;
+ case '%':
+ scale = 100;
+ suffix = '%';
+ type = 'f';
+ break;
+ case 'p':
+ scale = 100;
+ suffix = '%';
+ type = 'r';
+ break;
case 'b':
case 'o':
case 'x':
- case 'X': if (symbol == '#') prefix = '0' + type.toLowerCase(); break;
+ case 'X':
+ if (symbol == '#') prefix = '0' + type.toLowerCase();
+ break;
case 'c':
- case 'd': integer = true; precision = 0; break;
- case 's': scale = -1; type = 'r'; break;
+ case 'd':
+ integer = true;
+ precision = 0;
+ break;
+ case 's':
+ scale = -1;
+ type = 'r';
+ break;
}
if (symbol == '\$') {
@@ -141,8 +159,8 @@
// format. Preserve the existing suffix, if any, such as the
// currency symbol.
if (scale < 0) {
- FormatPrefix unit = new FormatPrefix(value,
- (precision != null) ? precision : 0);
+ FormatPrefix unit =
+ new FormatPrefix(value, (precision != null) ? precision : 0);
value = unit.scale(value);
fullSuffix = unit.symbol + suffix;
} else {
@@ -168,10 +186,13 @@
before = formatGroup(before);
}
- var length = prefix.length + before.length + after.length +
+ var length = prefix.length +
+ before.length +
+ after.length +
(zcomma ? 0 : negative.length),
- padding = length < width ? new List.filled(
- (length = width - length + 1), '').join(fill) : '';
+ padding = length < width
+ ? new List.filled((length = width - length + 1), '').join(fill)
+ : '';
// If the fill character is '0', grouping is applied after padding.
if (zcomma) {
@@ -185,17 +206,23 @@
value = before + after;
// Apply any padding and alignment attributes before returning the string.
- return (align == '<' ? negative + value + padding
- : align == '>' ? padding + negative + value
- : align == '^' ? padding.substring(0, length >>= 1) + negative +
- value + padding.substring(length)
- : negative + (zcomma ? value : padding + value)) + fullSuffix;
+ return (align == '<'
+ ? negative + value + padding
+ : align == '>'
+ ? padding + negative + value
+ : align == '^'
+ ? padding.substring(0, length >>= 1) +
+ negative +
+ value +
+ padding.substring(length)
+ : negative + (zcomma ? value : padding + value)) +
+ fullSuffix;
};
}
// Gets the format function by given type.
NumberFormatFunction _getFormatFunction(String type) {
- switch(type) {
+ switch (type) {
case 'b':
return (num x, [int p = 0]) => x.toInt().toRadixString(2);
case 'c':
@@ -218,4 +245,4 @@
return (num x, [int p = 0]) => x.toString();
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/charted/lib/locale/format/time_format.dart b/packages/charted/lib/locale/format/time_format.dart
index d2c16c6..d1b0e6b 100644
--- a/packages/charted/lib/locale/format/time_format.dart
+++ b/packages/charted/lib/locale/format/time_format.dart
@@ -19,8 +19,8 @@
TimeFormat([String template = null, String identifier = 'en_US']) {
_template = template;
_locale = identifier;
- if (_template != null)
- _dateFormat = new DateFormat(_wrapStrptime2ICU(_template), _locale);
+ if (_template != null) _dateFormat =
+ new DateFormat(_wrapStrptime2ICU(_template), _locale);
}
TimeFormat _getInstance(String template) {
@@ -40,16 +40,13 @@
}
TimeFormatFunction multi(List<List> formats) {
- var n = formats.length,
- i = -1;
- while (++i < n)
- formats[i][0] = _getInstance(formats[i][0] as String);
+ var n = formats.length, i = -1;
+ while (++i < n) formats[i][0] = _getInstance(formats[i][0] as String);
return (var date) {
if (date is num) {
date = new DateTime.fromMillisecondsSinceEpoch(date.toInt());
}
- var i = 0,
- f = formats[i];
+ var i = 0, f = formats[i];
while (f.length < 2 || f[1](date) == false) {
i++;
if (i < n) f = formats[i];
@@ -60,8 +57,8 @@
}
UTCTimeFormat utc([String specifier = null]) {
- return new UTCTimeFormat(specifier == null ?
- _template : specifier, _locale);
+ return new UTCTimeFormat(
+ specifier == null ? _template : specifier, _locale);
}
static UTCTimeFormat iso() {
@@ -77,7 +74,7 @@
'B': 'MMMM',
'c': 'EEE MMM d HH:mm:ss yyyy',
'd': 'dd',
- 'e': 'd', // TODO(songrenchu): zero padding not supported
+ 'e': 'd', // TODO(songrenchu): zero padding not supported
'H': 'HH',
'I': 'hh',
'j': 'DDD',
@@ -86,11 +83,11 @@
'L': 'SSS',
'p': 'a',
'S': 'ss',
- 'U': 'ww', // TODO(songrenchu): ICU doesn't distinguish 'U' and 'W',
- // and not supported by Dart: DateFormat
- 'w': 'ee', // TODO(songrenchu): e not supported by Dart: DateFormat
- 'W': 'ww', // TODO(songrenchu): ICU doesn't distinguish 'U' and 'W',
- // and not supported by Dart: DateFormat
+ 'U': 'ww', // TODO(songrenchu): ICU doesn't distinguish 'U' and 'W',
+ // and not supported by Dart: DateFormat
+ 'w': 'ee', // TODO(songrenchu): e not supported by Dart: DateFormat
+ 'W': 'ww', // TODO(songrenchu): ICU doesn't distinguish 'U' and 'W',
+ // and not supported by Dart: DateFormat
'x': 'MM/dd/yyyy',
'X': 'HH:mm:ss',
'y': 'yy',
@@ -100,30 +97,25 @@
};
String _wrapStrptime2ICU(String template) {
- var string = [],
- i = -1,
- j = 0,
- n = template.length,
- tempChar;
+ var string = [], i = -1, j = 0, n = template.length, tempChar;
while (++i < n) {
if (template[i] == '%') {
string.add(template.substring(j, i));
- if ((timeFormatPads[tempChar = template[++i]]) != null)
- tempChar = template[++i];
- if (timeFormatsTransform[tempChar] != null)
- string.add(timeFormatsTransform[tempChar]);
+ if ((timeFormatPads[tempChar = template[++i]]) != null) tempChar =
+ template[++i];
+ if (timeFormatsTransform[tempChar] != null) string
+ .add(timeFormatsTransform[tempChar]);
j = i + 1;
}
}
- if (j < i)
- string.add("'" + template.substring(j, i) + "'");
+ if (j < i) string.add("'" + template.substring(j, i) + "'");
return string.join("");
}
}
class UTCTimeFormat extends TimeFormat {
- UTCTimeFormat(String template, [String identifier = 'en_US']):
- super(template, identifier);
+ UTCTimeFormat(String template, [String identifier = 'en_US'])
+ : super(template, identifier);
UTCTimeFormat _getInstance(String template) {
return new UTCTimeFormat(template, _locale);
@@ -133,4 +125,4 @@
assert(_dateFormat != null);
return _dateFormat.parseUTC(string);
}
-}
\ No newline at end of file
+}
diff --git a/packages/charted/lib/locale/languages/en_us.dart b/packages/charted/lib/locale/languages/en_us.dart
index 53be549..7d6b354 100644
--- a/packages/charted/lib/locale/languages/en_us.dart
+++ b/packages/charted/lib/locale/languages/en_us.dart
@@ -23,22 +23,50 @@
final identifier = 'en_US';
final decimal = '.';
final thousands = ',';
- final grouping = const[3];
- final currency = const['\$', ''];
+ final grouping = const [3];
+ final currency = const ['\$', ''];
final dateTime = '%a %b %e %X %Y';
final date = '%m/%d/%Y';
final time = '%H =>%M =>%S';
- final periods = const['AM', 'PM'];
+ final periods = const ['AM', 'PM'];
- final days = const[
- 'Sunday', 'Monday', 'Tuesday',
- 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
- final shortDays = const['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+ final days = const [
+ 'Sunday',
+ 'Monday',
+ 'Tuesday',
+ 'Wednesday',
+ 'Thursday',
+ 'Friday',
+ 'Saturday'
+ ];
+ final shortDays = const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
- final months = const[
- 'January', 'February', 'March', 'April', 'May', 'June',
- 'July', 'August', 'September', 'October', 'November', 'December'];
- final shortMonths = const[
- 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
- 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+ final months = const [
+ 'January',
+ 'February',
+ 'March',
+ 'April',
+ 'May',
+ 'June',
+ 'July',
+ 'August',
+ 'September',
+ 'October',
+ 'November',
+ 'December'
+ ];
+ final shortMonths = const [
+ 'Jan',
+ 'Feb',
+ 'Mar',
+ 'Apr',
+ 'May',
+ 'Jun',
+ 'Jul',
+ 'Aug',
+ 'Sep',
+ 'Oct',
+ 'Nov',
+ 'Dec'
+ ];
}
diff --git a/packages/charted/lib/selection/selection.dart b/packages/charted/lib/selection/selection.dart
index d040745..c9036dc 100644
--- a/packages/charted/lib/selection/selection.dart
+++ b/packages/charted/lib/selection/selection.dart
@@ -152,8 +152,8 @@
* selection (with data associated to the element, index of the element in
* it's group and the element itself) to get the value of the property.
*/
- void styleWithCallback(String property,
- SelectionCallback<String> fn, {String priority});
+ void styleWithCallback(String property, SelectionCallback<String> fn,
+ {String priority});
/**
* Sets textContent of all elements in the selection to [val]. A side-effect
@@ -262,8 +262,8 @@
* it's group and the element itself) to get the data to be set on the
* current element.
*/
- DataSelection dataWithCallback(
- SelectionCallback<Iterable> fn, [SelectionKeyFunction keyFn]);
+ DataSelection dataWithCallback(SelectionCallback<Iterable> fn,
+ [SelectionKeyFunction keyFn]);
/**
* Associates data with all the elements - no join is performed. Unlike
@@ -287,7 +287,6 @@
Transition transition();
}
-
/*
* Group of elements in the selection.
* Each selection may contain more than one group of elements.
@@ -297,7 +296,6 @@
Element parent;
}
-
/**
* [EnterSelection] is a sub-selection that represents missing elements of a
* selection - an element is considered missing when there is data and no
diff --git a/packages/charted/lib/selection/selection_scope.dart b/packages/charted/lib/selection/selection_scope.dart
index 3543f92..38d43f2 100644
--- a/packages/charted/lib/selection/selection_scope.dart
+++ b/packages/charted/lib/selection/selection_scope.dart
@@ -24,7 +24,7 @@
* [selector] as the root.
*/
SelectionScope.selector(String selector) {
- if (selector == null || selector.isEmpty ){
+ if (selector == null || selector.isEmpty) {
throw new ArgumentError('Selector cannot be empty');
}
_root = document.querySelector(selector);
@@ -74,7 +74,7 @@
* elements in it.
*/
Selection selectAll(String selector) =>
- new _SelectionImpl.all(selector:selector, scope:this);
+ new _SelectionImpl.all(selector: selector, scope: this);
/**
* Creates a new [Selection] containing [elements]. Assumes that
diff --git a/packages/charted/lib/selection/src/selection_impl.dart b/packages/charted/lib/selection/src/selection_impl.dart
index 8604da7..14de177 100644
--- a/packages/charted/lib/selection/src/selection_impl.dart
+++ b/packages/charted/lib/selection/src/selection_impl.dart
@@ -13,7 +13,6 @@
* the select or selectAll methods on [SelectionScope] and [Selection].
*/
class _SelectionImpl implements Selection {
-
Iterable<SelectionGroup> groups;
SelectionScope scope;
@@ -30,15 +29,18 @@
* "element" itself passed as parameters. [fn] must return an iterable of
* elements to be used in each group.
*/
- _SelectionImpl.all({String selector, SelectionCallback<Iterable<Element>> fn,
- SelectionScope this.scope, Selection source}) {
+ _SelectionImpl.all(
+ {String selector,
+ SelectionCallback<Iterable<Element>> fn,
+ SelectionScope this.scope,
+ Selection source}) {
assert(selector != null || fn != null);
assert(source != null || scope != null);
if (selector != null) {
- fn = (d, i, c) => c == null ?
- scope.root.querySelectorAll(selector) :
- c.querySelectorAll(selector);
+ fn = (d, i, c) => c == null
+ ? scope.root.querySelectorAll(selector)
+ : c.querySelectorAll(selector);
}
var tmpGroups = new List<SelectionGroup>();
@@ -50,14 +52,13 @@
final e = g.elements.elementAt(ei);
if (e != null) {
tmpGroups.add(
- new _SelectionGroupImpl(
- fn(scope.datum(e), gi, e), parent: e));
+ new _SelectionGroupImpl(fn(scope.datum(e), gi, e), parent: e));
}
}
}
} else {
- tmpGroups.add(
- new _SelectionGroupImpl(fn(null, 0, null), parent: scope.root));
+ tmpGroups
+ .add(new _SelectionGroupImpl(fn(null, 0, null), parent: scope.root));
}
groups = tmpGroups;
}
@@ -67,15 +68,18 @@
* [selector] is specified. Otherwise, call [fn] which must return the
* element to be selected.
*/
- _SelectionImpl.single({String selector, SelectionCallback<Element> fn,
- SelectionScope this.scope, Selection source}) {
+ _SelectionImpl.single(
+ {String selector,
+ SelectionCallback<Element> fn,
+ SelectionScope this.scope,
+ Selection source}) {
assert(selector != null || fn != null);
assert(source != null || scope != null);
if (selector != null) {
- fn = (d, i, c) => c == null ?
- scope.root.querySelector(selector) :
- c.querySelector(selector);
+ fn = (d, i, c) => c == null
+ ? scope.root.querySelector(selector)
+ : c.querySelector(selector);
}
if (source != null) {
@@ -84,23 +88,26 @@
SelectionGroup g = source.groups.elementAt(gi);
return new _SelectionGroupImpl(
new List.generate(g.elements.length, (ei) {
- var e = g.elements.elementAt(ei);
- if (e != null) {
- var datum = scope.datum(e);
- var enterElement = fn(datum, ei, e);
- if (datum != null) {
- scope.associate(enterElement, datum);
- }
- return enterElement;
- } else {
- return null;
- }
- }), parent: g.parent);
+ var e = g.elements.elementAt(ei);
+ if (e != null) {
+ var datum = scope.datum(e);
+ var enterElement = fn(datum, ei, e);
+ if (datum != null) {
+ scope.associate(enterElement, datum);
+ }
+ return enterElement;
+ } else {
+ return null;
+ }
+ }),
+ parent: g.parent);
});
} else {
- groups = new List<SelectionGroup>.generate(1,
- (_) => new _SelectionGroupImpl(new List.generate(1,
- (_) => fn(null, 0, null), growable: false)), growable: false);
+ groups = new List<SelectionGroup>.generate(
+ 1,
+ (_) => new _SelectionGroupImpl(
+ new List.generate(1, (_) => fn(null, 0, null), growable: false)),
+ growable: false);
}
}
@@ -113,8 +120,7 @@
* be part of the same group, with [SelectionScope.root] as the group's parent
*/
_SelectionImpl.elements(Iterable elements, SelectionScope this.scope) {
- groups = new List<SelectionGroup>()
- ..add(new _SelectionGroupImpl(elements));
+ groups = new List<SelectionGroup>()..add(new _SelectionGroupImpl(elements));
}
/**
@@ -140,19 +146,19 @@
void on(String type, [SelectionCallback listener, bool capture]) {
Function getEventHandler(i, e) => (Event event) {
- var previous = scope.event;
- scope.event = event;
- try {
- listener(scope.datum(e), i, e);
- } finally {
- scope.event = previous;
- }
- };
+ var previous = scope.event;
+ scope.event = event;
+ try {
+ listener(scope.datum(e), i, e);
+ } finally {
+ scope.event = previous;
+ }
+ };
if (!type.startsWith('.')) {
if (listener != null) {
// Add a listener to each element.
- each((d, i, Element e){
+ each((d, i, Element e) {
var handlers = scope._listeners[e];
if (handlers == null) scope._listeners[e] = handlers = {};
handlers[type] = new Pair(getEventHandler(i, e), capture);
@@ -171,8 +177,7 @@
} else {
// Remove all listeners on the event type (ignoring the namespace)
each((d, i, Element e) {
- var handlers = scope._listeners[e],
- t = type.substring(1);
+ var handlers = scope._listeners[e], t = type.substring(1);
handlers.forEach((String s, Pair<Function, bool> value) {
if (s.split('.')[0] == t) {
e.removeEventListener(s, value.first, value.last);
@@ -210,8 +215,10 @@
void attrWithCallback(String name, SelectionCallback fn) {
assert(fn != null);
- _do(fn, (e, v) => v == null ?
- e.attributes.remove(name) : e.attributes[name] = "$v");
+ _do(
+ fn,
+ (e, v) =>
+ v == null ? e.attributes.remove(name) : e.attributes[name] = "$v");
}
void classed(String name, [bool val = true]) {
@@ -221,23 +228,23 @@
void classedWithCallback(String name, SelectionCallback<bool> fn) {
assert(fn != null);
- _do(fn, (e, v) =>
- v == false ? e.classes.remove(name) : e.classes.add(name));
+ _do(fn,
+ (e, v) => v == false ? e.classes.remove(name) : e.classes.add(name));
}
void style(String property, val, {String priority}) {
assert(property != null && property.isNotEmpty);
- styleWithCallback(property,
- toCallback(val as String), priority: priority);
+ styleWithCallback(property, toCallback(val as String), priority: priority);
}
- void styleWithCallback(String property,
- SelectionCallback<String> fn, {String priority}) {
+ void styleWithCallback(String property, SelectionCallback<String> fn,
+ {String priority}) {
assert(fn != null);
- _do(fn, (Element e, String v) =>
- v == null || v.isEmpty ?
- e.style.removeProperty(property) :
- e.style.setProperty(property, v, priority));
+ _do(
+ fn,
+ (Element e, String v) => v == null || v.isEmpty
+ ? e.style.removeProperty(property)
+ : e.style.setProperty(property, v, priority));
}
void text(String val) => textWithCallback(toCallback(val));
@@ -263,7 +270,7 @@
Selection selectWithCallback(SelectionCallback<Element> fn) {
assert(fn != null);
- return new _SelectionImpl.single(fn: fn, source:this);
+ return new _SelectionImpl.single(fn: fn, source: this);
}
Selection append(String tag) {
@@ -275,9 +282,9 @@
Selection appendWithCallback(SelectionCallback<Element> fn) {
assert(fn != null);
return new _SelectionImpl.single(fn: (datum, ei, e) {
- Element child = fn(datum, ei, e);
- return child == null ? null : e.append(child);
- }, source: this);
+ Element child = fn(datum, ei, e);
+ return child == null ? null : e.append(child);
+ }, source: this);
}
Selection insert(String tag,
@@ -285,7 +292,8 @@
assert(tag != null && tag.isNotEmpty);
return insertWithCallback(
(d, ei, e) => Namespace.createChildElement(tag, e),
- before: before, beforeFn: beforeFn);
+ before: before,
+ beforeFn: beforeFn);
}
Selection insertWithCallback(SelectionCallback<Element> fn,
@@ -293,13 +301,11 @@
assert(fn != null);
beforeFn =
before == null ? beforeFn : (d, ei, e) => e.querySelector(before);
- return new _SelectionImpl.single(
- fn: (datum, ei, e) {
- Element child = fn(datum, ei, e);
- Element before = beforeFn(datum, ei, e);
- return child == null ? null : e.insertBefore(child, before);
- },
- source: this);
+ return new _SelectionImpl.single(fn: (datum, ei, e) {
+ Element child = fn(datum, ei, e);
+ Element before = beforeFn(datum, ei, e);
+ return child == null ? null : e.insertBefore(child, before);
+ }, source: this);
}
Selection selectAll(String selector) {
@@ -309,7 +315,7 @@
Selection selectAllWithCallback(SelectionCallback<Iterable<Element>> fn) {
assert(fn != null);
- return new _SelectionImpl.all(fn: fn, source:this);
+ return new _SelectionImpl.all(fn: fn, source: this);
}
DataSelection data(Iterable vals, [SelectionKeyFunction keyFn]) {
@@ -317,20 +323,19 @@
return dataWithCallback(toCallback(vals), keyFn);
}
- DataSelection dataWithCallback(
- SelectionCallback<Iterable> fn, [SelectionKeyFunction keyFn]) {
+ DataSelection dataWithCallback(SelectionCallback<Iterable> fn,
+ [SelectionKeyFunction keyFn]) {
assert(fn != null);
- var enterGroups = [],
- updateGroups = [],
- exitGroups = [];
+ var enterGroups = [], updateGroups = [], exitGroups = [];
// Create a dummy node to be used with enter() selection.
Object dummy(val) {
var element = new Object();
scope.associate(element, val);
return element;
- };
+ }
+ ;
// Joins data to all elements in the group.
void join(SelectionGroup g, Iterable vals) {
@@ -346,9 +351,7 @@
// Use key function to determine DOMElement to data associations.
if (keyFn != null) {
- var keysOnDOM = [],
- elementsByKey = {},
- valuesByKey = {};
+ var keysOnDOM = [], elementsByKey = {}, valuesByKey = {};
// Create a key to DOM element map.
// Used later to see if an element already exists for a key.
@@ -419,15 +422,15 @@
enterGroups.add(new _SelectionGroupImpl(enter, parent: g.parent));
updateGroups.add(new _SelectionGroupImpl(update, parent: g.parent));
exitGroups.add(new _SelectionGroupImpl(exit, parent: g.parent));
- };
+ }
+ ;
for (int gi = 0; gi < groups.length; ++gi) {
final g = groups.elementAt(gi);
join(g, fn(scope.datum(g.parent), gi, g.parent));
}
- return new _DataSelectionImpl(
- updateGroups, enterGroups, exitGroups, scope);
+ return new _DataSelectionImpl(updateGroups, enterGroups, exitGroups, scope);
}
void datum(Iterable vals) {
@@ -447,7 +450,8 @@
ExitSelection exit;
_DataSelectionImpl(Iterable updated, Iterable entering, Iterable exiting,
- SelectionScope scope) : super.selectionGroups(updated, scope) {
+ SelectionScope scope)
+ : super.selectionGroups(updated, scope) {
enter = new _EnterSelectionImpl(entering, this);
exit = new _ExitSelectionImpl(exiting, this);
}
@@ -471,7 +475,8 @@
assert(tag != null && tag.isNotEmpty);
return insertWithCallback(
(d, ei, e) => Namespace.createChildElement(tag, e),
- before: before, beforeFn: beforeFn);
+ before: before,
+ beforeFn: beforeFn);
}
Selection insertWithCallback(SelectionCallback<Element> fn,
@@ -493,10 +498,10 @@
Selection appendWithCallback(SelectionCallback<Element> fn) {
assert(fn != null);
return selectWithCallback((datum, ei, e) {
- Element child = fn(datum, ei, e);
- e.append(child);
- return child;
- });
+ Element child = fn(datum, ei, e);
+ e.append(child);
+ return child;
+ });
}
Selection select(String selector) {
@@ -513,8 +518,7 @@
for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) {
final e = g.elements.elementAt(ei);
if (e != null) {
- var datum = scope.datum(e),
- selected = fn(datum, ei, g.parent);
+ var datum = scope.datum(e), selected = fn(datum, ei, g.parent);
scope.associate(selected, datum);
u.elements[ei] = selected;
subgroup.add(selected);
@@ -532,7 +536,8 @@
class _ExitSelectionImpl extends _SelectionImpl implements ExitSelection {
final DataSelection update;
_ExitSelectionImpl(Iterable groups, DataSelection update)
- : update = update, super.selectionGroups(groups, update.scope);
+ : update = update,
+ super.selectionGroups(groups, update.scope);
}
class _SelectionGroupImpl implements SelectionGroup {
diff --git a/packages/charted/lib/selection/src/transition_impl.dart b/packages/charted/lib/selection/src/transition_impl.dart
index 53bb72a..204f28c 100644
--- a/packages/charted/lib/selection/src/transition_impl.dart
+++ b/packages/charted/lib/selection/src/transition_impl.dart
@@ -13,8 +13,8 @@
class _TransitionImpl implements Transition {
SelectionCallback _delay = (d, i, c) => 0;
- SelectionCallback _duration =
- (d, i, c) => Transition.defaultDurationMilliseconds;
+ SelectionCallback _duration = (d, i, c) =>
+ Transition.defaultDurationMilliseconds;
Selection _selection;
Map _attrs = {};
Map _styles = {};
@@ -32,8 +32,8 @@
_timerDelay = delay;
}
- Interpolator ease = clampEasingFn(
- Transition.defaultEasingMode(Transition.defaultEasingType));
+ Interpolator ease =
+ clampEasingFn(Transition.defaultEasingMode(Transition.defaultEasingType));
void delay(int millisecond) {
delayWithCallback(toCallback(millisecond));
@@ -84,27 +84,29 @@
_selection.each((d, i, c) {
var tweenList = [];
_attrs.forEach((key, value) {
- tweenList.add(_getAttrInterpolator(c, key, value(d, i, c)));
+ tweenList.add(_getAttrInterpolator(c, key, value(d, i, c)));
});
_attrTweens.forEach((key, value) {
- tweenList.add((t) => c.setAttribute(key,
- value(d, i, c.getAttribute(key))(t)));
+ tweenList.add(
+ (t) => c.setAttribute(key, value(d, i, c.getAttribute(key))(t)));
});
_styles.forEach((key, value) {
- tweenList.add(_getStyleInterpolator(c, key,
- value['callback'](d, i, c), value['priority']));
+ tweenList.add(_getStyleInterpolator(
+ c, key, value['callback'](d, i, c), value['priority']));
});
_styleTweens.forEach((key, value) {
- tweenList.add((t) => c.style.setProperty(key,
- value['callback'](d, i,
- c.style.getPropertyValue(key))(t).toString(), value['priority']));
+ tweenList.add((t) => c.style.setProperty(
+ key,
+ value['callback'](d, i, c.style.getPropertyValue(key))(t)
+ .toString(),
+ value['priority']));
});
_attrMap[c] = tweenList;
_durationMap[c] = _duration(d, i, c);
_timerMap[new AnimationTimer(_tick, delay: _delay(d, i, c))] = c;
- if(!_transitionMap.containsKey(c)) {
+ if (!_transitionMap.containsKey(c)) {
_transitionMap[c] = 1;
} else {
_transitionMap[c]++;
@@ -127,8 +129,8 @@
var interpolator = createStringInterpolator(style, newValue.toString());
- return (t) => element.style.setProperty(styleName,
- interpolator(t).toString(), priority);
+ return (t) => element.style
+ .setProperty(styleName, interpolator(t).toString(), priority);
}
// Ticks of the transition, this is the callback registered to the
@@ -149,7 +151,7 @@
activeNode.remove();
}
- if(_transitionMap[activeNode] > 1) {
+ if (_transitionMap[activeNode] > 1) {
_transitionMap[activeNode]--;
} else {
_transitionMap.remove(activeNode);
@@ -185,7 +187,8 @@
Transition transition() {
var e = _selection.first;
var delay = _delay(_selection.scope.datum(e), 0, e) +
- _duration(_selection.scope.datum(e), 0, e) + _timerDelay;
+ _duration(_selection.scope.datum(e), 0, e) +
+ _timerDelay;
var t = new _TransitionImpl(_selection, delay);
t.ease = ease;
t.durationWithCallback(_duration);
diff --git a/packages/charted/lib/selection/transition.dart b/packages/charted/lib/selection/transition.dart
index d0c6be2..0f54c0c 100644
--- a/packages/charted/lib/selection/transition.dart
+++ b/packages/charted/lib/selection/transition.dart
@@ -7,7 +7,7 @@
*/
library charted.selection.transition;
-import "dart:html" show Element,document;
+import "dart:html" show Element, document;
import "package:charted/core/timer.dart";
import "package:charted/selection/selection.dart";
import "package:charted/core/interpolators.dart";
@@ -25,7 +25,6 @@
* Transitions have a default duration of 250ms.
*/
abstract class Transition {
-
/** A settable default easing type */
static EasingFunction defaultEasingType = easeCubic();
@@ -108,8 +107,8 @@
* The function's return value is then used to transition each element's
* style property.
*/
- void styleWithCallback(String property,
- SelectionCallback<String> fn, [String priority]);
+ void styleWithCallback(String property, SelectionCallback<String> fn,
+ [String priority]);
/**
* Transitions the value of the CSS style property with the specified name
diff --git a/packages/charted/lib/svg/axis.dart b/packages/charted/lib/svg/axis.dart
index 81fc756..474b436 100644
--- a/packages/charted/lib/svg/axis.dart
+++ b/packages/charted/lib/svg/axis.dart
@@ -43,17 +43,17 @@
/// Formatter for the tick labels
FormatFunction _tickFormat;
- SvgAxis({
- this.orientation: ORIENTATION_BOTTOM,
+ SvgAxis(
+ {this.orientation: ORIENTATION_BOTTOM,
this.innerTickSize: 6,
this.outerTickSize: 6,
this.tickPadding: 3,
Iterable tickValues,
FormatFunction tickFormat,
- Scale scale }) : scale = scale == null ? new LinearScale() : scale {
- _tickFormat = tickFormat == null
- ? this.scale.createTickFormatter()
- : tickFormat;
+ Scale scale})
+ : scale = scale == null ? new LinearScale() : scale {
+ _tickFormat =
+ tickFormat == null ? this.scale.createTickFormatter() : tickFormat;
_tickValues = isNullOrEmpty(tickValues) ? this.scale.ticks : tickValues;
}
@@ -63,13 +63,12 @@
/// Draw an axis on each non-null element in selection
draw(Selection g, {SvgAxisTicks axisTicksBuilder, bool isRTL: false}) =>
- g.each((d, i, e) => create(
- e, g.scope, axisTicksBuilder: axisTicksBuilder, isRTL: isRTL));
+ g.each((d, i, e) =>
+ create(e, g.scope, axisTicksBuilder: axisTicksBuilder, isRTL: isRTL));
/// Create an axis on [element].
- create(Element element, SelectionScope scope, {
- SvgAxisTicks axisTicksBuilder, bool isRTL: false}) {
-
+ create(Element element, SelectionScope scope,
+ {SvgAxisTicks axisTicksBuilder, bool isRTL: false}) {
var group = scope.selectElements([element]),
older = _scales[element],
current = _scales[element] = scale.clone(),
@@ -101,10 +100,10 @@
var enter = ticks.enter.appendWithCallback((d, i, e) {
var group = Namespace.createChildElement('g', e)
..attributes['class'] = 'tick'
- ..append(Namespace.createChildElement('line', e))
+ ..append(Namespace.createChildElement('line', e))
..append(Namespace.createChildElement('text', e)
- ..attributes['dy'] =
- isVertical ? '0.32em' : (isBottom ? '0.71em' : '0'));
+ ..attributes['dy'] =
+ isVertical ? '0.32em' : (isBottom ? '0.71em' : '0'));
if (!isInitialRender) {
group.style.setProperty('opacity', EPSILON.toString());
}
@@ -183,7 +182,7 @@
range = current.rangeExtent;
if (path == null) {
path = Namespace.createChildElement('path', element)
- ..setAttribute('class', 'domain');
+ ..setAttribute('class', 'domain');
}
path.attributes['d'] = isLeft || isRight
? 'M${tickSize},${range.min}H0V${range.max}H${tickSize}'
diff --git a/packages/charted/lib/svg/shapes/arc.dart b/packages/charted/lib/svg/shapes/arc.dart
index 78797b1..6e2a396 100644
--- a/packages/charted/lib/svg/shapes/arc.dart
+++ b/packages/charted/lib/svg/shapes/arc.dart
@@ -36,12 +36,11 @@
/// and element in the context.
final SelectionCallback<num> endAngleCallback;
- SvgArc({
- this.innerRadiusCallback : defaultInnerRadiusCallback,
+ SvgArc(
+ {this.innerRadiusCallback: defaultInnerRadiusCallback,
this.outerRadiusCallback: defaultOuterRadiusCallback,
this.startAngleCallback: defaultStartAngleCallback,
- this.endAngleCallback: defaultEndAngleCallback
- });
+ this.endAngleCallback: defaultEndAngleCallback});
String path(d, int i, Element e) {
var ir = innerRadiusCallback(d, i, e),
@@ -54,8 +53,13 @@
if (delta > _MAX) {
return ir > 0
- ? "M0,$or" "A$or,$or 0 1,1 0,-$or" "A$or,$or 0 1,1 0,$or"
- "M0,$ir" "A$ir,$ir 0 1,0 0,-$ir" "A$ir,$ir 0 1,0 0,$ir" "Z"
+ ? "M0,$or"
+ "A$or,$or 0 1,1 0,-$or"
+ "A$or,$or 0 1,1 0,$or"
+ "M0,$ir"
+ "A$ir,$ir 0 1,0 0,-$ir"
+ "A$ir,$ir 0 1,0 0,$ir"
+ "Z"
: "M0,$or" "A$or,$or 0 1,1 0,-$or" "A$or,$or 0 1,1 0,$or" "Z";
}
@@ -66,11 +70,15 @@
df = delta < PI ? 0 : 1;
return ir > 0
- ? "M${or * cs},${or * ss}" "A$or,$or 0 $df,1 ${or * ce},${or * se}"
- "L${ir * ce},${ir * se}" "A$ir,$ir 0 $df,0 ${ir * cs},${ir * ss}"
- "Z"
- : "M${or * cs},${or * ss}" "A$or,$or 0 $df,1 ${or * ce},${or * se}"
- "L0,0" "Z";
+ ? "M${or * cs},${or * ss}"
+ "A$or,$or 0 $df,1 ${or * ce},${or * se}"
+ "L${ir * ce},${ir * se}"
+ "A$ir,$ir 0 $df,0 ${ir * cs},${ir * ss}"
+ "Z"
+ : "M${or * cs},${or * ss}"
+ "A$or,$or 0 $df,1 ${or * ce},${or * se}"
+ "L0,0"
+ "Z";
}
List centroid(d, int i, Element e) {
@@ -106,12 +114,10 @@
num startAngle;
num endAngle;
- SvgArcData(this.data, this.value,
- this.startAngle, this.endAngle, [
- this.innerRadius = 0, this.outerRadius = 100 ]);
+ SvgArcData(this.data, this.value, this.startAngle, this.endAngle,
+ [this.innerRadius = 0, this.outerRadius = 100]);
}
-
/// Returns the interpolator between two [SvgArcData] [a] and [b].
///
/// The interpolator will interpolate the older innerRadius and outerRadius with
@@ -126,6 +132,6 @@
bi = b.innerRadius - ai,
bo = b.outerRadius - ao;
- return (t) => new SvgArcData(b.data, b.value,
- (ast + bst * t), (aen + ben * t), (ai + bi * t), (ao + bo * t));
+ return (t) => new SvgArcData(b.data, b.value, (ast + bst * t),
+ (aen + ben * t), (ai + bi * t), (ao + bo * t));
}
diff --git a/packages/charted/lib/svg/shapes/line.dart b/packages/charted/lib/svg/shapes/line.dart
index 8b663ec..7e0f6ac 100644
--- a/packages/charted/lib/svg/shapes/line.dart
+++ b/packages/charted/lib/svg/shapes/line.dart
@@ -18,9 +18,7 @@
class SvgLine implements SvgShape {
static const LINE_INTERPOLATOR_LINEAR = 'linear';
- static final LINE_INTERPOLATORS = {
- LINE_INTERPOLATOR_LINEAR: _linear
- };
+ static final LINE_INTERPOLATORS = {LINE_INTERPOLATOR_LINEAR: _linear};
/// Callback to access/convert datum to x coordinate value.
final SelectionValueAccessor<num> xValueAccessor;
@@ -39,12 +37,12 @@
/// Tension of the line, as used by a few interpolators.
final int tension;
- SvgLine({
- this.xValueAccessor: defaultDataToX,
+ SvgLine(
+ {this.xValueAccessor: defaultDataToX,
this.yValueAccessor: defaultDataToY,
this.isDefined: defaultIsDefined,
this.tension: 0,
- String interpolate: LINE_INTERPOLATOR_LINEAR })
+ String interpolate: LINE_INTERPOLATOR_LINEAR})
: interpolator = LINE_INTERPOLATORS[interpolate] {
assert(interpolator != null);
}
@@ -53,13 +51,12 @@
@override
String path(data, int index, Element e) {
assert(data is Iterable);
- var segments = new StringBuffer(),
- points = [];
+ var segments = new StringBuffer(), points = [];
for (int i = 0, len = data.length; i < len; ++i) {
final d = data.elementAt(i);
if (isDefined(d, i, e)) {
points.add(new math.Point(xValueAccessor(d, i), yValueAccessor(d, i)));
- } else if (points.isNotEmpty){
+ } else if (points.isNotEmpty) {
segments.write('M${interpolator(points, tension)}');
points.clear();
}
diff --git a/packages/charted/lib/svg/shapes/rect.dart b/packages/charted/lib/svg/shapes/rect.dart
index 554efbf..cd89cad 100644
--- a/packages/charted/lib/svg/shapes/rect.dart
+++ b/packages/charted/lib/svg/shapes/rect.dart
@@ -11,17 +11,17 @@
/// Draw a rectangle at [x], [y] which is [width] pixels wide and
/// [height] pixels height. [topLeft], [topRight], [bottomRight] and
/// [bottomLeft] are the corner radius at each of the four corners.
-String roundedRect(int x, int y, int width, int height,
- int topLeft, int topRight, int bottomRight, int bottomLeft) =>
- 'M${x+topLeft},${y} '
- 'L${x+width-topRight},${y} '
- 'Q${x+width},${y} ${x+width},${y+topRight}'
- 'L${x+width},${y+height-bottomRight} '
- 'Q${x+width},${y+height} ${x+width-bottomRight},${y+height}'
- 'L${x+bottomLeft},${y+height} '
- 'Q${x},${y+height} ${x},${y+height-bottomLeft}'
- 'L${x},${y+topLeft} '
- 'Q${x},${y} ${x+topLeft},${y} Z';
+String roundedRect(int x, int y, int width, int height, int topLeft,
+ int topRight, int bottomRight, int bottomLeft) =>
+ 'M${x+topLeft},${y} '
+ 'L${x+width-topRight},${y} '
+ 'Q${x+width},${y} ${x+width},${y+topRight}'
+ 'L${x+width},${y+height-bottomRight} '
+ 'Q${x+width},${y+height} ${x+width-bottomRight},${y+height}'
+ 'L${x+bottomLeft},${y+height} '
+ 'Q${x},${y+height} ${x},${y+height-bottomLeft}'
+ 'L${x},${y+topLeft} '
+ 'Q${x},${y} ${x+topLeft},${y} Z';
/// Draw a rectangle with rounded corners on both corners on the right.
String rightRoundedRect(int x, int y, int width, int height, int radius) {
diff --git a/packages/charted/pubspec.yaml b/packages/charted/pubspec.yaml
index a15a2e3..5dc5abc 100644
--- a/packages/charted/pubspec.yaml
+++ b/packages/charted/pubspec.yaml
@@ -1,5 +1,5 @@
name: charted
-version: 0.2.9
+version: 0.3.0
authors:
- Prasad Sunkari <prsd@google.com>
- Michael Cheng <midoringo@google.com>
diff --git a/packages/code_transformers/.travis.yml b/packages/code_transformers/.travis.yml
new file mode 100644
index 0000000..7012012
--- /dev/null
+++ b/packages/code_transformers/.travis.yml
@@ -0,0 +1,8 @@
+language: dart
+sudo: false
+dart:
+ - dev
+ - stable
+cache:
+ directories:
+ - $HOME/.pub-cache/hosted
diff --git a/packages/code_transformers/CHANGELOG.md b/packages/code_transformers/CHANGELOG.md
index 5bc681c..2e397c7 100644
--- a/packages/code_transformers/CHANGELOG.md
+++ b/packages/code_transformers/CHANGELOG.md
@@ -1,3 +1,12 @@
+## 0.2.11
+
+* Revert `0.2.10` release, will be re-released as `0.3.0` since it is actually
+ a breaking change.
+
+## 0.2.10
+
+* Update to use the `test` package instead of the `unittest` package.
+
## 0.2.9+4
* Republish 0.2.9+2 under new version.
diff --git a/packages/code_transformers/pubspec.yaml b/packages/code_transformers/pubspec.yaml
index 705aad4..67da04e 100644
--- a/packages/code_transformers/pubspec.yaml
+++ b/packages/code_transformers/pubspec.yaml
@@ -1,5 +1,5 @@
name: code_transformers
-version: 0.2.9+4
+version: 0.2.11
author: Dart Team <misc@dartlang.org>
description: Collection of utilities related to creating barback transformers.
homepage: https://github.com/dart-lang/code-transformers
@@ -12,5 +12,4 @@
path: '>=0.9.0 <2.0.0'
source_maps: '>=0.9.4 <0.11.0'
source_span: '>=1.0.0 <2.0.0'
-dev_dependencies:
unittest: '>=0.10.1 <0.12.0'
diff --git a/packages/collection/CHANGELOG.md b/packages/collection/CHANGELOG.md
index ad50fa1..a39a2b5 100644
--- a/packages/collection/CHANGELOG.md
+++ b/packages/collection/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.2.0
+
+* Add string comparators that ignore ASCII case and sort numbers numerically.
+
## 1.1.3
* Fix type inconsistencies with `Map` and `Set`.
diff --git a/packages/collection/README.md b/packages/collection/README.md
index 73429a5..8772e65 100644
--- a/packages/collection/README.md
+++ b/packages/collection/README.md
@@ -1,6 +1,4 @@
-#Helper libraries for working with collections.
-
-The `collection` package contains a number of separate libraries
+Contains a number libraries
with utility functions and classes that makes working with collections easier.
## Using
@@ -8,15 +6,19 @@
The `collection` package can be imported as separate libraries, or
in totality:
- import 'package:collection/algorithms.dart';
- import 'package:collection/equality.dart';
- import 'package:collection/iterable_zip.dart';
- import 'package:collection/priority_queue.dart';
- import 'package:collection/wrappers.dart';
+```dart
+import 'package:collection/algorithms.dart';
+import 'package:collection/equality.dart';
+import 'package:collection/iterable_zip.dart';
+import 'package:collection/priority_queue.dart';
+import 'package:collection/wrappers.dart';
+```
or
- import 'package:collection/collection.dart';
+```dart
+import 'package:collection/collection.dart';
+```
## Algorithms
@@ -41,7 +43,9 @@
The library provides ways to define equalities on `Iterable`s, `List`s, `Set`s,
and `Map`s, as well as combinations of these, such as:
- const MapEquality(const IdentityEquality(), const ListEquality());
+```dart
+const MapEquality(const IdentityEquality(), const ListEquality());
+```
This equality considers maps equal if they have identical keys, and the
corresponding values are lists with equal (`operator==`) values.
@@ -54,7 +58,7 @@
## Priority Queue
-An interface and implemention of a priority queue.
+An interface and implementation of a priority queue.
## Wrappers
diff --git a/packages/collection/lib/collection.dart b/packages/collection/lib/collection.dart
index 45d3867..6d451b8 100644
--- a/packages/collection/lib/collection.dart
+++ b/packages/collection/lib/collection.dart
@@ -22,5 +22,6 @@
export "iterable_zip.dart";
export "priority_queue.dart";
export "src/canonicalized_map.dart";
+export "src/comparators.dart";
export "src/queue_list.dart";
export "wrappers.dart";
diff --git a/packages/collection/lib/equality.dart b/packages/collection/lib/equality.dart
index c6fdafa..5911863 100644
--- a/packages/collection/lib/equality.dart
+++ b/packages/collection/lib/equality.dart
@@ -357,7 +357,7 @@
*
* In ordered mode, lists and iterables are required to have equal elements
* in the same order. In unordered mode, the order of elements in iterables
- * and lists are not importan.
+ * and lists are not important.
*
* A list is only equal to another list, likewise for sets and maps. All other
* iterables are compared as iterables only.
diff --git a/packages/collection/lib/priority_queue.dart b/packages/collection/lib/priority_queue.dart
index efb3239..e1a0177 100644
--- a/packages/collection/lib/priority_queue.dart
+++ b/packages/collection/lib/priority_queue.dart
@@ -275,7 +275,7 @@
*/
int _locate(E object) {
if (_length == 0) return -1;
- // Count positions from one instad of zero. This gives the numbers
+ // Count positions from one instead of zero. This gives the numbers
// some nice properties. For example, all right children are odd,
// their left sibling is even, and the parent is found by shifting
// right by one.
diff --git a/packages/collection/lib/src/comparators.dart b/packages/collection/lib/src/comparators.dart
new file mode 100644
index 0000000..05615ba
--- /dev/null
+++ b/packages/collection/lib/src/comparators.dart
@@ -0,0 +1,399 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library dart.pkg.collection.comparators;
+
+// Character constants.
+const int _zero = 0x30;
+const int _upperCaseA = 0x41;
+const int _upperCaseZ = 0x5a;
+const int _lowerCaseA = 0x61;
+const int _lowerCaseZ = 0x7a;
+const int _asciiCaseBit = 0x20;
+
+/// Checks if strings [a] and [b] differ only on the case of ASCII letters.
+///
+/// Strings are equal if they have the same length, and the characters at
+/// each index are the same, or they are ASCII letters where one is upper-case
+/// and the other is the lower-case version of the same letter.
+///
+/// The comparison does not ignore the case of non-ASCII letters, so
+/// an upper-case ae-ligature (Æ) is different from
+/// a lower case ae-ligature (æ).
+///
+/// Ignoring non-ASCII letters is not generally a good idea, but it makes sense
+/// for situations where the strings are known to be ASCII. Examples could
+/// be Dart identifiers, base-64 or hex encoded strings, GUIDs or similar
+/// strings with a known structure.
+bool equalsIgnoreAsciiCase(String a, String b) {
+ if (a.length != b.length) return false;
+ for (int i = 0; i < a.length; i++) {
+ var aChar = a.codeUnitAt(i);
+ var bChar = b.codeUnitAt(i);
+ if (aChar == bChar) continue;
+ // Quick-check for whether this may be different cases of the same letter.
+ if (aChar ^ bChar != _asciiCaseBit) return false;
+ // If it's possible, then check if either character is actually an ASCII
+ // letter.
+ int aCharUpperCase = aChar | _asciiCaseBit;
+ if (_upperCaseA <= aCharUpperCase && aCharUpperCase <= _upperCaseZ) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+
+/// Hash code for a string which is compatible with [equalsIgnoreAsciiCase].
+///
+/// The hash code is unaffected by changing the case of ASCII letters, but
+/// the case of non-ASCII letters do affect the result.
+int hashIgnoreAsciiCase(String string) {
+ // Jenkins hash code ( http://en.wikipedia.org/wiki/Jenkins_hash_function).
+ // adapted to smi values.
+ // Same hash used by dart2js for strings, modified to ignore ASCII letter
+ // case.
+ int hash = 0;
+ for (int i = 0; i < string.length; i++) {
+ int char = string.codeUnitAt(i);
+ // Convert lower-case ASCII letters to upper case.upper
+ // This ensures that strings that differ only in case will have the
+ // same hash code.
+ if (_lowerCaseA <= char && char <= _lowerCaseZ) char -= _asciiCaseBit;
+ hash = 0x1fffffff & (hash + char);
+ hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
+ hash >>= 6;
+ }
+ hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
+ hash >>= 11;
+ return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
+}
+
+
+/// Compares [a] and [b] lexically, converting ASCII letters to upper case.
+///
+/// Comparison treats all lower-case ASCII letters as upper-case letters,
+/// but does no case conversion for non-ASCII letters.
+///
+/// If two strings differ only on the case of ASCII letters, the one with the
+/// capital letter at the first difference will compare as less than the other
+/// string. This tie-breaking ensures that the comparison is a total ordering
+/// on strings and is compatible with equality.
+///
+/// Ignoring non-ASCII letters is not generally a good idea, but it makes sense
+/// for situations where the strings are known to be ASCII. Examples could
+/// be Dart identifiers, base-64 or hex encoded strings, GUIDs or similar
+/// strings with a known structure.
+int compareAsciiUpperCase(String a, String b) {
+ int defaultResult = 0; // Returned if no difference found.
+ for (int i = 0; i < a.length; i++) {
+ if (i >= b.length) return 1;
+ var aChar = a.codeUnitAt(i);
+ var bChar = b.codeUnitAt(i);
+ if (aChar == bChar) continue;
+ // Upper-case if letters.
+ int aUpperCase = aChar;
+ int bUpperCase = bChar;
+ if (_lowerCaseA <= aChar && aChar <= _lowerCaseZ) {
+ aUpperCase -= _asciiCaseBit;
+ }
+ if (_lowerCaseA <= bChar && bChar <= _lowerCaseZ) {
+ bUpperCase -= _asciiCaseBit;
+ }
+ if (aUpperCase != bUpperCase) return (aUpperCase - bUpperCase).sign;
+ if (defaultResult == 0) defaultResult = (aChar - bChar);
+ }
+ if (b.length > a.length) return -1;
+ return defaultResult.sign;
+}
+
+
+/// Compares [a] and [b] lexically, converting ASCII letters to lower case.
+///
+/// Comparison treats all upper-case ASCII letters as lower-case letters,
+/// but does no case conversion for non-ASCII letters.
+///
+/// If two strings differ only on the case of ASCII letters, the one with the
+/// capital letter at the first difference will compare as less than the other
+/// string. This tie-breaking ensures that the comparison is a total ordering
+/// on strings.
+///
+/// Ignoring non-ASCII letters is not generally a good idea, but it makes sense
+/// for situations where the strings are known to be ASCII. Examples could
+/// be Dart identifiers, base-64 or hex encoded strings, GUIDs or similar
+/// strings with a known structure.
+int compareAsciiLowerCase(String a, String b) {
+ int defaultResult = 0;
+ for (int i = 0; i < a.length; i++) {
+ if (i >= b.length) return 1;
+ var aChar = a.codeUnitAt(i);
+ var bChar = b.codeUnitAt(i);
+ if (aChar == bChar) continue;
+ int aLowerCase = aChar;
+ int bLowerCase = bChar;
+ // Upper case if ASCII letters.
+ if (_upperCaseA <= bChar && bChar <= _upperCaseZ) {
+ bLowerCase += _asciiCaseBit;
+ }
+ if (_upperCaseA <= aChar && aChar <= _upperCaseZ) {
+ aLowerCase += _asciiCaseBit;
+ }
+ if (aLowerCase != bLowerCase) return (aLowerCase - bLowerCase).sign;
+ if (defaultResult == 0) defaultResult = aChar - bChar;
+ }
+ if (b.length > a.length) return -1;
+ return defaultResult.sign;
+}
+
+/// Compares strings [a] and [b] according to [natural sort ordering].
+///
+/// A natural sort ordering is a lexical ordering where embedded
+/// numerals (digit sequences) are treated as a single unit and ordered by
+/// numerical value.
+/// This means that `"a10b"` will be ordered after `"a7b"` in natural
+/// ordering, where lexical ordering would put the `1` before the `7`, ignoring
+/// that the `1` is part of a larger number.
+///
+/// Example:
+/// The following strings are in the order they would be sorted by using this
+/// comparison function:
+///
+/// "a", "a0", "a0b", "a1", "a01", "a9", "a10", "a100", "a100b", "aa"
+///
+/// [natural sort ordering]: https://en.wikipedia.org/wiki/Natural_sort_order
+int compareNatural(String a, String b) {
+ for (int i = 0; i < a.length; i++) {
+ if (i >= b.length) return 1;
+ var aChar = a.codeUnitAt(i);
+ var bChar = b.codeUnitAt(i);
+ if (aChar != bChar) {
+ return _compareNaturally(a, b, i, aChar, bChar);
+ }
+ }
+ if (b.length > a.length) return -1;
+ return 0;
+}
+
+/// Compares strings [a] and [b] according to lower-case
+/// [natural sort ordering].
+///
+/// ASCII letters are converted to lower case before being compared, like
+/// for [compareAsciiLowerCase], then the result is compared like for
+/// [compareNatural].
+///
+/// If two strings differ only on the case of ASCII letters, the one with the
+/// capital letter at the first difference will compare as less than the other
+/// string. This tie-breaking ensures that the comparison is a total ordering
+/// on strings.
+///
+/// [natural sort ordering]: https://en.wikipedia.org/wiki/Natural_sort_order
+int compareAsciiLowerCaseNatural(String a, String b) {
+ int defaultResult = 0; // Returned if no difference found.
+ for (int i = 0; i < a.length; i++) {
+ if (i >= b.length) return 1;
+ var aChar = a.codeUnitAt(i);
+ var bChar = b.codeUnitAt(i);
+ if (aChar == bChar) continue;
+ int aLowerCase = aChar;
+ int bLowerCase = bChar;
+ if (_upperCaseA <= aChar && aChar <= _upperCaseZ) {
+ aLowerCase += _asciiCaseBit;
+ }
+ if (_upperCaseA <= bChar && bChar <= _upperCaseZ) {
+ bLowerCase += _asciiCaseBit;
+ }
+ if (aLowerCase != bLowerCase) {
+ return _compareNaturally(a, b, i, aLowerCase, bLowerCase);
+ }
+ if (defaultResult == 0) defaultResult = aChar - bChar;
+ }
+ if (b.length > a.length) return -1;
+ return defaultResult.sign;
+}
+
+/// Compares strings [a] and [b] according to upper-case
+/// [natural sort ordering].
+///
+/// ASCII letters are converted to upper case before being compared, like
+/// for [compareAsciiUpperCase], then the result is compared like for
+/// [compareNatural].
+///
+/// If two strings differ only on the case of ASCII letters, the one with the
+/// capital letter at the first difference will compare as less than the other
+/// string. This tie-breaking ensures that the comparison is a total ordering
+/// on strings
+///
+/// [natural sort ordering]: https://en.wikipedia.org/wiki/Natural_sort_order
+int compareAsciiUpperCaseNatural(String a, String b) {
+ int defaultResult = 0;
+ for (int i = 0; i < a.length; i++) {
+ if (i >= b.length) return 1;
+ var aChar = a.codeUnitAt(i);
+ var bChar = b.codeUnitAt(i);
+ if (aChar == bChar) continue;
+ int aUpperCase = aChar;
+ int bUpperCase = bChar;
+ if (_lowerCaseA <= aChar && aChar <= _lowerCaseZ) {
+ aUpperCase -= _asciiCaseBit;
+ }
+ if (_lowerCaseA <= bChar && bChar <= _lowerCaseZ) {
+ bUpperCase -= _asciiCaseBit;
+ }
+ if (aUpperCase != bUpperCase) {
+ return _compareNaturally(a, b, i, aUpperCase, bUpperCase);
+ }
+ if (defaultResult == 0) defaultResult = aChar - bChar;
+ }
+ if (b.length > a.length) return -1;
+ return defaultResult.sign;
+}
+
+/// Check for numbers overlapping the current mismatched characters.
+///
+/// If both [aChar] and [bChar] are digits, use numerical comparison.
+/// Check if the previous characters is a non-zero number, and if not,
+/// skip - but count - leading zeros before comparing numbers.
+///
+/// If one is a digit and the other isn't, check if the previous character
+/// is a digit, and if so, the the one with the digit is the greater number.
+///
+/// Otherwise just returns the difference between [aChar] and [bChar].
+int _compareNaturally(
+ String a, String b, int index, int aChar, int bChar) {
+ assert(aChar != bChar);
+ var aIsDigit = _isDigit(aChar);
+ var bIsDigit = _isDigit(bChar);
+ if (aIsDigit) {
+ if (bIsDigit) {
+ return _compareNumerically(a, b, aChar, bChar, index);
+ } else if (index > 0 && _isDigit(a.codeUnitAt(index - 1))) {
+ // aChar is the continuation of a longer number.
+ return 1;
+ }
+ } else if (bIsDigit && index > 0 && _isDigit(b.codeUnitAt(index - 1))) {
+ // bChar is the continuation of a longer number.
+ return -1;
+ }
+ // Characters are both non-digits, or not continuation of earlier number.
+ return (aChar - bChar).sign;
+}
+
+/// Compare numbers overlapping [aChar] and [bChar] numerically.
+///
+/// If the numbers have the same numerical value, but one has more leading
+/// zeros, the longer number is considered greater than the shorter one.
+///
+/// This ensures a total ordering on strings compatible with equality.
+int _compareNumerically(String a, String b, int aChar, int bChar, int index) {
+ // Both are digits. Find the first significant different digit, then find
+ // the length of the numbers.
+ if (_isNonZeroNumberSuffix(a, index)) {
+ // Part of a longer number, differs at this index, just count the length.
+ int result = _compareDigitCount(a, b, index, index);
+ if (result != 0) return result;
+ // If same length, the current character is the most significant differing
+ // digit.
+ return (aChar - bChar).sign;
+ }
+ // Not part of larger (non-zero) number, so skip leading zeros before
+ // comparing numbers.
+ int aIndex = index;
+ int bIndex = index;
+ if (aChar == _zero) {
+ do {
+ aIndex++;
+ if (aIndex == a.length) return -1; // number in a is zero, b is not.
+ aChar = a.codeUnitAt(aIndex);
+ } while (aChar == _zero);
+ if (!_isDigit(aChar)) return -1;
+ } else if (bChar == _zero) {
+ do {
+ bIndex++;
+ if (bIndex == b.length) return 1; // number in b is zero, a is not.
+ bChar = b.codeUnitAt(bIndex);
+ } while (bChar == _zero);
+ if (!_isDigit(bChar)) return 1;
+ }
+ if (aChar != bChar) {
+ int result = _compareDigitCount(a, b, aIndex, bIndex);
+ if (result != 0) return result;
+ return (aChar - bChar).sign;
+ }
+ // Same leading digit, one had more leading zeros.
+ // Compare digits until reaching a difference.
+ while (true) {
+ var aIsDigit = false;
+ var bIsDigit = false;
+ aChar = 0;
+ bChar = 0;
+ if (++aIndex < a.length) {
+ aChar = a.codeUnitAt(aIndex);
+ aIsDigit = _isDigit(aChar);
+ }
+ if (++bIndex < b.length) {
+ bChar = b.codeUnitAt(bIndex);
+ bIsDigit = _isDigit(bChar);
+ }
+ if (aIsDigit) {
+ if (bIsDigit) {
+ if (aChar == bChar) continue;
+ // First different digit found.
+ break;
+ }
+ // bChar is non-digit, so a has longer number.
+ return 1;
+ } else if (bIsDigit) {
+ return -1; // b has longer number.
+ } else {
+ // Neither is digit, so numbers had same numerical value.
+ // Fall back on number of leading zeros
+ // (reflected by difference in indices).
+ return (aIndex - bIndex).sign;
+ }
+ }
+ // At first differing digits.
+ int result = _compareDigitCount(a, b, aIndex, bIndex);
+ if (result != 0) return result;
+ return (aChar - bChar).sign;
+}
+
+/// Checks which of [a] and [b] has the longest sequence of digits.
+///
+/// Starts counting from `i + 1` and `j + 1` (assumes that `a[i]` and `b[j]` are
+/// both already known to be digits).
+int _compareDigitCount(String a, String b, int i, int j) {
+ while (++i < a.length) {
+ bool aIsDigit = _isDigit(a.codeUnitAt(i));
+ if (++j == b.length) return aIsDigit ? 1 : 0;
+ bool bIsDigit = _isDigit(b.codeUnitAt(j));
+ if (aIsDigit) {
+ if (bIsDigit) continue;
+ return 1;
+ } else if (bIsDigit) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ if (++j < b.length && _isDigit(b.codeUnitAt(j))) {
+ return -1;
+ }
+ return 0;
+}
+
+bool _isDigit(int charCode) => (charCode ^ _zero) <= 9;
+
+/// Check if the digit at [index] is continuing a non-zero number.
+///
+/// If there is no non-zero digits before, then leading zeros at [index]
+/// are also ignored when comparing numerically. If there is a non-zero digit
+/// before, then zeros at [index] are significant.
+bool _isNonZeroNumberSuffix(String string, int index) {
+ while (--index >= 0) {
+ int char = string.codeUnitAt(index);
+ if (char != _zero) return _isDigit(char);
+ }
+ return false;
+}
diff --git a/packages/collection/lib/wrappers.dart b/packages/collection/lib/wrappers.dart
index 9f8b833..30c736e 100644
--- a/packages/collection/lib/wrappers.dart
+++ b/packages/collection/lib/wrappers.dart
@@ -6,8 +6,8 @@
* Delegating wrappers for [Iterable], [List], [Set], [Queue] and [Map].
*
* Also adds unmodifiable views for `Set` and `Map`, and a fixed length
- * view for `List`. The unmodifable list view from `dart:collection` is exported
- * as well, just for completeness.
+ * view for `List`. The unmodifiable list view from `dart:collection` is
+ * exported as well, just for completeness.
*/
library dart.pkg.collection.wrappers;
diff --git a/packages/collection/pubspec.yaml b/packages/collection/pubspec.yaml
index 514ec4b..3642321 100644
--- a/packages/collection/pubspec.yaml
+++ b/packages/collection/pubspec.yaml
@@ -1,5 +1,5 @@
name: collection
-version: 1.1.3
+version: 1.2.0
author: Dart Team <misc@dartlang.org>
description: Collections and utilities functions and classes related to collections.
homepage: https://www.github.com/dart-lang/collection
diff --git a/packages/collection/test/comparators_test.dart b/packages/collection/test/comparators_test.dart
new file mode 100644
index 0000000..4acdc2c
--- /dev/null
+++ b/packages/collection/test/comparators_test.dart
@@ -0,0 +1,119 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:collection/collection.dart";
+import "package:test/test.dart";
+
+void main() {
+ List<String> strings = [
+ "",
+ "\x00",
+ " ",
+ "+",
+ "/",
+ "0",
+ "00",
+ "000",
+ "001",
+ "01",
+ "011",
+ "1",
+ "100",
+ "11",
+ "110",
+ "9",
+ ":",
+ "=",
+ "@",
+ "A",
+ "A0",
+ "A000A",
+ "A001A",
+ "A00A",
+ "A01A",
+ "A0A",
+ "A1A",
+ "AA",
+ "AAB",
+ "AB",
+ "Z",
+ "[",
+ "_",
+ "`",
+ "a",
+ "a0",
+ "a000a",
+ "a001a",
+ "a00a",
+ "a01a",
+ "a0a",
+ "a1a",
+ "aa",
+ "aab",
+ "ab",
+ "z",
+ "{",
+ "~"
+ ];
+
+ sortedBy(compare) => strings.toList()..shuffle()..sort(compare);
+
+ test("String.compareTo", () {
+ expect(sortedBy(null), strings);
+ });
+ test("compareAsciiLowerCase", () {
+ expect(sortedBy(compareAsciiLowerCase),
+ sortedBy((a, b) {
+ int delta = a.toLowerCase().compareTo(b.toLowerCase());
+ if (delta != 0) return delta;
+ if (a == b) return 0;
+ return a.compareTo(b);
+ }));
+ });
+ test("compareAsciiUpperCase", () {
+ expect(sortedBy(compareAsciiUpperCase),
+ sortedBy((a, b) {
+ int delta = a.toUpperCase().compareTo(b.toUpperCase());
+ if (delta != 0) return delta;
+ if (a == b) return 0;
+ return a.compareTo(b);
+ }));
+ });
+
+ // Replace any digit sequence by ("0", value, length) as char codes.
+ // This will sort alphabetically (by charcode) the way digits sort
+ // numerically, and the leading 0 means it sorts like a digit
+ // compared to non-digits.
+ replaceNumbers(string) => string.replaceAllMapped(new RegExp(r"\d+"), (m) {
+ var digits = m[0];
+ return new String.fromCharCodes([0x30, int.parse(digits), digits.length]);
+ });
+
+ test("compareNatural", () {
+ expect(sortedBy(compareNatural),
+ sortedBy((a, b) => replaceNumbers(a).compareTo(replaceNumbers(b))));
+ });
+
+ test("compareAsciiLowerCaseNatural", () {
+ expect(sortedBy(compareAsciiLowerCaseNatural),
+ sortedBy((a, b) {
+ int delta = replaceNumbers(a.toLowerCase()).compareTo(
+ replaceNumbers(b.toLowerCase()));
+ if (delta != 0) return delta;
+ if (a == b) return 0;
+ return a.compareTo(b);
+ }));
+ });
+
+ test("compareAsciiUpperCaseNatural", () {
+ expect(sortedBy(compareAsciiUpperCaseNatural),
+ sortedBy((a, b) {
+ int delta = replaceNumbers(a.toUpperCase()).compareTo(
+ replaceNumbers(b.toUpperCase()));
+ if (delta != 0) return delta;
+ if (a == b) return 0;
+ return a.compareTo(b);
+ }));
+ });
+}
diff --git a/packages/collection/test/wrapper_test.dart b/packages/collection/test/wrapper_test.dart
index a3526a3..e3043a2 100644
--- a/packages/collection/test/wrapper_test.dart
+++ b/packages/collection/test/wrapper_test.dart
@@ -147,7 +147,7 @@
expect.first.equals.first;
// Default values of the Iterable interface will be added in the
// second call to firstWhere, so we must record them in our
- // expectation (which doesn't have the interface implementat or
+ // expectation (which doesn't have the interface implemented or
// its default values).
expect.firstWhere(func1, orElse: null).equals.firstWhere(func1);
expect.firstWhere(func1, orElse: func0).equals.
diff --git a/packages/csslib/.classpath b/packages/csslib/.classpath
new file mode 100644
index 0000000..fb50116
--- /dev/null
+++ b/packages/csslib/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/packages/csslib/.gitignore b/packages/csslib/.gitignore
index 388eff0..a6867f2 100644
--- a/packages/csslib/.gitignore
+++ b/packages/csslib/.gitignore
@@ -1,8 +1,8 @@
# Don’t commit the following directories created by pub.
-.buildlog
.pub/
build/
packages
+.packages
# Or the files created by dart2js.
*.dart.js
@@ -11,4 +11,4 @@
*.js.map
# Include when developing application packages.
-pubspec.lock
\ No newline at end of file
+pubspec.lock
diff --git a/packages/csslib/.packages b/packages/csslib/.packages
deleted file mode 100644
index 7c47ae3..0000000
--- a/packages/csslib/.packages
+++ /dev/null
@@ -1,37 +0,0 @@
-# Generate by pub on 2015-07-10 14:34:54.820.
-# This file contains a map from Dart package names to Dart package locations.
-# Dart tools, including the Dart VM and Dart analyzer, rely on the content.
-# AUTO GENERATED - DO NOT EDIT
-analyzer:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/analyzer-0.25.1/lib/
-args:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/args-0.13.2/lib/
-async:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/async-1.2.0/lib/
-barback:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/barback-0.15.2+4/lib/
-browser:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/browser-0.10.0+2/lib/
-charcode:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.0/lib/
-collection:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/collection-1.1.1/lib/
-crypto:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/crypto-0.9.0/lib/
-csslib:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/csslib-0.12.1/lib/
-glob:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/glob-1.0.4/lib/
-html:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/html-0.12.1+2/lib/
-http_multi_server:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/http_multi_server-1.3.2/lib/
-http_parser:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/http_parser-0.0.2+7/lib/
-logging:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/logging-0.11.1/lib/
-matcher:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.0+1/lib/
-mime:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/mime-0.9.3/lib/
-package_config:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/package_config-0.1.1/lib/
-path:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/path-1.3.6/lib/
-plugin:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/plugin-0.1.0/lib/
-pool:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/pool-1.1.0/lib/
-pub_semver:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.2.1/lib/
-shelf:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/shelf-0.6.2/lib/
-shelf_static:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.2/lib/
-shelf_web_socket:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.0.1+2/lib/
-source_map_stack_trace:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-1.0.4/lib/
-source_maps:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.1/lib/
-source_span:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/source_span-1.1.2/lib/
-stack_trace:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.3.4/lib/
-string_scanner:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/string_scanner-0.1.3+1/lib/
-test:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/test-0.12.3+6/lib/
-utf:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+2/lib/
-watcher:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.6/lib/
-yaml:file:///usr/local/google/home/sigmund/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.3/lib/
diff --git a/packages/csslib/.project b/packages/csslib/.project
new file mode 100644
index 0000000..081cd0c
--- /dev/null
+++ b/packages/csslib/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>csslib</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/packages/csslib/.status b/packages/csslib/.status
deleted file mode 100644
index e9f2b00..0000000
--- a/packages/csslib/.status
+++ /dev/null
@@ -1,3 +0,0 @@
-# Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
diff --git a/packages/csslib/.test_config b/packages/csslib/.test_config
new file mode 100644
index 0000000..412fc5c
--- /dev/null
+++ b/packages/csslib/.test_config
@@ -0,0 +1,3 @@
+{
+ "test_package": true
+}
\ No newline at end of file
diff --git a/packages/csslib/CHANGELOG.md b/packages/csslib/CHANGELOG.md
index 4d14f0b..b4ee9e1 100644
--- a/packages/csslib/CHANGELOG.md
+++ b/packages/csslib/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.12.2
+
+ * Fix to handle calc functions however, the expressions are treated as a
+ LiteralTerm and not fully parsed into the AST.
+
## 0.12.1
* Fix to handling of escapes in strings.
diff --git a/packages/csslib/README.md b/packages/csslib/README.md
index e64fd9b..1610d3b 100644
--- a/packages/csslib/README.md
+++ b/packages/csslib/README.md
@@ -1,20 +1,12 @@
-csslib in Pure Dart
-===================
+CSS parser library for Dart
+==========================
-This is a pure [Dart][dart] [CSS parser][cssparse]. Since it's 100%
-Dart you can use it safely from a script or server side app.
+This is a [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS) parser written entirely in [Dart][dart].
+It can be used in the client/server/command line.
-Installation
-------------
-
-Add this to your `pubspec.yaml` (or create it):
-```yaml
-dependencies:
- csslib: any
-```
-Then run the [Pub Package Manager][pub] (comes with the Dart SDK):
-
- pub install
+This package is installed with [Pub][pub], see:
+[install instructions](https://pub.dartlang.org/packages/csslib#installing)
+for this package.
Usage
-----
@@ -34,48 +26,18 @@
You can pass a String or list of bytes to `parse`.
-Updating
---------
-
-You can upgrade the library with:
-
- pub update
-
-Disclaimer: the APIs are not finished. Updating may break your code. If that
-happens, you can check the
-[commit log](https://github.com/dart-lang/csslib/commits/master), to figure
-out what the change was.
-
-If you want to avoid breakage, you can also put the version constraint in your
-`pubspec.yaml` in place of the word `any`.
-
Running Tests
-------------
-All tests (both canary and suite) should be passing. Canary are quick test
-verifies that basic CSS is working. The suite tests are a comprehensive set of
-~11,000 tests.
-
+Basic tests can be found in this repository:
```bash
-export DART_SDK=path/to/dart/sdk
-
-# Make sure dependencies are installed
-pub install
-
-# Run command both canary and the suite tests
-test/run.sh
+pub run test
```
- Run only the canary test:
-
+The full CSS test suite can be found in https://github.com/dart-lang/csslib-test-suite
```bash
- test/run.sh canary
-```
-
- Run only the suite tests:
-
-```bash
- test/run.sh suite
+cd ../csslib-test-suite
+./run.sh
```
[dart]: http://www.dartlang.org/
diff --git a/packages/csslib/lib/parser.dart b/packages/csslib/lib/parser.dart
index 8692693..b3a22d6 100644
--- a/packages/csslib/lib/parser.dart
+++ b/packages/csslib/lib/parser.dart
@@ -64,7 +64,7 @@
analyze([tree], errors: errors, options: options);
if (polyfill) {
- var processCss = new PolyFill(messages, true);
+ var processCss = new PolyFill(messages);
processCss.process(tree, includes: includes);
}
@@ -430,6 +430,7 @@
if (unaryOp != -1 || type != null || exprs.length > 0) {
return new MediaQuery(unaryOp, type, exprs, _makeSpan(start));
}
+ return null;
}
MediaExpression processMediaExpression([bool andOperator = false]) {
@@ -453,9 +454,9 @@
}
} else if (isChecked) {
_warning("Missing media feature in media expression", _makeSpan(start));
- return null;
}
}
+ return null;
}
/**
@@ -798,7 +799,6 @@
_eat(TokenKind.LBRACE);
List<TreeNode> productions = [];
- List<TreeNode> declarations = [];
var mixinDirective;
var start = _peekToken.span;
@@ -984,6 +984,7 @@
return new RuleSet(
selectorGroup, processDeclarations(), selectorGroup.span);
}
+ return null;
}
/**
@@ -1191,6 +1192,7 @@
if (selectors.length > 0) {
return new SelectorGroup(selectors, _makeSpan(start));
}
+ return null;
}
/**
@@ -1602,6 +1604,7 @@
return new AttributeSelector(attrName, op, value, _makeSpan(start));
}
+ return null;
}
// Declaration grammar:
@@ -1763,6 +1766,7 @@
if (styleType != null) {
return buildDartStyleNode(styleType, exprs, dartStyles);
}
+ return null;
}
FontExpression _mergeFontStyles(FontExpression fontExpr, List dartStyles) {
@@ -1910,10 +1914,8 @@
return processOneNumber(exprs, styleType);
}
break;
- default:
- // Don't handle it.
- return null;
}
+ return null;
}
// TODO(terry): Look at handling width of thin, thick, etc. any none numbers
@@ -1956,6 +1958,7 @@
return new PaddingExpression(exprs.span, bottom: value);
}
}
+ return null;
}
/**
@@ -2185,6 +2188,8 @@
var nameValue = identifier(); // Snarf up the ident we'll remap, maybe.
if (!ieFilter && _maybeEat(TokenKind.LPAREN)) {
+ var calc = processCalc(nameValue);
+ if (calc != null) return calc;
// FUNCTION
return processFunction(nameValue);
}
@@ -2439,6 +2444,64 @@
}
}
+ // TODO(terry): Hack to gobble up the calc expression as a string looking
+ // for the matching RPAREN the expression is not parsed into the
+ // AST.
+ //
+ // grammar should be:
+ //
+ // <calc()> = calc( <calc-sum> )
+ // <calc-sum> = <calc-product> [ [ '+' | '-' ] <calc-product> ]*
+ // <calc-product> = <calc-value> [ '*' <calc-value> | '/' <number> ]*
+ // <calc-value> = <number> | <dimension> | <percentage> | ( <calc-sum> )
+ //
+ String processCalcExpression() {
+ var inString = tokenizer._inString;
+ tokenizer._inString = false;
+
+ // Gobble up everything until we hit our stop token.
+ var stringValue = new StringBuffer();
+ var left = 1;
+ var matchingParens = false;
+ while (_peek() != TokenKind.END_OF_FILE && !matchingParens) {
+ var token = _peek();
+ if (token == TokenKind.LPAREN)
+ left++;
+ else if (token == TokenKind.RPAREN)
+ left--;
+
+ matchingParens = left == 0;
+ if (!matchingParens) stringValue.write(_next().text);
+ }
+
+ if (!matchingParens) {
+ _error("problem parsing function expected ), ", _peekToken.span);
+ }
+
+ tokenizer._inString = inString;
+
+ return stringValue.toString();
+ }
+
+ CalcTerm processCalc(Identifier func) {
+ var start = _peekToken.span;
+
+ var name = func.name;
+ if (name == 'calc') {
+ // TODO(terry): Implement expression parsing properly.
+ String expression = processCalcExpression();
+ var calcExpr = new LiteralTerm(expression, expression, _makeSpan(start));
+
+ if (!_maybeEat(TokenKind.RPAREN)) {
+ _error("problem parsing function expected ), ", _peekToken.span);
+ }
+
+ return new CalcTerm(name, name, calcExpr, _makeSpan(start));
+ }
+
+ return null;
+ }
+
// Function grammar:
//
// function: IDENT '(' expr ')'
@@ -2463,9 +2526,6 @@
}
return new UriTerm(urlParam, _makeSpan(start));
- case 'calc':
- // TODO(terry): Implement expression handling...
- break;
case 'var':
// TODO(terry): Consider handling var in IE specific filter/progid. This
// will require parsing entire IE specific syntax e.g.,
diff --git a/packages/csslib/lib/src/analyzer.dart b/packages/csslib/lib/src/analyzer.dart
index 4fdd833..fc27ceb 100644
--- a/packages/csslib/lib/src/analyzer.dart
+++ b/packages/csslib/lib/src/analyzer.dart
@@ -463,8 +463,8 @@
} else if (currDef is MixinRulesetDirective && _anyRulesets(currDef)) {
// currDef is MixinRulesetDirective
MixinRulesetDirective mixinRuleset = currDef;
- int index = mixinRuleset.rulesets.indexOf(node as dynamic);
- mixinRuleset.rulesets.replaceRange(index, index + 1, [new NoOp()]);
+ int index = mixinRuleset.rulesets.indexOf(node);
+ mixinRuleset.rulesets.removeAt(index);
_messages.warning(
'Using declaration mixin ${node.name} as top-level mixin',
node.span);
@@ -472,13 +472,12 @@
} else {
if (currDef is MixinRulesetDirective) {
MixinRulesetDirective rulesetDirect = currDef as MixinRulesetDirective;
- var index = 0;
- rulesetDirect.rulesets.forEach((entry) {
+ rulesetDirect.rulesets.removeWhere((entry) {
if (entry == node) {
- rulesetDirect.rulesets.replaceRange(index, index + 1, [new NoOp()]);
_messages.warning('Undefined mixin ${node.name}', node.span);
+ return true;
}
- index++;
+ return false;
});
}
}
diff --git a/packages/csslib/lib/src/css_printer.dart b/packages/csslib/lib/src/css_printer.dart
index 125b5ae..a62ca47 100644
--- a/packages/csslib/lib/src/css_printer.dart
+++ b/packages/csslib/lib/src/css_printer.dart
@@ -38,6 +38,12 @@
// flag for obfuscation.
bool get _isTesting => !prettyPrint;
+ void visitCalcTerm(CalcTerm node) {
+ emit('${node.text}(');
+ node.expr.visit(this);
+ emit(')');
+ }
+
void visitCssComment(CssComment node) {
emit('/* ${node.comment} */');
}
diff --git a/packages/csslib/lib/src/polyfill.dart b/packages/csslib/lib/src/polyfill.dart
index 9b682ce..b18abd1 100644
--- a/packages/csslib/lib/src/polyfill.dart
+++ b/packages/csslib/lib/src/polyfill.dart
@@ -10,7 +10,6 @@
*/
class PolyFill {
final Messages _messages;
- final bool _warningsAsErrors;
Map<String, VarDefinition> _allVarDefinitions =
new Map<String, VarDefinition>();
@@ -21,7 +20,7 @@
* CSS pseudo-elements 'name::custom-element' is mapped to the manged name
* associated with the pseudo-element key.
*/
- PolyFill(this._messages, this._warningsAsErrors);
+ PolyFill(this._messages);
/**
* Run the analyzer on every file that is a style sheet or any component that
@@ -227,7 +226,7 @@
var expressions = varDef.expression as Expressions;
for (var expr in expressions.expressions) {
if (expr is VarUsage) {
- var usageName = (expr as VarUsage).name;
+ var usageName = expr.name;
var foundDef = varDefs[usageName];
// If foundDef is unknown check if defaultValues; if it exist then resolve
@@ -236,7 +235,7 @@
// We're either a VarUsage or terminal definition if in varDefs;
// either way replace VarUsage with it's default value because the
// VarDefinition isn't found.
- var defaultValues = (expr as VarUsage).defaultValues;
+ var defaultValues = expr.defaultValues;
var replaceExprs = expressions.expressions;
assert(replaceExprs.length == 1);
replaceExprs.replaceRange(0, 1, defaultValues);
diff --git a/packages/csslib/lib/src/property.dart b/packages/csslib/lib/src/property.dart
index 5d6dc14..c2cf776 100644
--- a/packages/csslib/lib/src/property.dart
+++ b/packages/csslib/lib/src/property.dart
@@ -278,10 +278,11 @@
return new Hsla(args[0], args[1], args[2], args[3]).toHexArgbString();
default:
// Type not defined UnsupportedOperationException should have thrown.
- assert(true);
+ assert(false);
break;
}
}
+ return null;
}
static int hexToInt(String hex) => int.parse(hex, radix: 16);
@@ -785,6 +786,7 @@
String get cssExpression {
// TODO(terry): TBD
+ return null;
}
}
diff --git a/packages/csslib/lib/src/tokenkind.dart b/packages/csslib/lib/src/tokenkind.dart
index 617f062..27ccb4b 100644
--- a/packages/csslib/lib/src/tokenkind.dart
+++ b/packages/csslib/lib/src/tokenkind.dart
@@ -192,7 +192,7 @@
static const int PSEUDO_CLASS_NAME = 705; // :pseudoClass
static const int NEGATION = 706; // NOT
- static const List<Map<int, String>> _DIRECTIVES = const [
+ static const List<Map<String, dynamic>> _DIRECTIVES = const [
const {'type': TokenKind.DIRECTIVE_IMPORT, 'value': 'import'},
const {'type': TokenKind.DIRECTIVE_MEDIA, 'value': 'media'},
const {'type': TokenKind.DIRECTIVE_PAGE, 'value': 'page'},
@@ -218,13 +218,13 @@
const {'type': TokenKind.DIRECTIVE_EXTEND, 'value': 'extend'},
];
- static const List<Map<int, String>> MEDIA_OPERATORS = const [
+ static const List<Map<String, dynamic>> MEDIA_OPERATORS = const [
const {'type': TokenKind.MEDIA_OP_ONLY, 'value': 'only'},
const {'type': TokenKind.MEDIA_OP_NOT, 'value': 'not'},
const {'type': TokenKind.MEDIA_OP_AND, 'value': 'and'},
];
- static const List<Map<int, String>> MARGIN_DIRECTIVES = const [
+ static const List<Map<String, dynamic>> MARGIN_DIRECTIVES = const [
const {
'type': TokenKind.MARGIN_DIRECTIVE_TOPLEFTCORNER,
'value': 'top-left-corner'
diff --git a/packages/csslib/lib/src/tree.dart b/packages/csslib/lib/src/tree.dart
index 5dad435..ba8370e 100644
--- a/packages/csslib/lib/src/tree.dart
+++ b/packages/csslib/lib/src/tree.dart
@@ -44,6 +44,21 @@
String get name => 'not';
}
+// calc(...)
+// TODO(terry): Hack to handle calc however the expressions should be fully
+// parsed and in the AST.
+class CalcTerm extends LiteralTerm {
+ final LiteralTerm expr;
+
+ CalcTerm(var value, String t, this.expr, SourceSpan span)
+ : super(value, t, span);
+
+ CalcTerm clone() => new CalcTerm(value, text, expr.clone(), span);
+ visit(VisitorBase visitor) => visitor.visitCalcTerm(this);
+
+ String toString() => "$text($expr)";
+}
+
// /* .... */
class CssComment extends TreeNode {
final String comment;
@@ -197,6 +212,7 @@
case TokenKind.NO_MATCH:
return '';
}
+ return null;
}
// Return the TokenKind for operator used by visitAttributeSelector.
@@ -215,6 +231,7 @@
case TokenKind.SUBSTRING_MATCH:
return 'SUBSTRING_MATCH';
}
+ return null;
}
String valueToString() {
@@ -572,6 +589,7 @@
case TokenKind.DIRECTIVE_O_KEYFRAMES:
return '@-o-keyframes';
}
+ return null;
}
KeyFrameDirective clone() {
@@ -676,7 +694,7 @@
/** Support a Sass @mixin. See http://sass-lang.com for description. */
class MixinRulesetDirective extends MixinDefinition {
- final List<RuleSet> rulesets;
+ final List rulesets;
MixinRulesetDirective(String name, List<VarDefinitionDirective> args,
bool varArgs, this.rulesets, SourceSpan span)
diff --git a/packages/csslib/lib/src/tree_printer.dart b/packages/csslib/lib/src/tree_printer.dart
index 030a868..9b0a6c2 100644
--- a/packages/csslib/lib/src/tree_printer.dart
+++ b/packages/csslib/lib/src/tree_printer.dart
@@ -46,6 +46,13 @@
heading('Directive', node);
}
+ void visitCalcTerm(CalcTerm node) {
+ heading('CalcTerm', node);
+ output.depth++;
+ super.visitCalcTerm(node);
+ output.depth--;
+ }
+
void visitCssComment(CssComment node) {
heading('Comment', node);
output.depth++;
diff --git a/packages/csslib/lib/src/validate.dart b/packages/csslib/lib/src/validate.dart
index d45cd95..e716e66 100644
--- a/packages/csslib/lib/src/validate.dart
+++ b/packages/csslib/lib/src/validate.dart
@@ -53,7 +53,6 @@
// Validate the @{css expression} only .class and #elementId are valid inside
// of @{...}.
static template(List<Selector> selectors) {
- var errorSelector; // signal which selector didn't match.
bool found = false; // signal if a selector is matched.
int matches = 0; // < 0 IdSelectors, > 0 ClassSelector
diff --git a/packages/csslib/lib/visitor.dart b/packages/csslib/lib/visitor.dart
index fa0f8d2..b6babbd 100644
--- a/packages/csslib/lib/visitor.dart
+++ b/packages/csslib/lib/visitor.dart
@@ -13,99 +13,100 @@
part 'src/tree_printer.dart';
abstract class VisitorBase {
- void visitCssComment(CssComment node);
- void visitCommentDefinition(CommentDefinition node);
- void visitStyleSheet(StyleSheet node);
- void visitNoOp(NoOp node);
- void visitTopLevelProduction(TopLevelProduction node);
- void visitDirective(Directive node);
- void visitMediaExpression(MediaExpression node);
- void visitMediaQuery(MediaQuery node);
- void visitMediaDirective(MediaDirective node);
- void visitHostDirective(HostDirective node);
- void visitPageDirective(PageDirective node);
- void visitCharsetDirective(CharsetDirective node);
- void visitImportDirective(ImportDirective node);
- void visitKeyFrameDirective(KeyFrameDirective node);
- void visitKeyFrameBlock(KeyFrameBlock node);
- void visitFontFaceDirective(FontFaceDirective node);
- void visitStyletDirective(StyletDirective node);
- void visitNamespaceDirective(NamespaceDirective node);
- void visitVarDefinitionDirective(VarDefinitionDirective node);
- void visitMixinDefinition(MixinDefinition node);
- void visitMixinRulesetDirective(MixinRulesetDirective node);
- void visitMixinDeclarationDirective(MixinDeclarationDirective node);
- void visitIncludeDirective(IncludeDirective node);
- void visitContentDirective(ContentDirective node);
+ visitCalcTerm(CalcTerm node);
+ visitCssComment(CssComment node);
+ visitCommentDefinition(CommentDefinition node);
+ visitStyleSheet(StyleSheet node);
+ visitNoOp(NoOp node);
+ visitTopLevelProduction(TopLevelProduction node);
+ visitDirective(Directive node);
+ visitMediaExpression(MediaExpression node);
+ visitMediaQuery(MediaQuery node);
+ visitMediaDirective(MediaDirective node);
+ visitHostDirective(HostDirective node);
+ visitPageDirective(PageDirective node);
+ visitCharsetDirective(CharsetDirective node);
+ visitImportDirective(ImportDirective node);
+ visitKeyFrameDirective(KeyFrameDirective node);
+ visitKeyFrameBlock(KeyFrameBlock node);
+ visitFontFaceDirective(FontFaceDirective node);
+ visitStyletDirective(StyletDirective node);
+ visitNamespaceDirective(NamespaceDirective node);
+ visitVarDefinitionDirective(VarDefinitionDirective node);
+ visitMixinDefinition(MixinDefinition node);
+ visitMixinRulesetDirective(MixinRulesetDirective node);
+ visitMixinDeclarationDirective(MixinDeclarationDirective node);
+ visitIncludeDirective(IncludeDirective node);
+ visitContentDirective(ContentDirective node);
- void visitRuleSet(RuleSet node);
- void visitDeclarationGroup(DeclarationGroup node);
- void visitMarginGroup(MarginGroup node);
- void visitDeclaration(Declaration node);
- void visitVarDefinition(VarDefinition node);
- void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node);
- void visitExtendDeclaration(ExtendDeclaration node);
- void visitSelectorGroup(SelectorGroup node);
- void visitSelector(Selector node);
- void visitSimpleSelectorSequence(SimpleSelectorSequence node);
- void visitSimpleSelector(SimpleSelector node);
- void visitElementSelector(ElementSelector node);
- void visitNamespaceSelector(NamespaceSelector node);
- void visitAttributeSelector(AttributeSelector node);
- void visitIdSelector(IdSelector node);
- void visitClassSelector(ClassSelector node);
- void visitPseudoClassSelector(PseudoClassSelector node);
- void visitPseudoElementSelector(PseudoElementSelector node);
- void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node);
- void visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node);
- void visitNegationSelector(NegationSelector node);
- void visitSelectorExpression(SelectorExpression node);
+ visitRuleSet(RuleSet node);
+ visitDeclarationGroup(DeclarationGroup node);
+ visitMarginGroup(MarginGroup node);
+ visitDeclaration(Declaration node);
+ visitVarDefinition(VarDefinition node);
+ visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node);
+ visitExtendDeclaration(ExtendDeclaration node);
+ visitSelectorGroup(SelectorGroup node);
+ visitSelector(Selector node);
+ visitSimpleSelectorSequence(SimpleSelectorSequence node);
+ visitSimpleSelector(SimpleSelector node);
+ visitElementSelector(ElementSelector node);
+ visitNamespaceSelector(NamespaceSelector node);
+ visitAttributeSelector(AttributeSelector node);
+ visitIdSelector(IdSelector node);
+ visitClassSelector(ClassSelector node);
+ visitPseudoClassSelector(PseudoClassSelector node);
+ visitPseudoElementSelector(PseudoElementSelector node);
+ visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node);
+ visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node);
+ visitNegationSelector(NegationSelector node);
+ visitSelectorExpression(SelectorExpression node);
- void visitUnicodeRangeTerm(UnicodeRangeTerm node);
- void visitLiteralTerm(LiteralTerm node);
- void visitHexColorTerm(HexColorTerm node);
- void visitNumberTerm(NumberTerm node);
- void visitUnitTerm(UnitTerm node);
- void visitLengthTerm(LengthTerm node);
- void visitPercentageTerm(PercentageTerm node);
- void visitEmTerm(EmTerm node);
- void visitExTerm(ExTerm node);
- void visitAngleTerm(AngleTerm node);
- void visitTimeTerm(TimeTerm node);
- void visitFreqTerm(FreqTerm node);
- void visitFractionTerm(FractionTerm node);
- void visitUriTerm(UriTerm node);
- void visitResolutionTerm(ResolutionTerm node);
- void visitChTerm(ChTerm node);
- void visitRemTerm(RemTerm node);
- void visitViewportTerm(ViewportTerm node);
- void visitFunctionTerm(FunctionTerm node);
- void visitGroupTerm(GroupTerm node);
- void visitItemTerm(ItemTerm node);
- void visitIE8Term(IE8Term node);
- void visitOperatorSlash(OperatorSlash node);
- void visitOperatorComma(OperatorComma node);
- void visitOperatorPlus(OperatorPlus node);
- void visitOperatorMinus(OperatorMinus node);
- void visitVarUsage(VarUsage node);
+ visitUnicodeRangeTerm(UnicodeRangeTerm node);
+ visitLiteralTerm(LiteralTerm node);
+ visitHexColorTerm(HexColorTerm node);
+ visitNumberTerm(NumberTerm node);
+ visitUnitTerm(UnitTerm node);
+ visitLengthTerm(LengthTerm node);
+ visitPercentageTerm(PercentageTerm node);
+ visitEmTerm(EmTerm node);
+ visitExTerm(ExTerm node);
+ visitAngleTerm(AngleTerm node);
+ visitTimeTerm(TimeTerm node);
+ visitFreqTerm(FreqTerm node);
+ visitFractionTerm(FractionTerm node);
+ visitUriTerm(UriTerm node);
+ visitResolutionTerm(ResolutionTerm node);
+ visitChTerm(ChTerm node);
+ visitRemTerm(RemTerm node);
+ visitViewportTerm(ViewportTerm node);
+ visitFunctionTerm(FunctionTerm node);
+ visitGroupTerm(GroupTerm node);
+ visitItemTerm(ItemTerm node);
+ visitIE8Term(IE8Term node);
+ visitOperatorSlash(OperatorSlash node);
+ visitOperatorComma(OperatorComma node);
+ visitOperatorPlus(OperatorPlus node);
+ visitOperatorMinus(OperatorMinus node);
+ visitVarUsage(VarUsage node);
- void visitExpressions(Expressions node);
- void visitBinaryExpression(BinaryExpression node);
- void visitUnaryExpression(UnaryExpression node);
+ visitExpressions(Expressions node);
+ visitBinaryExpression(BinaryExpression node);
+ visitUnaryExpression(UnaryExpression node);
- void visitIdentifier(Identifier node);
- void visitWildcard(Wildcard node);
- void visitThisOperator(ThisOperator node);
- void visitNegation(Negation node);
+ visitIdentifier(Identifier node);
+ visitWildcard(Wildcard node);
+ visitThisOperator(ThisOperator node);
+ visitNegation(Negation node);
- void visitDartStyleExpression(DartStyleExpression node);
- void visitFontExpression(FontExpression node);
- void visitBoxExpression(BoxExpression node);
- void visitMarginExpression(MarginExpression node);
- void visitBorderExpression(BorderExpression node);
- void visitHeightExpression(HeightExpression node);
- void visitPaddingExpression(PaddingExpression node);
- void visitWidthExpression(WidthExpression node);
+ visitDartStyleExpression(DartStyleExpression node);
+ visitFontExpression(FontExpression node);
+ visitBoxExpression(BoxExpression node);
+ visitMarginExpression(MarginExpression node);
+ visitBorderExpression(BorderExpression node);
+ visitHeightExpression(HeightExpression node);
+ visitPaddingExpression(PaddingExpression node);
+ visitWidthExpression(WidthExpression node);
}
/** Base vistor class for the style sheet AST. */
@@ -120,33 +121,38 @@
}
}
- void visitTree(StyleSheet tree) => visitStyleSheet(tree);
+ visitTree(StyleSheet tree) => visitStyleSheet(tree);
- void visitStyleSheet(StyleSheet ss) {
+ visitStyleSheet(StyleSheet ss) {
_visitNodeList(ss.topLevels);
}
- void visitNoOp(NoOp node) {}
+ visitNoOp(NoOp node) {}
- void visitTopLevelProduction(TopLevelProduction node) {}
+ visitTopLevelProduction(TopLevelProduction node) {}
- void visitDirective(Directive node) {}
+ visitDirective(Directive node) {}
- void visitCssComment(CssComment node) {}
+ visitCalcTerm(CalcTerm node) {
+ visitLiteralTerm(node);
+ visitLiteralTerm(node.expr);
+ }
- void visitCommentDefinition(CommentDefinition node) {}
+ visitCssComment(CssComment node) {}
- void visitMediaExpression(MediaExpression node) {
+ visitCommentDefinition(CommentDefinition node) {}
+
+ visitMediaExpression(MediaExpression node) {
visitExpressions(node.exprs);
}
- void visitMediaQuery(MediaQuery node) {
+ visitMediaQuery(MediaQuery node) {
for (var mediaExpr in node.expressions) {
visitMediaExpression(mediaExpr);
}
}
- void visitMediaDirective(MediaDirective node) {
+ visitMediaDirective(MediaDirective node) {
for (var mediaQuery in node.mediaQueries) {
visitMediaQuery(mediaQuery);
}
@@ -155,13 +161,13 @@
}
}
- void visitHostDirective(HostDirective node) {
+ visitHostDirective(HostDirective node) {
for (var ruleset in node.rulesets) {
visitRuleSet(ruleset);
}
}
- void visitPageDirective(PageDirective node) {
+ visitPageDirective(PageDirective node) {
for (var declGroup in node._declsMargin) {
if (declGroup is MarginGroup) {
visitMarginGroup(declGroup);
@@ -171,285 +177,285 @@
}
}
- void visitCharsetDirective(CharsetDirective node) {}
+ visitCharsetDirective(CharsetDirective node) {}
- void visitImportDirective(ImportDirective node) {
+ visitImportDirective(ImportDirective node) {
for (var mediaQuery in node.mediaQueries) {
visitMediaQuery(mediaQuery);
}
}
- void visitKeyFrameDirective(KeyFrameDirective node) {
+ visitKeyFrameDirective(KeyFrameDirective node) {
visitIdentifier(node.name);
_visitNodeList(node._blocks);
}
- void visitKeyFrameBlock(KeyFrameBlock node) {
+ visitKeyFrameBlock(KeyFrameBlock node) {
visitExpressions(node._blockSelectors);
visitDeclarationGroup(node._declarations);
}
- void visitFontFaceDirective(FontFaceDirective node) {
+ visitFontFaceDirective(FontFaceDirective node) {
visitDeclarationGroup(node._declarations);
}
- void visitStyletDirective(StyletDirective node) {
+ visitStyletDirective(StyletDirective node) {
_visitNodeList(node.rulesets);
}
- void visitNamespaceDirective(NamespaceDirective node) {}
+ visitNamespaceDirective(NamespaceDirective node) {}
- void visitVarDefinitionDirective(VarDefinitionDirective node) {
+ visitVarDefinitionDirective(VarDefinitionDirective node) {
visitVarDefinition(node.def);
}
- void visitMixinRulesetDirective(MixinRulesetDirective node) {
+ visitMixinRulesetDirective(MixinRulesetDirective node) {
_visitNodeList(node.rulesets);
}
- void visitMixinDefinition(MixinDefinition node) {}
+ visitMixinDefinition(MixinDefinition node) {}
- void visitMixinDeclarationDirective(MixinDeclarationDirective node) {
+ visitMixinDeclarationDirective(MixinDeclarationDirective node) {
visitDeclarationGroup(node.declarations);
}
- void visitIncludeDirective(IncludeDirective node) {
+ visitIncludeDirective(IncludeDirective node) {
for (var index = 0; index < node.args.length; index++) {
var param = node.args[index];
_visitNodeList(param);
}
}
- void visitContentDirective(ContentDirective node) {
+ visitContentDirective(ContentDirective node) {
// TODO(terry): TBD
}
- void visitRuleSet(RuleSet node) {
+ visitRuleSet(RuleSet node) {
visitSelectorGroup(node._selectorGroup);
visitDeclarationGroup(node._declarationGroup);
}
- void visitDeclarationGroup(DeclarationGroup node) {
+ visitDeclarationGroup(DeclarationGroup node) {
_visitNodeList(node.declarations);
}
- void visitMarginGroup(MarginGroup node) => visitDeclarationGroup(node);
+ visitMarginGroup(MarginGroup node) => visitDeclarationGroup(node);
- void visitDeclaration(Declaration node) {
+ visitDeclaration(Declaration node) {
visitIdentifier(node._property);
if (node._expression != null) node._expression.visit(this);
}
- void visitVarDefinition(VarDefinition node) {
+ visitVarDefinition(VarDefinition node) {
visitIdentifier(node._property);
if (node._expression != null) node._expression.visit(this);
}
- void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) {
+ visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) {
visitIncludeDirective(node.include);
}
- void visitExtendDeclaration(ExtendDeclaration node) {
+ visitExtendDeclaration(ExtendDeclaration node) {
_visitNodeList(node.selectors);
}
- void visitSelectorGroup(SelectorGroup node) {
+ visitSelectorGroup(SelectorGroup node) {
_visitNodeList(node.selectors);
}
- void visitSelector(Selector node) {
+ visitSelector(Selector node) {
_visitNodeList(node.simpleSelectorSequences);
}
- void visitSimpleSelectorSequence(SimpleSelectorSequence node) {
+ visitSimpleSelectorSequence(SimpleSelectorSequence node) {
node.simpleSelector.visit(this);
}
- void visitSimpleSelector(SimpleSelector node) => node._name.visit(this);
+ visitSimpleSelector(SimpleSelector node) => node._name.visit(this);
- void visitNamespaceSelector(NamespaceSelector node) {
+ visitNamespaceSelector(NamespaceSelector node) {
if (node._namespace != null) node._namespace.visit(this);
if (node.nameAsSimpleSelector != null) {
node.nameAsSimpleSelector.visit(this);
}
}
- void visitElementSelector(ElementSelector node) => visitSimpleSelector(node);
+ visitElementSelector(ElementSelector node) => visitSimpleSelector(node);
- void visitAttributeSelector(AttributeSelector node) {
+ visitAttributeSelector(AttributeSelector node) {
visitSimpleSelector(node);
}
- void visitIdSelector(IdSelector node) => visitSimpleSelector(node);
+ visitIdSelector(IdSelector node) => visitSimpleSelector(node);
- void visitClassSelector(ClassSelector node) => visitSimpleSelector(node);
+ visitClassSelector(ClassSelector node) => visitSimpleSelector(node);
- void visitPseudoClassSelector(PseudoClassSelector node) =>
+ visitPseudoClassSelector(PseudoClassSelector node) =>
visitSimpleSelector(node);
- void visitPseudoElementSelector(PseudoElementSelector node) =>
+ visitPseudoElementSelector(PseudoElementSelector node) =>
visitSimpleSelector(node);
- void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) =>
+ visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) =>
visitSimpleSelector(node);
- void visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node) =>
+ visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node) =>
visitSimpleSelector(node);
- void visitNegationSelector(NegationSelector node) =>
+ visitNegationSelector(NegationSelector node) =>
visitSimpleSelector(node);
- void visitSelectorExpression(SelectorExpression node) {
+ visitSelectorExpression(SelectorExpression node) {
_visitNodeList(node.expressions);
}
- void visitUnicodeRangeTerm(UnicodeRangeTerm node) {}
+ visitUnicodeRangeTerm(UnicodeRangeTerm node) {}
- void visitLiteralTerm(LiteralTerm node) {}
+ visitLiteralTerm(LiteralTerm node) {}
- void visitHexColorTerm(HexColorTerm node) {}
+ visitHexColorTerm(HexColorTerm node) {}
- void visitNumberTerm(NumberTerm node) {}
+ visitNumberTerm(NumberTerm node) {}
- void visitUnitTerm(UnitTerm node) {}
+ visitUnitTerm(UnitTerm node) {}
- void visitLengthTerm(LengthTerm node) {
+ visitLengthTerm(LengthTerm node) {
visitUnitTerm(node);
}
- void visitPercentageTerm(PercentageTerm node) {
+ visitPercentageTerm(PercentageTerm node) {
visitLiteralTerm(node);
}
- void visitEmTerm(EmTerm node) {
+ visitEmTerm(EmTerm node) {
visitLiteralTerm(node);
}
- void visitExTerm(ExTerm node) {
+ visitExTerm(ExTerm node) {
visitLiteralTerm(node);
}
- void visitAngleTerm(AngleTerm node) {
+ visitAngleTerm(AngleTerm node) {
visitUnitTerm(node);
}
- void visitTimeTerm(TimeTerm node) {
+ visitTimeTerm(TimeTerm node) {
visitUnitTerm(node);
}
- void visitFreqTerm(FreqTerm node) {
+ visitFreqTerm(FreqTerm node) {
visitUnitTerm(node);
}
- void visitFractionTerm(FractionTerm node) {
+ visitFractionTerm(FractionTerm node) {
visitLiteralTerm(node);
}
- void visitUriTerm(UriTerm node) {
+ visitUriTerm(UriTerm node) {
visitLiteralTerm(node);
}
- void visitResolutionTerm(ResolutionTerm node) {
+ visitResolutionTerm(ResolutionTerm node) {
visitUnitTerm(node);
}
- void visitChTerm(ChTerm node) {
+ visitChTerm(ChTerm node) {
visitUnitTerm(node);
}
- void visitRemTerm(RemTerm node) {
+ visitRemTerm(RemTerm node) {
visitUnitTerm(node);
}
- void visitViewportTerm(ViewportTerm node) {
+ visitViewportTerm(ViewportTerm node) {
visitUnitTerm(node);
}
- void visitFunctionTerm(FunctionTerm node) {
+ visitFunctionTerm(FunctionTerm node) {
visitLiteralTerm(node);
visitExpressions(node._params);
}
- void visitGroupTerm(GroupTerm node) {
+ visitGroupTerm(GroupTerm node) {
for (var term in node._terms) {
term.visit(this);
}
}
- void visitItemTerm(ItemTerm node) {
+ visitItemTerm(ItemTerm node) {
visitNumberTerm(node);
}
- void visitIE8Term(IE8Term node) {}
+ visitIE8Term(IE8Term node) {}
- void visitOperatorSlash(OperatorSlash node) {}
+ visitOperatorSlash(OperatorSlash node) {}
- void visitOperatorComma(OperatorComma node) {}
+ visitOperatorComma(OperatorComma node) {}
- void visitOperatorPlus(OperatorPlus node) {}
+ visitOperatorPlus(OperatorPlus node) {}
- void visitOperatorMinus(OperatorMinus node) {}
+ visitOperatorMinus(OperatorMinus node) {}
- void visitVarUsage(VarUsage node) {
+ visitVarUsage(VarUsage node) {
_visitNodeList(node.defaultValues);
}
- void visitExpressions(Expressions node) {
+ visitExpressions(Expressions node) {
_visitNodeList(node.expressions);
}
- void visitBinaryExpression(BinaryExpression node) {
+ visitBinaryExpression(BinaryExpression node) {
// TODO(terry): TBD
- throw UnimplementedError;
+ throw new UnimplementedError();
}
- void visitUnaryExpression(UnaryExpression node) {
+ visitUnaryExpression(UnaryExpression node) {
// TODO(terry): TBD
- throw UnimplementedError;
+ throw new UnimplementedError();
}
- void visitIdentifier(Identifier node) {}
+ visitIdentifier(Identifier node) {}
- void visitWildcard(Wildcard node) {}
+ visitWildcard(Wildcard node) {}
- void visitThisOperator(ThisOperator node) {}
+ visitThisOperator(ThisOperator node) {}
- void visitNegation(Negation node) {}
+ visitNegation(Negation node) {}
- void visitDartStyleExpression(DartStyleExpression node) {}
+ visitDartStyleExpression(DartStyleExpression node) {}
- void visitFontExpression(FontExpression node) {
+ visitFontExpression(FontExpression node) {
// TODO(terry): TBD
- throw UnimplementedError;
+ throw new UnimplementedError();
}
- void visitBoxExpression(BoxExpression node) {
+ visitBoxExpression(BoxExpression node) {
// TODO(terry): TBD
- throw UnimplementedError;
+ throw new UnimplementedError();
}
- void visitMarginExpression(MarginExpression node) {
+ visitMarginExpression(MarginExpression node) {
// TODO(terry): TBD
- throw UnimplementedError;
+ throw new UnimplementedError();
}
- void visitBorderExpression(BorderExpression node) {
+ visitBorderExpression(BorderExpression node) {
// TODO(terry): TBD
- throw UnimplementedError;
+ throw new UnimplementedError();
}
- void visitHeightExpression(HeightExpression node) {
+ visitHeightExpression(HeightExpression node) {
// TODO(terry): TB
- throw UnimplementedError;
+ throw new UnimplementedError();
}
- void visitPaddingExpression(PaddingExpression node) {
+ visitPaddingExpression(PaddingExpression node) {
// TODO(terry): TBD
- throw UnimplementedError;
+ throw new UnimplementedError();
}
- void visitWidthExpression(WidthExpression node) {
+ visitWidthExpression(WidthExpression node) {
// TODO(terry): TBD
- throw UnimplementedError;
+ throw new UnimplementedError();
}
}
diff --git a/packages/csslib/pubspec.yaml b/packages/csslib/pubspec.yaml
index 70923da..96f403e 100644
--- a/packages/csslib/pubspec.yaml
+++ b/packages/csslib/pubspec.yaml
@@ -1,5 +1,5 @@
name: csslib
-version: 0.12.1
+version: 0.12.2
author: Polymer.dart Team <web-ui-dev@dartlang.org>
description: A library for parsing CSS.
homepage: https://github.com/dart-lang/csslib
diff --git a/packages/csslib/test/declaration_test.dart b/packages/csslib/test/declaration_test.dart
index 8326a03..de589d7 100644
--- a/packages/csslib/test/declaration_test.dart
+++ b/packages/csslib/test/declaration_test.dart
@@ -1005,6 +1005,62 @@
expect(errorMessage.span.text.trim(), '');
}
+void testExpressionSpans() {
+ final input = r'''.foo { width: 50px; }''';
+ var stylesheet = parseCss(input);
+ var decl = stylesheet.topLevels.single.declarationGroup.declarations.single;
+ // This passes
+ expect(decl.span.text, 'width: 50px');
+ // This currently fails
+ expect(decl.expression.span.text, '50px');
+}
+
+void simpleCalc() {
+ final input = r'''.foo { height: calc(100% - 55px); }''';
+ var stylesheet = parseCss(input);
+ var decl = stylesheet.topLevels.single.declarationGroup.declarations.single;
+ expect(decl.span.text, 'height: calc(100% - 55px)');
+}
+
+void complexCalc() {
+ final input = r'''.foo { left: calc((100%/3 - 2) * 1em - 2 * 1px); }''';
+ var stylesheet = parseCss(input);
+ var decl = stylesheet.topLevels.single.declarationGroup.declarations.single;
+ expect(decl.span.text, 'left: calc((100%/3 - 2) * 1em - 2 * 1px)');
+}
+
+void twoCalcs() {
+ final input = r'''.foo { margin: calc(1rem - 2px) calc(1rem - 1px); }''';
+ var stylesheet = parseCss(input);
+ var decl = stylesheet.topLevels.single.declarationGroup.declarations.single;
+ expect(decl.span.text, 'margin: calc(1rem - 2px) calc(1rem - 1px)');
+}
+
+void selectorWithCalcs() {
+ var errors = [];
+ final String input = r'''
+.foo {
+ width: calc(1em + 5 * 2em);
+ height: calc(1px + 2%) !important;
+ border: 5px calc(1pt + 2cm) 6px calc(1em + 1in + 2px) red;
+ border: calc(5px + 1em) 0px 1px calc(10 + 20 + 1px);
+ margin: 25px calc(50px + (100% / (3 - 1em) - 20%)) calc(10px + 10 * 20) calc(100% - 10px);
+}''';
+ final String generated = r'''
+.foo {
+ width: calc(1em + 5 * 2em);
+ height: calc(1px + 2%) !important;
+ border: 5px calc(1pt + 2cm) 6px calc(1em + 1in + 2px) #f00;
+ border: calc(5px + 1em) 0px 1px calc(10 + 20 + 1px);
+ margin: 25px calc(50px + (100% / (3 - 1em) - 20%)) calc(10px + 10 * 20) calc(100% - 10px);
+}''';
+
+ var stylesheet = parseCss(input, errors: errors);
+ expect(stylesheet != null, true);
+ expect(errors.isEmpty, true, reason: errors.toString());
+ expect(prettyPrint(stylesheet), generated);
+}
+
main() {
test('Simple Terms', testSimpleTerms);
test('Declarations', testDeclarations);
@@ -1021,4 +1077,14 @@
test('IE stuff', testIE);
test('IE declaration syntax', testIEDeclaration);
test('Hanging bugs', testHangs);
+ test('Expression spans', testExpressionSpans,
+ skip: 'expression spans are broken'
+ ' (https://github.com/dart-lang/csslib/issues/15)');
+ group('calc function', () {
+ test('simple calc', simpleCalc);
+ test('single complex', complexCalc);
+ test('two calc terms for same declaration', twoCalcs);
+ test('selector with many calc declarations', selectorWithCalcs);
+ });
}
+
diff --git a/packages/dart_style/._.status b/packages/dart_style/._.status
deleted file mode 100644
index 4080994..0000000
--- a/packages/dart_style/._.status
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/._AUTHORS b/packages/dart_style/._AUTHORS
deleted file mode 100644
index fa0c94c..0000000
--- a/packages/dart_style/._AUTHORS
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/._LICENSE b/packages/dart_style/._LICENSE
deleted file mode 100644
index 8d50f72..0000000
--- a/packages/dart_style/._LICENSE
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/._PATENTS b/packages/dart_style/._PATENTS
deleted file mode 100644
index 0abd44b..0000000
--- a/packages/dart_style/._PATENTS
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/CHANGELOG.md b/packages/dart_style/CHANGELOG.md
index 731fa32..93ae7dd 100644
--- a/packages/dart_style/CHANGELOG.md
+++ b/packages/dart_style/CHANGELOG.md
@@ -1,3 +1,32 @@
+# 0.2.1
+
+* `--version` command line argument (#240).
+* Split the first `.` in a method chain if the target splits (#255).
+* Don't collapse states that differ by unbound rule constraints (#424).
+* Better handling for functions in method chains (#367, #398).
+* Better handling of large parameter metadata annotations (#387, #444).
+* Smarter splitting around collections in named parameters (#394).
+* Split calls if properties in a chain split (#399).
+* Don't allow splitting inside empty functions (#404).
+* Consider a rule live if it constrains a rule in the overflow line (#407).
+* Allow splitting in prefix expressions (#410).
+* Correctly constrain collections in argument lists (#420, #463, #465).
+* Better indentation of collection literals (#421, #469).
+* Only show a hidden directory once in the output (#428).
+* Allow splitting between type and variable name (#429, #439, #454).
+* Better indentation for binary operators in `=>` bodies (#434.
+* Tweak splitting around assignment (#436, #437).
+* Indent multi-line collections in default values (#441).
+* Don't drop metadata on part directives (#443).
+* Handle `if` statements without curly bodies better (#448).
+* Handle loop statements without curly bodies better (#449).
+* Allow splitting before `get` and `set` (#462).
+* Add `--indent` to specify leading indent (#464).
+* Ensure collection elements line split separately (#474).
+* Allow redirecting constructors to wrap (#475).
+* Handle index expressions in the middle of call chains.
+* Optimize splitting lines with many rules.
+
# 0.2.0
* Treat functions nested inside function calls like block arguments (#366).
diff --git a/packages/dart_style/README.md b/packages/dart_style/README.md
index 3a7ed96..0594b6e 100644
--- a/packages/dart_style/README.md
+++ b/packages/dart_style/README.md
@@ -1,46 +1,88 @@
The dart_style package defines an automatic, opinionated formatter for Dart
code. It replaces the whitespace in your program with what it deems to be the
-best formatting for it. Resulting code should following the [Dart style guide][]
+best formatting for it. Resulting code should follow the [Dart style guide][]
but, moreso, should look nice to most human readers, most of the time.
[dart style guide]: https://www.dartlang.org/articles/style-guide/
-It handles indentation, inline whitespace and (by far the most difficult),
-intelligent line wrapping. It has no problems with nested collections, function
+The formatter handles indentation, inline whitespace and
+(by far the most difficult), intelligent line wrapping.
+It has no problems with nested collections, function
expressions, long argument lists, or otherwise tricky code.
-## Usage
+The formatter turns code like this:
-The package exposes a simple command-line wrapper around the core formatting
-library. The easiest way to invoke it is to [globally activate][] the package
-and let pub put its executable on your path:
+```
+// BEFORE formatting
+if (tag=='style'||tag=='script'&&(type==null||type == TYPE_JS
+ ||type==TYPE_DART)||
+ tag=='link'&&(rel=='stylesheet'||rel=='import')) {}
+```
+
+into:
+
+```
+ // AFTER formatting
+ if (tag == 'style' ||
+ tag == 'script' &&
+ (type == null || type == TYPE_JS || type == TYPE_DART) ||
+ tag == 'link' && (rel == 'stylesheet' || rel == 'import')) {}
+```
+
+The formatter will never break your code—you can safely invoke it
+automatically from build and presubmit scripts.
+
+## Getting dartfmt
+
+Dartfmt is included in the Dart SDK, so you might want to add the SDK's bin
+directory to your system path.
+
+If you want to make sure you are running the latest version of dartfmt,
+you can [globally activate][] the package from the dart_style package
+on pub.dartlang.org, and let pub put its executable on your path:
$ pub global activate dart_style
$ dartfmt ...
[globally activate]: https://www.dartlang.org/tools/pub/cmd/pub-global.html
-If you don't want `dartformat` on your path, you can run it explicitly:
+If you don't want `dartfmt` on your path, you can run it explicitly:
$ pub global activate dart_style --no-executables
$ pub global run dart_style:format ...
-The formatter takes a list of paths, which can point to directories or files.
+## Using dartfmt
+
+IDEs and editors that support Dart usually provide easy ways to run the
+formatter. For example, in WebStorm you can right-click a .dart file
+and then choose **Reformat with Dart Style**.
+
+Here's a simple example of using dartfmt on the command line:
+
+```
+dartfmt test.dart
+```
+
+This command formats the `test.dart` file and writes the result to
+standard output.
+
+Dartfmt takes a list of paths, which can point to directories or files.
If the path is a directory, it processes every `.dart` file in that directory
or any of its subdirectories.
+If no file or directory is specified, dartfmt reads from standard input.
By default, it formats each file and just prints the resulting code to stdout.
-If you pass `-w`, it will instead overwrite your existing files with the
+If you pass `-w`, it overwrites your existing files with the
formatted results.
-You may pass a `--line-length` option to control the width of the page that it
+You may pass a `-l` option to control the width of the page that it
wraps lines to fit within, but you're strongly encouraged to keep the default
line length of 80 columns.
### Validating files
If you want to use the formatter in something like a [presubmit script][] or
-[commit hook][], you can use the `--dry-run` option. If you pass that, the
+[commit hook][], you can use the `-n` dry run option. If you specify `-n`, the
formatter prints the paths of the files whose contents would change if the
formatter were run normally. If it prints no output, then everything is already
correctly formatted.
@@ -48,7 +90,7 @@
[presubmit script]: http://www.chromium.org/developers/how-tos/depottools/presubmit-scripts
[commit hook]: http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
-### Using it programmatically
+## Using the dart_style API
The package also exposes a single dart_style library containing a programmatic
API for formatting code. Simple usage looks like this:
@@ -71,219 +113,13 @@
}
}
-## FAQ
+## Other resources
-### Why have a formatter?
+* Before sending an email, see if you are asking a
+ [frequently asked question][faq].
-The has a few goals, in order of descending priority:
-
-1. **Produce consistently formatted code.** Consistent style improves
- readability because you aren't distracted by variance in style between
- different parts of a program. It makes it easier to contribute to others'
- code because their style will already be familiar to you.
-
-2. **End debates about style issues in code reviews.** This consumes an
- astonishingly large quantity of very valuable engineering energy. Style
- debates are time-consuming, upset people, and rarely change anyone's mind.
- They make code reviews take longer and be more acromonious.
-
-3. **Free users from having to think about and apply formatting.** When
- writing code, you don't have to try to figure out the best way to split a
- line and then pain-stakingly add in the line breaks. When you do a global
- refactor that changes the length of some identifier, you don't have to go
- back and rewrap all of the lines. When you're in the zone, you can just
- pump out code and let the formatter tidy it up for you as you go.
-
-4. **Produce beautiful, readable output that helps users understand the code.**
- We could solve all of the above goals with a formatter that just removed
- *all* whitespace, but that wouldn't be very human-friendly. So, finally,
- the formatter tries very hard to produce output that is not just consistent
- but readable to a human. It tries to use indentation and line breaks to
- highlight the structure and organization of the code.
-
- In several cases, the formatter has pointed out bugs where the existing
- indentation was misleading and didn't represent what the code actually did.
- For example, automated formatted would have helped make Apple's
- ["gotofail"][gotofail] security bug easier to notice:
-
- ```c
- if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
- goto fail;
- goto fail;
- if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
- goto fail;
- ```
-
- The formatter would change this to:
-
- ```c
- if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
- goto fail;
- goto fail; // <-- not clearly not under the "if".
- if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
- goto fail;
- ```
+* Before filing a bug, or if you want to understand how work on the
+ formatter is managed, see how we [track issues][].
-[gotofail]: https://gotofail.com/
-
-### I don't like the output!
-
-First of all, that's not a question. But, yes, sometimes you may dislike the
-output of the formatter. This may be a bug or it may be a deliberate stylistic
-choice of the formatter that you disagree with. The simplest way to find out is
-to file an [issue][].
-
-[issue]: https://github.com/dart-lang/dart_style/issues
-
-Now that the formatter is fairly mature, it's more likely that the output is
-deliberate. If your bug gets closed as "as designed", try not to be too sad.
-Even if the formatter doesn't follow your personal preferences, what it *does*
-do is spare you the effort of hand-formatting, and ensure your code is
-*consistently* formatted. I hope you'll appreciate the real value in both of
-those.
-
-### How stable is it?
-
-You can rely on the formatter to not break your code or change its semantics.
-If it does do so, this is a critical bug and we'll fix it quickly.
-
-The rules the formatter uses to determine the "best" way to split a line may
-change over time. We don't promise that code produced by the formatter today
-will be identical to the same code run through a later version of the formatter.
-We do hope that you'll like the output of the later version more.
-
-### Why can't I tell the formatter to ignore a region of code?
-
-Even a really sophisticated formatter can't beat a human in *all* cases. Our
-semantic knowledge of the code can let us show more than the formatter can. One
-escape hatch would be to have a comment telling the formatter "leave this
-alone".
-
-This might help the fourth goal above, but does so at the expense of the first
-three. We want code that is *consistent* and we want you to stop thinking about
-formatting. If you can decide to turn off the formatter, now you have regions
-of code that are inconsistent by design.
-
-Further, you're right back into debates about how the code in there should be
-formatted, with the extra bonus of now debating whether or not that annotation
-should be used and where. None of this is making your life better.
-
-Yes, *maybe* you can hand-format some things better than the formatter. (Though,
-in most cases where users have asked for this, I've seen formatting errors in
-the examples they provided!) But does doing that really add enough value to
-make up for re-opening that can of worms?
-
-### Why does the formatter mess up my collection literals?
-
-Large collection literals are often used to define big chunks of structured
-data, like:
-
-```dart
-/// Maps ASCII character values to what kind of character they represent.
-const characterTypes = const [
- other, other, other, other, other, other, other, other,
- other, white, white, other, other, white,
- other, other, other, other, other, other, other, other,
- other, other, other, other, other, other, other, other,
- other, other, white,
- punct, other, punct, punct, punct, punct, other,
- brace, brace, punct, punct, comma, punct, punct, punct,
- digit, digit, digit, digit, digit,
- digit, digit, digit, digit, digit,
- punct, punct, punct, punct, punct, punct, punct,
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, brace, punct, brace, punct, alpha, other,
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, brace, punct, brace, punct
-];
-```
-
-The formatter doesn't know those newlines are meaningful, so it wipes it out
-to:
-
-```dart
-/// Maps ASCII character values to what kind of character they represent.
-const characterTypes = const [
- other,
- other,
- other,
-
- // lots more ...
-
- punct,
- brace,
- punct
-];
-```
-
-In many cases, ignoring these newlines is a good thing. If you've removed a few
-items from a list, it's a win for the formatter to repack it into one line if
-it fits. But here it clearly loses useful information.
-
-Fortunately, in most cases, structured collections like this have comments
-describing their structure:
-
-```dart
-const characterTypes = const [
- other, other, other, other, other, other, other, other,
- other, white, white, other, other, white,
- other, other, other, other, other, other, other, other,
- other, other, other, other, other, other, other, other,
- other, other, white,
- punct, other, punct, punct, punct, punct, other, // !"#$%&´
- brace, brace, punct, punct, comma, punct, punct, punct, // ()*+,-./
- digit, digit, digit, digit, digit, // 01234
- digit, digit, digit, digit, digit, // 56789
- punct, punct, punct, punct, punct, punct, punct, // :;<=>?@
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, // ABCDEFGH
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, brace, punct, brace, punct, alpha, other, // YZ[\]^_'
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha, // abcdefgh
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, alpha, alpha, alpha, alpha, alpha, alpha,
- alpha, alpha, brace, punct, brace, punct // yz{|}~
-];
-```
-
-In that case, the formatter is smart enough to recognize this and preserve your
-original newlines. So, if you have a collection that you have carefully split
-into lines, add at least one line comment somewhere inside it to get it to
-preserve all of the newlines in it.
-
-### Why doesn't the formatter handle multi-line `if` statements better?
-
-If you have a statement like:
-
-```dart
-if (someVeryLongConditional || anotherLongConditional) function(argument, argument);
-```
-
-It will format it like:
-
-```dart
-if (someVeryLongConditional || anotherLongConditional) function(
- argument, argument);
-```
-
-You might expect it to break before `function`. But the Dart style guide
-explicitly forbids multi-line `if` statements that do not use `{}` bodies.
-Given that, there's never a reason for the formatter to allow splitting after
-the condition. This is true of other control flow statements too, of course.
-
-### Why doesn't the formatter add curlies or otherwise clean up code then?
-
-The formatter has a simple, restricted charter: it rewrites *only the
-non-semantic whitespace of your program.* It makes absolutely no other changes
-to your code.
-
-This helps keep the scope of the project limited. The set of "clean-ups" you
-may want to do is unbounded and much fuzzier to define.
-
-It also makes it more reliable to run the formatter automatically in things
-like presubmit scripts where a human may not be vetting the output. If the
-formatter only touches whitespace, it's easier for a human to trust its output.
+[faq]: https://github.com/dart-lang/dart_style/wiki/FAQ
+[track issues]: https://github.com/dart-lang/dart_style/wiki/Tracking-issues
diff --git a/packages/dart_style/bin/format.dart b/packages/dart_style/bin/format.dart
index cd97fb0..8d179c1 100644
--- a/packages/dart_style/bin/format.dart
+++ b/packages/dart_style/bin/format.dart
@@ -6,19 +6,27 @@
import 'dart:io';
import 'package:args/args.dart';
+
import 'package:dart_style/src/dart_formatter.dart';
import 'package:dart_style/src/formatter_exception.dart';
import 'package:dart_style/src/formatter_options.dart';
import 'package:dart_style/src/io.dart';
import 'package:dart_style/src/source_code.dart';
+// Note: The following line of code is modified by tool/grind.dart.
+const version = "0.2.1";
+
void main(List<String> args) {
var parser = new ArgParser(allowTrailingOptions: true);
parser.addFlag("help",
abbr: "h", negatable: false, help: "Shows usage information.");
+ parser.addFlag("version",
+ negatable: false, help: "Shows version information.");
parser.addOption("line-length",
abbr: "l", help: "Wrap lines longer than this.", defaultsTo: "80");
+ parser.addOption("indent",
+ abbr: "i", help: "Spaces of leading indentation.", defaultsTo: "0");
parser.addOption("preserve",
help: 'Selection to preserve, formatted as "start:length".');
parser.addFlag("dry-run",
@@ -33,6 +41,8 @@
abbr: "m",
negatable: false,
help: "Produce machine-readable JSON output.");
+ parser.addFlag("profile",
+ negatable: false, help: "Display profile times after running.");
parser.addFlag("follow-links",
negatable: false,
help: "Follow links to files and directories.\n"
@@ -54,6 +64,11 @@
return;
}
+ if (argResults["version"]) {
+ print(version);
+ return;
+ }
+
// Can only preserve a selection when parsing from stdin.
var selection;
@@ -100,8 +115,11 @@
reporter = OutputReporter.printJson;
}
- var pageWidth;
+ if (argResults["profile"]) {
+ reporter = new ProfileReporter(reporter);
+ }
+ var pageWidth;
try {
pageWidth = int.parse(argResults["line-length"]);
} on FormatException catch (_) {
@@ -111,16 +129,32 @@
'"${argResults['line-length']}".');
}
+ var indent;
+
+ try {
+ indent = int.parse(argResults["indent"]);
+ if (indent < 0 || indent.toInt() != indent) throw new FormatException();
+ } on FormatException catch (_) {
+ usageError(
+ parser,
+ '--indent must be a non-negative integer, was '
+ '"${argResults['indent']}".');
+ }
+
var followLinks = argResults["follow-links"];
var options = new FormatterOptions(reporter,
- pageWidth: pageWidth, followLinks: followLinks);
+ indent: indent, pageWidth: pageWidth, followLinks: followLinks);
if (argResults.rest.isEmpty) {
formatStdin(options, selection);
} else {
formatPaths(options, argResults.rest);
}
+
+ if (argResults["profile"]) {
+ (reporter as ProfileReporter).showProfile();
+ }
}
List<int> parseSelection(String selection) {
@@ -147,15 +181,17 @@
var input = new StringBuffer();
stdin.transform(new Utf8Decoder()).listen(input.write, onDone: () {
- var formatter = new DartFormatter(pageWidth: options.pageWidth);
+ var formatter =
+ new DartFormatter(indent: options.indent, pageWidth: options.pageWidth);
try {
+ options.reporter.beforeFile(null, "<stdin>");
var source = new SourceCode(input.toString(),
uri: "stdin",
selectionStart: selectionStart,
selectionLength: selectionLength);
var output = formatter.formatSource(source);
options.reporter
- .showFile(null, "<stdin>", output, changed: source != output);
+ .afterFile(null, "<stdin>", output, changed: source != output);
return true;
} on FormatterException catch (err) {
stderr.writeln(err.message());
diff --git a/packages/dart_style/codereview.settings b/packages/dart_style/codereview.settings
new file mode 100644
index 0000000..25510f0
--- /dev/null
+++ b/packages/dart_style/codereview.settings
@@ -0,0 +1,3 @@
+CODE_REVIEW_SERVER: https://codereview.chromium.org/
+VIEW_VC: https://github.com/dart-lang/dart_style/commit/
+CC_LIST: reviews@dartlang.org
diff --git a/packages/dart_style/example/format.dart b/packages/dart_style/example/format.dart
index 4eec856..6d3ea61 100644
--- a/packages/dart_style/example/format.dart
+++ b/packages/dart_style/example/format.dart
@@ -44,10 +44,6 @@
result = formatter.formatStatement(source);
}
- if (debug.useAnsiColors) {
- result = result.replaceAll(" ", debug.gray(debug.unicodeMidDot));
- }
-
drawRuler("before", pageWidth);
print(source);
drawRuler("after", pageWidth);
diff --git a/packages/dart_style/lib/src/._error_listener.dart b/packages/dart_style/lib/src/._error_listener.dart
deleted file mode 100644
index bcc5ac5..0000000
--- a/packages/dart_style/lib/src/._error_listener.dart
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/lib/src/._formatter_options.dart b/packages/dart_style/lib/src/._formatter_options.dart
deleted file mode 100644
index 04ab7e8..0000000
--- a/packages/dart_style/lib/src/._formatter_options.dart
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/lib/src/argument_list_visitor.dart b/packages/dart_style/lib/src/argument_list_visitor.dart
index c45b737..b071987 100644
--- a/packages/dart_style/lib/src/argument_list_visitor.dart
+++ b/packages/dart_style/lib/src/argument_list_visitor.dart
@@ -4,7 +4,10 @@
library dart_style.src.argument_list_visitor;
+import 'dart:math' as math;
+
import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/src/generated/scanner.dart';
import 'chunk.dart';
import 'rule/argument.dart';
@@ -37,25 +40,13 @@
_node.arguments.length == 1 && _node.arguments.single is! NamedExpression;
/// Whether this argument list has any collection or block function arguments.
+ // TODO(rnystrom): Returning true based on collections is non-optimal. It
+ // forces a method chain to break into two but the result collection may not
+ // actually split which can lead to a method chain that's allowed to break
+ // where it shouldn't.
bool get hasBlockArguments =>
_arguments._collections.isNotEmpty || _functions != null;
- /// Whether this argument list should force the containing method chain to
- /// add a level of block nesting.
- bool get nestMethodArguments {
- // If there are block arguments, we don't want the method to force them to
- // the right.
- if (hasBlockArguments) return false;
-
- // Corner case: If there is just a single argument, don't bump the nesting.
- // This lets us avoid spurious indentation in cases like:
- //
- // object.method(function(() {
- // body;
- // }));
- return _node.arguments.length > 1;
- }
-
factory ArgumentListVisitor(SourceVisitor visitor, ArgumentList node) {
// Look for a single contiguous range of block function arguments.
var functionsStart;
@@ -151,8 +142,8 @@
if (_isSingle) _visitor.builder.endSpan();
}
- /// Returns `true` if [expression] is a [FunctionExpression] with a block
- /// body.
+ /// Returns `true` if [expression] is a [FunctionExpression] with a non-empty
+ /// block body.
static bool _isBlockFunction(Expression expression) {
if (expression is NamedExpression) {
expression = (expression as NamedExpression).expression;
@@ -166,10 +157,23 @@
return _isBlockFunction(expression.argumentList.arguments.single);
}
- // Curly body functions are.
+ if (expression is InstanceCreationExpression) {
+ if (expression.argumentList.arguments.length != 1) return false;
+
+ return _isBlockFunction(expression.argumentList.arguments.single);
+ }
+
+ // Must be a function.
if (expression is! FunctionExpression) return false;
+
+ // With a curly body.
var function = expression as FunctionExpression;
- return function.body is BlockFunctionBody;
+ if (function.body is! BlockFunctionBody) return false;
+
+ // That isn't empty.
+ var body = function.body as BlockFunctionBody;
+ return body.block.statements.isNotEmpty ||
+ body.block.rightBracket.precedingComments != null;
}
/// Returns `true` if [expression] is a valid method invocation target for
@@ -206,8 +210,9 @@
/// The named arguments, in order.
final List<Expression> _named;
- /// The arguments that are collection literals that get special formatting.
- final Set<Expression> _collections;
+ /// Maps each argument that is a collection literal that get special
+ /// formatting to the token for the collection's open bracket.
+ final Map<Expression, Token> _collections;
/// The number of leading collections.
///
@@ -220,16 +225,12 @@
final int _trailingCollections;
/// The rule used to split the bodies of all of the collection arguments.
- Rule get _collectionRule {
- // Lazy initialize.
- if (_collectionRuleField == null && _collections.isNotEmpty) {
- _collectionRuleField = new SimpleRule(cost: Cost.splitCollections);
- }
+ Rule get collectionRule => _collectionRule;
+ Rule _collectionRule;
- return _collectionRuleField;
- }
-
- Rule _collectionRuleField;
+ /// The most recent chunk that split before an argument.
+ Chunk get previousSplit => _previousSplit;
+ Chunk _previousSplit;
bool get _hasMultipleArguments => _positional.length + _named.length > 1;
@@ -240,12 +241,16 @@
arguments.takeWhile((arg) => arg is! NamedExpression).toList();
var named = arguments.skip(positional.length).toList();
- var collections = arguments.where(_isCollectionArgument).toSet();
+ var collections = {};
+ for (var argument in arguments) {
+ var bracket = _getCollectionBracket(argument);
+ if (bracket != null) collections[argument] = bracket;
+ }
// Count the leading arguments that are collection literals.
var leadingCollections = 0;
for (var argument in arguments) {
- if (!collections.contains(argument)) break;
+ if (!collections.containsKey(argument)) break;
leadingCollections++;
}
@@ -253,21 +258,11 @@
var trailingCollections = 0;
if (leadingCollections != arguments.length) {
for (var argument in arguments.reversed) {
- if (!collections.contains(argument)) break;
+ if (!collections.containsKey(argument)) break;
trailingCollections++;
}
}
- // If only some of the named arguments are collections, treat none of them
- // specially. Avoids cases like:
- //
- // function(
- // a: arg,
- // b: [
- // ...
- // ]);
- if (trailingCollections < named.length) trailingCollections = 0;
-
// Collections must all be a prefix or suffix of the argument list (and not
// both).
if (leadingCollections != collections.length) leadingCollections = 0;
@@ -286,6 +281,10 @@
this._collections, this._leadingCollections, this._trailingCollections);
void visit(SourceVisitor visitor) {
+ if (_collections.isNotEmpty) {
+ _collectionRule = new Rule(Cost.splitCollections);
+ }
+
var rule = _visitPositional(visitor);
_visitNamed(visitor, rule);
}
@@ -299,85 +298,80 @@
if (_positional.length == 1) {
rule = new SinglePositionalRule(_collectionRule,
splitsOnInnerRules: _allArguments.length > 1 &&
- !_isCollectionArgument(_positional.first));
+ !_collections.containsKey(_positional.first));
} else {
- // Only count the positional bodies in the positional rule.
- var leadingPositional = _leadingCollections;
- if (_leadingCollections == _positional.length + _named.length) {
- leadingPositional -= _named.length;
- }
-
- var trailingPositional = _trailingCollections - _named.length;
+ // Only count the collections in the positional rule.
+ var leadingCollections =
+ math.min(_leadingCollections, _positional.length);
+ var trailingCollections =
+ math.max(_trailingCollections - _named.length, 0);
rule = new MultiplePositionalRule(
- _collectionRule, leadingPositional, trailingPositional);
+ _collectionRule, leadingCollections, trailingCollections);
}
- visitor.builder.startRule(rule);
-
- var chunk;
- if (_isFirstArgument(_positional.first)) {
- chunk = visitor.zeroSplit();
- } else {
- chunk = visitor.split();
- }
- rule.beforeArgument(chunk);
-
- // Try to not split the arguments.
- visitor.builder.startSpan(Cost.positionalArguments);
-
- for (var argument in _positional) {
- _visitArgument(visitor, rule, argument);
-
- // Positional arguments split independently.
- if (argument != _positional.last) {
- rule.beforeArgument(visitor.split());
- }
- }
-
- visitor.builder.endSpan();
- visitor.builder.endRule();
-
+ _visitArguments(visitor, _positional, rule);
return rule;
}
/// Writes the named arguments, if any.
- void _visitNamed(SourceVisitor visitor, PositionalRule rule) {
+ void _visitNamed(SourceVisitor visitor, PositionalRule positionalRule) {
if (_named.isEmpty) return;
- var positionalRule = rule;
- var namedRule = new NamedRule(_collectionRule);
- visitor.builder.startRule(namedRule);
+ // Only count the collections in the named rule.
+ var leadingCollections =
+ math.max(_leadingCollections - _positional.length, 0);
+ var trailingCollections = math.min(_trailingCollections, _named.length);
+ var namedRule =
+ new NamedRule(_collectionRule, leadingCollections, trailingCollections);
// Let the positional args force the named ones to split.
if (positionalRule != null) {
positionalRule.setNamedArgsRule(namedRule);
}
- // Split before the first named argument.
- namedRule.beforeArguments(
- visitor.builder.split(space: !_isFirstArgument(_named.first)));
+ _visitArguments(visitor, _named, namedRule);
+ }
- for (var argument in _named) {
- _visitArgument(visitor, namedRule, argument);
+ void _visitArguments(
+ SourceVisitor visitor, List<Expression> arguments, ArgumentRule rule) {
+ visitor.builder.startRule(rule);
+
+ // Split before the first argument.
+ _previousSplit =
+ visitor.builder.split(space: !_isFirstArgument(arguments.first));
+ rule.beforeArgument(_previousSplit);
+
+ // Try to not split the positional arguments.
+ if (arguments == _positional) {
+ visitor.builder.startSpan(Cost.positionalArguments);
+ }
+
+ for (var argument in arguments) {
+ _visitArgument(visitor, rule, argument);
// Write the split.
- if (argument != _named.last) visitor.split();
+ if (argument != arguments.last) {
+ _previousSplit = visitor.split();
+ rule.beforeArgument(_previousSplit);
+ }
}
+ if (arguments == _positional) visitor.builder.endSpan();
+
visitor.builder.endRule();
}
void _visitArgument(
SourceVisitor visitor, ArgumentRule rule, Expression argument) {
// If we're about to write a collection argument, handle it specially.
- if (_collections.contains(argument)) {
+ if (_collections.containsKey(argument)) {
if (rule != null) rule.beforeCollection();
// Tell it to use the rule we've already created.
- visitor.setNextLiteralBodyRule(_collectionRule);
+ visitor.beforeCollection(_collections[argument], this);
} else if (_hasMultipleArguments) {
- // Corner case: If there is just a single argument, don't bump the
- // nesting. This lets us avoid spurious indentation in cases like:
+ // Edge case: If there is just a single argument, don't bump the nesting.
+ // This lets us avoid spurious indentation in cases like:
//
// function(function(() {
// body;
@@ -387,7 +381,7 @@
visitor.visit(argument);
- if (_collections.contains(argument)) {
+ if (_collections.containsKey(argument)) {
if (rule != null) rule.afterCollection();
} else if (_hasMultipleArguments) {
visitor.builder.endBlockArgumentNesting();
@@ -403,17 +397,22 @@
bool _isLastArgument(Expression argument) => argument == _allArguments.last;
- /// Returns true if [expression] denotes a collection literal argument.
+ /// Returns the token for the left bracket if [expression] denotes a
+ /// collection literal argument.
///
/// Similar to block functions, collection arguments can get special
/// indentation to make them look more statement-like.
- static bool _isCollectionArgument(Expression expression) {
+ static Token _getCollectionBracket(Expression expression) {
if (expression is NamedExpression) {
expression = (expression as NamedExpression).expression;
}
// TODO(rnystrom): Should we step into parenthesized expressions?
- return expression is ListLiteral || expression is MapLiteral;
+ if (expression is ListLiteral) return expression.leftBracket;
+ if (expression is MapLiteral) return expression.leftBracket;
+
+ // Not a collection literal.
+ return null;
}
}
diff --git a/packages/dart_style/lib/src/call_chain_visitor.dart b/packages/dart_style/lib/src/call_chain_visitor.dart
index 36facca..7b7fb26 100644
--- a/packages/dart_style/lib/src/call_chain_visitor.dart
+++ b/packages/dart_style/lib/src/call_chain_visitor.dart
@@ -8,6 +8,7 @@
import 'argument_list_visitor.dart';
import 'rule/argument.dart';
+import 'rule/rule.dart';
import 'source_visitor.dart';
/// Helper class for [SourceVisitor] that handles visiting and writing a
@@ -32,12 +33,58 @@
/// order that they appear in the source.
final List<Expression> _calls;
+ /// The method calls containing block function literals that break the method
+ /// chain and escape its indentation.
+ ///
+ /// receiver.a().b().c(() {
+ /// ;
+ /// }).d(() {
+ /// ;
+ /// }).e();
+ ///
+ /// Here, it will contain `c` and `d`.
+ ///
+ /// The block calls must be contiguous and must be a suffix of the list of
+ /// calls (except for the one allowed hanging call). Otherwise, none of them
+ /// are treated as block calls:
+ ///
+ /// receiver
+ /// .a()
+ /// .b(() {
+ /// ;
+ /// })
+ /// .c(() {
+ /// ;
+ /// })
+ /// .d()
+ /// .e();
+ final List<Expression> _blockCalls;
+
+ /// If there is one or more block calls and a single chained expression after
+ /// that, this will be that expression.
+ ///
+ /// receiver.a().b().c(() {
+ /// ;
+ /// }).d(() {
+ /// ;
+ /// }).e();
+ ///
+ /// We allow a single hanging call after the blocks because it will never
+ /// need to split before its `.` and this accommodates the common pattern of
+ /// a trailing `toList()` or `toSet()` after a series of higher-order methods
+ /// on an iterable.
+ final Expression _hangingCall;
+
/// Whether or not a [Rule] is currently active for the call chain.
bool _ruleEnabled = false;
/// Whether or not the span wrapping the call chain is currently active.
bool _spanEnded = false;
+ /// After the properties are visited (if there are any), this will be the
+ /// rule used to split between them.
+ PositionalRule _propertyRule;
+
/// Creates a new call chain visitor for [visitor] starting with [node].
///
/// The [node] is the outermost expression containing the chained "."
@@ -51,14 +98,25 @@
flatten(expression) {
target = expression;
- if (expression is MethodInvocation && expression.target != null) {
- flatten(expression.target);
+ // Treat index expressions where the target is a valid call in a method
+ // chain as being part of the call. Handles cases like:
+ //
+ // receiver
+ // .property
+ // .property[0]
+ // .property
+ // .method()[1][2];
+ var call = expression;
+ while (call is IndexExpression) call = call.target;
+
+ if (call is MethodInvocation && call.target != null) {
+ flatten(call.target);
calls.add(expression);
- } else if (expression is PropertyAccess && expression.target != null) {
- flatten(expression.target);
+ } else if (call is PropertyAccess && call.target != null) {
+ flatten(call.target);
calls.add(expression);
- } else if (expression is PrefixedIdentifier) {
- flatten(expression.prefix);
+ } else if (call is PrefixedIdentifier) {
+ flatten(call.prefix);
calls.add(expression);
}
}
@@ -74,17 +132,60 @@
// .length;
var properties = [];
if (target is SimpleIdentifier) {
- properties =
- calls.takeWhile((call) => call is! MethodInvocation).toList();
+ properties = calls.takeWhile((call) {
+ // Step into index expressions to see what the index is on.
+ while (call is IndexExpression) call = call.target;
+ return call is! MethodInvocation;
+ }).toList();
}
calls.removeRange(0, properties.length);
- return new CallChainVisitor._(visitor, target, properties, calls);
+ // Separate out the block calls, if there are any.
+ var blockCalls;
+ var hangingCall;
+
+ var inBlockCalls = false;
+ for (var call in calls) {
+ // See if this call is a method call whose arguments are block formatted.
+ var isBlockCall = false;
+ if (call is MethodInvocation) {
+ var args = new ArgumentListVisitor(visitor, call.argumentList);
+ isBlockCall = args.hasBlockArguments;
+ }
+
+ if (isBlockCall) {
+ inBlockCalls = true;
+ if (blockCalls == null) blockCalls = [];
+ blockCalls.add(call);
+ } else if (inBlockCalls) {
+ // We found a non-block call after a block call.
+ if (call == calls.last) {
+ // It's the one allowed hanging one, so it's OK.
+ hangingCall = call;
+ break;
+ }
+
+ // Don't allow any of the calls to be block formatted.
+ blockCalls = null;
+ break;
+ }
+ }
+
+ if (blockCalls != null) {
+ for (var blockCall in blockCalls) calls.remove(blockCall);
+ }
+
+ if (hangingCall != null) {
+ calls.remove(hangingCall);
+ }
+
+ return new CallChainVisitor._(
+ visitor, target, properties, calls, blockCalls, hangingCall);
}
- CallChainVisitor._(
- this._visitor, this._target, this._properties, this._calls);
+ CallChainVisitor._(this._visitor, this._target, this._properties, this._calls,
+ this._blockCalls, this._hangingCall);
/// Builds chunks for the call chain.
///
@@ -100,6 +201,19 @@
// Try to keep the entire method invocation one line.
_visitor.builder.startSpan();
+ // If a split in the target expression forces the first `.` to split, then
+ // start the rule now so that it surrounds the target.
+ var splitOnTarget = _forcesSplit(_target);
+
+ if (splitOnTarget) {
+ if (_properties.length > 1) {
+ _propertyRule = new MultiplePositionalRule(null, 0, 0);
+ _visitor.builder.startLazyRule(_propertyRule);
+ } else if (_calls.isNotEmpty) {
+ _enableRule(lazy: true);
+ }
+ }
+
_visitor.visit(_target);
// Leading properties split like positional arguments: either not at all,
@@ -108,39 +222,138 @@
_visitor.soloZeroSplit();
_writeCall(_properties.single);
} else if (_properties.length > 1) {
- var argRule = new MultiplePositionalRule(null, 0, 0);
- _visitor.builder.startRule(argRule);
+ if (!splitOnTarget) {
+ _propertyRule = new MultiplePositionalRule(null, 0, 0);
+ _visitor.builder.startRule(_propertyRule);
+ }
for (var property in _properties) {
- argRule.beforeArgument(_visitor.zeroSplit());
+ _propertyRule.beforeArgument(_visitor.zeroSplit());
_writeCall(property);
}
_visitor.builder.endRule();
}
- // The remaining chain of calls generally split atomically (either all or
- // none), except that block arguments may split a chain into two parts.
+ // Indent any block arguments in the chain that don't get special formatting
+ // below. Only do this if there is more than one argument to avoid spurious
+ // indentation in cases like:
+ //
+ // object.method(wrapper(() {
+ // body;
+ // });
+ // TODO(rnystrom): Come up with a less arbitrary way to express this?
+ if (_calls.length > 1) _visitor.builder.startBlockArgumentNesting();
+
+ // The chain of calls splits atomically (either all or none). Any block
+ // arguments inside them get indented to line up with the `.`.
for (var call in _calls) {
_enableRule();
_visitor.zeroSplit();
_writeCall(call);
}
+ if (_calls.length > 1) _visitor.builder.endBlockArgumentNesting();
+
+ // If there are block calls, end the chain and write those without any
+ // extra indentation.
+ if (_blockCalls != null) {
+ _enableRule();
+ _visitor.zeroSplit();
+ _disableRule();
+
+ for (var blockCall in _blockCalls) {
+ _writeBlockCall(blockCall);
+ }
+
+ // If there is a hanging call after the last block, write it without any
+ // split before the ".".
+ if (_hangingCall != null) {
+ _writeCall(_hangingCall);
+ }
+ }
+
_disableRule();
_endSpan();
if (unnest) _visitor.builder.unnest();
}
+ /// Returns `true` if the method chain should split if a split occurs inside
+ /// [expression].
+ ///
+ /// In most cases, splitting in a method chain's target forces the chain to
+ /// split too:
+ ///
+ /// receiver(very, long, argument,
+ /// list) // <-- Split here...
+ /// .method(); // ...forces split here.
+ ///
+ /// However, if the target is a collection or function literal (or an
+ /// argument list ending in one of those), we don't want to split:
+ ///
+ /// receiver(inner(() {
+ /// ;
+ /// }).method(); // <-- Unsplit.
+ bool _forcesSplit(Expression expression) {
+ // TODO(rnystrom): Other cases we may want to consider handling and
+ // recursing into:
+ // * ParenthesizedExpression.
+ // * The right operand in an infix operator call.
+ // * The body of a `=>` function.
+
+ // Don't split right after a collection literal.
+ if (expression is ListLiteral) return false;
+ if (expression is MapLiteral) return false;
+
+ // Don't split right after a non-empty curly-bodied function.
+ if (expression is FunctionExpression) {
+ if (expression.body is! BlockFunctionBody) return false;
+
+ return (expression.body as BlockFunctionBody).block.statements.isEmpty;
+ }
+
+ // If the expression ends in an argument list, base the splitting on the
+ // last argument.
+ var argumentList;
+ if (expression is MethodInvocation) {
+ argumentList = expression.argumentList;
+ } else if (expression is InstanceCreationExpression) {
+ argumentList = expression.argumentList;
+ } else if (expression is FunctionExpressionInvocation) {
+ argumentList = expression.argumentList;
+ }
+
+ // Any other kind of expression always splits.
+ if (argumentList == null) return true;
+ if (argumentList.arguments.isEmpty) return true;
+
+ var argument = argumentList.arguments.last;
+ if (argument is NamedExpression) argument = argument.expression;
+
+ // TODO(rnystrom): This logic is similar (but not identical) to
+ // ArgumentListVisitor.hasBlockArguments. They overlap conceptually and
+ // both have their own peculiar heuristics. It would be good to unify and
+ // rationalize them.
+
+ return _forcesSplit(argument);
+ }
+
/// Writes [call], which must be one of the supported expression types.
void _writeCall(Expression call) {
- if (call is MethodInvocation) {
+ if (call is IndexExpression) {
+ _visitor.builder.nestExpression();
+ _writeCall(call.target);
+ _visitor.finishIndexExpression(call);
+ _visitor.builder.unnest();
+ } else if (call is MethodInvocation) {
_writeInvocation(call);
} else if (call is PropertyAccess) {
- _writePropertyAccess(call);
+ _visitor.token(call.operator);
+ _visitor.visit(call.propertyName);
} else if (call is PrefixedIdentifier) {
- _writePrefixedIdentifier(call);
+ _visitor.token(call.period);
+ _visitor.visit(call.identifier);
} else {
// Unexpected type.
assert(false);
@@ -151,38 +364,15 @@
_visitor.token(invocation.operator);
_visitor.token(invocation.methodName.token);
- // If a method's argument list includes any block arguments, there's a
- // good chance it will split. Treat the chains before and after that as
- // separate unrelated method chains.
- //
- // This is kind of a hack since it treats methods before and after a
- // collection literal argument differently even when the collection
- // doesn't split, but it works out OK in practice.
- //
- // Doing something more precise would require setting up a bunch of complex
- // constraints between various rules. You'd basically have to say "if the
- // block argument splits then allow the chain after it to split
- // independently, otherwise force it to follow the previous chain".
- var args = new ArgumentListVisitor(_visitor, invocation.argumentList);
-
- // Stop the rule after the last call, but before its arguments. This
- // allows unsplit chains where the last argument list wraps, like:
+ // If we don't have any block calls, stop the rule after the last method
+ // call name, but before its arguments. This allows unsplit chains where
+ // the last argument list wraps, like:
//
// foo().bar().baz(
// argument, list);
- //
- // Also stop the rule to split the argument list at any call with
- // block arguments. This makes for nicer chains of higher-order method
- // calls, like:
- //
- // items.map((element) {
- // ...
- // }).where((element) {
- // ...
- // });
- if (invocation == _calls.last || args.hasBlockArguments) _disableRule();
-
- if (args.nestMethodArguments) _visitor.builder.startBlockArgumentNesting();
+ if (_blockCalls == null && _calls.isNotEmpty && invocation == _calls.last) {
+ _disableRule();
+ }
// For a single method call on an identifier, stop the span before the
// arguments to make it easier to keep the call name with the target. In
@@ -202,23 +392,18 @@
// there looks really odd.
if (_properties.isEmpty &&
_calls.length == 1 &&
+ _blockCalls == null &&
_target is SimpleIdentifier) {
_endSpan();
}
_visitor.visit(invocation.argumentList);
-
- if (args.nestMethodArguments) _visitor.builder.endBlockArgumentNesting();
}
- void _writePropertyAccess(PropertyAccess property) {
- _visitor.token(property.operator);
- _visitor.visit(property.propertyName);
- }
-
- void _writePrefixedIdentifier(PrefixedIdentifier prefix) {
- _visitor.token(prefix.period);
- _visitor.visit(prefix.identifier);
+ void _writeBlockCall(MethodInvocation invocation) {
+ _visitor.token(invocation.operator);
+ _visitor.token(invocation.methodName.token);
+ _visitor.visit(invocation.argumentList);
}
/// If a [Rule] for the method chain is currently active, ends it.
@@ -230,10 +415,19 @@
}
/// Creates a new method chain [Rule] if one is not already active.
- void _enableRule() {
+ void _enableRule({bool lazy: false}) {
if (_ruleEnabled) return;
- _visitor.builder.startRule();
+ // If the properties split, force the calls to split too.
+ var rule = new Rule();
+ if (_propertyRule != null) _propertyRule.setNamedArgsRule(rule);
+
+ if (lazy) {
+ _visitor.builder.startLazyRule(rule);
+ } else {
+ _visitor.builder.startRule(rule);
+ }
+
_ruleEnabled = true;
}
diff --git a/packages/dart_style/lib/src/chunk.dart b/packages/dart_style/lib/src/chunk.dart
index e92a85b..da6d7bb 100644
--- a/packages/dart_style/lib/src/chunk.dart
+++ b/packages/dart_style/lib/src/chunk.dart
@@ -92,9 +92,13 @@
NestingLevel get nesting => _nesting;
NestingLevel _nesting;
- /// If this chunk marks the beginning of a block, these are the chunks
- /// contained in the block.
- final blockChunks = <Chunk>[];
+ /// If this chunk marks the beginning of a block, this contains the child
+ /// chunks and other data about that nested block.
+ ChunkBlock get block => _block;
+ ChunkBlock _block;
+
+ /// Whether this chunk has a [block].
+ bool get isBlock => _block != null;
/// Whether it's valid to add more text to this chunk or not.
///
@@ -110,10 +114,6 @@
Rule get rule => _rule;
Rule _rule;
- /// Whether this chunk is always followed by a newline or whether the line
- /// splitter may choose to keep the next chunk on the same line.
- bool get isHardSplit => _rule is HardSplitRule;
-
/// Whether or not an extra blank line should be output after this chunk if
/// it's split.
///
@@ -136,9 +136,9 @@
/// If `true`, then the line after this chunk and its contained block should
/// be flush left.
bool get flushLeftAfter {
- if (blockChunks.isEmpty) return _flushLeft;
+ if (!isBlock) return _flushLeft;
- return blockChunks.last.flushLeftAfter;
+ return _block.chunks.last.flushLeftAfter;
}
/// Whether this chunk should append an extra space if it does not split.
@@ -165,8 +165,10 @@
/// Does not include this chunk's own length, just the length of its child
/// block chunks (recursively).
int get unsplitBlockLength {
+ if (_block == null) return 0;
+
var length = 0;
- for (var chunk in blockChunks) {
+ for (var chunk in _block.chunks) {
length += chunk.length + chunk.unsplitBlockLength;
}
@@ -191,15 +193,6 @@
_text += text;
}
- /// Forces this soft split to become a hard split.
- ///
- /// This is called on the soft splits owned by a rule that decides to harden
- /// when it finds out another hard split occurs within its chunks.
- void harden() {
- _rule = new HardSplitRule();
- spans.clear();
- }
-
/// Finishes off this chunk with the given [rule] and split information.
///
/// This may be called multiple times on the same split since the splits
@@ -207,10 +200,10 @@
/// preserved whitespace often overlap. When that happens, this has logic to
/// combine that information into a single split.
void applySplit(Rule rule, int indent, NestingLevel nesting,
- {bool flushLeft, bool spaceWhenUnsplit, bool isDouble}) {
+ {bool flushLeft, bool isDouble, bool space}) {
if (flushLeft == null) flushLeft = false;
- if (spaceWhenUnsplit == null) spaceWhenUnsplit = false;
- if (isHardSplit || rule is HardSplitRule) {
+ if (space == null) space = false;
+ if (rule.isHardened) {
// A hard split always wins.
_rule = rule;
} else if (_rule == null) {
@@ -223,12 +216,28 @@
_nesting = nesting;
_indent = indent;
- _spaceWhenUnsplit = spaceWhenUnsplit;
+ _spaceWhenUnsplit = space;
// Pin down the double state, if given and we haven't already.
if (_isDouble == null) _isDouble = isDouble;
}
+ /// Turns this chunk into one that can contain a block of child chunks.
+ void makeBlock(Chunk blockArgument) {
+ assert(_block == null);
+ _block = new ChunkBlock(blockArgument);
+ }
+
+ /// Returns `true` if the block body owned by this chunk should be expression
+ /// indented given a set of rule values provided by [getValue].
+ bool indentBlock(int getValue(Rule rule)) {
+ if (_block == null) return false;
+ if (_block.argument == null) return false;
+
+ return _block.argument.rule
+ .isSplit(getValue(_block.argument.rule), _block.argument);
+ }
+
// Mark whether this chunk can divide the range of chunks.
void markDivide(canDivide) {
// Should only do this once.
@@ -243,19 +252,18 @@
if (text.isNotEmpty) parts.add(text);
if (_indent != null) parts.add("indent:$_indent");
- if (spaceWhenUnsplit) parts.add("space");
- if (_isDouble) parts.add("double");
- if (_flushLeft) parts.add("flush");
+ if (spaceWhenUnsplit == true) parts.add("space");
+ if (_isDouble == true) parts.add("double");
+ if (_flushLeft == true) parts.add("flush");
if (_rule == null) {
parts.add("(no split)");
- } else if (isHardSplit) {
- parts.add("hard");
} else {
parts.add(rule.toString());
+ if (rule.isHardened) parts.add("(hard)");
- if (_rule.outerRules.isNotEmpty) {
- parts.add("-> ${_rule.outerRules.join(' ')}");
+ if (_rule.constrainedRules.isNotEmpty) {
+ parts.add("-> ${_rule.constrainedRules.join(' ')}");
}
}
@@ -263,6 +271,22 @@
}
}
+/// The child chunks owned by a chunk that begins a "block" -- an actual block
+/// statement, function expression, or collection literal.
+class ChunkBlock {
+ /// If this block is for a collection literal in an argument list, this will
+ /// be the chunk preceding this literal argument.
+ ///
+ /// That chunk is owned by the argument list and if it splits, this collection
+ /// may need extra expression-level indentation.
+ final Chunk argument;
+
+ /// The child chunks in this block.
+ final List<Chunk> chunks = [];
+
+ ChunkBlock(this.argument);
+}
+
/// Constants for the cost heuristics used to determine which set of splits is
/// most desirable.
class Cost {
@@ -280,8 +304,11 @@
/// number of nested spans.
static const normal = 1;
- /// Splitting after a "=" both for assignment and initialization.
- static const assignment = 2;
+ /// Splitting after a "=".
+ static const assign = 1;
+
+ /// Splitting after a "=" when the right-hand side is a collection or cascade.
+ static const assignBlock = 2;
/// Splitting before the first argument when it happens to be a function
/// expression with a block body.
@@ -299,6 +326,9 @@
/// collection contents.
static const splitCollections = 2;
+ /// Splitting on the "." in a named constructor.
+ static const constructorName = 3;
+
/// Splitting before a type argument or type parameter.
static const typeArgument = 4;
}
diff --git a/packages/dart_style/lib/src/chunk_builder.dart b/packages/dart_style/lib/src/chunk_builder.dart
index c7ff91b..3aa95a5 100644
--- a/packages/dart_style/lib/src/chunk_builder.dart
+++ b/packages/dart_style/lib/src/chunk_builder.dart
@@ -61,9 +61,6 @@
/// written before they start.
final _lazyRules = <Rule>[];
- /// The indexes of the chunks owned by each rule (except for hard splits).
- final _ruleChunks = <Rule, List<int>>{};
-
/// The nested stack of spans that are currently being written.
final _openSpans = <OpenSpan>[];
@@ -101,8 +98,8 @@
/// token pair.
bool get needsToPreserveNewlines =>
_pendingWhitespace == Whitespace.oneOrTwoNewlines ||
- _pendingWhitespace == Whitespace.spaceOrNewline ||
- _pendingWhitespace == Whitespace.splitOrNewline;
+ _pendingWhitespace == Whitespace.spaceOrNewline ||
+ _pendingWhitespace == Whitespace.splitOrNewline;
/// The number of characters of code that can fit in a single line.
int get pageWidth => _formatter.pageWidth;
@@ -151,25 +148,18 @@
/// Write a split owned by the current innermost rule.
///
- /// If [nesting] is given, uses that. Otherwise, uses the current nesting
- /// level. If unsplit, it expands to a space if [space] is `true`.
- ///
/// If [flushLeft] is `true`, then forces the next line to start at column
/// one regardless of any indentation or nesting.
///
/// If [isDouble] is passed, forces the split to either be a single or double
/// newline. Otherwise, leaves it indeterminate.
- Chunk split({bool space, bool isDouble, bool flushLeft}) =>
- _writeSplit(_rules.last, null,
- flushLeft: flushLeft, isDouble: isDouble, spaceWhenUnsplit: space);
-
- /// Write a split owned by the current innermost rule.
///
- /// Unlike [split()], this ignores any current expression nesting. It always
- /// indents the next line at the statement level.
- Chunk blockSplit({bool space, bool isDouble}) =>
- _writeSplit(_rules.last, _nesting.blockNesting,
- isDouble: isDouble, spaceWhenUnsplit: space);
+ /// If [nest] is `false`, ignores any current expression nesting. Otherwise,
+ /// uses the current nesting level. If unsplit, it expands to a space if
+ /// [space] is `true`.
+ Chunk split({bool flushLeft, bool isDouble, bool nest, bool space}) =>
+ _writeSplit(_rules.last,
+ flushLeft: flushLeft, isDouble: isDouble, nest: nest, space: space);
/// Outputs the series of [comments] and associated whitespace that appear
/// before [token] (which is not written by this).
@@ -257,9 +247,9 @@
} else {
// The comment starts a line, so make sure it stays on its own line.
_writeHardSplit(
- nest: true,
flushLeft: comment.flushLeft,
- double: comment.linesBefore > 1);
+ isDouble: comment.linesBefore > 1,
+ nest: true);
}
_writeText(comment.text);
@@ -291,7 +281,7 @@
}
}
- if (linesAfter > 0) _writeHardSplit(nest: true, double: linesAfter > 1);
+ if (linesAfter > 0) _writeHardSplit(isDouble: linesAfter > 1, nest: true);
}
// If the comment has text following it (aside from a grouping character),
@@ -368,15 +358,15 @@
var span = new Span(openSpan.cost);
for (var i = openSpan.start; i < end; i++) {
var chunk = _chunks[i];
- if (!chunk.isHardSplit) chunk.spans.add(span);
+ if (!chunk.rule.isHardened) chunk.spans.add(span);
}
}
/// Starts a new [Rule].
///
- /// If omitted, defaults to a new [SimpleRule].
+ /// If omitted, defaults to a new [Rule].
void startRule([Rule rule]) {
- if (rule == null) rule = new SimpleRule();
+ if (rule == null) rule = new Rule();
// See if any of the rules that contain this one care if it splits.
_rules.forEach((outer) => outer.contain(rule));
@@ -390,9 +380,9 @@
/// first operand but not get forced to split if a comment appears before the
/// entire expression.
///
- /// If [rule] is omitted, defaults to a new [SimpleRule].
+ /// If [rule] is omitted, defaults to a new [Rule].
void startLazyRule([Rule rule]) {
- if (rule == null) rule = new SimpleRule();
+ if (rule == null) rule = new Rule();
_lazyRules.add(rule);
}
@@ -431,8 +421,14 @@
///
/// Expressions that are more nested will get increased indentation when split
/// if the previous line has a lower level of nesting.
- void unnest() {
+ ///
+ /// If [now] is `false`, does not commit the nesting change until after the
+ /// next chunk of text is written.
+ void unnest({bool now}) {
+ if (now == null) now = true;
+
_nesting.unnest();
+ if (now) _nesting.commitNesting();
}
/// Marks the selection starting point as occurring [fromEnd] characters to
@@ -469,9 +465,12 @@
/// Starts a new block as a child of the current chunk.
///
/// Nested blocks are handled using their own independent [LineWriter].
- ChunkBuilder startBlock() {
+ ChunkBuilder startBlock(Chunk argumentChunk) {
+ var chunk = _chunks.last;
+ chunk.makeBlock(argumentChunk);
+
var builder =
- new ChunkBuilder._(this, _formatter, _source, _chunks.last.blockChunks);
+ new ChunkBuilder._(this, _formatter, _source, chunk.block.chunks);
// A block always starts off indented one level.
builder.indent();
@@ -488,7 +487,7 @@
/// `true`, the block is considered to always split.
///
/// Returns the previous writer for the surrounding block.
- ChunkBuilder endBlock(HardSplitRule ignoredSplit, {bool forceSplit}) {
+ ChunkBuilder endBlock(Rule ignoredSplit, {bool forceSplit}) {
_divideChunks();
// If we don't already know if the block is going to split, see if it
@@ -502,21 +501,33 @@
break;
}
- if (chunk.isHardSplit && chunk.rule != ignoredSplit) {
+ if (chunk.rule != null &&
+ chunk.rule.isHardened &&
+ chunk.rule != ignoredSplit) {
forceSplit = true;
break;
}
}
}
+ _parent._endChildBlock(
+ firstFlushLeft: _firstFlushLeft, forceSplit: forceSplit);
+
+ return _parent;
+ }
+
+ /// Finishes off the last chunk in a child block of this parent.
+ void _endChildBlock({bool firstFlushLeft, bool forceSplit}) {
// If there is a hard newline within the block, force the surrounding rule
// for it so that we apply that constraint.
- if (forceSplit) _parent.forceRules();
+ if (forceSplit) forceRules();
// Write the split for the block contents themselves.
- _parent._writeSplit(_parent._rules.last, _parent._blockArgumentNesting.last,
- flushLeft: _firstFlushLeft);
- return _parent;
+ var chunk = _chunks.last;
+ chunk.applySplit(rule, _nesting.indentation, _blockArgumentNesting.last,
+ flushLeft: firstFlushLeft);
+
+ if (chunk.rule.isHardened) _handleHardSplit();
}
/// Finishes writing and returns a [SourceCode] containing the final output
@@ -577,11 +588,11 @@
break;
case Whitespace.newlineFlushLeft:
- _writeHardSplit(nest: true, flushLeft: true);
+ _writeHardSplit(flushLeft: true, nest: true);
break;
case Whitespace.twoNewlines:
- _writeHardSplit(double: true);
+ _writeHardSplit(isDouble: true);
break;
case Whitespace.spaceOrNewline:
@@ -661,46 +672,38 @@
/// Appends a hard split with the current indentation and nesting (the latter
/// only if [nest] is `true`).
///
- /// If [double] is `true` or `false`, forces a since or double line to be
+ /// If [double] is `true` or `false`, forces a single or double line to be
/// output. Otherwise, it is left indeterminate.
///
/// If [flushLeft] is `true`, then the split will always cause the next line
/// to be at column zero. Otherwise, it uses the normal indentation and
/// nesting behavior.
- void _writeHardSplit({bool nest: false, bool double, bool flushLeft}) {
+ void _writeHardSplit({bool isDouble, bool flushLeft, bool nest: false}) {
// A hard split overrides any other whitespace.
_pendingWhitespace = null;
- _writeSplit(new HardSplitRule(), nest ? null : _nesting.blockNesting,
- flushLeft: flushLeft, isDouble: double);
+ _writeSplit(new Rule.hard(),
+ flushLeft: flushLeft, isDouble: isDouble, nest: nest);
}
/// Ends the current chunk (if any) with the given split information.
///
/// Returns the chunk.
- Chunk _writeSplit(Rule rule, NestingLevel nesting,
- {bool flushLeft, bool isDouble, bool spaceWhenUnsplit}) {
+ Chunk _writeSplit(Rule rule,
+ {bool flushLeft, bool isDouble, bool nest, bool space}) {
+ if (nest == null) nest = true;
+
if (_chunks.isEmpty) {
if (flushLeft != null) _firstFlushLeft = flushLeft;
return null;
}
- if (nesting == null) nesting = _nesting.nesting;
+ _chunks.last.applySplit(rule, _nesting.indentation,
+ nest ? _nesting.nesting : new NestingLevel(),
+ flushLeft: flushLeft, isDouble: isDouble, space: space);
- var chunk = _chunks.last;
- chunk.applySplit(rule, _nesting.indentation, nesting,
- flushLeft: flushLeft,
- isDouble: isDouble,
- spaceWhenUnsplit: spaceWhenUnsplit);
-
- // Keep track of which chunks are owned by the rule.
- if (rule is! HardSplitRule) {
- _ruleChunks.putIfAbsent(rule, () => []).add(_chunks.length - 1);
- }
-
- if (chunk.isHardSplit) _handleHardSplit();
-
- return chunk;
+ if (_chunks.last.rule.isHardened) _handleHardSplit();
+ return _chunks.last;
}
/// Writes [text] to either the current chunk or a new one if the current
@@ -716,14 +719,13 @@
/// Returns true if we can divide the chunks at [index] and line split the
/// ones before and after that separately.
bool _canDivideAt(int i) {
- var chunk = _chunks[i];
- if (!chunk.isHardSplit) return false;
- if (chunk.nesting.isNested) return false;
- if (chunk.blockChunks.isNotEmpty) return false;
+ // Don't divide after the last chunk.
+ if (i == _chunks.length - 1) return false;
- // Make sure we don't split the line in the middle of a rule.
- var chunks = _ruleChunks[chunk.rule];
- if (chunks != null && chunks.any((other) => other > i)) return false;
+ var chunk = _chunks[i];
+ if (!chunk.rule.isHardened) return false;
+ if (chunk.nesting.isNested) return false;
+ if (chunk.isBlock) return false;
return true;
}
@@ -765,18 +767,16 @@
void _hardenRules() {
if (_hardSplitRules.isEmpty) return;
- // Harden all of the rules that are constrained by [rules] as well.
- var hardenedRules = new Set();
walkConstraints(rule) {
- if (hardenedRules.contains(rule)) return;
- hardenedRules.add(rule);
+ rule.harden();
// Follow this rule's constraints, recursively.
- for (var other in _ruleChunks.keys) {
+ for (var other in rule.constrainedRules) {
if (other == rule) continue;
- if (rule.constrain(rule.fullySplitValue, other) ==
- other.fullySplitValue) {
+ if (!other.isHardened &&
+ rule.constrain(rule.fullySplitValue, other) ==
+ other.fullySplitValue) {
walkConstraints(other);
}
}
@@ -786,10 +786,11 @@
walkConstraints(rule);
}
- // Harden every chunk that uses one of these rules.
+ // Discard spans in hardened chunks since we know for certain they will
+ // split anyway.
for (var chunk in _chunks) {
- if (hardenedRules.contains(chunk.rule)) {
- chunk.harden();
+ if (chunk.rule != null && chunk.rule.isHardened) {
+ chunk.spans.clear();
}
}
}
diff --git a/packages/dart_style/lib/src/dart_formatter.dart b/packages/dart_style/lib/src/dart_formatter.dart
index fc2e789..428aeec 100644
--- a/packages/dart_style/lib/src/dart_formatter.dart
+++ b/packages/dart_style/lib/src/dart_formatter.dart
@@ -60,7 +60,8 @@
}
return formatSource(
- new SourceCode(source, uri: uri, isCompilationUnit: true)).text;
+ new SourceCode(source, uri: uri, isCompilationUnit: true))
+ .text;
}
/// Formats the given [source] string containing a single Dart statement.
@@ -109,7 +110,7 @@
// Make sure we consumed all of the source.
var token = node.endToken.next;
if (token.type != TokenType.EOF) {
- var error = new AnalysisError.con2(
+ var error = new AnalysisError(
stringSource,
token.offset,
math.max(token.length, 1),
diff --git a/packages/dart_style/lib/src/debug.dart b/packages/dart_style/lib/src/debug.dart
index 47ca7fe..5d0eb2b 100644
--- a/packages/dart_style/lib/src/debug.dart
+++ b/packages/dart_style/lib/src/debug.dart
@@ -9,7 +9,6 @@
import 'chunk.dart';
import 'line_splitting/rule_set.dart';
-import 'rule/rule.dart';
/// Set this to `true` to turn on diagnostic output while building chunks.
bool traceChunkBuilder = false;
@@ -68,15 +67,16 @@
/// Prints [chunks] to stdout, one chunk per line, with detailed information
/// about each chunk.
void dumpChunks(int start, List<Chunk> chunks) {
- if (chunks.isEmpty) return;
+ if (chunks.skip(start).isEmpty) return;
- // Show the spans as vertical bands over their range.
+ // Show the spans as vertical bands over their range (unless there are too
+ // many).
var spans = new Set();
addSpans(chunks) {
for (var chunk in chunks) {
spans.addAll(chunk.spans);
- addSpans(chunk.blockChunks);
+ if (chunk.isBlock) addSpans(chunk.block.chunks);
}
}
@@ -84,28 +84,41 @@
spans = spans.toList();
- var rules = chunks
- .map((chunk) => chunk.rule)
- .where((rule) => rule != null && rule is! HardSplitRule)
- .toSet();
+ var rules =
+ chunks.map((chunk) => chunk.rule).where((rule) => rule != null).toSet();
var rows = [];
- addChunk(chunk, prefix, index) {
+ addChunk(List<Chunk> chunks, String prefix, int index) {
var row = [];
row.add("$prefix$index:");
+ var chunk = chunks[index];
if (chunk.text.length > 70) {
row.add(chunk.text.substring(0, 70));
} else {
row.add(chunk.text);
}
- var spanBars = "";
- for (var span in spans) {
- spanBars += chunk.spans.contains(span) ? "|" : " ";
+ if (spans.length <= 20) {
+ var spanBars = "";
+ for (var span in spans) {
+ if (chunk.spans.contains(span)) {
+ if (index == 0 || !chunks[index - 1].spans.contains(span)) {
+ spanBars += "â•–";
+ } else {
+ spanBars += "â•‘";
+ }
+ } else {
+ if (index > 0 && chunks[index - 1].spans.contains(span)) {
+ spanBars += "╜";
+ } else {
+ spanBars += " ";
+ }
+ }
+ }
+ row.add(spanBars);
}
- row.add(spanBars);
writeIf(predicate, String callback()) {
if (predicate) {
@@ -115,16 +128,21 @@
}
}
- if (chunk.rule != null) {
- row.add(chunk.isHardSplit ? "" : chunk.rule.toString());
-
- var outerRules = chunk.rule.outerRules.toSet().intersection(rules);
- writeIf(outerRules.isNotEmpty, () => "-> ${outerRules.join(" ")}");
- } else {
- row.add("(no rule)");
-
- // Outer rules.
+ if (chunk.rule == null) {
row.add("");
+ row.add("(no rule)");
+ row.add("");
+ } else {
+ writeIf(chunk.rule.cost != 0, () => "\$${chunk.rule.cost}");
+
+ var ruleString = chunk.rule.toString();
+ if (chunk.rule.isHardened) ruleString += "!";
+ row.add(ruleString);
+
+ var constrainedRules =
+ chunk.rule.constrainedRules.toSet().intersection(rules);
+ writeIf(constrainedRules.isNotEmpty,
+ () => "-> ${constrainedRules.join(" ")}");
}
writeIf(chunk.indent != null && chunk.indent != 0,
@@ -135,17 +153,19 @@
writeIf(chunk.flushLeft != null && chunk.flushLeft, () => "flush");
+ writeIf(chunk.canDivide, () => "divide");
+
rows.add(row);
- for (var j = 0; j < chunk.blockChunks.length; j++) {
- addChunk(chunk.blockChunks[j], "$prefix$index.", j);
+ if (chunk.isBlock) {
+ for (var j = 0; j < chunk.block.chunks.length; j++) {
+ addChunk(chunk.block.chunks, "$prefix$index.", j);
+ }
}
}
- var i = start;
- for (var chunk in chunks) {
- addChunk(chunk, "", i);
- i++;
+ for (var i = start; i < chunks.length; i++) {
+ addChunk(chunks, "", i);
}
var rowWidths = new List.filled(rows.first.length, 0);
@@ -172,6 +192,33 @@
print(buffer.toString());
}
+/// Shows all of the constraints between the rules used by [chunks].
+void dumpConstraints(List<Chunk> chunks) {
+ var rules =
+ chunks.map((chunk) => chunk.rule).where((rule) => rule != null).toSet();
+
+ for (var rule in rules) {
+ var constrainedValues = [];
+ for (var value = 0; value < rule.numValues; value++) {
+ var constraints = [];
+ for (var other in rules) {
+ if (rule == other) continue;
+
+ var constraint = rule.constrain(value, other);
+ if (constraint != null) {
+ constraints.add("$other->$constraint");
+ }
+ }
+
+ if (constraints.isNotEmpty) {
+ constrainedValues.add("$value:(${constraints.join(' ')})");
+ }
+ }
+
+ log("$rule ${constrainedValues.join(' ')}");
+ }
+}
+
/// Convert the line to a [String] representation.
///
/// It will determine how best to split it into multiple lines of output and
@@ -187,7 +234,7 @@
if (chunk.spaceWhenUnsplit) buffer.write(" ");
// Recurse into the block.
- writeChunksUnsplit(chunk.blockChunks);
+ if (chunk.isBlock) writeChunksUnsplit(chunk.block.chunks);
}
}
@@ -203,7 +250,7 @@
writeIndent(splits.getColumn(i));
}
} else {
- writeChunksUnsplit(chunk.blockChunks);
+ if (chunk.isBlock) writeChunksUnsplit(chunk.block.chunks);
if (chunk.spaceWhenUnsplit) buffer.write(" ");
}
diff --git a/packages/dart_style/lib/src/formatter_options.dart b/packages/dart_style/lib/src/formatter_options.dart
index 3869e8a..69a46ff 100644
--- a/packages/dart_style/lib/src/formatter_options.dart
+++ b/packages/dart_style/lib/src/formatter_options.dart
@@ -14,6 +14,9 @@
/// The [OutputReporter] used to show the formatting results.
final OutputReporter reporter;
+ /// The number of spaces of indentation to prefix the output with.
+ final int indent;
+
/// The number of columns that formatted output should be constrained to fit
/// within.
final int pageWidth;
@@ -22,7 +25,7 @@
final bool followLinks;
FormatterOptions(this.reporter,
- {this.pageWidth: 80, this.followLinks: false});
+ {this.indent: 0, this.pageWidth: 80, this.followLinks: false});
}
/// How the formatter reports the results it produces.
@@ -47,20 +50,23 @@
/// Describe the symlink at [path] that wasn't followed.
void showSkippedLink(String path) {}
- /// Describe the hidden file at [path] that wasn't processed.
- void showHiddenFile(String path) {}
+ /// Describe the hidden [path] that wasn't processed.
+ void showHiddenPath(String path) {}
+
+ /// Called when [file] is about to be formatted.
+ void beforeFile(File file, String label) {}
/// Describe the processed file at [path] whose formatted result is [output].
///
/// If the contents of the file are the same as the formatted output,
/// [changed] will be false.
- void showFile(File file, String label, SourceCode output, {bool changed});
+ void afterFile(File file, String label, SourceCode output, {bool changed});
}
/// Prints only the names of files whose contents are different from their
/// formatted version.
class _DryRunReporter extends OutputReporter {
- void showFile(File file, String label, SourceCode output, {bool changed}) {
+ void afterFile(File file, String label, SourceCode output, {bool changed}) {
// Only show the changed files.
if (changed) print(label);
}
@@ -76,11 +82,11 @@
print("Skipping link $path");
}
- void showHiddenFile(String path) {
- print("Skipping hidden file $path");
+ void showHiddenPath(String path) {
+ print("Skipping hidden path $path");
}
- void showFile(File file, String label, SourceCode output, {bool changed}) {
+ void afterFile(File file, String label, SourceCode output, {bool changed}) {
// Don't add an extra newline.
stdout.write(output.text);
}
@@ -89,7 +95,7 @@
/// Prints the formatted result and selection info of each file to stdout as a
/// JSON map.
class _PrintJsonReporter extends OutputReporter {
- void showFile(File file, String label, SourceCode output, {bool changed}) {
+ void afterFile(File file, String label, SourceCode output, {bool changed}) {
// TODO(rnystrom): Put an empty selection in here to remain compatible with
// the old formatter. Since there's no way to pass a selection on the
// command line, this will never be used, which is why it's hard-coded to
@@ -108,7 +114,7 @@
/// Overwrites each file with its formatted result.
class _OverwriteReporter extends _PrintReporter {
- void showFile(File file, String label, SourceCode output, {bool changed}) {
+ void afterFile(File file, String label, SourceCode output, {bool changed}) {
if (changed) {
file.writeAsStringSync(output.text);
print("Formatted $label");
@@ -117,3 +123,77 @@
}
}
}
+
+/// A decorating reporter that reports how long it took for format each file.
+class ProfileReporter implements OutputReporter {
+ final OutputReporter _inner;
+
+ /// The files that have been started but have not completed yet.
+ ///
+ /// Maps a file label to the time that it started being formatted.
+ final Map<String, DateTime> _ongoing = {};
+
+ /// The elapsed time it took to format each completed file.
+ final Map<String, Duration> _elapsed = {};
+
+ /// The number of files that completed so fast that they aren't worth
+ /// tracking.
+ int _elided = 0;
+
+ ProfileReporter(this._inner);
+
+ /// Show the times for the slowest files to format.
+ void showProfile() {
+ // Everything should be done.
+ assert(_ongoing.isEmpty);
+
+ var files = _elapsed.keys.toList();
+ files.sort((a, b) => _elapsed[b].compareTo(_elapsed[a]));
+
+ for (var file in files) {
+ print("${_elapsed[file]}: $file");
+ }
+
+ if (_elided >= 1) {
+ var s = _elided > 1 ? 's' : '';
+ print("...$_elided more file$s each took less than 10ms.");
+ }
+ }
+
+ /// Describe the directory whose contents are about to be processed.
+ void showDirectory(String path) {
+ _inner.showDirectory(path);
+ }
+
+ /// Describe the symlink at [path] that wasn't followed.
+ void showSkippedLink(String path) {
+ _inner.showSkippedLink(path);
+ }
+
+ /// Describe the hidden [path] that wasn't processed.
+ void showHiddenPath(String path) {
+ _inner.showHiddenPath(path);
+ }
+
+ /// Called when [file] is about to be formatted.
+ void beforeFile(File file, String label) {
+ _inner.beforeFile(file, label);
+
+ _ongoing[label] = new DateTime.now();
+ }
+
+ /// Describe the processed file at [path] whose formatted result is [output].
+ ///
+ /// If the contents of the file are the same as the formatted output,
+ /// [changed] will be false.
+ void afterFile(File file, String label, SourceCode output, {bool changed}) {
+ var elapsed = new DateTime.now().difference(_ongoing.remove(label));
+ if (elapsed.inMilliseconds >= 10) {
+ _elapsed[label] = elapsed;
+ } else {
+ _elided++;
+ }
+
+ _inner.afterFile(file, label, output, changed: changed);
+ }
+}
diff --git a/packages/dart_style/lib/src/io.dart b/packages/dart_style/lib/src/io.dart
index 765ac3e..51f3805 100644
--- a/packages/dart_style/lib/src/io.dart
+++ b/packages/dart_style/lib/src/io.dart
@@ -22,6 +22,8 @@
options.reporter.showDirectory(directory.path);
var success = true;
+ var shownHiddenPaths = new Set<String>();
+
for (var entry in directory.listSync(
recursive: true, followLinks: options.followLinks)) {
var relative = p.relative(entry.path, from: directory.path);
@@ -34,8 +36,22 @@
if (entry is! File || !entry.path.endsWith(".dart")) continue;
// If the path is in a subdirectory starting with ".", ignore it.
- if (p.split(relative).any((part) => part.startsWith("."))) {
- options.reporter.showHiddenFile(relative);
+ var parts = p.split(relative);
+ var hiddenIndex;
+ for (var i = 0; i < parts.length; i++) {
+ if (parts[i].startsWith(".")) {
+ hiddenIndex = i;
+ break;
+ }
+ }
+
+ if (hiddenIndex != null) {
+ // Since we'll hide everything inside the directory starting with ".",
+ // show the directory name once instead of once for each file.
+ var hiddenPath = p.joinAll(parts.take(hiddenIndex + 1));
+ if (shownHiddenPaths.add(hiddenPath)) {
+ options.reporter.showHiddenPath(hiddenPath);
+ }
continue;
}
@@ -51,12 +67,14 @@
bool processFile(FormatterOptions options, File file, {String label}) {
if (label == null) label = file.path;
- var formatter = new DartFormatter(pageWidth: options.pageWidth);
+ var formatter =
+ new DartFormatter(indent: options.indent, pageWidth: options.pageWidth);
try {
var source = new SourceCode(file.readAsStringSync(), uri: file.path);
+ options.reporter.beforeFile(file, label);
var output = formatter.formatSource(source);
options.reporter
- .showFile(file, label, output, changed: source.text != output.text);
+ .afterFile(file, label, output, changed: source.text != output.text);
return true;
} on FormatterException catch (err) {
var color = Platform.operatingSystem != "windows" &&
diff --git a/packages/dart_style/lib/src/line_splitting/line_splitter.dart b/packages/dart_style/lib/src/line_splitting/line_splitter.dart
index f562ca4..420c509 100644
--- a/packages/dart_style/lib/src/line_splitting/line_splitter.dart
+++ b/packages/dart_style/lib/src/line_splitting/line_splitter.dart
@@ -130,10 +130,10 @@
int firstLineIndent,
{bool flushLeft: false})
: chunks = chunks,
- // Collect the set of soft rules that we need to select values for.
+ // Collect the set of rules that we need to select values for.
rules = chunks
.map((chunk) => chunk.rule)
- .where((rule) => rule != null && rule is! HardSplitRule)
+ .where((rule) => rule != null)
.toSet()
.toList(growable: false),
blockIndentation = blockIndentation,
@@ -145,6 +145,12 @@
for (var i = 0; i < rules.length; i++) {
rules[i].index = i;
}
+
+ // Now that every used rule has an index, tell the rules to discard any
+ // constraints on unindexed rules.
+ for (var rule in rules) {
+ rule.forgetUnusedRules();
+ }
}
/// Determine the best way to split the chunks into lines that fit in the
diff --git a/packages/dart_style/lib/src/line_splitting/rule_set.dart b/packages/dart_style/lib/src/line_splitting/rule_set.dart
index 709b373..714875d 100644
--- a/packages/dart_style/lib/src/line_splitting/rule_set.dart
+++ b/packages/dart_style/lib/src/line_splitting/rule_set.dart
@@ -23,14 +23,22 @@
RuleSet._(this._values);
/// Returns `true` of [rule] is bound in this set.
- bool contains(Rule rule) => _values[rule.index] != null;
+ bool contains(Rule rule) {
+ // Treat hardened rules as implicitly bound.
+ if (rule.isHardened) return true;
- /// Gets the bound value for [rule] or `0` if it is not bound.
+ return _values[rule.index] != null;
+ }
+
+ /// Gets the bound value for [rule] or [Rule.unsplit] if it is not bound.
int getValue(Rule rule) {
+ // Hardened rules are implicitly bound.
+ if (rule.isHardened) return rule.fullySplitValue;
+
var value = _values[rule.index];
if (value != null) return value;
- return 0;
+ return Rule.unsplit;
}
/// Invokes [callback] for each rule in [rules] with the rule's value, which
@@ -56,19 +64,26 @@
/// If an unbound rule gets constrained to `-1` (meaning it must split, but
/// can split any way it wants), invokes [onSplitRule] with it.
bool tryBind(List<Rule> rules, Rule rule, int value, onSplitRule(Rule rule)) {
+ assert(!rule.isHardened);
+
_values[rule.index] = value;
// Test this rule against the other rules being bound.
- for (var other in rules) {
- if (rule == other) continue;
+ for (var other in rule.constrainedRules) {
+ var otherValue;
+ // Hardened rules are implicitly bound.
+ if (other.isHardened) {
+ otherValue = other.fullySplitValue;
+ } else {
+ otherValue = _values[other.index];
+ }
- var otherValue = _values[other.index];
var constraint = rule.constrain(value, other);
if (otherValue == null) {
// The other rule is unbound, so see if we can constrain it eagerly to
// a value now.
- if (constraint == -1) {
+ if (constraint == Rule.mustSplit) {
// If we know the rule has to split and there's only one way it can,
// just bind that.
if (other.numValues == 2) {
@@ -84,16 +99,16 @@
} else {
// It's already bound, so see if the new rule's constraint disallows
// that value.
- if (constraint == -1) {
- if (otherValue == 0) return false;
+ if (constraint == Rule.mustSplit) {
+ if (otherValue == Rule.unsplit) return false;
} else if (constraint != null) {
if (otherValue != constraint) return false;
}
// See if the other rule's constraint allows us to use this value.
constraint = other.constrain(otherValue, rule);
- if (constraint == -1) {
- if (value == 0) return false;
+ if (constraint == Rule.mustSplit) {
+ if (value == Rule.unsplit) return false;
} else if (constraint != null) {
if (value != constraint) return false;
}
diff --git a/packages/dart_style/lib/src/line_splitting/solve_state.dart b/packages/dart_style/lib/src/line_splitting/solve_state.dart
index b419756..e1e9168 100644
--- a/packages/dart_style/lib/src/line_splitting/solve_state.dart
+++ b/packages/dart_style/lib/src/line_splitting/solve_state.dart
@@ -6,6 +6,7 @@
import '../debug.dart' as debug;
import '../rule/rule.dart';
+import '../whitespace.dart';
import 'line_splitter.dart';
import 'rule_set.dart';
@@ -25,11 +26,21 @@
final LineSplitter _splitter;
final RuleSet _ruleValues;
+ /// The set of [Rule]s that are bound by [_ruleValues].
+ ///
+ /// Cached by [_ensureConstraints] for use by [_ensureUnboundConstraints].
+ Set<Rule> _boundRules;
+
+ /// The set of [Rule]s that are not bound by [_ruleValues].
+ ///
+ /// Cached by [_ensureConstraints] for use by [_ensureUnboundConstraints].
+ Set<Rule> _unboundRules;
+
/// The unbound rules in this state that can be bound to produce new more
/// refined states.
///
/// Keeping this set small is the key to make the entire line splitter
- /// perform well. If we consider too make rules at each state, our
+ /// perform well. If we consider too many rules at each state, our
/// exploration of the solution space is too branchy and we waste time on
/// dead end solutions.
///
@@ -75,6 +86,19 @@
/// unbound rules.
Map<Rule, int> _constraints;
+ /// The unbound rule values that are disallowed because they would place
+ /// invalid constraints on the currently bound values.
+ ///
+ /// For example, say rule A is bound to 1 and B is unbound. If B has value
+ /// 1, it constrains A to be 1. Likewise, if B is 2, it constrains A to be
+ /// 2 as well. Since we already know A is 1, that means we know B cannot be
+ /// bound to value 2. This means B is more limited in this state than it
+ /// might be in another state that binds A to a different value.
+ ///
+ /// It's important to track this, because we can't allow to states to overlap
+ /// if one permits more values for some unbound rule than the other does.
+ Map<Rule, Set<int>> _unboundConstraints;
+
/// The bound rules that appear inside lines also containing unbound rules.
///
/// By appearing in the same line, it means these bound rules may affect the
@@ -87,13 +111,9 @@
_calculateCost();
}
- /// Gets the value to use for [rule], either the bound value or `0` if it
- /// isn't bound.
- int getValue(Rule rule) {
- if (rule is HardSplitRule) return 0;
-
- return _ruleValues.getValue(rule);
- }
+ /// Gets the value to use for [rule], either the bound value or
+ /// [Rule.unsplit] if it isn't bound.
+ int getValue(Rule rule) => _ruleValues.getValue(rule);
/// Returns `true` if this state is a better solution to use as the final
/// result than [other].
@@ -212,11 +232,10 @@
/// Returns `true` if [other] overlaps this state.
bool _isOverlapping(SolveState other) {
- _ensureOverlapFields();
- other._ensureOverlapFields();
-
// Lines that contain both bound and unbound rules must have the same
// bound values.
+ _ensureBoundRulesInUnboundLines();
+ other._ensureBoundRulesInUnboundLines();
if (_boundRulesInUnboundLines.length !=
other._boundRulesInUnboundLines.length) {
return false;
@@ -229,12 +248,30 @@
}
}
+ _ensureConstraints();
+ other._ensureConstraints();
if (_constraints.length != other._constraints.length) return false;
for (var rule in _constraints.keys) {
if (_constraints[rule] != other._constraints[rule]) return false;
}
+ _ensureUnboundConstraints();
+ other._ensureUnboundConstraints();
+ if (_unboundConstraints.length != other._unboundConstraints.length) {
+ return false;
+ }
+
+ for (var rule in _unboundConstraints.keys) {
+ var disallowed = _unboundConstraints[rule];
+ var otherDisallowed = other._unboundConstraints[rule];
+
+ if (disallowed.length != otherDisallowed.length) return false;
+ for (var value in disallowed) {
+ if (!otherDisallowed.contains(value)) return false;
+ }
+ }
+
return true;
}
@@ -267,6 +304,8 @@
// And any expression nesting.
indent += chunk.nesting.totalUsedIndent;
+
+ if (chunk.indentBlock(getValue)) indent += Indent.expression;
}
_splits.add(i, indent);
@@ -288,7 +327,8 @@
// The unbound rules in use by the current line. This will be null after
// the first long line has completed.
- var currentLineRules = [];
+ var foundOverflowRules = false;
+ var start = 0;
endLine(int end) {
// Track lines that went over the length. It is only rules contained in
@@ -298,16 +338,16 @@
// Only try rules that are in the first long line, since we know at
// least one of them *will* be split.
- if (currentLineRules != null && currentLineRules.isNotEmpty) {
- _liveRules.addAll(currentLineRules);
- currentLineRules = null;
- }
- } else {
- // The line fit, so don't keep track of its rules.
- if (currentLineRules != null) {
- currentLineRules.clear();
+ if (!foundOverflowRules) {
+ for (var i = start; i < end; i++) {
+ if (_addLiveRules(_splitter.chunks[i].rule)) {
+ foundOverflowRules = true;
+ }
+ }
}
}
+
+ start = end;
}
// The set of spans that contain chunks that ended up splitting. We store
@@ -315,6 +355,9 @@
// one split occurs in it.
var splitSpans = new Set();
+ // The nesting level of the chunk that ended the previous line.
+ var previousNesting;
+
for (var i = 0; i < _splitter.chunks.length; i++) {
var chunk = _splitter.chunks[i];
@@ -329,11 +372,37 @@
splitSpans.addAll(chunk.spans);
// Include the cost of the nested block.
- if (chunk.blockChunks.isNotEmpty) {
+ if (chunk.isBlock) {
cost +=
_splitter.writer.formatBlock(chunk, _splits.getColumn(i)).cost;
}
+ // Do not allow sequential lines to have the same indentation but for
+ // different reasons. In other words, don't allow different expressions
+ // to claim the same nesting level on subsequent lines.
+ //
+ // A contrived example would be:
+ //
+ // function(inner(
+ // argument), second(
+ // another);
+ //
+ // For the most part, we prevent this by the constraints on splits.
+ // For example, the above can't happen because the split before
+ // "argument", forces the split before "second".
+ //
+ // But there are a couple of squirrely cases where it's hard to prevent
+ // by construction. Instead, this outlaws it by penalizing it very
+ // heavily if it happens to get this far.
+ if (previousNesting != null &&
+ chunk.nesting.totalUsedIndent != 0 &&
+ chunk.nesting.totalUsedIndent == previousNesting.totalUsedIndent &&
+ !identical(chunk.nesting, previousNesting)) {
+ _overflowChars += 10000;
+ }
+
+ previousNesting = chunk.nesting;
+
// Start the new line.
length = _splits.getColumn(i);
} else {
@@ -341,23 +410,12 @@
// Include the nested block inline, if any.
length += chunk.unsplitBlockLength;
-
- // If we might be in the first overly long line, keep track of any
- // unbound rules we encounter. These are ones that we'll want to try to
- // bind to shorten the long line.
- if (currentLineRules != null &&
- chunk.rule != null &&
- !chunk.isHardSplit &&
- !_ruleValues.contains(chunk.rule)) {
- currentLineRules.add(chunk.rule);
- }
}
}
- // Add the costs for the rules that split.
+ // Add the costs for the rules that have any splits.
_ruleValues.forEach(_splitter.rules, (rule, value) {
- // A rule may be bound to zero if another rule constrains it to not split.
- if (value != 0) cost += rule.cost;
+ if (value != Rule.unsplit) cost += rule.cost;
});
// Add the costs for the spans containing splits.
@@ -369,46 +427,32 @@
_splits.setCost(cost);
}
- /// Lazily initializes the fields needed to compare two states for overlap.
+ /// Adds [rule] and all of the rules it constrains to the set of [_liveRules].
+ ///
+ /// Only does this if [rule] is a valid soft rule. Returns `true` if any new
+ /// live rules were added.
+ bool _addLiveRules(Rule rule) {
+ if (rule == null) return false;
+
+ var added = false;
+ for (var constrained in rule.allConstrainedRules) {
+ if (_ruleValues.contains(constrained)) continue;
+
+ _liveRules.add(constrained);
+ added = true;
+ }
+
+ return added;
+ }
+
+ /// Lazily initializes the [_boundInUnboundLines], which is needed to compare
+ /// two states for overlap.
///
/// We do this lazily because the calculation is a bit slow, and is only
/// needed when we have two states with the same score.
- void _ensureOverlapFields() {
- if (_constraints != null) return;
+ void _ensureBoundRulesInUnboundLines() {
+ if (_boundRulesInUnboundLines != null) return;
- _calculateConstraints();
- _calculateBoundRulesInUnboundLines();
- }
-
- /// Initializes [_constraints] with any constraints the bound rules place on
- /// the unbound rules.
- void _calculateConstraints() {
- _constraints = {};
-
- var unboundRules = [];
- var boundRules = [];
-
- for (var rule in _splitter.rules) {
- if (_ruleValues.contains(rule)) {
- boundRules.add(rule);
- } else {
- unboundRules.add(rule);
- }
- }
-
- for (var bound in boundRules) {
- var value = _ruleValues.getValue(bound);
-
- for (var unbound in unboundRules) {
- var constraint = bound.constrain(value, unbound);
- if (constraint != null) {
- _constraints[unbound] = constraint;
- }
- }
- }
- }
-
- void _calculateBoundRulesInUnboundLines() {
_boundRulesInUnboundLines = new Set();
var boundInLine = new Set();
@@ -423,7 +467,7 @@
}
var rule = _splitter.chunks[i].rule;
- if (rule != null && rule is! HardSplitRule) {
+ if (rule != null) {
if (_ruleValues.contains(rule)) {
boundInLine.add(rule);
} else {
@@ -435,6 +479,89 @@
if (hasUnbound) _boundRulesInUnboundLines.addAll(boundInLine);
}
+ /// Lazily initializes the [_constraints], which is needed to compare two
+ /// states for overlap.
+ ///
+ /// We do this lazily because the calculation is a bit slow, and is only
+ /// needed when we have two states with the same score.
+ void _ensureConstraints() {
+ if (_constraints != null) return;
+
+ _unboundRules = new Set();
+ _boundRules = new Set();
+
+ for (var rule in _splitter.rules) {
+ if (_ruleValues.contains(rule)) {
+ _boundRules.add(rule);
+ } else {
+ _unboundRules.add(rule);
+ }
+ }
+
+ _constraints = {};
+
+ for (var bound in _boundRules) {
+ for (var unbound in bound.constrainedRules) {
+ if (!_unboundRules.contains(unbound)) continue;
+
+ var value = _ruleValues.getValue(bound);
+ var constraint = bound.constrain(value, unbound);
+ if (constraint != null) {
+ _constraints[unbound] = constraint;
+ }
+ }
+ }
+ }
+
+ /// Lazily initializes the [_unboundConstraints], which is needed to compare
+ /// two states for overlap.
+ ///
+ /// We do this lazily because the calculation is a bit slow, and is only
+ /// needed when we have two states with the same score.
+ void _ensureUnboundConstraints() {
+ if (_unboundConstraints != null) return;
+
+ // _ensureConstraints should be called first which initializes these.
+ assert(_boundRules != null);
+ assert(_unboundRules != null);
+
+ _unboundConstraints = {};
+
+ for (var unbound in _unboundRules) {
+ var disallowedValues;
+
+ for (var bound in unbound.constrainedRules) {
+ if (!_boundRules.contains(bound)) continue;
+
+ var boundValue = _ruleValues.getValue(bound);
+
+ for (var value = 0; value < unbound.numValues; value++) {
+ var constraint = unbound.constrain(value, bound);
+
+ // If the unbound rule doesn't place any constraint on this bound
+ // rule, we're fine.
+ if (constraint == null) continue;
+
+ // If the bound rule's value already meets the constraint it applies,
+ // we don't need to track it. This way, two states that have the
+ // same bound value, one of which has a satisfied constraint, are
+ // still allowed to overlap.
+ if (constraint == boundValue) continue;
+ if (constraint == Rule.mustSplit && boundValue != Rule.unsplit) {
+ continue;
+ }
+
+ if (disallowedValues == null) {
+ disallowedValues = new Set();
+ _unboundConstraints[unbound] = disallowedValues;
+ }
+
+ disallowedValues.add(value);
+ }
+ }
+ }
+ }
+
String toString() {
var buffer = new StringBuffer();
diff --git a/packages/dart_style/lib/src/line_writer.dart b/packages/dart_style/lib/src/line_writer.dart
index 055892e..9fb5d39 100644
--- a/packages/dart_style/lib/src/line_writer.dart
+++ b/packages/dart_style/lib/src/line_writer.dart
@@ -82,7 +82,7 @@
if (cached != null) return cached;
var writer = new LineWriter._(
- chunk.blockChunks, _lineEnding, pageWidth, column, _blockCache);
+ chunk.block.chunks, _lineEnding, pageWidth, column, _blockCache);
// TODO(rnystrom): Passing in an initial indent here is hacky. The
// LineWriter ensures all but the first chunk have a block indent, and this
@@ -131,7 +131,7 @@
_buffer.toString(), totalCost, _selectionStart, _selectionEnd);
}
- /// Takes the first [length] of the chunks with leading [indent], removes
+ /// Takes the chunks from [start] to [end] with leading [indent], removes
/// them, and runs the [LineSplitter] on them.
int _completeLine(int newlines, int indent, int start, int end,
{bool flushLeft}) {
@@ -144,7 +144,7 @@
if (debug.traceLineWriter) {
debug.log(debug.green("\nWriting:"));
- debug.dumpChunks(start, chunks);
+ debug.dumpChunks(0, chunks);
debug.log();
}
@@ -163,11 +163,11 @@
var chunk = chunks[i];
_writeChunk(chunk);
- if (chunk.blockChunks.isNotEmpty) {
+ if (chunk.isBlock) {
if (!splits.shouldSplitAt(i)) {
// This block didn't split (which implies none of the child blocks
// of that block split either, recursively), so write them all inline.
- _writeChunksUnsplit(chunk.blockChunks);
+ _writeChunksUnsplit(chunk);
} else {
// Include the formatted block contents.
var block = formatBlock(chunk, splits.getColumn(i));
@@ -201,16 +201,18 @@
return splits.cost;
}
- /// Writes [chunks] (and any child chunks of them, recursively) without any
- /// splitting.
- void _writeChunksUnsplit(List<Chunk> chunks) {
- for (var chunk in chunks) {
- _writeChunk(chunk);
+ /// Writes the block chunks of [chunk] (and any child chunks of them,
+ /// recursively) without any splitting.
+ void _writeChunksUnsplit(Chunk chunk) {
+ if (!chunk.isBlock) return;
- if (chunk.spaceWhenUnsplit) _buffer.write(" ");
+ for (var blockChunk in chunk.block.chunks) {
+ _writeChunk(blockChunk);
+
+ if (blockChunk.spaceWhenUnsplit) _buffer.write(" ");
// Recurse into the block.
- _writeChunksUnsplit(chunk.blockChunks);
+ _writeChunksUnsplit(blockChunk);
}
}
diff --git a/packages/dart_style/lib/src/nesting_builder.dart b/packages/dart_style/lib/src/nesting_builder.dart
index 095f247..2efebb3 100644
--- a/packages/dart_style/lib/src/nesting_builder.dart
+++ b/packages/dart_style/lib/src/nesting_builder.dart
@@ -7,36 +7,25 @@
import 'nesting_level.dart';
import 'whitespace.dart';
-/// Keeps track of expression nesting while the source code is being visited
-/// and the chunks are being built.
+/// Keeps track of block indentation and expression nesting while the source
+/// code is being visited and the chunks are being built.
+///
+/// This class requires (and verifies) that indentation and nesting are
+/// stratified from each other. Expression nesting is always inside block
+/// indentation, which means it is an error to try to change the block
+/// indentation while any expression nesting is in effect.
class NestingBuilder {
- /// The expression nesting levels and block indentation levels.
+ /// The block indentation levels.
///
- /// This is tracked as a stack of [_IndentLevel]s. Each element in the stack
- /// represents a level of block indentation. It's stored as a stack because
- /// expressions may contain blocks which in turn contain other expressions.
- /// The nesting level of the inner expressions are unrelated to the
- /// surrounding ones. For example:
- ///
- /// outer(invocation(() {
- /// inner(lambda());
- /// }));
- ///
- /// When writing `inner(lambda())`, we need to track its nesting level. At
- /// the same time, when the lambda is done, we need to return to the nesting
- /// level of `outer(invocation(...`.
- // TODO(rnystrom): I think this is no longer true now that blocks are handled
- // as separate nested chunks. Once cascades use expression nesting, we may
- // be able to just store a single nesting depth in NestingBuilder.
- ///
- /// Has an implicit entry for top-most expression nesting outside of any
- /// block for things like wrapped directives.
- final List<_IndentLevel> _stack = [new _IndentLevel(0)];
+ /// This is tracked as a stack of numbers, each of which is the total number
+ /// of spaces of block indentation. We only store the stack of previous
+ /// levels as a convenience to the caller: it spares you from having to pass
+ /// the unindent amount to [unindent()].
+ final List<int> _stack = [0];
- /// When not `null`, the nesting level of the current innermost block after
- /// the next token is written.
+ /// When not `null`, the expression nesting after the next token is written.
///
- /// When the nesting level is increased, we don't want it to take effect until
+ /// When the nesting is increased, we don't want it to take effect until
/// after at least one token has been written. That ensures that comments
/// appearing before the first token are correctly indented. For example, a
/// binary operator expression increases the nesting before the first operand
@@ -51,24 +40,35 @@
/// would incorrectly get indented because the line comment adds a split which
/// would take the nesting level of the binary operator into account even
/// though we haven't written any of its tokens yet.
+ ///
+ /// Likewise, when nesting is decreased, we may want to defer that until
+ /// we've written the next token to handle uncommon cases like:
+ ///
+ /// do // comment
+ /// {
+ /// ...
+ /// }
+ ///
+ /// Here, if we discard the expression nesting before we reach the "{", then
+ /// it won't get indented as it should.
NestingLevel _pendingNesting;
/// The current number of characters of block indentation.
- int get indentation => _stack.last.indent;
+ int get indentation => _stack.last;
- /// The nesting depth of the current inner-most block.
- NestingLevel get nesting => _stack.last.nesting;
+ /// The current nesting, ignoring any pending nesting.
+ NestingLevel get nesting => _nesting;
+ NestingLevel _nesting = new NestingLevel();
- /// The nesting depth of the current inner-most block, including any pending
- /// nesting.
+ /// The current nesting, including any pending nesting.
NestingLevel get currentNesting =>
- _pendingNesting != null ? _pendingNesting : _stack.last.nesting;
+ _pendingNesting != null ? _pendingNesting : _nesting;
/// The top "nesting level" that represents no expression nesting for the
/// current block.
NestingLevel get blockNesting {
// Walk the nesting levels until we bottom out.
- var result = nesting;
+ var result = _nesting;
while (result.parent != null) {
result = result.parent;
}
@@ -81,20 +81,31 @@
void indent([int spaces]) {
if (spaces == null) spaces = Indent.block;
+ // Indentation should only change outside of nesting.
assert(_pendingNesting == null);
+ assert(_nesting.indent == 0);
- _stack.add(new _IndentLevel(_stack.last.indent + spaces));
+ _stack.add(_stack.last + spaces);
}
/// Discards the most recent indentation level.
void unindent() {
+ // Indentation should only change outside of nesting.
assert(_pendingNesting == null);
+ assert(_nesting.indent == 0);
+
+ // If this fails, an unindent() call did not have a preceding indent() call.
+ assert(_stack.isNotEmpty);
+
_stack.removeLast();
}
/// Begins a new expression nesting level [indent] deeper than the current
/// one if it splits.
///
+ /// Expressions that are more nested will get increased indentation when split
+ /// if the previous line has a lower level of nesting.
+ ///
/// If [indent] is omitted, defaults to [Indent.expression].
void nest([int indent]) {
if (indent == null) indent = Indent.expression;
@@ -102,46 +113,27 @@
if (_pendingNesting != null) {
_pendingNesting = _pendingNesting.nest(indent);
} else {
- _pendingNesting = nesting.nest(indent);
+ _pendingNesting = _nesting.nest(indent);
}
}
/// Discards the most recent level of expression nesting.
- ///
- /// Expressions that are more nested will get increased indentation when split
- /// if the previous line has a lower level of nesting.
void unnest() {
- // By the time the nesting is done, it should have emitted some text and
- // not be pending anymore.
- assert(_pendingNesting == null);
+ if (_pendingNesting != null) {
+ _pendingNesting = _pendingNesting.parent;
+ } else {
+ _pendingNesting = _nesting.parent;
+ }
- _setNesting(nesting.parent);
+ // If this fails, an unnest() call did not have a preceding nest() call.
+ assert(_pendingNesting != null);
}
/// Applies any pending nesting now that we are ready for it to take effect.
void commitNesting() {
if (_pendingNesting == null) return;
- _setNesting(_pendingNesting);
+ _nesting = _pendingNesting;
_pendingNesting = null;
}
-
- /// Sets the nesting level of the innermost block to [level].
- void _setNesting(NestingLevel level) {
- _stack.last.nesting = level;
- }
-}
-
-/// A level of block nesting.
-///
-/// This represents indentation changes that typically occur at statement or
-/// block boundaries.
-class _IndentLevel {
- /// The number of spaces of indentation at this level.
- final int indent;
-
- /// The current expression nesting in this indentation level.
- NestingLevel nesting = new NestingLevel();
-
- _IndentLevel(this.indent);
}
diff --git a/packages/dart_style/lib/src/rule/argument.dart b/packages/dart_style/lib/src/rule/argument.dart
index 813bfb1..ed7758a 100644
--- a/packages/dart_style/lib/src/rule/argument.dart
+++ b/packages/dart_style/lib/src/rule/argument.dart
@@ -9,8 +9,23 @@
/// Base class for a rule that handles argument or parameter lists.
abstract class ArgumentRule extends Rule {
+ /// The chunks prior to each positional argument.
+ final List<Chunk> _arguments = [];
+
/// The rule used to split collections in the argument list, if any.
- final Rule _collectionRule;
+ Rule _collectionRule;
+
+ /// The number of leading collection arguments.
+ ///
+ /// This and [_trailingCollections] cannot both be positive. If every
+ /// argument is a collection, this will be [_arguments.length] and
+ /// [_trailingCollections] will be 0.
+ final int _leadingCollections;
+
+ /// The number of trailing collections.
+ ///
+ /// This and [_leadingCollections] cannot both be positive.
+ final int _trailingCollections;
/// If true, then inner rules that are written will force this rule to split.
///
@@ -21,11 +36,26 @@
/// Don't split when an inner collection rule splits.
bool get splitsOnInnerRules => _trackInnerRules;
- /// Creates a new rule for a positional argument list.
- ///
- /// If [_collectionRule] is given, it is the rule used to split the
- /// collections in the list.
- ArgumentRule(this._collectionRule);
+ ArgumentRule(this._collectionRule, this._leadingCollections,
+ this._trailingCollections);
+
+ void addConstrainedRules(Set<Rule> rules) {
+ super.addConstrainedRules(rules);
+ if (_collectionRule != null) rules.add(_collectionRule);
+ }
+
+ void forgetUnusedRules() {
+ super.forgetUnusedRules();
+ if (_collectionRule != null && _collectionRule.index == null) {
+ _collectionRule = null;
+ }
+ }
+
+ /// Remembers [chunk] as containing the split that occurs right before an
+ /// argument in the list.
+ void beforeArgument(Chunk chunk) {
+ _arguments.add(chunk);
+ }
/// Called before a collection argument is written.
///
@@ -46,28 +76,37 @@
/// Base class for a rule for handling positional argument lists.
abstract class PositionalRule extends ArgumentRule {
- /// The chunks prior to each positional argument.
- final List<Chunk> _arguments = [];
-
/// If there are named arguments following these positional ones, this will
/// be their rule.
Rule _namedArgsRule;
/// Creates a new rule for a positional argument list.
///
- /// If [collectionRule] is given, it is the rule used to split the collection
+ /// If [_collectionRule] is given, it is the rule used to split the collection
/// arguments in the list.
- PositionalRule(Rule collectionRule) : super(collectionRule);
+ PositionalRule(
+ Rule collectionRule, int leadingCollections, int trailingCollections)
+ : super(collectionRule, leadingCollections, trailingCollections);
- /// Remembers [chunk] as containing the split that occurs right before an
- /// argument in the list.
- void beforeArgument(Chunk chunk) {
- _arguments.add(chunk);
+ void addConstrainedRules(Set<Rule> rules) {
+ super.addConstrainedRules(rules);
+ if (_namedArgsRule != null) rules.add(_namedArgsRule);
}
- /// Remembers that [rule] is the [NamedArgsRule] immediately following this
+ void forgetUnusedRules() {
+ super.forgetUnusedRules();
+ if (_namedArgsRule != null && _namedArgsRule.index == null) {
+ _namedArgsRule = null;
+ }
+ }
+
+ /// Remembers that [rule] is the [Rule] immediately following this positional
/// positional argument list.
- void setNamedArgsRule(NamedRule rule) {
+ ///
+ /// This is normally a [NamedRule] but [PositionalRule] is also used for the
+ /// property accesses at the beginning of a call chain, in which case this
+ /// is just a [SimpleRule].
+ void setNamedArgsRule(Rule rule) {
_namedArgsRule = rule;
}
@@ -104,20 +143,16 @@
/// internally without forcing a split before the argument.
final bool splitsOnInnerRules;
- bool hack = false;
-
/// Creates a new rule for a positional argument list.
///
/// If [collectionRule] is given, it is the rule used to split the
/// collections in the list. If [splitsOnInnerRules] is `true`, then we will
/// split before the argument if the argument itself contains a split.
SinglePositionalRule(Rule collectionRule, {bool splitsOnInnerRules})
- : super(collectionRule),
+ : super(collectionRule, 0, 0),
splitsOnInnerRules =
splitsOnInnerRules != null ? splitsOnInnerRules : false;
- bool isSplit(int value, Chunk chunk) => value == 1;
-
int constrain(int value, Rule other) {
var constrained = super.constrain(value, other);
if (constrained != null) return constrained;
@@ -125,10 +160,10 @@
if (other != _collectionRule) return null;
// If we aren't splitting any args, we can split the collection.
- if (value == 0) return null;
+ if (value == Rule.unsplit) return null;
// We are splitting before a collection, so don't let it split internally.
- return 0;
+ return Rule.unsplit;
}
String toString() => "1Pos${super.toString()}";
@@ -152,18 +187,6 @@
/// splits before all of the non-collection arguments, but does not split
/// before the collections, so that they can split internally.
class MultiplePositionalRule extends PositionalRule {
- /// The number of leading collection arguments.
- ///
- /// This and [_trailingCollections] cannot both be positive. If every
- /// argument is a collection, this will be [_arguments.length] and
- /// [_trailingCollections] will be 0.
- final int _leadingCollections;
-
- /// The number of trailing collections.
- ///
- /// This and [_leadingCollections] cannot both be positive.
- final int _trailingCollections;
-
int get numValues {
// Can split before any one argument, none, or all.
var result = 2 + _arguments.length;
@@ -181,15 +204,12 @@
}
MultiplePositionalRule(
- Rule collectionRule, this._leadingCollections, this._trailingCollections)
- : super(collectionRule);
+ Rule collectionRule, int leadingCollections, int trailingCollections)
+ : super(collectionRule, leadingCollections, trailingCollections);
String toString() => "*Pos${super.toString()}";
- bool isSplit(int value, Chunk chunk) {
- // Don't split at all.
- if (value == 0) return false;
-
+ bool isSplitAtValue(int value, Chunk chunk) {
// Split only before the first argument. Keep the entire argument list
// together on the next line.
if (value == 1) return chunk == _arguments.first;
@@ -224,17 +244,22 @@
var constrained = super.constrain(value, other);
if (constrained != null) return constrained;
+ // Decide how to constrain the collection rule.
if (other != _collectionRule) return null;
+ // If all of the collections are in the named arguments, [_collectionRule]
+ // will not be null, but we don't have to handle it.
+ if (_leadingCollections == 0 && _trailingCollections == 0) return null;
+
// If we aren't splitting any args, we can split the collection.
- if (value == 0) return null;
+ if (value == Rule.unsplit) return null;
// Split only before the first argument.
if (value == 1) {
if (_leadingCollections > 0) {
// We are splitting before a collection, so don't let it split
// internally.
- return 0;
+ return Rule.unsplit;
} else {
// The split is outside of the collections so they can split or not.
return null;
@@ -245,8 +270,10 @@
// arguments, don't allow them to split.
if (value <= _arguments.length) {
var argument = _arguments.length - value + 1;
- if (argument < _leadingCollections) return 0;
- if (argument >= _arguments.length - _trailingCollections) return 0;
+ if (argument < _leadingCollections ||
+ argument >= _arguments.length - _trailingCollections) {
+ return Rule.unsplit;
+ }
return null;
}
@@ -255,55 +282,53 @@
// play when we do want to split the collection, so force that here.
if (value == _arguments.length + 1) return 1;
- // Split before all of the arguments, even the collection, so don't let
- // them split.
- return 0;
+ // Split before all of the arguments, even the collections. We'll allow
+ // them to split but indent their bodies if they do.
+ return null;
}
}
/// Splitting rule for a list of named arguments or parameters. Its values mean:
///
-/// * 0: Do not split at all.
-/// * 1: Split only before first argument.
-/// * 2: Split before all arguments, including the first.
+/// * Do not split at all.
+/// * Split only before first argument.
+/// * Split before all arguments.
class NamedRule extends ArgumentRule {
- /// The chunk prior to the first named argument.
- Chunk _first;
-
int get numValues => 3;
- NamedRule(Rule collectionRule) : super(collectionRule);
+ NamedRule(
+ Rule collectionRule, int leadingCollections, int trailingCollections)
+ : super(collectionRule, leadingCollections, trailingCollections);
- void beforeArguments(Chunk chunk) {
- assert(_first == null);
- _first = chunk;
- }
+ bool isSplitAtValue(int value, Chunk chunk) {
+ // Move all arguments to the second line as a unit.
+ if (value == 1) return chunk == _arguments.first;
- bool isSplit(int value, Chunk chunk) {
- switch (value) {
- case 0:
- return false;
- case 1:
- return chunk == _first;
- case 2:
- return true;
- }
-
- throw "unreachable";
+ // Otherwise, split before all arguments.
+ return true;
}
int constrain(int value, Rule other) {
var constrained = super.constrain(value, other);
if (constrained != null) return constrained;
+ // Decide how to constrain the collection rule.
if (other != _collectionRule) return null;
- // If we aren't splitting any args, we can split the collection.
- if (value == 0) return null;
+ // If all of the collections are in the named arguments, [_collectionRule]
+ // will not be null, but we don't have to handle it.
+ if (_leadingCollections == 0 && _trailingCollections == 0) return null;
- // Split before all of the arguments, even the collections, so don't let
- // them split.
- return 0;
+ // If we aren't splitting any args, we can split the collection.
+ if (value == Rule.unsplit) return null;
+
+ // Split only before the first argument. Don't allow the collections to
+ // split.
+ if (value == 1) return Rule.unsplit;
+
+ // Split before all of the arguments, even the collections. We'll allow
+ // them to split but indent their bodies if they do.
+ return null;
}
String toString() => "Named${super.toString()}";
diff --git a/packages/dart_style/lib/src/rule/combinator.dart b/packages/dart_style/lib/src/rule/combinator.dart
index 62ddfdb..1d73031 100644
--- a/packages/dart_style/lib/src/rule/combinator.dart
+++ b/packages/dart_style/lib/src/rule/combinator.dart
@@ -80,12 +80,8 @@
_names.last.add(chunk);
}
- bool isSplit(int value, Chunk chunk) {
+ bool isSplitAtValue(int value, Chunk chunk) {
switch (value) {
- case 0:
- // Don't split at all.
- return false;
-
case 1:
// Just split at the combinators.
return _combinators.contains(chunk);
@@ -106,11 +102,9 @@
// Split everything.
return true;
- case 4:
+ default:
return true;
}
-
- throw "unreachable";
}
/// Returns `true` if [chunk] is for a combinator or a name in the
diff --git a/packages/dart_style/lib/src/rule/metadata.dart b/packages/dart_style/lib/src/rule/metadata.dart
new file mode 100644
index 0000000..9abbe8f
--- /dev/null
+++ b/packages/dart_style/lib/src/rule/metadata.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library dart_style.src.rule.metadata;
+
+import 'argument.dart';
+import 'rule.dart';
+
+/// Rule for handling splits between parameter metadata annotations and the
+/// following parameter.
+///
+/// Metadata annotations for parameters (and type parameters) get some special
+/// handling. We use a single rule for all annotations in the parameter list.
+/// If any of the annotations split, they all do.
+///
+/// Also, if the annotations split, we force the entire parameter list to fully
+/// split, both named and positional.
+class MetadataRule extends Rule {
+ Rule _positionalRule;
+ Rule _namedRule;
+
+ /// Remembers that [rule] is the [PositionalRule] used by the argument list
+ /// containing the parameter metadata using this rule.
+ void bindPositionalRule(PositionalRule rule) {
+ _positionalRule = rule;
+ }
+
+ /// Remembers that [rule] is the [NamedRule] used by the argument list
+ /// containing the parameter metadata using this rule.
+ void bindNamedRule(NamedRule rule) {
+ _namedRule = rule;
+ }
+
+ /// Constrains the surrounding argument list rules to fully split if the
+ /// metadata does.
+ int constrain(int value, Rule other) {
+ var constrained = super.constrain(value, other);
+ if (constrained != null) return constrained;
+
+ // If the metadata doesn't split, we don't care what the arguments do.
+ if (value == Rule.unsplit) return null;
+
+ // Otherwise, they have to split.
+ if (other == _positionalRule) return _positionalRule.fullySplitValue;
+ if (other == _namedRule) return _namedRule.fullySplitValue;
+
+ return null;
+ }
+
+ void addConstrainedRules(Set<Rule> rules) {
+ if (_positionalRule != null) rules.add(_positionalRule);
+ if (_namedRule != null) rules.add(_namedRule);
+ }
+
+ void forgetUnusedRules() {
+ super.forgetUnusedRules();
+ if (_positionalRule != null && _positionalRule.index == null) {
+ _positionalRule = null;
+ }
+
+ if (_namedRule != null && _namedRule.index == null) {
+ _namedRule = null;
+ }
+ }
+}
diff --git a/packages/dart_style/lib/src/rule/rule.dart b/packages/dart_style/lib/src/rule/rule.dart
index 43a7f6a..e01d3b0 100644
--- a/packages/dart_style/lib/src/rule/rule.dart
+++ b/packages/dart_style/lib/src/rule/rule.dart
@@ -9,26 +9,43 @@
/// A constraint that determines the different ways a related set of chunks may
/// be split.
-abstract class Rule extends FastHash {
+class Rule extends FastHash {
+ /// Rule value that splits no chunks.
+ ///
+ /// Every rule is required to treat this value as fully unsplit.
+ static const unsplit = 0;
+
+ /// Rule constraint value that means "any value as long as something splits".
+ ///
+ /// It disallows [unsplit] but allows any other value.
+ static const mustSplit = -1;
+
/// The number of different states this rule can be in.
///
/// Each state determines which set of chunks using this rule are split and
/// which aren't. Values range from zero to one minus this. Value zero
/// always means "no chunks are split" and increasing values by convention
/// mean increasingly undesirable splits.
- int get numValues;
+ ///
+ /// By default, a rule has two values: fully unsplit and fully split.
+ int get numValues => 2;
/// The rule value that forces this rule into its maximally split state.
///
/// By convention, this is the highest of the range of allowed values.
int get fullySplitValue => numValues - 1;
- int get cost => Cost.normal;
+ final int cost;
/// During line splitting [LineSplitter] sets this to the index of this
/// rule in its list of rules.
int index;
+ /// If `true`, the rule has been "hardened" meaning it's been placed into a
+ /// permanent "must fully split" state.
+ bool get isHardened => _isHardened;
+ bool _isHardened = false;
+
/// The other [Rule]s that "surround" this one (and care about that fact).
///
/// In many cases, if a split occurs inside an expression, surrounding rules
@@ -41,7 +58,6 @@
///
/// This contains all direct as well as transitive relationships. If A
/// contains B which contains C, C's outerRules contains both B and A.
- Iterable<Rule> get outerRules => _outerRules;
final Set<Rule> _outerRules = new Set<Rule>();
/// Adds [inner] as an inner rule of this rule if it cares about inner rules.
@@ -60,7 +76,38 @@
/// rules.
bool get splitsOnInnerRules => true;
- bool isSplit(int value, Chunk chunk);
+ Rule([int cost]) : cost = cost ?? Cost.normal;
+
+ /// Creates a new rule that is already fully split.
+ Rule.hard() : cost = 0 {
+ // Set the cost to zero since it will always be applied, so there's no
+ // point in penalizing it.
+ //
+ // Also, this avoids doubled counting in literal blocks where there is both
+ // a split in the outer chunk containing the block and the inner hard split
+ // between the elements or statements.
+ harden();
+ }
+
+ /// Fixes this rule into a "fully split" state.
+ void harden() {
+ _isHardened = true;
+ }
+
+ /// Returns `true` if [chunk] should split when this rule has [value].
+ bool isSplit(int value, Chunk chunk) {
+ if (_isHardened) return true;
+
+ if (value == Rule.unsplit) return false;
+
+ // Let the subclass decide.
+ return isSplitAtValue(value, chunk);
+ }
+
+ /// Subclasses can override this to determine which values split which chunks.
+ ///
+ /// By default, this assumes every chunk splits.
+ bool isSplitAtValue(value, chunk) => true;
/// Given that this rule has [value], determine if [other]'s value should be
/// constrained.
@@ -70,51 +117,70 @@
/// value. Returns -1 to allow [other] to take any non-zero value. Returns
/// null to not constrain other.
int constrain(int value, Rule other) {
- // By default, any implied rule will be fully split if this one is fully
- // split.
- if (value == 0) return null;
+ // By default, any containing rule will be fully split if this one is split.
+ if (value == Rule.unsplit) return null;
if (_outerRules.contains(other)) return other.fullySplitValue;
return null;
}
- String toString() => "$id";
-}
-
-/// A rule that always splits a chunk.
-class HardSplitRule extends Rule {
- int get numValues => 1;
-
- /// It's always going to be applied, so there's no point in penalizing it.
+ /// A protected method for subclasses to add the rules that they constrain
+ /// to [rules].
///
- /// Also, this avoids doubled counting in literal blocks where there is both
- /// a split in the outer chunk containing the block and the inner hard split
- /// between the elements or statements.
- int get cost => 0;
+ /// Called by [Rule] the first time [constrainedRules] is accessed.
+ void addConstrainedRules(Set<Rule> rules) {}
- /// It's always split anyway.
- bool get splitsOnInnerRules => false;
+ /// Discards constraints on any rule that doesn't have an index.
+ ///
+ /// This is called by [LineSplitter] after it has indexed all of the in-use
+ /// rules. A rule may end up with a constraint on a rule that's no longer
+ /// used by any chunk. This can happen if the rule gets hardened, or if it
+ /// simply never got used by a chunk. For example, a rule for splitting an
+ /// empty list of metadata annotations.
+ ///
+ /// This removes all of those.
+ void forgetUnusedRules() {
+ _outerRules.retainWhere((rule) => rule.index != null);
- bool isSplit(int value, Chunk chunk) => true;
+ // Clear the cached ones too.
+ _constrainedRules = null;
+ _allConstrainedRules = null;
+ }
- String toString() => "Hard";
-}
+ /// The other [Rule]s that this rule places immediate constraints on.
+ Set<Rule> get constrainedRules {
+ // Lazy initialize this on first use. Note: Assumes this is only called
+ // after the chunks have been written and any constraints have been wired
+ // up.
+ if (_constrainedRules == null) {
+ _constrainedRules = _outerRules.toSet();
+ addConstrainedRules(_constrainedRules);
+ }
-/// A basic rule that has two states: unsplit or split.
-class SimpleRule extends Rule {
- /// Two values: 0 is unsplit, 1 is split.
- int get numValues => 2;
+ return _constrainedRules;
+ }
- final int cost;
+ Set<Rule> _constrainedRules;
- final bool splitsOnInnerRules;
+ /// The transitive closure of all of the rules this rule places constraints
+ /// on, directly or indirectly, including itself.
+ Set<Rule> get allConstrainedRules {
+ if (_allConstrainedRules == null) {
+ visit(Rule rule) {
+ if (_allConstrainedRules.contains(rule)) return;
- SimpleRule({int cost, bool splitsOnInnerRules})
- : cost = cost != null ? cost : Cost.normal,
- splitsOnInnerRules =
- splitsOnInnerRules != null ? splitsOnInnerRules : true;
+ _allConstrainedRules.add(rule);
+ rule.constrainedRules.forEach(visit);
+ }
- bool isSplit(int value, Chunk chunk) => value == 1;
+ _allConstrainedRules = new Set();
+ visit(this);
+ }
- String toString() => "Simple${super.toString()}";
+ return _allConstrainedRules;
+ }
+
+ Set<Rule> _allConstrainedRules;
+
+ String toString() => "$id";
}
diff --git a/packages/dart_style/lib/src/rule/type_argument.dart b/packages/dart_style/lib/src/rule/type_argument.dart
index ebac6c8..8fcc117 100644
--- a/packages/dart_style/lib/src/rule/type_argument.dart
+++ b/packages/dart_style/lib/src/rule/type_argument.dart
@@ -43,7 +43,7 @@
bool isSplit(int value, Chunk chunk) {
// Don't split at all.
- if (value == 0) return false;
+ if (value == Rule.unsplit) return false;
// Split before every argument.
if (value == numValues - 1) return true;
@@ -52,4 +52,6 @@
// to try to keep as much on the first line as possible.
return chunk == _arguments[_arguments.length - value];
}
+
+ String toString() => "TypeArg${super.toString()}";
}
diff --git a/packages/dart_style/lib/src/source_visitor.dart b/packages/dart_style/lib/src/source_visitor.dart
index 524db2e..a34b367 100644
--- a/packages/dart_style/lib/src/source_visitor.dart
+++ b/packages/dart_style/lib/src/source_visitor.dart
@@ -15,6 +15,7 @@
import 'dart_formatter.dart';
import 'rule/argument.dart';
import 'rule/combinator.dart';
+import 'rule/metadata.dart';
import 'rule/rule.dart';
import 'rule/type_argument.dart';
import 'source_code.dart';
@@ -47,15 +48,6 @@
/// This is calculated and cached by [_findSelectionEnd].
int _selectionEnd;
- /// The rule that should be used for the contents of a literal body that are
- /// about to be written.
- ///
- /// This is set by [visitArgumentList] to ensure that all block arguments
- /// share a rule.
- ///
- /// If `null`, a literal body creates its own rule.
- Rule _nextLiteralBodyRule;
-
/// A stack that tracks forcing nested collections to split.
///
/// Each entry corresponds to a collection currently being visited and the
@@ -68,6 +60,29 @@
/// split.
final List<bool> _collectionSplits = [];
+ /// The stack of current rules for handling parameter metadata.
+ ///
+ /// Each time a parameter (or type parameter) list is begun, a single rule
+ /// for all of the metadata annotations on parameters in that list is pushed
+ /// onto this stack. We reuse this rule for all annotations so that they split
+ /// in unison.
+ final List<MetadataRule> _metadataRules = [];
+
+ /// The mapping for collection literals that are managed by the argument
+ /// list that contains them.
+ ///
+ /// When a collection literal appears inside an [ArgumentSublist], the
+ /// argument list provides a rule for the body to split to ensure that all
+ /// collections split in unison. It also tracks the chunk before the
+ /// argument that determines whether or not the collection body is indented
+ /// like an expression or a statement.
+ ///
+ /// Before a collection literal argument is visited, [ArgumentSublist] binds
+ /// itself to the left bracket token of each collection literal it controls.
+ /// When we later visit that literal, we use the token to find that
+ /// association.
+ final Map<Token, ArgumentSublist> _collectionArgumentLists = {};
+
/// Initialize a newly created visitor to write source code representing
/// the visited nodes to the given [writer].
SourceVisitor(this._formatter, this._lineInfo, this._source) {
@@ -170,7 +185,17 @@
visitBinaryExpression(BinaryExpression node) {
builder.startSpan();
- builder.nestExpression();
+
+ // If a binary operator sequence appears immediately after a `=>`, don't
+ // add an extra level of nesting. Instead, let the subsequent operands line
+ // up with the first, as in:
+ //
+ // method() =>
+ // argument &&
+ // argument &&
+ // argument;
+ var isArrowBody = node.parent is ExpressionFunctionBody;
+ if (!isArrowBody) builder.nestExpression();
// Start lazily so we don't force the operator to split if a line comment
// appears before the first operand.
@@ -202,12 +227,20 @@
builder.endBlockArgumentNesting();
- builder.unnest();
+ if (!isArrowBody) builder.unnest();
builder.endSpan();
builder.endRule();
}
visitBlock(Block node) {
+ // Don't allow splitting in an empty block.
+ if (node.statements.isEmpty &&
+ node.rightBracket.precedingComments == null) {
+ token(node.leftBracket);
+ token(node.rightBracket);
+ return;
+ }
+
// For a block that is not a function body, just bump the indentation and
// keep it in the current block.
if (node.parent is! BlockFunctionBody) {
@@ -284,7 +317,7 @@
visitNodes(node.cascadeSections, between: zeroSplit);
builder.endRule();
} else {
- builder.startRule(new HardSplitRule());
+ builder.startRule(new Rule.hard());
zeroSplit();
visitNodes(node.cascadeSections, between: zeroSplit);
builder.endRule();
@@ -515,10 +548,15 @@
// ":" if the parameters and initialization list don't all fit on one line.
builder.startRule();
+ // If the redirecting constructor happens to wrap, we want to make sure
+ // the parameter list gets more deeply indented.
+ if (node.redirectedConstructor != null) builder.nestExpression();
+
_visitBody(node.parameters, node.body, () {
// Check for redirects or initializer lists.
if (node.redirectedConstructor != null) {
_visitConstructorRedirects(node);
+ builder.unnest();
} else if (node.initializers.isNotEmpty) {
_visitConstructorInitializers(node);
}
@@ -526,7 +564,8 @@
}
void _visitConstructorRedirects(ConstructorDeclaration node) {
- token(node.separator /* = */, before: space, after: space);
+ token(node.separator /* = */, before: space);
+ soloSplit();
visitCommaSeparatedNodes(node.initializers);
visit(node.redirectedConstructor);
}
@@ -600,7 +639,7 @@
if (node.separator.type == TokenType.EQ) space();
token(node.separator);
- soloSplit(Cost.assignment);
+ soloSplit(_assignmentCost(node.defaultValue));
visit(node.defaultValue);
builder.unnest();
@@ -609,18 +648,22 @@
}
visitDoStatement(DoStatement node) {
- _simpleStatement(node, () {
- token(node.doKeyword);
- space();
- visit(node.body);
- space();
- token(node.whileKeyword);
- space();
- token(node.leftParenthesis);
- soloZeroSplit();
- visit(node.condition);
- token(node.rightParenthesis);
- });
+ builder.nestExpression();
+ token(node.doKeyword);
+ space();
+ builder.unnest(now: false);
+ visit(node.body);
+
+ builder.nestExpression();
+ space();
+ token(node.whileKeyword);
+ space();
+ token(node.leftParenthesis);
+ soloZeroSplit();
+ visit(node.condition);
+ token(node.rightParenthesis);
+ token(node.semicolon);
+ builder.unnest();
}
visitDoubleLiteral(DoubleLiteral node) {
@@ -682,7 +725,10 @@
// Split after the "=>", using the rule created before the parameters
// by _visitBody().
split();
- builder.endRule();
+
+ // If the body is a binary operator expression, then we want to force the
+ // split at `=>` if the operators split. See visitBinaryExpression().
+ if (node.expression is! BinaryExpression) builder.endRule();
if (_isInLambda(node)) builder.endSpan();
@@ -692,6 +738,8 @@
builder.endSpan();
builder.endBlockArgumentNesting();
+ if (node.expression is BinaryExpression) builder.endRule();
+
token(node.semicolon);
}
@@ -744,9 +792,9 @@
space();
visit(node.iterable);
token(node.rightParenthesis);
- space();
- visit(node.body);
- builder.unnest();
+ builder.unnest(now: false);
+
+ _visitLoopBody(node.body);
}
visitFormalParameterList(FormalParameterList node) {
@@ -771,6 +819,8 @@
builder.nestExpression();
token(node.leftParenthesis);
+ _metadataRules.add(new MetadataRule());
+
var rule;
if (requiredParams.isNotEmpty) {
if (requiredParams.length > 1) {
@@ -779,6 +829,8 @@
rule = new SinglePositionalRule(null);
}
+ _metadataRules.last.bindPositionalRule(rule);
+
builder.startRule(rule);
if (_isInLambda(node)) {
// Don't allow splitting before the first argument (i.e. right after
@@ -806,13 +858,17 @@
}
if (optionalParams.isNotEmpty) {
- var namedRule = new NamedRule(null);
+ var namedRule = new NamedRule(null, 0, 0);
if (rule != null) rule.setNamedArgsRule(namedRule);
+ _metadataRules.last.bindNamedRule(namedRule);
+
builder.startRule(namedRule);
- namedRule
- .beforeArguments(builder.split(space: requiredParams.isNotEmpty));
+ // Make sure multi-line default values are indented.
+ builder.startBlockArgumentNesting();
+
+ namedRule.beforeArgument(builder.split(space: requiredParams.isNotEmpty));
// "[" or "{" for optional parameters.
token(node.leftDelimiter);
@@ -822,15 +878,18 @@
// Write the trailing comma.
if (param != node.parameters.last) token(param.endToken.next);
- if (param != optionalParams.last) split();
+ if (param != optionalParams.last) namedRule.beforeArgument(split());
}
+ builder.endBlockArgumentNesting();
builder.endRule();
// "]" or "}" for optional parameters.
token(node.rightDelimiter);
}
+ _metadataRules.removeLast();
+
token(node.rightParenthesis);
builder.unnest();
}
@@ -847,9 +906,9 @@
if (node.initialization != null) {
visit(node.initialization);
} else if (node.variables != null) {
- // Indent split variables more so they aren't at the same level
+ // Nest split variables more so they aren't at the same level
// as the rest of the loop clauses.
- builder.indent(Indent.loopVariable);
+ builder.nestExpression();
// Allow the variables to stay unsplit even if the clauses split.
builder.startRule();
@@ -864,7 +923,7 @@
});
builder.endRule();
- builder.unindent();
+ builder.unnest();
}
token(node.leftSeparator);
@@ -890,21 +949,11 @@
builder.endRule();
builder.unnest();
- // The body.
- if (node.body is! EmptyStatement) space();
- visit(node.body);
+ _visitLoopBody(node.body);
}
visitFunctionDeclaration(FunctionDeclaration node) {
- visitMemberMetadata(node.metadata);
-
- builder.nestExpression();
- modifier(node.externalKeyword);
- visit(node.returnType, after: space);
- modifier(node.propertyKeyword);
- visit(node.name);
- visit(node.functionExpression);
- builder.unnest();
+ _visitMemberDeclaration(node, node.functionExpression);
}
visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
@@ -956,10 +1005,35 @@
token(node.leftParenthesis);
visit(node.condition);
token(node.rightParenthesis);
+ builder.unnest(now: false);
- space();
- visit(node.thenStatement);
- builder.unnest();
+ visitClause(Statement clause) {
+ if (clause is Block || clause is IfStatement) {
+ space();
+ visit(clause);
+ } else {
+ // Allow splitting in an expression-bodied if even though it's against
+ // the style guide. Since we can't fix the code itself to follow the
+ // style guide, we should at least format it as well as we can.
+ builder.nestExpression(indent: 2, now: true);
+ builder.startRule();
+
+ // If there is an else clause, always split before both the then and
+ // else statements.
+ if (node.elseStatement != null) {
+ builder.writeWhitespace(Whitespace.nestedNewline);
+ } else {
+ split();
+ }
+
+ visit(clause);
+
+ builder.endRule();
+ builder.unnest();
+ }
+ }
+
+ visitClause(node.thenStatement);
if (node.elseStatement != null) {
if (node.thenStatement is Block) {
@@ -972,8 +1046,7 @@
}
token(node.elseKeyword);
- space();
- visit(node.elseStatement);
+ visitClause(node.elseStatement);
}
}
@@ -1012,8 +1085,18 @@
visit(node.target);
}
+ finishIndexExpression(node);
+
+ builder.unnest();
+ }
+
+ /// Visit the index part of [node], excluding the target.
+ ///
+ /// Called by [CallChainVisitor] to handle index expressions in the middle of
+ /// call chains.
+ void finishIndexExpression(IndexExpression node) {
if (node.target is IndexExpression) {
- // Corner case: On a chain of [] accesses, allow splitting between them.
+ // Edge case: On a chain of [] accesses, allow splitting between them.
// Produces nicer output in cases like:
//
// someJson['property']['property']['property']['property']...
@@ -1026,14 +1109,15 @@
visit(node.index);
token(node.rightBracket);
builder.endSpan();
- builder.unnest();
}
visitInstanceCreationExpression(InstanceCreationExpression node) {
builder.startSpan();
token(node.keyword);
space();
+ builder.startSpan(Cost.constructorName);
visit(node.constructorName);
+ builder.endSpan();
visit(node.argumentList);
builder.endSpan();
}
@@ -1112,16 +1196,7 @@
}
visitMethodDeclaration(MethodDeclaration node) {
- visitMemberMetadata(node.metadata);
-
- modifier(node.externalKeyword);
- modifier(node.modifierKeyword);
- visit(node.returnType, after: space);
- modifier(node.propertyKeyword);
- modifier(node.operatorKeyword);
- visit(node.name);
-
- _visitBody(node.parameters, node.body);
+ _visitMemberDeclaration(node, node);
}
visitMethodInvocation(MethodInvocation node) {
@@ -1149,7 +1224,16 @@
builder.nestExpression();
builder.startSpan();
visit(node.name);
- visit(node.expression, before: soloSplit);
+
+ // Don't allow a split between a name and a collection. Instead, we want
+ // the collection itself to split, or to split before the argument.
+ if (node.expression is ListLiteral || node.expression is MapLiteral) {
+ space();
+ } else {
+ soloSplit();
+ }
+
+ visit(node.expression);
builder.endSpan();
builder.unnest();
}
@@ -1184,6 +1268,8 @@
}
visitPartDirective(PartDirective node) {
+ visitDeclarationMetadata(node.metadata);
+
_simpleStatement(node, () {
token(node.keyword);
space();
@@ -1192,6 +1278,8 @@
}
visitPartOfDirective(PartOfDirective node) {
+ visitDeclarationMetadata(node.metadata);
+
_simpleStatement(node, () {
token(node.keyword);
space();
@@ -1207,9 +1295,7 @@
}
visitPrefixedIdentifier(PrefixedIdentifier node) {
- visit(node.prefix);
- token(node.period);
- visit(node.identifier);
+ new CallChainVisitor(this, node).visit();
}
visitPrefixExpression(PrefixExpression node) {
@@ -1357,6 +1443,7 @@
token(node.rightParenthesis);
space();
token(node.leftBracket);
+ builder.unnest();
builder.indent();
newline();
@@ -1365,7 +1452,6 @@
builder.unindent();
newline();
});
- builder.unnest();
}
visitSymbolLiteral(SymbolLiteral node) {
@@ -1425,7 +1511,11 @@
}
visitTypeParameterList(TypeParameterList node) {
+ _metadataRules.add(new MetadataRule());
+
_visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters);
+
+ _metadataRules.removeLast();
}
visitVariableDeclaration(VariableDeclaration node) {
@@ -1437,8 +1527,14 @@
visitVariableDeclarationList(VariableDeclarationList node) {
visitDeclarationMetadata(node.metadata);
+
+ // Allow but try to avoid splitting between the type and name.
+ builder.startSpan();
+
modifier(node.keyword);
- visit(node.type, after: space);
+ visit(node.type, after: soloSplit);
+
+ builder.endSpan();
// Use a single rule for all of the variables. If there are multiple
// declarations, we will try to keep them all on one line. If that isn't
@@ -1463,9 +1559,9 @@
soloZeroSplit();
visit(node.condition);
token(node.rightParenthesis);
- if (node.body is! EmptyStatement) space();
- visit(node.body);
- builder.unnest();
+ builder.unnest(now: false);
+
+ _visitLoopBody(node.body);
}
visitWithClause(WithClause node) {
@@ -1524,11 +1620,29 @@
/// These are always on the same line as the parameter.
void visitParameterMetadata(
NodeList<Annotation> metadata, void visitParameter()) {
+ if (metadata == null || metadata.isEmpty) {
+ visitParameter();
+ return;
+ }
+
// Split before all of the annotations or none.
- builder.startRule();
- visitNodes(metadata, between: split, after: split);
+ builder.startLazyRule(_metadataRules.last);
+
+ visitNodes(metadata, between: split, after: () {
+ // Don't nest until right before the last metadata. Ensures we only
+ // indent the parameter and not any of the metadata:
+ //
+ // function(
+ // @LongAnnotation
+ // @LongAnnotation
+ // indentedParameter) {}
+ builder.nestExpression(now: true);
+ split();
+ });
visitParameter();
+ builder.unnest();
+
// Wrap the rule around the parameter too. If it splits, we want to force
// the annotations to split as well.
builder.endRule();
@@ -1543,7 +1657,7 @@
void _visitAssignment(Token equalsOperator, Expression rightHandSide) {
space();
token(equalsOperator);
- soloSplit(Cost.assignment);
+ soloSplit(_assignmentCost(rightHandSide));
builder.startSpan();
visit(rightHandSide);
builder.endSpan();
@@ -1577,6 +1691,37 @@
builder.endRule();
}
+ /// Visits a top-level function or method declaration.
+ ///
+ /// The two AST node types are very similar but, alas, share no common
+ /// interface type in analyzer, hence the dynamic typing.
+ void _visitMemberDeclaration(
+ /* FunctionDeclaration|MethodDeclaration */ node,
+ /* FunctionExpression|MethodDeclaration */ function) {
+ visitMemberMetadata(node.metadata);
+
+ // Nest the signature in case we have to split between the return type and
+ // name.
+ builder.nestExpression();
+ builder.startSpan();
+ modifier(node.externalKeyword);
+ if (node is MethodDeclaration) modifier(node.modifierKeyword);
+ visit(node.returnType, after: soloSplit);
+ modifier(node.propertyKeyword);
+ if (node is MethodDeclaration) modifier(node.operatorKeyword);
+ visit(node.name);
+ builder.endSpan();
+
+ // If the body is a block, we need to exit any nesting first. If it's an
+ // expression, we want to wrap the nesting around that so that the body
+ // gets nested farther.
+ if (function.body is! ExpressionFunctionBody) builder.unnest();
+
+ _visitBody(function.parameters, function.body);
+
+ if (function.body is ExpressionFunctionBody) builder.unnest();
+ }
+
/// Visit the given function [parameters] followed by its [body], printing a
/// space before it if it's not empty.
///
@@ -1611,16 +1756,15 @@
builder.nestExpression();
// This rule is ended by visitExpressionFunctionBody().
- builder.startLazyRule(new SimpleRule(cost: Cost.arrow));
+ builder.startLazyRule(new Rule(Cost.arrow));
}
if (parameters != null) {
builder.nestExpression();
-
visit(parameters);
- if (afterParameters != null) afterParameters();
-
builder.unnest();
+
+ if (afterParameters != null) afterParameters();
}
visit(body);
@@ -1628,6 +1772,29 @@
if (body is ExpressionFunctionBody) builder.unnest();
}
+ /// Visits the body statement of a `for` or `for in` loop.
+ void _visitLoopBody(Statement body) {
+ if (body is EmptyStatement) {
+ // No space before the ";".
+ visit(body);
+ } else if (body is Block) {
+ space();
+ visit(body);
+ } else {
+ // Allow splitting in an expression-bodied for even though it's against
+ // the style guide. Since we can't fix the code itself to follow the
+ // style guide, we should at least format it as well as we can.
+ builder.nestExpression(indent: 2, now: true);
+ builder.startRule();
+
+ split();
+ visit(body);
+
+ builder.endRule();
+ builder.unnest();
+ }
+ }
+
/// Visit a list of [nodes] if not null, optionally separated and/or preceded
/// and followed by the given functions.
void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) {
@@ -1674,10 +1841,6 @@
if (elements.isEmpty && rightBracket.precedingComments == null) {
token(leftBracket);
token(rightBracket);
-
- // Clear this out in case this empty collection is in an argument list.
- // We don't want this rule to bleed over to some other collection.
- _nextLiteralBodyRule = null;
return;
}
@@ -1694,7 +1857,7 @@
// Always use a hard rule to split the elements. The parent chunk of
// the collection will handle the unsplit case, so this only comes
// into play when the collection is split.
- var rule = new HardSplitRule();
+ var rule = new Rule.hard();
builder.startRule(rule);
// If a collection contains a line comment, we assume it's a big complex
@@ -1712,7 +1875,7 @@
soloSplit();
}
} else {
- builder.blockSplit(space: true);
+ builder.split(nest: false, space: true);
}
}
@@ -1733,6 +1896,37 @@
_endLiteralBody(rightBracket, ignoredRule: rule, forceSplit: force);
}
+ /// Gets the cost to split at an assignment (or `:` in the case of a named
+ /// default value) with the given [rightHandSide].
+ ///
+ /// "Block-like" expressions (collections and cascades) bind a bit tighter
+ /// because it looks better to have code like:
+ ///
+ /// var list = [
+ /// element,
+ /// element,
+ /// element
+ /// ];
+ ///
+ /// var builder = new SomeBuilderClass()
+ /// ..method()
+ /// ..method();
+ ///
+ /// over:
+ ///
+ /// var list =
+ /// [element, element, element];
+ ///
+ /// var builder =
+ /// new SomeBuilderClass()..method()..method();
+ int _assignmentCost(Expression rightHandSide) {
+ if (rightHandSide is ListLiteral) return Cost.assignBlock;
+ if (rightHandSide is MapLiteral) return Cost.assignBlock;
+ if (rightHandSide is CascadeExpression) return Cost.assignBlock;
+
+ return Cost.assign;
+ }
+
/// Returns `true` if the collection withs [elements] delimited by
/// [rightBracket] contains any line comments.
///
@@ -1765,16 +1959,21 @@
void _startLiteralBody(Token leftBracket) {
token(leftBracket);
- // Split the literal. Use the explicitly given rule if we have one.
- // Otherwise, create a new rule.
- var rule = _nextLiteralBodyRule;
- _nextLiteralBodyRule = null;
+ // See if this literal is associated with an argument list that wants to
+ // handle splitting and indenting it. If not, we'll use a default rule.
+ var rule;
+ var argumentChunk;
+ if (_collectionArgumentLists.containsKey(leftBracket)) {
+ var argumentList = _collectionArgumentLists[leftBracket];
+ rule = argumentList.collectionRule;
+ argumentChunk = argumentList.previousSplit;
+ }
// Create a rule for whether or not to split the block contents.
builder.startRule(rule);
// Process the collection contents as a separate set of chunks.
- builder = builder.startBlock();
+ builder = builder.startBlock(argumentChunk);
}
/// Ends the literal body started by a call to [_startLiteralBody()].
@@ -1834,10 +2033,13 @@
builder.unnest();
}
- /// Makes [rule] the rule that will be used for the contents of a collection
- /// or function literal body that are about to be visited.
- void setNextLiteralBodyRule(Rule rule) {
- _nextLiteralBodyRule = rule;
+ /// Marks the collection literal that starts with [leftBracket] as being
+ /// controlled by [argumentList].
+ ///
+ /// When the collection is visited, [argumentList] will determine the
+ /// indentation and splitting rule for the collection.
+ void beforeCollection(Token leftBracket, ArgumentSublist argumentList) {
+ _collectionArgumentLists[leftBracket] = argumentList;
}
/// Writes an bracket-delimited body and handles indenting and starting the
@@ -1854,14 +2056,14 @@
// Split after the bracket.
builder.startRule();
- builder.blockSplit(space: space, isDouble: false);
+ builder.split(isDouble: false, nest: false, space: space);
body();
token(rightBracket, before: () {
// Split before the closing bracket character.
builder.unindent();
- builder.blockSplit(space: space);
+ builder.split(nest: false, space: space);
});
builder.endRule();
@@ -1869,7 +2071,8 @@
/// Returns `true` if [node] is immediately contained within an anonymous
/// [FunctionExpression].
- bool _isInLambda(AstNode node) => node.parent is FunctionExpression &&
+ bool _isInLambda(AstNode node) =>
+ node.parent is FunctionExpression &&
node.parent.parent is! FunctionDeclaration;
/// Writes the string literal [string] to the output.
@@ -1945,7 +2148,7 @@
/// Writes a single space split with its own rule.
void soloSplit([int cost]) {
- builder.startRule(new SimpleRule(cost: cost));
+ builder.startRule(new Rule(cost));
split();
builder.endRule();
}
diff --git a/packages/dart_style/lib/src/whitespace.dart b/packages/dart_style/lib/src/whitespace.dart
index 481f498..b217fa1 100644
--- a/packages/dart_style/lib/src/whitespace.dart
+++ b/packages/dart_style/lib/src/whitespace.dart
@@ -17,18 +17,6 @@
/// The ":" on a wrapped constructor initialization list.
static const constructorInitializer = 4;
-
- /// The indentation for subsequent variables when a for loop defines multiple
- /// variables that wrap, like:
- ///
- /// for (var a = initializer,
- /// b = another,
- /// c = third;
- /// a + b + c < 100;
- /// a++) {
- /// ...
- /// }
- static const loopVariable = 8;
}
/// The kind of pending whitespace that has been "written", but not actually
diff --git a/packages/dart_style/pubspec.yaml b/packages/dart_style/pubspec.yaml
index 46f2018..c165eb8 100644
--- a/packages/dart_style/pubspec.yaml
+++ b/packages/dart_style/pubspec.yaml
@@ -1,5 +1,5 @@
name: dart_style
-version: 0.2.0
+version: 0.2.1
author: Dart Team <misc@dartlang.org>
description: Opinionated, automatic Dart source code formatter.
homepage: https://github.com/dart-lang/dart_style
@@ -8,13 +8,16 @@
dependencies:
analyzer: '>=0.25.0 <0.27.0'
args: '>=0.12.1 <0.14.0'
- collection: '^1.0.0'
path: '>=1.0.0 <2.0.0'
source_span: '>=1.1.1 <2.0.0'
dev_dependencies:
+ async: '>=1.0.0 <=2.0.0'
browser: '>=0.10.0 <0.11.0'
+ grinder: '^0.7.2'
+ pub_semver: '^1.2.3'
scheduled_test: '>=0.12.0 <0.13.0'
test: '>=0.12.0 <0.13.0'
+ yaml: '^2.0.0'
executables:
dartfmt: format
dartformat: format # Allow the old name for compatibility.
diff --git a/packages/dart_style/test/command_line_test.dart b/packages/dart_style/test/command_line_test.dart
index de64449..425143d 100644
--- a/packages/dart_style/test/command_line_test.dart
+++ b/packages/dart_style/test/command_line_test.dart
@@ -16,62 +16,85 @@
void main() {
setUpTestSuite();
- test("Exits with 0 on success.", () {
+ test("exits with 0 on success", () {
d.dir("code", [d.file("a.dart", unformattedSource)]).create();
var process = runFormatterOnDir();
process.shouldExit(0);
});
- test("Exits with 64 on a command line argument error.", () {
+ test("exits with 64 on a command line argument error", () {
var process = runFormatterOnDir(["-wat"]);
process.shouldExit(64);
});
- test("Exits with 65 on a parse error.", () {
+ test("exits with 65 on a parse error", () {
d.dir("code", [d.file("a.dart", "herp derp i are a dart")]).create();
var process = runFormatterOnDir();
process.shouldExit(65);
});
- test("Errors if --dry-run and --overwrite are both passed.", () {
+ test("errors if --dry-run and --overwrite are both passed", () {
d.dir("code", [d.file("a.dart", unformattedSource)]).create();
var process = runFormatterOnDir(["--dry-run", "--overwrite"]);
process.shouldExit(64);
});
- test("Errors if --dry-run and --machine are both passed.", () {
+ test("errors if --dry-run and --machine are both passed", () {
d.dir("code", [d.file("a.dart", unformattedSource)]).create();
var process = runFormatterOnDir(["--dry-run", "--machine"]);
process.shouldExit(64);
});
- test("Errors if --machine and --overwrite are both passed.", () {
+ test("errors if --machine and --overwrite are both passed", () {
d.dir("code", [d.file("a.dart", unformattedSource)]).create();
var process = runFormatterOnDir(["--machine", "--overwrite"]);
process.shouldExit(64);
});
- test("Errors if --dry-run and --machine are both passed.", () {
+ test("errors if --dry-run and --machine are both passed", () {
d.dir("code", [d.file("a.dart", unformattedSource)]).create();
var process = runFormatter(["--dry-run", "--machine"]);
process.shouldExit(64);
});
- test("Errors if --machine and --overwrite are both passed.", () {
+ test("errors if --machine and --overwrite are both passed", () {
d.dir("code", [d.file("a.dart", unformattedSource)]).create();
var process = runFormatter(["--machine", "--overwrite"]);
process.shouldExit(64);
});
+ test("--version prints the version number", () {
+ var process = runFormatter(["--version"]);
+
+ // Match something roughly semver-like.
+ process.stdout.expect(matches(r"\d+\.\d+\.\d+.*"));
+ process.shouldExit(0);
+ });
+
+ test("only prints a hidden directory once", () {
+ d.dir('code', [
+ d.dir('.skip', [
+ d.file('a.dart', unformattedSource),
+ d.file('b.dart', unformattedSource)
+ ])
+ ]).create();
+
+ var process = runFormatterOnDir();
+
+ process.stdout.expect(startsWith("Formatting directory"));
+ process.stdout.expect("Skipping hidden path ${p.join("code", ".skip")}");
+ process.shouldExit();
+ });
+
group("--dry-run", () {
- test("prints names of files that would change.", () {
+ test("prints names of files that would change", () {
d.dir("code", [
d.file("a_bad.dart", unformattedSource),
d.file("b_good.dart", formattedSource),
@@ -90,7 +113,7 @@
process.shouldExit();
});
- test("does not modify files.", () {
+ test("does not modify files", () {
d.dir("code", [d.file("a.dart", unformattedSource)]).create();
var process = runFormatterOnDir(["--dry-run"]);
@@ -130,12 +153,12 @@
});
group("--preserve", () {
- test("errors if given paths.", () {
+ test("errors if given paths", () {
var process = runFormatter(["--preserve", "path", "another"]);
process.shouldExit(64);
});
- test("errors on wrong number of components.", () {
+ test("errors on wrong number of components", () {
var process = runFormatter(["--preserve", "1"]);
process.shouldExit(64);
@@ -143,12 +166,12 @@
process.shouldExit(64);
});
- test("errors on non-integer component.", () {
+ test("errors on non-integer component", () {
var process = runFormatter(["--preserve", "1:2.3"]);
process.shouldExit(64);
});
- test("updates selection.", () {
+ test("updates selection", () {
var process = runFormatter(["--preserve", "6:10", "-m"]);
process.writeLine(unformattedSource);
process.closeStdin();
@@ -164,20 +187,43 @@
});
});
+ group("--indent", () {
+ test("sets the leading indentation of the output", () {
+ var process = runFormatter(["--indent", "3"]);
+ process.writeLine("main() {'''");
+ process.writeLine("a flush left multi-line string''';}");
+ process.closeStdin();
+
+ process.stdout.expect(" main() {");
+ process.stdout.expect(" '''");
+ process.stdout.expect("a flush left multi-line string''';");
+ process.stdout.expect(" }");
+ process.shouldExit(0);
+ });
+
+ test("errors if the indent is not a non-negative number", () {
+ var process = runFormatter(["--indent", "notanum"]);
+ process.shouldExit(64);
+
+ process = runFormatter(["--preserve", "-4"]);
+ process.shouldExit(64);
+ });
+ });
+
group("with no paths", () {
- test("errors on --overwrite.", () {
+ test("errors on --overwrite", () {
var process = runFormatter(["--overwrite"]);
process.shouldExit(64);
});
- test("exits with 65 on parse error.", () {
+ test("exits with 65 on parse error", () {
var process = runFormatter();
process.writeLine("herp derp i are a dart");
process.closeStdin();
process.shouldExit(65);
});
- test("reads from stdin.", () {
+ test("reads from stdin", () {
var process = runFormatter();
process.writeLine(unformattedSource);
process.closeStdin();
diff --git a/packages/dart_style/test/formatter_test.dart b/packages/dart_style/test/formatter_test.dart
index d227133..b50271f 100644
--- a/packages/dart_style/test/formatter_test.dart
+++ b/packages/dart_style/test/formatter_test.dart
@@ -149,7 +149,8 @@
}
group("$name ${p.basename(entry.path)}", () {
- var lines = (entry as File).readAsLinesSync();
+ // Explicitly create a File, in case the entry is a Link.
+ var lines = new File(entry.path).readAsLinesSync();
// The first line may have a "|" to indicate the page width.
var pageWidth;
@@ -207,7 +208,13 @@
var actualText = actual.text;
if (!isCompilationUnit) actualText += "\n";
- expect(actualText, equals(expected.text));
+ // Fail with an explicit message because it's easier to read than
+ // the matcher output.
+ if (actualText != expected.text) {
+ fail("Formatting did not match expectation. Expected:\n"
+ "${expected.text}\nActual:\n$actualText");
+ }
+
expect(actual.selectionStart, equals(expected.selectionStart));
expect(actual.selectionLength, equals(expected.selectionLength));
});
diff --git a/packages/dart_style/test/io_test.dart b/packages/dart_style/test/io_test.dart
index 3962de6..64acf4c 100644
--- a/packages/dart_style/test/io_test.dart
+++ b/packages/dart_style/test/io_test.dart
@@ -156,8 +156,8 @@
processDirectory(overwriteOptions, dir);
}, 'Run formatter.');
- d.dir('code', [d.file('linked_file.dart', unformattedSource),])
- .validate();
+ d.dir(
+ 'code', [d.file('linked_file.dart', unformattedSource),]).validate();
});
test("follows file symlinks when 'followLinks' is true", () {
diff --git a/packages/dart_style/test/regression/0000/0005.stmt b/packages/dart_style/test/regression/0000/0005.stmt
index e1d9b89..cba7a18 100644
--- a/packages/dart_style/test/regression/0000/0005.stmt
+++ b/packages/dart_style/test/regression/0000/0005.stmt
@@ -4,7 +4,8 @@
path.isWithin(rootDirectory, directory)).toList();
<<<
var overlapping = _directories.keys
- .where((directory) => path.isWithin(directory, rootDirectory) ||
+ .where((directory) =>
+ path.isWithin(directory, rootDirectory) ||
path.isWithin(rootDirectory, directory))
.toList();
>>>
diff --git a/packages/dart_style/test/regression/0000/0006.stmt b/packages/dart_style/test/regression/0000/0006.stmt
index ed8ad97..e7d45c2 100644
--- a/packages/dart_style/test/regression/0000/0006.stmt
+++ b/packages/dart_style/test/regression/0000/0006.stmt
@@ -10,5 +10,6 @@
messageMentions(id.toString()) ||
messageMentions(path.fromUri(entry.assetId.path));
<<<
-messageMentionsAsset(id) => messageMentions(id.toString()) ||
+messageMentionsAsset(id) =>
+ messageMentions(id.toString()) ||
messageMentions(path.fromUri(entry.assetId.path));
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0000/0026.stmt b/packages/dart_style/test/regression/0000/0026.stmt
index b5a1c7e..4fa6c76 100644
--- a/packages/dart_style/test/regression/0000/0026.stmt
+++ b/packages/dart_style/test/regression/0000/0026.stmt
@@ -6,4 +6,4 @@
<<<
experimentalBootstrap = document.querySelectorAll('link').any((link) =>
link.attributes['rel'] == 'import' &&
- link.attributes['href'] == POLYMER_EXPERIMENTAL_HTML);
\ No newline at end of file
+ link.attributes['href'] == POLYMER_EXPERIMENTAL_HTML);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0000/0045.stmt b/packages/dart_style/test/regression/0000/0045.stmt
index de743a8..661344f 100644
--- a/packages/dart_style/test/regression/0000/0045.stmt
+++ b/packages/dart_style/test/regression/0000/0045.stmt
@@ -5,7 +5,6 @@
}
<<<
foo() {
- if (xxxxxxxxxxxxxxxx || xxxxxxxxxxxxxxxxxxxxx) return xxxxxxxxxxxxxx
- ? xxxxxxxxxxxxxxxxxxxx
- : xxxxxxxxxxxxxxxxxxxxxxxxxx;
+ if (xxxxxxxxxxxxxxxx || xxxxxxxxxxxxxxxxxxxxx)
+ return xxxxxxxxxxxxxx ? xxxxxxxxxxxxxxxxxxxx : xxxxxxxxxxxxxxxxxxxxxxxxxx;
}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0000/0046.stmt b/packages/dart_style/test/regression/0000/0046.stmt
index 3897265..74cea63 100644
--- a/packages/dart_style/test/regression/0000/0046.stmt
+++ b/packages/dart_style/test/regression/0000/0046.stmt
@@ -7,6 +7,6 @@
<<<
foo() {
if (true)
- // comment!
- return 0;
+ // comment!
+ return 0;
}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0000/0076.unit b/packages/dart_style/test/regression/0000/0076.unit
index 0c6dd96..632b8e1 100644
--- a/packages/dart_style/test/regression/0000/0076.unit
+++ b/packages/dart_style/test/regression/0000/0076.unit
@@ -4,7 +4,8 @@
}
<<<
class TokenType {
- bool get isUserDefinableOperator => identical(lexeme, "==") ||
+ bool get isUserDefinableOperator =>
+ identical(lexeme, "==") ||
identical(lexeme, "~") ||
identical(lexeme, "[]") ||
identical(lexeme, "[]=") ||
diff --git a/packages/dart_style/test/regression/0100/0108.unit b/packages/dart_style/test/regression/0100/0108.unit
index 64522f2..59bb316 100644
--- a/packages/dart_style/test/regression/0100/0108.unit
+++ b/packages/dart_style/test/regression/0100/0108.unit
@@ -51,7 +51,8 @@
javaBooleanAnd(
_isEqualNodes(
node.documentationComment,
- toNode.documentationComment),
+ toNode
+ .documentationComment),
_isEqualNodeLists(node.metadata,
toNode.metadata)),
_isEqualTokens(node.abstractKeyword,
@@ -145,7 +146,8 @@
javaBooleanAnd(
_isEqualNodes(
node.documentationComment,
- toNode.documentationComment),
+ toNode
+ .documentationComment),
_isEqualNodeLists(
node.metadata,
_isEqualNodeLists(
@@ -155,11 +157,14 @@
_isEqualNodeLists(
node.metadata,
_isEqualNodeLists(
- node.metadata,
+ node
+ .metadata,
_isEqualNodeLists(
- node.metadata,
+ node
+ .metadata,
_isEqualNodeLists(
- node.metadata,
+ node
+ .metadata,
_isEqualNodeLists(node.metadata,
_isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, _isEqualNodeLists(node.metadata, toNode.metadata))))))))))))))))))))))),
_isEqualTokens(node.abstractKeyword, toNode.abstractKeyword)),
@@ -209,9 +214,12 @@
<<<
async.Future<List<dom.StyleElement>> call(String tag, List<String> cssUrls, {Type type}) =>
(DDC$RT.cast(
- async.Future.wait(
- (DDC$RT.cast(cssUrls.map((url) => _styleElement(tag, (DDC$RT.cast(url, String, key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:65")), type)),
- DDC$RT.type((Iterable<Future<dynamic>> _) {
- }), key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:25"))),
+ async.Future.wait((DDC$RT.cast(
+ cssUrls.map((url) => _styleElement(
+ tag,
+ (DDC$RT.cast(url, String, key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:65")),
+ type)),
+ DDC$RT.type((Iterable<Future<dynamic>> _) {}),
+ key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:25"))),
DDC$RT.type((Future<List<StyleElement>> _) {}),
key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:7"));
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0100/0130.unit b/packages/dart_style/test/regression/0100/0130.unit
index 8974b73..d63287b 100644
--- a/packages/dart_style/test/regression/0100/0130.unit
+++ b/packages/dart_style/test/regression/0100/0130.unit
@@ -4,7 +4,8 @@
_borderWidth +
clientY;
<<<
- int get screenY => window.screenTop +
+ int get screenY =>
+ window.screenTop +
window.outerHeight -
window.innerHeight -
_borderWidth +
diff --git a/packages/dart_style/test/regression/0100/0162.stmt b/packages/dart_style/test/regression/0100/0162.stmt
index daa92ac..5936f6f 100644
--- a/packages/dart_style/test/regression/0100/0162.stmt
+++ b/packages/dart_style/test/regression/0100/0162.stmt
@@ -28,22 +28,19 @@
}
<<<
void main() {
- useGeneratedCode(new StaticConfiguration(
- checkedMode: false,
- parents: {
- smoke_0.A: smoke_1.PolymerElement,
- smoke_0.B: smoke_0.A,
- smoke_0.C: smoke_0.B,
- smoke_0.D: smoke_0.B,
- smoke_0.E: smoke_0.C,
- },
- declarations: {
- smoke_0.A: {},
- smoke_0.B: {},
- smoke_0.C: {},
- smoke_0.D: {},
- smoke_0.E: {},
- }));
+ useGeneratedCode(new StaticConfiguration(checkedMode: false, parents: {
+ smoke_0.A: smoke_1.PolymerElement,
+ smoke_0.B: smoke_0.A,
+ smoke_0.C: smoke_0.B,
+ smoke_0.D: smoke_0.B,
+ smoke_0.E: smoke_0.C,
+ }, declarations: {
+ smoke_0.A: {},
+ smoke_0.B: {},
+ smoke_0.C: {},
+ smoke_0.D: {},
+ smoke_0.E: {},
+ }));
new LogInjector().injectLogsFromUrl('sort_registration_test.html._buildLogs');
configureForDeployment([
() => Polymer.register('x-a', i0.A),
diff --git a/packages/dart_style/test/regression/0200/0221.unit b/packages/dart_style/test/regression/0200/0221.unit
index a76f236..ab5fb23 100644
--- a/packages/dart_style/test/regression/0200/0221.unit
+++ b/packages/dart_style/test/regression/0200/0221.unit
@@ -12,10 +12,12 @@
class Foo {
static Column column(Handler onSelection) =>
(Column.defaultBuilder(videoMsg())
- ..id = 'VIDEO'
- ..segment = ((row) =>
- row.segmentedStats.map((s) => s.get(Stats.SEGMENTATION)).toList())
- ..cell = new Cell(onSelection)).build();
+ ..id = 'VIDEO'
+ ..segment = ((row) => row.segmentedStats
+ .map((s) => s.get(Stats.SEGMENTATION))
+ .toList())
+ ..cell = new Cell(onSelection))
+ .build();
}
>>>
void _updateChart() {
@@ -33,11 +35,13 @@
void _updateChart() {
if (_model.settings != null) {
_chart.update((ChartSettings.builder()
- ..ids.addAll(_model.ids)
- ..statusFilter = StatusFilter.ALL
- ..dateRange = chartDates.toChartDateRange(_model.settings.dateRange.value)
- ..segmentationDimension = _model.segmentation
- ..context = ChartContext.empty).build());
+ ..ids.addAll(_model.ids)
+ ..statusFilter = StatusFilter.ALL
+ ..dateRange =
+ chartDates.toChartDateRange(_model.settings.dateRange.value)
+ ..segmentationDimension = _model.segmentation
+ ..context = ChartContext.empty)
+ .build());
}
}
>>> (indent 2)
@@ -56,12 +60,13 @@
void _updateChart() {
if (_model.settings != null) {
_chart.update((ChartSettings.builder()
- ..ids.addAll(_model.ids)
- ..statusFilter = StatusFilter.ALL
- ..dateRange =
- chartDates.toChartDateRange(_model.settings.dateRange.value)
- ..segmentationDimension = _model.segmentation
- ..context = ChartContext.empty).build());
+ ..ids.addAll(_model.ids)
+ ..statusFilter = StatusFilter.ALL
+ ..dateRange =
+ chartDates.toChartDateRange(_model.settings.dateRange.value)
+ ..segmentationDimension = _model.segmentation
+ ..context = ChartContext.empty)
+ .build());
}
}
>>>
@@ -90,8 +95,9 @@
<<<
main() {
(new OrderBy()
- ..field = s.column
- ..sortOrder = s.isAscending
- ? OrderBySortOrder.ASCENDING
- : OrderBySortOrder.DESCENDING).toList();
+ ..field = s.column
+ ..sortOrder = s.isAscending
+ ? OrderBySortOrder.ASCENDING
+ : OrderBySortOrder.DESCENDING)
+ .toList();
}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0200/0224.stmt b/packages/dart_style/test/regression/0200/0224.stmt
index 11c8726..c2bfca8 100644
--- a/packages/dart_style/test/regression/0200/0224.stmt
+++ b/packages/dart_style/test/regression/0200/0224.stmt
@@ -31,14 +31,15 @@
]))
.catchError(cannotGetConveyorBeltRunning)
.then((_) => tellEveryoneDonutsAreJustAboutDone())
- .then((_) => Future.wait([
+ .then((_) => Future
+ .wait([
croissantFactory.start(),
_giantBakingOvens.start(),
butterbutterer.start()
])
- .catchError(_handleBakingFailures)
- .timeout(scriptLoadingTimeout, onTimeout: _handleBakingFailures)
- .catchError(cannotGetConveyorBeltRunning))
+ .catchError(_handleBakingFailures)
+ .timeout(scriptLoadingTimeout, onTimeout: _handleBakingFailures)
+ .catchError(cannotGetConveyorBeltRunning))
.catchError(cannotGetConveyorBeltRunning)
.then((_) {
_logger.info("Let's eat!");
diff --git a/packages/dart_style/test/regression/0200/0243.stmt b/packages/dart_style/test/regression/0200/0243.stmt
index d4c6fa5..b75dc65 100644
--- a/packages/dart_style/test/regression/0200/0243.stmt
+++ b/packages/dart_style/test/regression/0200/0243.stmt
@@ -7,12 +7,10 @@
style: _content_style,
children: appChildren)]);
<<<
- return new Container(
- key: 'ChatApp',
- children: [
- new Container(
- key: 'Content', style: _content_style, children: appChildren)
- ]);
+ return new Container(key: 'ChatApp', children: [
+ new Container(
+ key: 'Content', style: _content_style, children: appChildren)
+ ]);
>>> (indent 10)
return new Container(
key: 'ChatApp',
@@ -22,11 +20,7 @@
style: _content_style,
children: appChildren)]);
<<<
- return new Container(
- key: 'ChatApp',
- children: [
- new Container(
- key: 'Content',
- style: _content_style,
- children: appChildren)
- ]);
\ No newline at end of file
+ return new Container(key: 'ChatApp', children: [
+ new Container(
+ key: 'Content', style: _content_style, children: appChildren)
+ ]);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0200/0247.unit b/packages/dart_style/test/regression/0200/0247.unit
index 0a845a7..218c2bd 100644
--- a/packages/dart_style/test/regression/0200/0247.unit
+++ b/packages/dart_style/test/regression/0200/0247.unit
@@ -10,8 +10,8 @@
<<<
init(
{@Option(help: 'The git Uri containing the jefe.yaml.', abbr: 'g')
- String gitUri,
+ String gitUri,
@Option(help: 'The directory to install into', abbr: 'd')
- String installDirectory: '.',
+ String installDirectory: '.',
@Flag(help: 'Skips the checkout of the develop branch', abbr: 's')
- bool skipCheckout: false}) async {}
\ No newline at end of file
+ bool skipCheckout: false}) async {}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0200/0255.stmt b/packages/dart_style/test/regression/0200/0255.stmt
new file mode 100644
index 0000000..283e9fc
--- /dev/null
+++ b/packages/dart_style/test/regression/0200/0255.stmt
@@ -0,0 +1,10 @@
+>>> (indent 10)
+ return new Rewriter(code, codegen,
+ mirrorMode: mirrorMode, writeStaticInit: writeStaticInit).rewrite(
+ parseCompilationUnit(code,
+ name: reflectionEntryPointPath, parseFunctionBodies: false));
+<<<
+ return new Rewriter(code, codegen,
+ mirrorMode: mirrorMode, writeStaticInit: writeStaticInit)
+ .rewrite(parseCompilationUnit(code,
+ name: reflectionEntryPointPath, parseFunctionBodies: false));
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0200/0258.unit b/packages/dart_style/test/regression/0200/0258.unit
index 9e9466c..a2c1f25 100644
--- a/packages/dart_style/test/regression/0200/0258.unit
+++ b/packages/dart_style/test/regression/0200/0258.unit
@@ -18,7 +18,7 @@
GGGGGGGGConfigFactory, HHHHHHHHHHHHHConfigFactory),
_aaaaaaaaaaaConfig = _getaaaaaaaaaaaConfig(
GGGGGGGGConfigFactory, HHHHHHHHHHHHHConfigFactory),
- _defaultFFFFFFFConfig = FFFFFFFConfigurationStore
- ._getDefaultFFFFFFFConfig(
+ _defaultFFFFFFFConfig =
+ FFFFFFFConfigurationStore._getDefaultFFFFFFFConfig(
GGGGGGGGConfigFactory, HHHHHHHHHHHHHConfigFactory);
}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0357.stmt b/packages/dart_style/test/regression/0300/0357.stmt
new file mode 100644
index 0000000..bb27d07
--- /dev/null
+++ b/packages/dart_style/test/regression/0300/0357.stmt
@@ -0,0 +1,6 @@
+>>> (indent 2)
+ bool theMethodNameGoesHere(ParameterTyp result) => reallyLongIdentifier
+ .any((MyClass myParam) => myParam.id == result.myParam.id);
+<<<
+ bool theMethodNameGoesHere(ParameterTyp result) => reallyLongIdentifier
+ .any((MyClass myParam) => myParam.id == result.myParam.id);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0367.stmt b/packages/dart_style/test/regression/0300/0367.stmt
new file mode 100644
index 0000000..6108b7e
--- /dev/null
+++ b/packages/dart_style/test/regression/0300/0367.stmt
@@ -0,0 +1,31 @@
+>>> (indent 6)
+ identifier__ = identifier_____.identifier___
+ .transform(new StreamTransformer<TypeArg____, Type_>.fromHandlers(
+ handleData: (TypeName___ arg__, EventSink<Type_> arg_) {
+ ;
+ }))
+ .asBroadcastStream();
+<<<
+ identifier__ = identifier_____.identifier___.transform(
+ new StreamTransformer<TypeArg____, Type_>.fromHandlers(
+ handleData: (TypeName___ arg__, EventSink<Type_> arg_) {
+ ;
+ })).asBroadcastStream();
+>>> (indent 4)
+ _trigger
+ .then(ut.expectAsync((result) {
+ if (_deferExpectations == null || _deferExpectations == false) {
+ body(result);
+ } else {
+ defer(ut.expectAsync(() => body(result)));
+ }
+ }))
+ .catchError(ut.fail);
+<<<
+ _trigger.then(ut.expectAsync((result) {
+ if (_deferExpectations == null || _deferExpectations == false) {
+ body(result);
+ } else {
+ defer(ut.expectAsync(() => body(result)));
+ }
+ })).catchError(ut.fail);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0370.stmt b/packages/dart_style/test/regression/0300/0370.stmt
index 086bbf8..4f936d6 100644
--- a/packages/dart_style/test/regression/0300/0370.stmt
+++ b/packages/dart_style/test/regression/0300/0370.stmt
@@ -36,35 +36,40 @@
return notFound(request, asset: id);
});
<<<
-return environment.barback.getAssetById(id).then((result) {
- return result;
-}).then((asset) => _serveAsset(request, asset)).catchError((error, trace) {
- if (error is! AssetNotFoundException) throw error;
- return environment.barback
- .getAssetById(id.addExtension("/index.html"))
- .then((asset) {
- if (request.url.path.endsWith('/')) return _serveAsset(request, asset);
+return environment.barback
+ .getAssetById(id)
+ .then((result) {
+ return result;
+ })
+ .then((asset) => _serveAsset(request, asset))
+ .catchError((error, trace) {
+ if (error is! AssetNotFoundException) throw error;
+ return environment.barback
+ .getAssetById(id.addExtension("/index.html"))
+ .then((asset) {
+ if (request.url.path.endsWith('/')) return _serveAsset(request, asset);
- // We only want to serve index.html if the URL explicitly ends in a
- // slash. For other URLs, we redirect to one with the slash added to
- // implicitly support that too. This follows Apache's behavior.
- logRequest(request, "302 Redirect to ${request.url}/");
- return new shelf.Response.found('${request.url}/');
- }).catchError((newError, newTrace) {
- // If we find neither the original file or the index, we should report
- // the error about the original to the user.
- throw newError is AssetNotFoundException ? error : newError;
- });
-}).catchError((error, trace) {
- if (error is! AssetNotFoundException) {
- trace = new Chain.forTrace(trace);
- logRequest(request, "$error\n$trace");
+ // We only want to serve index.html if the URL explicitly ends in a
+ // slash. For other URLs, we redirect to one with the slash added to
+ // implicitly support that too. This follows Apache's behavior.
+ logRequest(request, "302 Redirect to ${request.url}/");
+ return new shelf.Response.found('${request.url}/');
+ }).catchError((newError, newTrace) {
+ // If we find neither the original file or the index, we should report
+ // the error about the original to the user.
+ throw newError is AssetNotFoundException ? error : newError;
+ });
+ })
+ .catchError((error, trace) {
+ if (error is! AssetNotFoundException) {
+ trace = new Chain.forTrace(trace);
+ logRequest(request, "$error\n$trace");
- addError(error, trace);
- close();
- return new shelf.Response.internalServerError();
- }
+ addError(error, trace);
+ close();
+ return new shelf.Response.internalServerError();
+ }
- addResult(new BarbackServerResult._failure(request.url, id, error));
- return notFound(request, asset: id);
-});
\ No newline at end of file
+ addResult(new BarbackServerResult._failure(request.url, id, error));
+ return notFound(request, asset: id);
+ });
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0373.unit b/packages/dart_style/test/regression/0300/0373.unit
new file mode 100644
index 0000000..25e4021
--- /dev/null
+++ b/packages/dart_style/test/regression/0300/0373.unit
@@ -0,0 +1,6 @@
+>>> (indent 4)
+ TreeNode lookupEntity(Iterable<TreeNode> iterable, int entity) => iterable
+ .firstWhere((node) => (node.entity == entity), orElse: () => null);
+<<<
+ TreeNode lookupEntity(Iterable<TreeNode> iterable, int entity) => iterable
+ .firstWhere((node) => (node.entity == entity), orElse: () => null);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0378.stmt b/packages/dart_style/test/regression/0300/0378.stmt
new file mode 100644
index 0000000..a7da1f0
--- /dev/null
+++ b/packages/dart_style/test/regression/0300/0378.stmt
@@ -0,0 +1,9 @@
+>>> (indent 4)
+ return new js.Fun(parameters, body, asyncModifier: asyncModifier)
+ .withSourceInformation(sourceInformationFactory.forContext(element)
+ .buildDeclaration(element));
+<<<
+ return new js.Fun(parameters, body, asyncModifier: asyncModifier)
+ .withSourceInformation(sourceInformationFactory
+ .forContext(element)
+ .buildDeclaration(element));
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0384.stmt b/packages/dart_style/test/regression/0300/0384.stmt
index fa0c6fa..0fbaa33 100644
--- a/packages/dart_style/test/regression/0300/0384.stmt
+++ b/packages/dart_style/test/regression/0300/0384.stmt
@@ -3,4 +3,5 @@
ErrorCollectingPublisher] as ErrorCollectingPublisher).verify();
<<<
(scubaMatchers.publishers[ErrorCollectingPublisher]
- as ErrorCollectingPublisher).verify();
\ No newline at end of file
+ as ErrorCollectingPublisher)
+ .verify();
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0387.unit b/packages/dart_style/test/regression/0300/0387.unit
new file mode 100644
index 0000000..88fcfbc
--- /dev/null
+++ b/packages/dart_style/test/regression/0300/0387.unit
@@ -0,0 +1,19 @@
+>>>
+greet(@Rest(valueHelp: 'who', help: 'Name(s) to greet.') List<String> who,
+ {@Group.start(title: 'Output')
+ @Option(help: 'How many !\'s to append.')
+ int enthusiasm: 0,
+ @Flag(abbr: 'l', help: 'Put names on separate lines.') bool lineMode: false,
+ @Option(name: 'greeting', help: 'Alternate word to greet with e.g. "Hi".')
+ String salutation: 'Hello'}) {}
+<<<
+greet(
+ @Rest(valueHelp: 'who', help: 'Name(s) to greet.')
+ List<String> who,
+ {@Group.start(title: 'Output')
+ @Option(help: 'How many !\'s to append.')
+ int enthusiasm: 0,
+ @Flag(abbr: 'l', help: 'Put names on separate lines.')
+ bool lineMode: false,
+ @Option(name: 'greeting', help: 'Alternate word to greet with e.g. "Hi".')
+ String salutation: 'Hello'}) {}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0394.stmt b/packages/dart_style/test/regression/0300/0394.stmt
new file mode 100644
index 0000000..139851a
--- /dev/null
+++ b/packages/dart_style/test/regression/0300/0394.stmt
@@ -0,0 +1,26 @@
+>>>
+return $.Div(inner: [
+
+ $.Div(id: "container", inner: [
+ $.Canvas(width: maxD, height: maxD, clazz: "center", ref: canvas),
+ $.Form(clazz: "center", inner: [
+ $.Input(type: "range", max: 1000, value: seeds, onChange: onSliderChange)
+ ]),
+ $.Img(src: "math.png", width: "350px", height: "42px", clazz: "center")
+ ]),
+
+ $.Footer(inner: [
+ $.P(id: "notes", inner: "${seeds} seeds")
+ ]),
+]);
+<<<
+return $.Div(inner: [
+ $.Div(id: "container", inner: [
+ $.Canvas(width: maxD, height: maxD, clazz: "center", ref: canvas),
+ $.Form(clazz: "center", inner: [
+ $.Input(type: "range", max: 1000, value: seeds, onChange: onSliderChange)
+ ]),
+ $.Img(src: "math.png", width: "350px", height: "42px", clazz: "center")
+ ]),
+ $.Footer(inner: [$.P(id: "notes", inner: "${seeds} seeds")]),
+]);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0398.stmt b/packages/dart_style/test/regression/0300/0398.stmt
new file mode 100644
index 0000000..7eed34a
--- /dev/null
+++ b/packages/dart_style/test/regression/0300/0398.stmt
@@ -0,0 +1,11 @@
+>>> (indent 4)
+ children.add(new DrawerHeader(children: [
+ new Flex([avatar, username],
+ justifyContent: FlexJustifyContent.center,
+ direction: FlexDirection.vertical)]));
+<<<
+ children.add(new DrawerHeader(children: [
+ new Flex([avatar, username],
+ justifyContent: FlexJustifyContent.center,
+ direction: FlexDirection.vertical)
+ ]));
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0300/0399.unit b/packages/dart_style/test/regression/0300/0399.unit
new file mode 100644
index 0000000..148eaa0
--- /dev/null
+++ b/packages/dart_style/test/regression/0300/0399.unit
@@ -0,0 +1,8 @@
+>>> (indent 2)
+ Optional<Cookie> getCookie(String name) => new Optional.fromNullable(_driver
+ .cookies
+ .all.firstWhere((cookie) => cookie.name == name, orElse: () => null));
+<<<
+ Optional<Cookie> getCookie(String name) =>
+ new Optional.fromNullable(_driver.cookies.all
+ .firstWhere((cookie) => cookie.name == name, orElse: () => null));
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0404.stmt b/packages/dart_style/test/regression/0400/0404.stmt
new file mode 100644
index 0000000..9049632
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0404.stmt
@@ -0,0 +1,14 @@
+>>> (indent 2)
+ group('((prevent timeout))', () {
+ setUp(() => ensureImageExists());
+
+ test('((dummy))', () {
+ }, timeout: const Timeout(const Duration(seconds: 300)));
+ });
+<<<
+ group('((prevent timeout))', () {
+ setUp(() => ensureImageExists());
+
+ test('((dummy))', () {},
+ timeout: const Timeout(const Duration(seconds: 300)));
+ });
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0407.unit b/packages/dart_style/test/regression/0400/0407.unit
new file mode 100644
index 0000000..ca50851
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0407.unit
@@ -0,0 +1,33 @@
+>>>
+void main() {
+ model..account = (new Account()
+ ..accountId = new Int64(111)
+ ..tags = (new Account_Tags()..accountHotlist.add(new Hotlist()..hotlistId = new Int64(10))));
+}
+<<<
+void main() {
+ model
+ ..account = (new Account()
+ ..accountId = new Int64(111)
+ ..tags = (new Account_Tags()
+ ..accountHotlist.add(new Hotlist()..hotlistId = new Int64(10))));
+}
+>>> (indent 4)
+main() {
+receiver
+ ..formattedTotal = _total == 0
+ ? ""
+ : _chartType == "PieChart"
+ ? _formatter.formatAsPercent(item.value / _total, fractionDigits: 1)
+ : _formatter.formatValue(item.value, item.valueType);
+}
+<<<
+ main() {
+ receiver
+ ..formattedTotal = _total == 0
+ ? ""
+ : _chartType == "PieChart"
+ ? _formatter.formatAsPercent(item.value / _total,
+ fractionDigits: 1)
+ : _formatter.formatValue(item.value, item.valueType);
+ }
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0410.stmt b/packages/dart_style/test/regression/0400/0410.stmt
new file mode 100644
index 0000000..2aa5952
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0410.stmt
@@ -0,0 +1,53 @@
+>>>
+test(
+ 'some test that has a very long description so the params need two lines',
+ async(() {
+ expect(
+ request.enabledApExperiments.contains(
+ ApExperimentName.SOME_VERY_LONG_ENUMS_THAT_LEADS_TO_MANY_PROBLEMS),
+ isFalse);
+ }));
+<<<
+test('some test that has a very long description so the params need two lines',
+ async(() {
+ expect(
+ request.enabledApExperiments.contains(
+ ApExperimentName.SOME_VERY_LONG_ENUMS_THAT_LEADS_TO_MANY_PROBLEMS),
+ isFalse);
+}));
+>>> (indent 2)
+test(
+ 'some test that has a very long description so the params need two lines',
+ async(() {
+ expect(
+ request.enabledApExperiments.contains(
+ ApExperimentName.SOME_VERY_LONG_ENUMS_THAT_LEADS_TO_MANY_PROBLEMS),
+ isFalse);
+}));
+<<<
+ test(
+ 'some test that has a very long description so the params need two lines',
+ async(() {
+ expect(
+ request.enabledApExperiments.contains(
+ ApExperimentName.SOME_VERY_LONG_ENUMS_THAT_LEADS_TO_MANY_PROBLEMS),
+ isFalse);
+ }));
+>>> (indent 4)
+test(
+ 'some test that has a very long description so the params need two lines',
+ async(() {
+ expect(
+ request.enabledApExperiments.contains(
+ ApExperimentName.SOME_VERY_LONG_ENUMS_THAT_LEADS_TO_MANY_PROBLEMS),
+ isFalse);
+ }));
+<<<
+ test(
+ 'some test that has a very long description so the params need two lines',
+ async(() {
+ expect(
+ request.enabledApExperiments.contains(ApExperimentName
+ .SOME_VERY_LONG_ENUMS_THAT_LEADS_TO_MANY_PROBLEMS),
+ isFalse);
+ }));
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0413.unit b/packages/dart_style/test/regression/0400/0413.unit
new file mode 100644
index 0000000..e627126
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0413.unit
@@ -0,0 +1,10 @@
+>>>
+List get bindings => [
+ bind(const StaticFilePath())
+ .toValue(_staticFilePath.isEmpty ? _libRoot : _staticFilePath)
+ ];
+<<<
+List get bindings => [
+ bind(const StaticFilePath())
+ .toValue(_staticFilePath.isEmpty ? _libRoot : _staticFilePath)
+ ];
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0420.unit b/packages/dart_style/test/regression/0400/0420.unit
new file mode 100644
index 0000000..b4d14cc
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0420.unit
@@ -0,0 +1,35 @@
+>>>
+createSomeObject(String s, Int64 id,
+ {Map<String, Int64> mapFromStringToInt64}) => null;
+
+main() {
+ void group() {
+ void initialize() {
+ final SOME_SUPER_LONG_CONST = 'Foo';
+ var objects = [
+ createSomeObject(SOME_SUPER_LONG_CONST, new Int64(1), mapFromStringToInt64:
+ {'ONE': new Int64(1), 'TWO': new Int64(2), 'THREE': new Int64(3)}),
+ ];
+ }
+ }
+}
+<<<
+createSomeObject(String s, Int64 id,
+ {Map<String, Int64> mapFromStringToInt64}) =>
+ null;
+
+main() {
+ void group() {
+ void initialize() {
+ final SOME_SUPER_LONG_CONST = 'Foo';
+ var objects = [
+ createSomeObject(SOME_SUPER_LONG_CONST, new Int64(1),
+ mapFromStringToInt64: {
+ 'ONE': new Int64(1),
+ 'TWO': new Int64(2),
+ 'THREE': new Int64(3)
+ }),
+ ];
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0421.unit b/packages/dart_style/test/regression/0400/0421.unit
new file mode 100644
index 0000000..ebd840c
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0421.unit
@@ -0,0 +1,82 @@
+>>>
+@Component(
+ selector: 'my-selector',
+ properties: const ['property-1', 'property-2', 'property-3', 'property-4', 'property-5'])
+@View(
+ directives: const [OtherComponent1, OtherComponent2, OtherComponent3, OtherDirective4, OtherDirective5, OtherDirective6, OtherDirective7,],
+ styleUrls: const ['package:path.to.my.amazing.component.library/my_amazing_component.scss.css'],
+ templateUrl: 'package:path.to.my.amazing.component.library/my_amazing_component.html')
+class MyAmazingComponent {
+ var a = [[[1]]];
+}
+<<<
+@Component(selector: 'my-selector', properties: const [
+ 'property-1',
+ 'property-2',
+ 'property-3',
+ 'property-4',
+ 'property-5'
+])
+@View(
+ directives: const [
+ OtherComponent1,
+ OtherComponent2,
+ OtherComponent3,
+ OtherDirective4,
+ OtherDirective5,
+ OtherDirective6,
+ OtherDirective7,
+ ],
+ styleUrls: const [
+ 'package:path.to.my.amazing.component.library/my_amazing_component.scss.css'
+ ],
+ templateUrl:
+ 'package:path.to.my.amazing.component.library/my_amazing_component.html')
+class MyAmazingComponent {
+ var a = [
+ [
+ [1]
+ ]
+ ];
+}
+>>>
+@Component(
+ selector: 'my-selector',
+ properties: const ['property-1', 'property-2', 'property-3', 'property-4', 'property-5'])
+@View(
+ directives: const [OtherComponent1, OtherComponent2, OtherComponent3, OtherDirective4, OtherDirective5, OtherDirective6, OtherDirective7,],
+ templateUrl: 'package:path.to.my.amazing.component.library/my_amazing_component.html',
+ styleUrls: const ['package:path.to.my.amazing.component.library/my_amazing_component.scss.css'])
+class MyAmazingComponent {
+ var a = [[[1]]];
+}
+<<<
+@Component(selector: 'my-selector', properties: const [
+ 'property-1',
+ 'property-2',
+ 'property-3',
+ 'property-4',
+ 'property-5'
+])
+@View(
+ directives: const [
+ OtherComponent1,
+ OtherComponent2,
+ OtherComponent3,
+ OtherDirective4,
+ OtherDirective5,
+ OtherDirective6,
+ OtherDirective7,
+ ],
+ templateUrl:
+ 'package:path.to.my.amazing.component.library/my_amazing_component.html',
+ styleUrls: const [
+ 'package:path.to.my.amazing.component.library/my_amazing_component.scss.css'
+ ])
+class MyAmazingComponent {
+ var a = [
+ [
+ [1]
+ ]
+ ];
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0422.unit b/packages/dart_style/test/regression/0400/0422.unit
new file mode 100644
index 0000000..ba60807
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0422.unit
@@ -0,0 +1,21 @@
+>>>
+void allTests() {
+ describe('inliner', () {
+ it('should inline `templateUrl` values', () async {
+ expect(view.namedParameters
+ .firstWhere((p) => p.name == 'templateUrl')
+ .value).toContain('template.html');
+ });
+ });
+}
+<<<
+void allTests() {
+ describe('inliner', () {
+ it('should inline `templateUrl` values', () async {
+ expect(view.namedParameters
+ .firstWhere((p) => p.name == 'templateUrl')
+ .value)
+ .toContain('template.html');
+ });
+ });
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0424.stmt b/packages/dart_style/test/regression/0400/0424.stmt
new file mode 100644
index 0000000..fc09d05
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0424.stmt
@@ -0,0 +1,13 @@
+>>>
+obj.functionWithTwoParameters(firstParameterGenerator()
+ ..id = myId
+ ..teamId = myTeamId
+ ..startTime = new Time(getEndOfDate(clock.currentTime()).millisecondsSinceEpoch), secondParameterGenerator());
+<<<
+obj.functionWithTwoParameters(
+ firstParameterGenerator()
+ ..id = myId
+ ..teamId = myTeamId
+ ..startTime =
+ new Time(getEndOfDate(clock.currentTime()).millisecondsSinceEpoch),
+ secondParameterGenerator());
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0429.stmt b/packages/dart_style/test/regression/0400/0429.stmt
new file mode 100644
index 0000000..411e7cd
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0429.stmt
@@ -0,0 +1,12 @@
+>>> (indent 2)
+ final Map<String,
+ SummaryTabDataRequest_CampaignSkinSubtype> _campaignSkinSubtypes =
+ new Map.fromIterable(SummaryTabDataRequest_CampaignSkinSubtype.values,
+ key: (campaignSkinSubtype) => campaignSkinSubtype.name,
+ value: (campaignSkinSubtype) => campaignSkinSubtype);
+<<<
+ final Map<String, SummaryTabDataRequest_CampaignSkinSubtype>
+ _campaignSkinSubtypes = new Map.fromIterable(
+ SummaryTabDataRequest_CampaignSkinSubtype.values,
+ key: (campaignSkinSubtype) => campaignSkinSubtype.name,
+ value: (campaignSkinSubtype) => campaignSkinSubtype);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0434.unit b/packages/dart_style/test/regression/0400/0434.unit
new file mode 100644
index 0000000..4089412
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0434.unit
@@ -0,0 +1,20 @@
+>>> (indent 2)
+ @eventHandler
+ bool computeChangePasswordDisabled(
+ String email, String password, String newPassword) =>
+ email == null ||
+ email.isEmpty ||
+ password == null ||
+ password.isEmpty ||
+ newPassword == null ||
+ newPassword.isEmpty;
+<<<
+ @eventHandler
+ bool computeChangePasswordDisabled(
+ String email, String password, String newPassword) =>
+ email == null ||
+ email.isEmpty ||
+ password == null ||
+ password.isEmpty ||
+ newPassword == null ||
+ newPassword.isEmpty;
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0436.unit b/packages/dart_style/test/regression/0400/0436.unit
new file mode 100644
index 0000000..bd83a42
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0436.unit
@@ -0,0 +1,17 @@
+>>>
+class Foo {
+ void main() {
+ List<TypeParameterElement> typeParameterElements = interfaceTypeContext
+ .element !=
+ null ? interfaceTypeContext.element.typeParameters : null;
+ }
+}
+<<<
+class Foo {
+ void main() {
+ List<TypeParameterElement> typeParameterElements =
+ interfaceTypeContext.element != null
+ ? interfaceTypeContext.element.typeParameters
+ : null;
+ }
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0437.unit b/packages/dart_style/test/regression/0400/0437.unit
new file mode 100644
index 0000000..3016314
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0437.unit
@@ -0,0 +1,22 @@
+>>>
+class Foo {
+ static ExportElementImpl exportFor(LibraryElement exportedLibrary,
+ [List<NamespaceCombinator> combinators = NamespaceCombinator
+ .EMPTY_LIST]) {
+ ExportElementImpl spec = new ExportElementImpl(-1);
+ spec.exportedLibrary = exportedLibrary;
+ spec.combinators = combinators;
+ return spec;
+ }
+}
+<<<
+class Foo {
+ static ExportElementImpl exportFor(LibraryElement exportedLibrary,
+ [List<NamespaceCombinator> combinators =
+ NamespaceCombinator.EMPTY_LIST]) {
+ ExportElementImpl spec = new ExportElementImpl(-1);
+ spec.exportedLibrary = exportedLibrary;
+ spec.combinators = combinators;
+ return spec;
+ }
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0438.stmt b/packages/dart_style/test/regression/0400/0438.stmt
new file mode 100644
index 0000000..19e237d
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0438.stmt
@@ -0,0 +1,6 @@
+>>>
+final JsFunction _setDartInstance = context['Polymer']['PolymerInterop']
+ ['setDartInstance'];
+<<<
+final JsFunction _setDartInstance =
+ context['Polymer']['PolymerInterop']['setDartInstance'];
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0439.stmt b/packages/dart_style/test/regression/0400/0439.stmt
new file mode 100644
index 0000000..bf44cae
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0439.stmt
@@ -0,0 +1,16 @@
+>>> (indent 2)
+ final aaaaaaaaaaaaaaaaaaaa = new Bbbbbbbbbbbbbbbbbbbbbbbb<
+ Cccccccc>((Cccccccc cc) =>
+ 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd');
+<<<
+ final aaaaaaaaaaaaaaaaaaaa = new Bbbbbbbbbbbbbbbbbbbbbbbb<Cccccccc>(
+ (Cccccccc cc) =>
+ 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd');
+>>> (indent 2)
+ final aaaaaaaaaaaaaaaaaaaa = new Bbbbbbbbbbbbbbbbbbbbbbbb<
+ Cccccccc>((Cccccccc cc) =>
+ 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd');
+<<<
+ final aaaaaaaaaaaaaaaaaaaa = new Bbbbbbbbbbbbbbbbbbbbbbbb<Cccccccc>(
+ (Cccccccc cc) =>
+ 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd');
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0441.unit b/packages/dart_style/test/regression/0400/0441.unit
new file mode 100644
index 0000000..787fbad
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0441.unit
@@ -0,0 +1,14 @@
+>>>
+getSomeFoo(
+ {fooooooooooooooooooooo: const [
+ 'baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
+]}) {
+ return 'rhubarb';
+}
+<<<
+getSomeFoo(
+ {fooooooooooooooooooooo: const [
+ 'baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
+ ]}) {
+ return 'rhubarb';
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0443.unit b/packages/dart_style/test/regression/0400/0443.unit
new file mode 100644
index 0000000..16e3333
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0443.unit
@@ -0,0 +1,6 @@
+>>>
+@sg.GeneratedPart('package:injection/generator.dart')
+part 'app.injection.dart';
+<<<
+@sg.GeneratedPart('package:injection/generator.dart')
+part 'app.injection.dart';
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0444.unit b/packages/dart_style/test/regression/0400/0444.unit
new file mode 100644
index 0000000..944f0f2
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0444.unit
@@ -0,0 +1,26 @@
+>>>
+class MyComponent {
+ Object firstArgument;
+ Object superDuperLongNamedArgument;
+ Object secondArgument;
+
+ MyComponent(
+ @Inject(const MyFirstArgument()) this.firstArgument,
+ @Inject(const MySuperDuperLongNamedArgument())
+ this.superDuperLongNamedArgument, // LOOK AT ME
+ @Inject(const MySecondArgument()) this.secondArgument);
+}
+<<<
+class MyComponent {
+ Object firstArgument;
+ Object superDuperLongNamedArgument;
+ Object secondArgument;
+
+ MyComponent(
+ @Inject(const MyFirstArgument())
+ this.firstArgument,
+ @Inject(const MySuperDuperLongNamedArgument())
+ this.superDuperLongNamedArgument, // LOOK AT ME
+ @Inject(const MySecondArgument())
+ this.secondArgument);
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0448.stmt b/packages/dart_style/test/regression/0400/0448.stmt
new file mode 100644
index 0000000..09e0262
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0448.stmt
@@ -0,0 +1,34 @@
+>>> (indent 6)
+ if (t == 0.0)
+ value = begin;
+ else if (t == 1.0)
+ value = end;
+ else
+ value = lerp(t);
+<<<
+ if (t == 0.0)
+ value = begin;
+ else if (t == 1.0)
+ value = end;
+ else
+ value = lerp(t);
+>>> (indent 4)
+ if (direction == AnimationDirection.forward ||
+ reverseCurve == null) return curve;
+<<<
+ if (direction == AnimationDirection.forward || reverseCurve == null)
+ return curve;
+>>> (indent 6)
+ if (outputSize.width / outputSize.height > sourceSize.width / sourceSize.height)
+ destinationSize = new Size(sourceSize.width * outputSize.height / sourceSize.height, outputSize.height);
+ else
+ destinationSize = new Size(outputSize.width, sourceSize.height * outputSize.width / sourceSize.width);
+<<<
+ if (outputSize.width / outputSize.height >
+ sourceSize.width / sourceSize.height)
+ destinationSize = new Size(
+ sourceSize.width * outputSize.height / sourceSize.height,
+ outputSize.height);
+ else
+ destinationSize = new Size(outputSize.width,
+ sourceSize.height * outputSize.width / sourceSize.width);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0449.stmt b/packages/dart_style/test/regression/0400/0449.stmt
new file mode 100644
index 0000000..b4146da
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0449.stmt
@@ -0,0 +1,6 @@
+>>> (indent 4)
+ for (int i = 0; i < variables.length; ++i)
+ performance.updateVariable(variables[i]);
+<<<
+ for (int i = 0; i < variables.length; ++i)
+ performance.updateVariable(variables[i]);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0454.unit b/packages/dart_style/test/regression/0400/0454.unit
new file mode 100644
index 0000000..6e9e5ef
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0454.unit
@@ -0,0 +1,19 @@
+>>> original bug report actually did fit in 80
+class F {
+ List<
+ AVeryLongTypeNameTHatTheUserCannotChange____________> aDescriptiveName__;
+}
+<<<
+class F {
+ List<AVeryLongTypeNameTHatTheUserCannotChange____________> aDescriptiveName__;
+}
+>>>
+class F {
+ List<
+ AVeryLongTypeNameTHatTheUserCannotChange_____________> aDescriptiveName__;
+}
+<<<
+class F {
+ List<AVeryLongTypeNameTHatTheUserCannotChange_____________>
+ aDescriptiveName__;
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0461.stmt b/packages/dart_style/test/regression/0400/0461.stmt
new file mode 100644
index 0000000..adf7b16
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0461.stmt
@@ -0,0 +1,11 @@
+>>> (indent 6)
+ receiver___________.property________.add(
+ new SomeClass___________________.someNamedConstructor_____________________());
+<<<
+ receiver___________.property________.add(new SomeClass___________________
+ .someNamedConstructor_____________________());
+>>> (indent 4)
+ new SomeClass___________________.someNamedConstructor_____________________();
+<<<
+ new SomeClass___________________
+ .someNamedConstructor_____________________();
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0462.unit b/packages/dart_style/test/regression/0400/0462.unit
new file mode 100644
index 0000000..1b728da
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0462.unit
@@ -0,0 +1,33 @@
+>>>
+class Foo {
+ Future<
+ ComponentSettings<
+ BiddingStrategyConfiguration>> _biddingStrategyConfigurationFuture;
+
+ Future<
+ ComponentSettings<
+ BiddingStrategyConfiguration>> get biddingStrategyConfigurationFuture =>
+ _biddingStrategyConfigurationFuture;
+}
+<<<
+class Foo {
+ Future<ComponentSettings<BiddingStrategyConfiguration>>
+ _biddingStrategyConfigurationFuture;
+
+ Future<ComponentSettings<BiddingStrategyConfiguration>>
+ get biddingStrategyConfigurationFuture =>
+ _biddingStrategyConfigurationFuture;
+}
+>>>
+class Foo {
+ HashMap<
+ AnalysisTarget,
+ List<
+ PendingFuture>> get pendingFutureSources_forTesting =>
+ _pendingFutureTargets;
+}
+<<<
+class Foo {
+ HashMap<AnalysisTarget, List<PendingFuture>>
+ get pendingFutureSources_forTesting => _pendingFutureTargets;
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0463.unit b/packages/dart_style/test/regression/0400/0463.unit
new file mode 100644
index 0000000..b307093
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0463.unit
@@ -0,0 +1,22 @@
+>>>
+class FilterBarTest {
+ FilterBarTest() {
+ model = new FilterModel(
+ [const TestComparisonPredicate('test1'), const TestComparisonPredicate('test2')],
+ suggestionProvider,
+ filterTypeProvider,
+ saveFilterAction,
+ hasReadOnlyMode: true,
+ valueEditorProvider: valueEditorProvider);
+ }
+}
+<<<
+class FilterBarTest {
+ FilterBarTest() {
+ model = new FilterModel([
+ const TestComparisonPredicate('test1'),
+ const TestComparisonPredicate('test2')
+ ], suggestionProvider, filterTypeProvider, saveFilterAction,
+ hasReadOnlyMode: true, valueEditorProvider: valueEditorProvider);
+ }
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0465.stmt b/packages/dart_style/test/regression/0400/0465.stmt
new file mode 100644
index 0000000..4b0d988
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0465.stmt
@@ -0,0 +1,32 @@
+>>> (indent 8)
+ if (_shouldReportMissingMember(staticType, staticMethod)) {
+ _recordUndefinedToken(
+ staticType.element,
+ StaticTypeWarningCode.UNDEFINED_METHOD,
+ operator, [methodName, staticType.displayName]);
+ } else if (_enableHints &&
+ _shouldReportMissingMember(propagatedType, propagatedMethod) &&
+ !_memberFoundInSubclass(
+ propagatedType.element, methodName, true, false)) {
+ _recordUndefinedToken(
+ propagatedType.element,
+ HintCode.UNDEFINED_METHOD,
+ operator, [methodName, propagatedType.displayName]);
+ }
+<<<
+ if (_shouldReportMissingMember(staticType, staticMethod)) {
+ _recordUndefinedToken(
+ staticType.element,
+ StaticTypeWarningCode.UNDEFINED_METHOD,
+ operator,
+ [methodName, staticType.displayName]);
+ } else if (_enableHints &&
+ _shouldReportMissingMember(propagatedType, propagatedMethod) &&
+ !_memberFoundInSubclass(
+ propagatedType.element, methodName, true, false)) {
+ _recordUndefinedToken(
+ propagatedType.element,
+ HintCode.UNDEFINED_METHOD,
+ operator,
+ [methodName, propagatedType.displayName]);
+ }
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0466.unit b/packages/dart_style/test/regression/0400/0466.unit
new file mode 100644
index 0000000..aa7a804
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0466.unit
@@ -0,0 +1,17 @@
+>>> (indent 4)
+ Future get identifier________ async {
+ var id = identifier________.identifier__;
+
+ return identifier_____________[
+ id] ??= await identifier_______.identifier____________________(
+ identifier___________________________.create()..identifier____ = id);
+ }
+<<<
+ Future get identifier________ async {
+ var id = identifier________.identifier__;
+
+ return identifier_____________[id] ??=
+ await identifier_______.identifier____________________(
+ identifier___________________________.create()
+ ..identifier____ = id);
+ }
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0467.unit b/packages/dart_style/test/regression/0400/0467.unit
new file mode 100644
index 0000000..57df4c3
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0467.unit
@@ -0,0 +1,26 @@
+>>>
+class MyElement extends PolymerElement {
+ MyElement.created() : super.created();
+
+ @reflectable
+ void showNodesAndEntryPoints([_, __]) {
+ addAll(
+ 'nodesAndEntryPoints',
+ new PolymerDom(
+ this).children.map((child) => '${child.outerHtml} ------> '
+ '${(new PolymerDom(child).getDestinationInsertionPoints()[0] as Element).outerHtml}'));
+ }
+}
+<<<
+class MyElement extends PolymerElement {
+ MyElement.created() : super.created();
+
+ @reflectable
+ void showNodesAndEntryPoints([_, __]) {
+ addAll(
+ 'nodesAndEntryPoints',
+ new PolymerDom(this).children.map((child) =>
+ '${child.outerHtml} ------> '
+ '${(new PolymerDom(child).getDestinationInsertionPoints()[0] as Element).outerHtml}'));
+ }
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0474.unit b/packages/dart_style/test/regression/0400/0474.unit
new file mode 100644
index 0000000..5e2dc91
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0474.unit
@@ -0,0 +1,678 @@
+>>>
+Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
+ 'parseCompilationUnit_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseCompilationUnit(arg0)),
+ 'parseDirectives_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseDirectives(arg0)),
+ 'parseExpression_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseExpression(arg0)),
+ 'parseStatement_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseStatement(arg0)),
+ 'parseStatements_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseStatements(arg0)),
+ 'parseAnnotation_0':
+ new MethodTrampoline(0, (Parser target) => target.parseAnnotation()),
+ 'parseArgument_0':
+ new MethodTrampoline(0, (Parser target) => target.parseArgument()),
+ 'parseArgumentList_0':
+ new MethodTrampoline(0, (Parser target) => target.parseArgumentList()),
+ 'parseBitwiseOrExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseBitwiseOrExpression()),
+ 'parseBlock_0':
+ new MethodTrampoline(0, (Parser target) => target.parseBlock()),
+ 'parseClassMember_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseClassMember(arg0)),
+ 'parseCompilationUnit_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseCompilationUnit2()),
+ 'parseConditionalExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseConditionalExpression()),
+ 'parseConstructorName_0':
+ new MethodTrampoline(0, (Parser target) => target.parseConstructorName()),
+ 'parseExpression_0':
+ new MethodTrampoline(0, (Parser target) => target.parseExpression2()),
+ 'parseExpressionWithoutCascade_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseExpressionWithoutCascade()),
+ 'parseExtendsClause_0':
+ new MethodTrampoline(0, (Parser target) => target.parseExtendsClause()),
+ 'parseFormalParameterList_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseFormalParameterList()),
+ 'parseFunctionExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseFunctionExpression()),
+ 'parseImplementsClause_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseImplementsClause()),
+ 'parseLabel_0':
+ new MethodTrampoline(0, (Parser target) => target.parseLabel()),
+ 'parseLibraryIdentifier_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseLibraryIdentifier()),
+ 'parseLogicalOrExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseLogicalOrExpression()),
+ 'parseMapLiteralEntry_0':
+ new MethodTrampoline(0, (Parser target) => target.parseMapLiteralEntry()),
+ 'parseNormalFormalParameter_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseNormalFormalParameter()),
+ 'parsePrefixedIdentifier_0': new MethodTrampoline(
+ 0, (Parser target) => target.parsePrefixedIdentifier()),
+ 'parseReturnType_0':
+ new MethodTrampoline(0, (Parser target) => target.parseReturnType()),
+ 'parseSimpleIdentifier_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseSimpleIdentifier()),
+ 'parseStatement_0':
+ new MethodTrampoline(0, (Parser target) => target.parseStatement2()),
+ 'parseStringLiteral_0':
+ new MethodTrampoline(0, (Parser target) => target.parseStringLiteral()),
+ 'parseTypeArgumentList_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseTypeArgumentList()),
+ 'parseTypeName_0':
+ new MethodTrampoline(0, (Parser target) => target.parseTypeName()),
+ 'parseTypeParameter_0':
+ new MethodTrampoline(0, (Parser target) => target.parseTypeParameter()),
+ 'parseTypeParameterList_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseTypeParameterList()),
+ 'parseWithClause_0':
+ new MethodTrampoline(0, (Parser target) => target.parseWithClause()),
+ 'advance_0': new MethodTrampoline(0, (Parser target) => target._advance()),
+ 'appendScalarValue_5': new MethodTrampoline(
+ 5,
+ (Parser target, arg0, arg1, arg2, arg3, arg4) =>
+ target._appendScalarValue(arg0, arg1, arg2, arg3, arg4)),
+ 'computeStringValue_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._computeStringValue(arg0, arg1, arg2)),
+ 'convertToFunctionDeclaration_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._convertToFunctionDeclaration(arg0)),
+ 'couldBeStartOfCompilationUnitMember_0': new MethodTrampoline(
+ 0, (Parser target) => target._couldBeStartOfCompilationUnitMember()),
+ 'createSyntheticIdentifier_0':
+ new MethodTrampoline(0, (Parser target) => target._createSyntheticIdentifier()),
+ 'createSyntheticKeyword_1': new MethodTrampoline(1, (Parser target, arg0) => target._createSyntheticKeyword(arg0)),
+ 'createSyntheticStringLiteral_0': new MethodTrampoline(0, (Parser target) => target._createSyntheticStringLiteral()),
+ 'createSyntheticToken_1': new MethodTrampoline(1, (Parser target, arg0) => target._createSyntheticToken(arg0)),
+ 'ensureAssignable_1': new MethodTrampoline(1, (Parser target, arg0) => target._ensureAssignable(arg0)),
+ 'expect_1': new MethodTrampoline(1, (Parser target, arg0) => target._expect(arg0)),
+ 'expectGt_0': new MethodTrampoline(0, (Parser target) => target._expectGt()),
+ 'expectKeyword_1': new MethodTrampoline(1, (Parser target, arg0) => target._expectKeyword(arg0)),
+ 'expectSemicolon_0': new MethodTrampoline(0, (Parser target) => target._expectSemicolon()),
+ 'findRange_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._findRange(arg0, arg1)),
+ 'getCodeBlockRanges_1': new MethodTrampoline(1, (Parser target, arg0) => target._getCodeBlockRanges(arg0)),
+ 'getEndToken_1': new MethodTrampoline(1, (Parser target, arg0) => target._getEndToken(arg0)),
+ 'injectToken_1': new MethodTrampoline(1, (Parser target, arg0) => target._injectToken(arg0)),
+ 'isFunctionDeclaration_0': new MethodTrampoline(0, (Parser target) => target._isFunctionDeclaration()),
+ 'isFunctionExpression_1': new MethodTrampoline(1, (Parser target, arg0) => target._isFunctionExpression(arg0)),
+ 'isHexDigit_1': new MethodTrampoline(1, (Parser target, arg0) => target._isHexDigit(arg0)),
+ 'isInitializedVariableDeclaration_0': new MethodTrampoline(0, (Parser target) => target._isInitializedVariableDeclaration()),
+ 'isLinkText_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._isLinkText(arg0, arg1)),
+ 'isOperator_1': new MethodTrampoline(1, (Parser target, arg0) => target._isOperator(arg0)),
+ 'isSwitchMember_0': new MethodTrampoline(0, (Parser target) => target._isSwitchMember()),
+ 'isTypedIdentifier_1': new MethodTrampoline(1, (Parser target, arg0) => target._isTypedIdentifier(arg0)),
+ 'lockErrorListener_0': new MethodTrampoline(0, (Parser target) => target._lockErrorListener()),
+ 'matches_1': new MethodTrampoline(1, (Parser target, arg0) => target._matches(arg0)),
+ 'matchesGt_0': new MethodTrampoline(0, (Parser target) => target._matchesGt()),
+ 'matchesIdentifier_0': new MethodTrampoline(0, (Parser target) => target._matchesIdentifier()),
+ 'matchesKeyword_1': new MethodTrampoline(1, (Parser target, arg0) => target._matchesKeyword(arg0)),
+ 'matchesString_1': new MethodTrampoline(1, (Parser target, arg0) => target._matchesString(arg0)),
+ 'optional_1': new MethodTrampoline(1, (Parser target, arg0) => target._optional(arg0)),
+ 'parseAdditiveExpression_0': new MethodTrampoline(0, (Parser target) => target._parseAdditiveExpression()),
+ 'parseAssertStatement_0': new MethodTrampoline(0, (Parser target) => target._parseAssertStatement()),
+ 'parseAssignableExpression_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseAssignableExpression(arg0)),
+ 'parseAssignableSelector_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseAssignableSelector(arg0, arg1)),
+ 'parseAwaitExpression_0': new MethodTrampoline(0, (Parser target) => target._parseAwaitExpression()),
+ 'parseBitwiseAndExpression_0': new MethodTrampoline(0, (Parser target) => target._parseBitwiseAndExpression()),
+ 'parseBitwiseXorExpression_0': new MethodTrampoline(0, (Parser target) => target._parseBitwiseXorExpression()),
+ 'parseBreakStatement_0': new MethodTrampoline(0, (Parser target) => target._parseBreakStatement()),
+ 'parseCascadeSection_0': new MethodTrampoline(0, (Parser target) => target._parseCascadeSection()),
+ 'parseClassDeclaration_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseClassDeclaration(arg0, arg1)),
+ 'parseClassMembers_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseClassMembers(arg0, arg1)),
+ 'parseClassTypeAlias_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseClassTypeAlias(arg0, arg1, arg2)),
+ 'parseCombinator_0': new MethodTrampoline(0, (Parser target) => target.parseCombinator()),
+ 'parseCombinators_0': new MethodTrampoline(0, (Parser target) => target._parseCombinators()),
+ 'parseCommentAndMetadata_0': new MethodTrampoline(0, (Parser target) => target._parseCommentAndMetadata()),
+ 'parseCommentReference_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseCommentReference(arg0, arg1)),
+ 'parseCommentReferences_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseCommentReferences(arg0)),
+ 'parseCompilationUnitMember_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseCompilationUnitMember(arg0)),
+ 'parseConstExpression_0': new MethodTrampoline(0, (Parser target) => target._parseConstExpression()),
+ 'parseConstructor_8': new MethodTrampoline(8, (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => target._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
+ 'parseConstructorFieldInitializer_0': new MethodTrampoline(0, (Parser target) => target._parseConstructorFieldInitializer()),
+ 'parseContinueStatement_0': new MethodTrampoline(0, (Parser target) => target._parseContinueStatement()),
+ 'parseDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseDirective(arg0)),
+ 'parseDirectives_0': new MethodTrampoline(0, (Parser target) => target._parseDirectives()),
+ 'parseDocumentationComment_0': new MethodTrampoline(0, (Parser target) => target._parseDocumentationComment()),
+ 'parseDoStatement_0': new MethodTrampoline(0, (Parser target) => target._parseDoStatement()),
+ 'parseEmptyStatement_0': new MethodTrampoline(0, (Parser target) => target._parseEmptyStatement()),
+ 'parseEnumConstantDeclaration_0': new MethodTrampoline(0, (Parser target) => target._parseEnumConstantDeclaration()),
+ 'parseEnumDeclaration_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseEnumDeclaration(arg0)),
+ 'parseEqualityExpression_0': new MethodTrampoline(0, (Parser target) => target._parseEqualityExpression()),
+ 'parseExportDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseExportDirective(arg0)),
+ 'parseExpressionList_0': new MethodTrampoline(0, (Parser target) => target._parseExpressionList()),
+ 'parseFinalConstVarOrType_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseFinalConstVarOrType(arg0)),
+ 'parseFormalParameter_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseFormalParameter(arg0)),
+ 'parseForStatement_0': new MethodTrampoline(0, (Parser target) => target._parseForStatement()),
+ 'parseFunctionBody_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseFunctionBody(arg0, arg1, arg2)),
+ 'parseFunctionDeclaration_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseFunctionDeclaration(arg0, arg1, arg2)),
+ 'parseFunctionDeclarationStatement_0': new MethodTrampoline(0, (Parser target) => target._parseFunctionDeclarationStatement()),
+ 'parseFunctionDeclarationStatementAfterReturnType_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseFunctionDeclarationStatementAfterReturnType(arg0, arg1)),
+ 'parseFunctionTypeAlias_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseFunctionTypeAlias(arg0, arg1)),
+ 'parseGetter_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2, arg3) => target._parseGetter(arg0, arg1, arg2, arg3)),
+ 'parseIdentifierList_0': new MethodTrampoline(0, (Parser target) => target._parseIdentifierList()),
+ 'parseIfStatement_0': new MethodTrampoline(0, (Parser target) => target._parseIfStatement()),
+ 'parseImportDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseImportDirective(arg0)),
+ 'parseInitializedIdentifierList_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2, arg3) => target._parseInitializedIdentifierList(arg0, arg1, arg2, arg3)),
+ 'parseInstanceCreationExpression_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseInstanceCreationExpression(arg0)),
+ 'parseLibraryDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseLibraryDirective(arg0)),
+ 'parseLibraryName_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseLibraryName(arg0, arg1)),
+ 'parseListLiteral_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseListLiteral(arg0, arg1)),
+ 'parseListOrMapLiteral_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseListOrMapLiteral(arg0)),
+ 'parseLogicalAndExpression_0': new MethodTrampoline(0, (Parser target) => target._parseLogicalAndExpression()),
+ 'parseMapLiteral_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseMapLiteral(arg0, arg1)),
+ 'parseMethodDeclarationAfterParameters_7': new MethodTrampoline(7, (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6) => target._parseMethodDeclarationAfterParameters(arg0, arg1, arg2, arg3, arg4, arg5, arg6)),
+ 'parseMethodDeclarationAfterReturnType_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2, arg3) => target._parseMethodDeclarationAfterReturnType(arg0, arg1, arg2, arg3)),
+ 'parseModifiers_0': new MethodTrampoline(0, (Parser target) => target._parseModifiers()),
+ 'parseMultiplicativeExpression_0': new MethodTrampoline(0, (Parser target) => target._parseMultiplicativeExpression()),
+ 'parseNativeClause_0': new MethodTrampoline(0, (Parser target) => target._parseNativeClause()),
+ 'parseNewExpression_0': new MethodTrampoline(0, (Parser target) => target._parseNewExpression()),
+ 'parseNonLabeledStatement_0': new MethodTrampoline(0, (Parser target) => target._parseNonLabeledStatement()),
+ 'parseOperator_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseOperator(arg0, arg1, arg2)),
+ 'parseOptionalReturnType_0': new MethodTrampoline(0, (Parser target) => target._parseOptionalReturnType()),
+ 'parsePartDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parsePartDirective(arg0)),
+ 'parsePostfixExpression_0': new MethodTrampoline(0, (Parser target) => target._parsePostfixExpression()),
+ 'parsePrimaryExpression_0': new MethodTrampoline(0, (Parser target) => target._parsePrimaryExpression()),
+ 'parseRedirectingConstructorInvocation_0': new MethodTrampoline(0, (Parser target) => target._parseRedirectingConstructorInvocation()),
+ 'parseRelationalExpression_0': new MethodTrampoline(0, (Parser target) => target._parseRelationalExpression()),
+ 'parseRethrowExpression_0': new MethodTrampoline(0, (Parser target) => target._parseRethrowExpression()),
+ 'parseReturnStatement_0': new MethodTrampoline(0, (Parser target) => target._parseReturnStatement()),
+ 'parseSetter_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2, arg3) => target._parseSetter(arg0, arg1, arg2, arg3)),
+ 'parseShiftExpression_0': new MethodTrampoline(0, (Parser target) => target._parseShiftExpression()),
+ 'parseStatementList_0': new MethodTrampoline(0, (Parser target) => target._parseStatementList()),
+ 'parseStringInterpolation_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseStringInterpolation(arg0)),
+ 'parseSuperConstructorInvocation_0': new MethodTrampoline(0, (Parser target) => target._parseSuperConstructorInvocation()),
+ 'parseSwitchStatement_0': new MethodTrampoline(0, (Parser target) => target._parseSwitchStatement()),
+ 'parseSymbolLiteral_0': new MethodTrampoline(0, (Parser target) => target._parseSymbolLiteral()),
+ 'parseThrowExpression_0': new MethodTrampoline(0, (Parser target) => target._parseThrowExpression()),
+ 'parseThrowExpressionWithoutCascade_0': new MethodTrampoline(0, (Parser target) => target._parseThrowExpressionWithoutCascade()),
+ 'parseTryStatement_0': new MethodTrampoline(0, (Parser target) => target._parseTryStatement()),
+ 'parseTypeAlias_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseTypeAlias(arg0)),
+ 'parseUnaryExpression_0': new MethodTrampoline(0, (Parser target) => target._parseUnaryExpression()),
+ 'parseVariableDeclaration_0': new MethodTrampoline(0, (Parser target) => target._parseVariableDeclaration()),
+ 'parseVariableDeclarationListAfterMetadata_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseVariableDeclarationListAfterMetadata(arg0)),
+ 'parseVariableDeclarationListAfterType_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseVariableDeclarationListAfterType(arg0, arg1, arg2)),
+ 'parseVariableDeclarationStatementAfterMetadata_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseVariableDeclarationStatementAfterMetadata(arg0)),
+ 'parseVariableDeclarationStatementAfterType_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseVariableDeclarationStatementAfterType(arg0, arg1, arg2)),
+ 'parseWhileStatement_0': new MethodTrampoline(0, (Parser target) => target._parseWhileStatement()),
+ 'parseYieldStatement_0': new MethodTrampoline(0, (Parser target) => target._parseYieldStatement()),
+ 'peek_0': new MethodTrampoline(0, (Parser target) => target._peek()),
+ 'peekAt_1': new MethodTrampoline(1, (Parser target, arg0) => target._peekAt(arg0)),
+ 'reportError_1': new MethodTrampoline(1, (Parser target, arg0) => target._reportError(arg0)),
+ 'reportErrorForCurrentToken_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._reportErrorForCurrentToken(arg0, arg1)),
+ 'reportErrorForNode_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._reportErrorForNode(arg0, arg1, arg2)),
+ 'reportErrorForToken_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._reportErrorForToken(arg0, arg1, arg2)),
+ 'skipBlock_0': new MethodTrampoline(0, (Parser target) => target._skipBlock()),
+ 'skipFinalConstVarOrType_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipFinalConstVarOrType(arg0)),
+ 'skipFormalParameterList_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipFormalParameterList(arg0)),
+ 'skipPastMatchingToken_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipPastMatchingToken(arg0)),
+ 'skipPrefixedIdentifier_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipPrefixedIdentifier(arg0)),
+ 'skipReturnType_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipReturnType(arg0)),
+ 'skipSimpleIdentifier_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipSimpleIdentifier(arg0)),
+ 'skipStringInterpolation_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipStringInterpolation(arg0)),
+ 'skipStringLiteral_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipStringLiteral(arg0)),
+ 'skipTypeArgumentList_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipTypeArgumentList(arg0)),
+ 'skipTypeName_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipTypeName(arg0)),
+ 'skipTypeParameterList_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipTypeParameterList(arg0)),
+ 'tokenMatches_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._tokenMatches(arg0, arg1)),
+ 'tokenMatchesIdentifier_1': new MethodTrampoline(1, (Parser target, arg0) => target._tokenMatchesIdentifier(arg0)),
+ 'tokenMatchesKeyword_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._tokenMatchesKeyword(arg0, arg1)),
+ 'tokenMatchesString_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._tokenMatchesString(arg0, arg1)),
+ 'translateCharacter_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._translateCharacter(arg0, arg1, arg2)),
+ 'unlockErrorListener_0': new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()),
+ 'validateFormalParameterList_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateFormalParameterList(arg0)),
+ 'validateModifiersForClass_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForClass(arg0)),
+ 'validateModifiersForConstructor_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForConstructor(arg0)),
+ 'validateModifiersForEnum_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForEnum(arg0)),
+ 'validateModifiersForField_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForField(arg0)),
+ 'validateModifiersForFunctionDeclarationStatement_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForFunctionDeclarationStatement(arg0)),
+ 'validateModifiersForGetterOrSetterOrMethod_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForGetterOrSetterOrMethod(arg0)),
+ 'validateModifiersForOperator_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForOperator(arg0)),
+ 'validateModifiersForTopLevelDeclaration_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForTopLevelDeclaration(arg0)),
+ 'validateModifiersForTopLevelFunction_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForTopLevelFunction(arg0)),
+ 'validateModifiersForTopLevelVariable_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForTopLevelVariable(arg0)),
+ 'validateModifiersForTypedef_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._validateModifiersForTypedef(arg0)),
+};
+<<<
+Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
+ 'parseCompilationUnit_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseCompilationUnit(arg0)),
+ 'parseDirectives_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseDirectives(arg0)),
+ 'parseExpression_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseExpression(arg0)),
+ 'parseStatement_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseStatement(arg0)),
+ 'parseStatements_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseStatements(arg0)),
+ 'parseAnnotation_0':
+ new MethodTrampoline(0, (Parser target) => target.parseAnnotation()),
+ 'parseArgument_0':
+ new MethodTrampoline(0, (Parser target) => target.parseArgument()),
+ 'parseArgumentList_0':
+ new MethodTrampoline(0, (Parser target) => target.parseArgumentList()),
+ 'parseBitwiseOrExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseBitwiseOrExpression()),
+ 'parseBlock_0':
+ new MethodTrampoline(0, (Parser target) => target.parseBlock()),
+ 'parseClassMember_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target.parseClassMember(arg0)),
+ 'parseCompilationUnit_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseCompilationUnit2()),
+ 'parseConditionalExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseConditionalExpression()),
+ 'parseConstructorName_0':
+ new MethodTrampoline(0, (Parser target) => target.parseConstructorName()),
+ 'parseExpression_0':
+ new MethodTrampoline(0, (Parser target) => target.parseExpression2()),
+ 'parseExpressionWithoutCascade_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseExpressionWithoutCascade()),
+ 'parseExtendsClause_0':
+ new MethodTrampoline(0, (Parser target) => target.parseExtendsClause()),
+ 'parseFormalParameterList_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseFormalParameterList()),
+ 'parseFunctionExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseFunctionExpression()),
+ 'parseImplementsClause_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseImplementsClause()),
+ 'parseLabel_0':
+ new MethodTrampoline(0, (Parser target) => target.parseLabel()),
+ 'parseLibraryIdentifier_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseLibraryIdentifier()),
+ 'parseLogicalOrExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseLogicalOrExpression()),
+ 'parseMapLiteralEntry_0':
+ new MethodTrampoline(0, (Parser target) => target.parseMapLiteralEntry()),
+ 'parseNormalFormalParameter_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseNormalFormalParameter()),
+ 'parsePrefixedIdentifier_0': new MethodTrampoline(
+ 0, (Parser target) => target.parsePrefixedIdentifier()),
+ 'parseReturnType_0':
+ new MethodTrampoline(0, (Parser target) => target.parseReturnType()),
+ 'parseSimpleIdentifier_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseSimpleIdentifier()),
+ 'parseStatement_0':
+ new MethodTrampoline(0, (Parser target) => target.parseStatement2()),
+ 'parseStringLiteral_0':
+ new MethodTrampoline(0, (Parser target) => target.parseStringLiteral()),
+ 'parseTypeArgumentList_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseTypeArgumentList()),
+ 'parseTypeName_0':
+ new MethodTrampoline(0, (Parser target) => target.parseTypeName()),
+ 'parseTypeParameter_0':
+ new MethodTrampoline(0, (Parser target) => target.parseTypeParameter()),
+ 'parseTypeParameterList_0': new MethodTrampoline(
+ 0, (Parser target) => target.parseTypeParameterList()),
+ 'parseWithClause_0':
+ new MethodTrampoline(0, (Parser target) => target.parseWithClause()),
+ 'advance_0': new MethodTrampoline(0, (Parser target) => target._advance()),
+ 'appendScalarValue_5': new MethodTrampoline(
+ 5,
+ (Parser target, arg0, arg1, arg2, arg3, arg4) =>
+ target._appendScalarValue(arg0, arg1, arg2, arg3, arg4)),
+ 'computeStringValue_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._computeStringValue(arg0, arg1, arg2)),
+ 'convertToFunctionDeclaration_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._convertToFunctionDeclaration(arg0)),
+ 'couldBeStartOfCompilationUnitMember_0': new MethodTrampoline(
+ 0, (Parser target) => target._couldBeStartOfCompilationUnitMember()),
+ 'createSyntheticIdentifier_0': new MethodTrampoline(
+ 0, (Parser target) => target._createSyntheticIdentifier()),
+ 'createSyntheticKeyword_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._createSyntheticKeyword(arg0)),
+ 'createSyntheticStringLiteral_0': new MethodTrampoline(
+ 0, (Parser target) => target._createSyntheticStringLiteral()),
+ 'createSyntheticToken_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._createSyntheticToken(arg0)),
+ 'ensureAssignable_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._ensureAssignable(arg0)),
+ 'expect_1':
+ new MethodTrampoline(1, (Parser target, arg0) => target._expect(arg0)),
+ 'expectGt_0': new MethodTrampoline(0, (Parser target) => target._expectGt()),
+ 'expectKeyword_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._expectKeyword(arg0)),
+ 'expectSemicolon_0':
+ new MethodTrampoline(0, (Parser target) => target._expectSemicolon()),
+ 'findRange_2': new MethodTrampoline(
+ 2, (Parser target, arg0, arg1) => target._findRange(arg0, arg1)),
+ 'getCodeBlockRanges_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._getCodeBlockRanges(arg0)),
+ 'getEndToken_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._getEndToken(arg0)),
+ 'injectToken_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._injectToken(arg0)),
+ 'isFunctionDeclaration_0': new MethodTrampoline(
+ 0, (Parser target) => target._isFunctionDeclaration()),
+ 'isFunctionExpression_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._isFunctionExpression(arg0)),
+ 'isHexDigit_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._isHexDigit(arg0)),
+ 'isInitializedVariableDeclaration_0': new MethodTrampoline(
+ 0, (Parser target) => target._isInitializedVariableDeclaration()),
+ 'isLinkText_2': new MethodTrampoline(
+ 2, (Parser target, arg0, arg1) => target._isLinkText(arg0, arg1)),
+ 'isOperator_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._isOperator(arg0)),
+ 'isSwitchMember_0':
+ new MethodTrampoline(0, (Parser target) => target._isSwitchMember()),
+ 'isTypedIdentifier_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._isTypedIdentifier(arg0)),
+ 'lockErrorListener_0':
+ new MethodTrampoline(0, (Parser target) => target._lockErrorListener()),
+ 'matches_1':
+ new MethodTrampoline(1, (Parser target, arg0) => target._matches(arg0)),
+ 'matchesGt_0':
+ new MethodTrampoline(0, (Parser target) => target._matchesGt()),
+ 'matchesIdentifier_0':
+ new MethodTrampoline(0, (Parser target) => target._matchesIdentifier()),
+ 'matchesKeyword_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._matchesKeyword(arg0)),
+ 'matchesString_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._matchesString(arg0)),
+ 'optional_1':
+ new MethodTrampoline(1, (Parser target, arg0) => target._optional(arg0)),
+ 'parseAdditiveExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseAdditiveExpression()),
+ 'parseAssertStatement_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseAssertStatement()),
+ 'parseAssignableExpression_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseAssignableExpression(arg0)),
+ 'parseAssignableSelector_2': new MethodTrampoline(
+ 2,
+ (Parser target, arg0, arg1) =>
+ target._parseAssignableSelector(arg0, arg1)),
+ 'parseAwaitExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseAwaitExpression()),
+ 'parseBitwiseAndExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseBitwiseAndExpression()),
+ 'parseBitwiseXorExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseBitwiseXorExpression()),
+ 'parseBreakStatement_0':
+ new MethodTrampoline(0, (Parser target) => target._parseBreakStatement()),
+ 'parseCascadeSection_0':
+ new MethodTrampoline(0, (Parser target) => target._parseCascadeSection()),
+ 'parseClassDeclaration_2': new MethodTrampoline(2,
+ (Parser target, arg0, arg1) => target._parseClassDeclaration(arg0, arg1)),
+ 'parseClassMembers_2': new MethodTrampoline(
+ 2, (Parser target, arg0, arg1) => target._parseClassMembers(arg0, arg1)),
+ 'parseClassTypeAlias_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._parseClassTypeAlias(arg0, arg1, arg2)),
+ 'parseCombinator_0':
+ new MethodTrampoline(0, (Parser target) => target.parseCombinator()),
+ 'parseCombinators_0':
+ new MethodTrampoline(0, (Parser target) => target._parseCombinators()),
+ 'parseCommentAndMetadata_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseCommentAndMetadata()),
+ 'parseCommentReference_2': new MethodTrampoline(2,
+ (Parser target, arg0, arg1) => target._parseCommentReference(arg0, arg1)),
+ 'parseCommentReferences_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseCommentReferences(arg0)),
+ 'parseCompilationUnitMember_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseCompilationUnitMember(arg0)),
+ 'parseConstExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseConstExpression()),
+ 'parseConstructor_8': new MethodTrampoline(
+ 8,
+ (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => target
+ ._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
+ 'parseConstructorFieldInitializer_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseConstructorFieldInitializer()),
+ 'parseContinueStatement_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseContinueStatement()),
+ 'parseDirective_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseDirective(arg0)),
+ 'parseDirectives_0':
+ new MethodTrampoline(0, (Parser target) => target._parseDirectives()),
+ 'parseDocumentationComment_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseDocumentationComment()),
+ 'parseDoStatement_0':
+ new MethodTrampoline(0, (Parser target) => target._parseDoStatement()),
+ 'parseEmptyStatement_0':
+ new MethodTrampoline(0, (Parser target) => target._parseEmptyStatement()),
+ 'parseEnumConstantDeclaration_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseEnumConstantDeclaration()),
+ 'parseEnumDeclaration_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseEnumDeclaration(arg0)),
+ 'parseEqualityExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseEqualityExpression()),
+ 'parseExportDirective_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseExportDirective(arg0)),
+ 'parseExpressionList_0':
+ new MethodTrampoline(0, (Parser target) => target._parseExpressionList()),
+ 'parseFinalConstVarOrType_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseFinalConstVarOrType(arg0)),
+ 'parseFormalParameter_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseFormalParameter(arg0)),
+ 'parseForStatement_0':
+ new MethodTrampoline(0, (Parser target) => target._parseForStatement()),
+ 'parseFunctionBody_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._parseFunctionBody(arg0, arg1, arg2)),
+ 'parseFunctionDeclaration_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._parseFunctionDeclaration(arg0, arg1, arg2)),
+ 'parseFunctionDeclarationStatement_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseFunctionDeclarationStatement()),
+ 'parseFunctionDeclarationStatementAfterReturnType_2': new MethodTrampoline(
+ 2,
+ (Parser target, arg0, arg1) =>
+ target._parseFunctionDeclarationStatementAfterReturnType(arg0, arg1)),
+ 'parseFunctionTypeAlias_2': new MethodTrampoline(
+ 2,
+ (Parser target, arg0, arg1) =>
+ target._parseFunctionTypeAlias(arg0, arg1)),
+ 'parseGetter_4': new MethodTrampoline(
+ 4,
+ (Parser target, arg0, arg1, arg2, arg3) =>
+ target._parseGetter(arg0, arg1, arg2, arg3)),
+ 'parseIdentifierList_0':
+ new MethodTrampoline(0, (Parser target) => target._parseIdentifierList()),
+ 'parseIfStatement_0':
+ new MethodTrampoline(0, (Parser target) => target._parseIfStatement()),
+ 'parseImportDirective_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseImportDirective(arg0)),
+ 'parseInitializedIdentifierList_4': new MethodTrampoline(
+ 4,
+ (Parser target, arg0, arg1, arg2, arg3) =>
+ target._parseInitializedIdentifierList(arg0, arg1, arg2, arg3)),
+ 'parseInstanceCreationExpression_1': new MethodTrampoline(1,
+ (Parser target, arg0) => target._parseInstanceCreationExpression(arg0)),
+ 'parseLibraryDirective_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseLibraryDirective(arg0)),
+ 'parseLibraryName_2': new MethodTrampoline(
+ 2, (Parser target, arg0, arg1) => target._parseLibraryName(arg0, arg1)),
+ 'parseListLiteral_2': new MethodTrampoline(
+ 2, (Parser target, arg0, arg1) => target._parseListLiteral(arg0, arg1)),
+ 'parseListOrMapLiteral_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseListOrMapLiteral(arg0)),
+ 'parseLogicalAndExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseLogicalAndExpression()),
+ 'parseMapLiteral_2': new MethodTrampoline(
+ 2, (Parser target, arg0, arg1) => target._parseMapLiteral(arg0, arg1)),
+ 'parseMethodDeclarationAfterParameters_7': new MethodTrampoline(
+ 7,
+ (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6) =>
+ target._parseMethodDeclarationAfterParameters(
+ arg0, arg1, arg2, arg3, arg4, arg5, arg6)),
+ 'parseMethodDeclarationAfterReturnType_4': new MethodTrampoline(
+ 4,
+ (Parser target, arg0, arg1, arg2, arg3) => target
+ ._parseMethodDeclarationAfterReturnType(arg0, arg1, arg2, arg3)),
+ 'parseModifiers_0':
+ new MethodTrampoline(0, (Parser target) => target._parseModifiers()),
+ 'parseMultiplicativeExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseMultiplicativeExpression()),
+ 'parseNativeClause_0':
+ new MethodTrampoline(0, (Parser target) => target._parseNativeClause()),
+ 'parseNewExpression_0':
+ new MethodTrampoline(0, (Parser target) => target._parseNewExpression()),
+ 'parseNonLabeledStatement_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseNonLabeledStatement()),
+ 'parseOperator_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._parseOperator(arg0, arg1, arg2)),
+ 'parseOptionalReturnType_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseOptionalReturnType()),
+ 'parsePartDirective_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parsePartDirective(arg0)),
+ 'parsePostfixExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parsePostfixExpression()),
+ 'parsePrimaryExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parsePrimaryExpression()),
+ 'parseRedirectingConstructorInvocation_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseRedirectingConstructorInvocation()),
+ 'parseRelationalExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseRelationalExpression()),
+ 'parseRethrowExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseRethrowExpression()),
+ 'parseReturnStatement_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseReturnStatement()),
+ 'parseSetter_4': new MethodTrampoline(
+ 4,
+ (Parser target, arg0, arg1, arg2, arg3) =>
+ target._parseSetter(arg0, arg1, arg2, arg3)),
+ 'parseShiftExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseShiftExpression()),
+ 'parseStatementList_0':
+ new MethodTrampoline(0, (Parser target) => target._parseStatementList()),
+ 'parseStringInterpolation_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseStringInterpolation(arg0)),
+ 'parseSuperConstructorInvocation_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseSuperConstructorInvocation()),
+ 'parseSwitchStatement_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseSwitchStatement()),
+ 'parseSymbolLiteral_0':
+ new MethodTrampoline(0, (Parser target) => target._parseSymbolLiteral()),
+ 'parseThrowExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseThrowExpression()),
+ 'parseThrowExpressionWithoutCascade_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseThrowExpressionWithoutCascade()),
+ 'parseTryStatement_0':
+ new MethodTrampoline(0, (Parser target) => target._parseTryStatement()),
+ 'parseTypeAlias_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._parseTypeAlias(arg0)),
+ 'parseUnaryExpression_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseUnaryExpression()),
+ 'parseVariableDeclaration_0': new MethodTrampoline(
+ 0, (Parser target) => target._parseVariableDeclaration()),
+ 'parseVariableDeclarationListAfterMetadata_1': new MethodTrampoline(
+ 1,
+ (Parser target, arg0) =>
+ target._parseVariableDeclarationListAfterMetadata(arg0)),
+ 'parseVariableDeclarationListAfterType_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._parseVariableDeclarationListAfterType(arg0, arg1, arg2)),
+ 'parseVariableDeclarationStatementAfterMetadata_1': new MethodTrampoline(
+ 1,
+ (Parser target, arg0) =>
+ target._parseVariableDeclarationStatementAfterMetadata(arg0)),
+ 'parseVariableDeclarationStatementAfterType_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._parseVariableDeclarationStatementAfterType(arg0, arg1, arg2)),
+ 'parseWhileStatement_0':
+ new MethodTrampoline(0, (Parser target) => target._parseWhileStatement()),
+ 'parseYieldStatement_0':
+ new MethodTrampoline(0, (Parser target) => target._parseYieldStatement()),
+ 'peek_0': new MethodTrampoline(0, (Parser target) => target._peek()),
+ 'peekAt_1':
+ new MethodTrampoline(1, (Parser target, arg0) => target._peekAt(arg0)),
+ 'reportError_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._reportError(arg0)),
+ 'reportErrorForCurrentToken_2': new MethodTrampoline(
+ 2,
+ (Parser target, arg0, arg1) =>
+ target._reportErrorForCurrentToken(arg0, arg1)),
+ 'reportErrorForNode_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._reportErrorForNode(arg0, arg1, arg2)),
+ 'reportErrorForToken_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._reportErrorForToken(arg0, arg1, arg2)),
+ 'skipBlock_0':
+ new MethodTrampoline(0, (Parser target) => target._skipBlock()),
+ 'skipFinalConstVarOrType_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipFinalConstVarOrType(arg0)),
+ 'skipFormalParameterList_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipFormalParameterList(arg0)),
+ 'skipPastMatchingToken_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipPastMatchingToken(arg0)),
+ 'skipPrefixedIdentifier_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipPrefixedIdentifier(arg0)),
+ 'skipReturnType_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipReturnType(arg0)),
+ 'skipSimpleIdentifier_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipSimpleIdentifier(arg0)),
+ 'skipStringInterpolation_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipStringInterpolation(arg0)),
+ 'skipStringLiteral_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipStringLiteral(arg0)),
+ 'skipTypeArgumentList_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipTypeArgumentList(arg0)),
+ 'skipTypeName_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipTypeName(arg0)),
+ 'skipTypeParameterList_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._skipTypeParameterList(arg0)),
+ 'tokenMatches_2': new MethodTrampoline(
+ 2, (Parser target, arg0, arg1) => target._tokenMatches(arg0, arg1)),
+ 'tokenMatchesIdentifier_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._tokenMatchesIdentifier(arg0)),
+ 'tokenMatchesKeyword_2': new MethodTrampoline(2,
+ (Parser target, arg0, arg1) => target._tokenMatchesKeyword(arg0, arg1)),
+ 'tokenMatchesString_2': new MethodTrampoline(
+ 2, (Parser target, arg0, arg1) => target._tokenMatchesString(arg0, arg1)),
+ 'translateCharacter_3': new MethodTrampoline(
+ 3,
+ (Parser target, arg0, arg1, arg2) =>
+ target._translateCharacter(arg0, arg1, arg2)),
+ 'unlockErrorListener_0':
+ new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()),
+ 'validateFormalParameterList_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._validateFormalParameterList(arg0)),
+ 'validateModifiersForClass_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._validateModifiersForClass(arg0)),
+ 'validateModifiersForConstructor_1': new MethodTrampoline(1,
+ (Parser target, arg0) => target._validateModifiersForConstructor(arg0)),
+ 'validateModifiersForEnum_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._validateModifiersForEnum(arg0)),
+ 'validateModifiersForField_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._validateModifiersForField(arg0)),
+ 'validateModifiersForFunctionDeclarationStatement_1': new MethodTrampoline(
+ 1,
+ (Parser target, arg0) =>
+ target._validateModifiersForFunctionDeclarationStatement(arg0)),
+ 'validateModifiersForGetterOrSetterOrMethod_1': new MethodTrampoline(
+ 1,
+ (Parser target, arg0) =>
+ target._validateModifiersForGetterOrSetterOrMethod(arg0)),
+ 'validateModifiersForOperator_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._validateModifiersForOperator(arg0)),
+ 'validateModifiersForTopLevelDeclaration_1': new MethodTrampoline(
+ 1,
+ (Parser target, arg0) =>
+ target._validateModifiersForTopLevelDeclaration(arg0)),
+ 'validateModifiersForTopLevelFunction_1': new MethodTrampoline(
+ 1,
+ (Parser target, arg0) =>
+ target._validateModifiersForTopLevelFunction(arg0)),
+ 'validateModifiersForTopLevelVariable_1': new MethodTrampoline(
+ 1,
+ (Parser target, arg0) =>
+ target._validateModifiersForTopLevelVariable(arg0)),
+ 'validateModifiersForTypedef_1': new MethodTrampoline(
+ 1, (Parser target, arg0) => target._validateModifiersForTypedef(arg0)),
+};
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/0400/0475.unit b/packages/dart_style/test/regression/0400/0475.unit
new file mode 100644
index 0000000..0240a3d
--- /dev/null
+++ b/packages/dart_style/test/regression/0400/0475.unit
@@ -0,0 +1,12 @@
+>>>
+class ListResultDescriptor {
+ factory ListResultDescriptor(String name, List<E> defaultValue,
+ {ResultCachingPolicy<
+ List<E>> cachingPolicy}) = ListResultDescriptorImpl<E>;
+}
+<<<
+class ListResultDescriptor {
+ factory ListResultDescriptor(String name, List<E> defaultValue,
+ {ResultCachingPolicy<List<E>> cachingPolicy}) =
+ ListResultDescriptorImpl<E>;
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/other/analysis_server.unit b/packages/dart_style/test/regression/other/analysis_server.unit
index 9e662a7..0c03cfa 100644
--- a/packages/dart_style/test/regression/other/analysis_server.unit
+++ b/packages/dart_style/test/regression/other/analysis_server.unit
@@ -246,4 +246,16 @@
build(body);
return irBuilder.makeConstructorDefinition(const [], const []);
});
- }
\ No newline at end of file
+ }
+>>> (indent 4)
+ Element expectedElement = (declarations[0] as TopLevelVariableDeclaration)
+ .variables
+ .variables[0]
+ .name
+ .staticElement;
+<<<
+ Element expectedElement = (declarations[0] as TopLevelVariableDeclaration)
+ .variables
+ .variables[0]
+ .name
+ .staticElement;
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/other/angular.unit b/packages/dart_style/test/regression/other/angular.unit
new file mode 100644
index 0000000..add59be
--- /dev/null
+++ b/packages/dart_style/test/regression/other/angular.unit
@@ -0,0 +1,45 @@
+>>> (indent 4)
+ main() {
+ it('should expose traverse locals', () {
+ expect(parser('a.b').bind({
+ 'a': {'b': 6}
+ }, ContextLocals.wrapper)({
+ 'a': {'b': 1}
+ }))
+ .toEqual(1);
+ });
+ }
+<<<
+ main() {
+ it('should expose traverse locals', () {
+ expect(parser('a.b').bind({
+ 'a': {'b': 6}
+ }, ContextLocals.wrapper)({
+ 'a': {'b': 1}
+ })).toEqual(1);
+ });
+ }
+>>>
+@Component(
+ selector: 'io-controller',
+ template: r'<content></content>',
+ map: const {
+ 'attr': '@attr',
+ 'expr': '<=>expr',
+ 'once': '=>!exprOnce',
+ 'ondone': '&onDone',
+ 'on-optional': '&onOptional'
+ })
+class IoControllerComponent implements ScopeAware {}
+<<<
+@Component(
+ selector: 'io-controller',
+ template: r'<content></content>',
+ map: const {
+ 'attr': '@attr',
+ 'expr': '<=>expr',
+ 'once': '=>!exprOnce',
+ 'ondone': '&onDone',
+ 'on-optional': '&onOptional'
+ })
+class IoControllerComponent implements ScopeAware {}
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/other/chains.stmt b/packages/dart_style/test/regression/other/chains.stmt
new file mode 100644
index 0000000..85dc56c
--- /dev/null
+++ b/packages/dart_style/test/regression/other/chains.stmt
@@ -0,0 +1,35 @@
+>>> (indent 2)
+ document.onChange.listen(expectAsync((Delta delta) {
+ observedDeltas.add(delta);
+ if (++observedDeltaCount == 3) applyToNewDocument();
+ }, count: 3));
+<<<
+ document.onChange.listen(expectAsync((Delta delta) {
+ observedDeltas.add(delta);
+ if (++observedDeltaCount == 3) applyToNewDocument();
+ }, count: 3));
+>>> (indent 4)
+ return queryWith(fds, otype, whereClause, whereValues).toList()
+ .catchError((ex, st) {
+ _logger.warning(
+ "Failed loadAllWith($fields, $whereClause, $whereValues)", ex, st);
+ return new Future.error(ex, st);
+ }).then((List<Row> rows) {
+ ;
+ });
+<<<
+ return queryWith(fds, otype, whereClause, whereValues)
+ .toList()
+ .catchError((ex, st) {
+ _logger.warning(
+ "Failed loadAllWith($fields, $whereClause, $whereValues)", ex, st);
+ return new Future.error(ex, st);
+ }).then((List<Row> rows) {
+ ;
+ });
+>>> (indent 4)
+ return _coll.update({"_id": doc["_id"]}, doc, upsert: true)
+ .then((_) => doc);
+<<< TODO(rnystrom): Should split before .then() but map literal splits chain.
+ return _coll
+ .update({"_id": doc["_id"]}, doc, upsert: true).then((_) => doc);
\ No newline at end of file
diff --git a/packages/dart_style/test/regression/other/misc.unit b/packages/dart_style/test/regression/other/misc.unit
new file mode 100644
index 0000000..e6175d7
--- /dev/null
+++ b/packages/dart_style/test/regression/other/misc.unit
@@ -0,0 +1,18 @@
+>>>
+main() {
+ addTask(
+ "publish",
+ createProcessTask("pub",
+ args: ["publish", "-f"], description: "Publishes a New Version"),
+ dependencies: [
+ "version"
+ ]);
+}
+<<<
+main() {
+ addTask(
+ "publish",
+ createProcessTask("pub",
+ args: ["publish", "-f"], description: "Publishes a New Version"),
+ dependencies: ["version"]);
+}
\ No newline at end of file
diff --git a/packages/dart_style/test/selections/._selections.stmt b/packages/dart_style/test/selections/._selections.stmt
deleted file mode 100644
index 9f912de..0000000
--- a/packages/dart_style/test/selections/._selections.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/splitting/._arrows.unit b/packages/dart_style/test/splitting/._arrows.unit
deleted file mode 100644
index fa01847..0000000
--- a/packages/dart_style/test/splitting/._arrows.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/splitting/._enums.unit b/packages/dart_style/test/splitting/._enums.unit
deleted file mode 100644
index 3e2969b..0000000
--- a/packages/dart_style/test/splitting/._enums.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/splitting/._members.unit b/packages/dart_style/test/splitting/._members.unit
deleted file mode 100644
index 473c9f3..0000000
--- a/packages/dart_style/test/splitting/._members.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/splitting/._parameters.unit b/packages/dart_style/test/splitting/._parameters.unit
deleted file mode 100644
index 51b5117..0000000
--- a/packages/dart_style/test/splitting/._parameters.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/splitting/._statements.stmt b/packages/dart_style/test/splitting/._statements.stmt
deleted file mode 100644
index 312e399..0000000
--- a/packages/dart_style/test/splitting/._statements.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/splitting/._strings.stmt b/packages/dart_style/test/splitting/._strings.stmt
deleted file mode 100644
index e65594f..0000000
--- a/packages/dart_style/test/splitting/._strings.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/splitting/expressions.stmt b/packages/dart_style/test/splitting/expressions.stmt
index 8ac19ef..9136aac 100644
--- a/packages/dart_style/test/splitting/expressions.stmt
+++ b/packages/dart_style/test/splitting/expressions.stmt
@@ -133,4 +133,9 @@
identifier &&
identifier &&
identifier &&
- identifier;
\ No newline at end of file
+ identifier;
+>>> "." in named constructor
+new VeryLongClassName.veryLongNamedConstructor();
+<<<
+new VeryLongClassName
+ .veryLongNamedConstructor();
\ No newline at end of file
diff --git a/packages/dart_style/test/splitting/invocations.stmt b/packages/dart_style/test/splitting/invocations.stmt
index 3d2a503..952efe9 100644
--- a/packages/dart_style/test/splitting/invocations.stmt
+++ b/packages/dart_style/test/splitting/invocations.stmt
@@ -17,7 +17,7 @@
<<<
return call({'type': type, 'id': id})
.then(deserializeAsset);
->>> allows chained calls on one line with multi-line last argument list
+>>> trailing functions in a chain do not force it to split
compiler
.run(script)
.then((_) {
@@ -27,26 +27,28 @@
compiler.run(script).then((_) {
body;
});
->>> allow inline chains before and after a hard newline
-compiler.a().b((_) {
- body;
-}).c().d();
+>>> a function in the middle of a chain is indented
+compiler.a().b((_) {body;}).c().d();
<<<
-compiler.a().b((_) {
- body;
-}).c().d();
->>> allow an inline chain before a hard newline but not after
-compiler.a().b((_) {
- body;
-}).somethingLong().somethingLong().somethingLong();
+compiler
+ .a()
+ .b((_) {
+ body;
+ })
+ .c()
+ .d();
+>>> a function in the middle of a chain is indented
+compiler.a().b((_) {body;}).somethingLong().somethingLong().somethingLong();
<<<
-compiler.a().b((_) {
- body;
-})
+compiler
+ .a()
+ .b((_) {
+ body;
+ })
.somethingLong()
.somethingLong()
.somethingLong();
->>> allow an inline chain after a hard newline but not before
+>>> a function in the middle of a chain is indented
compiler.somethingLong().somethingLong().somethingLong((_) {
body;
}).a().b();
@@ -55,8 +57,21 @@
.somethingLong()
.somethingLong()
.somethingLong((_) {
+ body;
+ })
+ .a()
+ .b();
+>>> one trailing call does not force function to indent
+compiler.somethingLong().somethingLong().somethingLong((_) {
body;
-}).a().b();
+}).a();
+<<<
+compiler
+ .somethingLong()
+ .somethingLong()
+ .somethingLong((_) {
+ body;
+}).a();
>>> nest calls one more than target
someVeryLongExpression = someVeryLongExpression.someLongMethod();
<<<
@@ -159,4 +174,109 @@
?.method()
.method()
?.method()
- .method();
\ No newline at end of file
+ .method();
+>>> index in property chain
+someReceiverObject.property1.property2
+ .property3[0]
+ .property4
+ .property5
+ .property6;
+<<<
+someReceiverObject
+ .property1
+ .property2
+ .property3[0]
+ .property4
+ .property5
+ .property6;
+>>> chained indexes
+someReceiverObject.property1.property2
+ .property3[argument]
+ [argument][argument]
+ .property4
+ .property5
+ .property6;
+<<<
+someReceiverObject
+ .property1
+ .property2
+ .property3[argument][argument]
+ [argument]
+ .property4
+ .property5
+ .property6;
+>>> index on method call
+someReceiverObject.property1.property2
+ .method3()[0]
+ .property4
+ .property5
+ .property6;
+<<<
+someReceiverObject.property1.property2
+ .method3()[0]
+ .property4
+ .property5
+ .property6;
+>>> target splits more deeply than method chain
+someTargetFunction(argument, argument, argument).method().method();
+<<<
+someTargetFunction(
+ argument, argument, argument)
+ .method()
+ .method();
+>>> splitting the target forces methods to split
+someVeryLongTargetFunction(argument, argument).one().two();
+<<<
+someVeryLongTargetFunction(
+ argument, argument)
+ .one()
+ .two();
+>>> target splits more deeply than property chain
+someTargetFunction(argument, argument, argument).property.property;
+<<<
+someTargetFunction(
+ argument, argument, argument)
+ .property
+ .property;
+>>> splitting the target forces methods to split
+someVeryLongTargetFunction(argument, argument).one.two;
+<<<
+someVeryLongTargetFunction(
+ argument, argument)
+ .one
+ .two;
+>>> do not split on "." when target is list
+[element, element, element, element, element].someLongMethod();
+<<<
+[
+ element,
+ element,
+ element,
+ element,
+ element
+].someLongMethod();
+>>> do not split on "." when target is map
+{"key": "value", "another": "another value"}.someLongMethod();
+<<<
+{
+ "key": "value",
+ "another": "another value"
+}.someLongMethod();
+>>> do not split on "." when target is function literal passed to method
+method(() {;}).someLongMethod();
+<<<
+method(() {
+ ;
+}).someLongMethod();
+>>> do not split on "." when target is function literal passed to constructor
+new Foo(() {;}).someLongMethod();
+<<<
+new Foo(() {
+ ;
+}).someLongMethod();
+>>> do not split on "." when target is function literal passed to function
+(function)(() {;}).someLongMethod();
+<<<
+(function)(() {
+ ;
+}).someLongMethod();
\ No newline at end of file
diff --git a/packages/dart_style/test/splitting/list_arguments.stmt b/packages/dart_style/test/splitting/list_arguments.stmt
index 8a2b17a..ac10cc1 100644
--- a/packages/dart_style/test/splitting/list_arguments.stmt
+++ b/packages/dart_style/test/splitting/list_arguments.stmt
@@ -273,10 +273,35 @@
element,
element
]);
->>> nest trailing named if there are non-body named
-longFunctionName(a: argument, b: argument,
+>>> allow leading non-collection to not split
+longFunctionName(a: arg, b: arg,
c: [element, element, element, element], d: [element, element, element, element]);
<<<
+longFunctionName(a: arg, b: arg, c: [
+ element,
+ element,
+ element,
+ element
+], d: [
+ element,
+ element,
+ element,
+ element
+]);
+>>> don't allow splitting before first arg while splitting collections
+longFunctionName(
+ a: argument, b: argument, c: [
+ element,
+ element,
+ element,
+ element
+], d: [
+ element,
+ element,
+ element,
+ element
+]);
+<<<
longFunctionName(
a: argument,
b: argument,
@@ -292,6 +317,41 @@
element,
element
]);
+>>> trailing named arguments that do not split
+longFunctionName(a: [element, element, element, element],
+b: [element, element, element, element], c: argument, d: argument);
+<<<
+longFunctionName(a: [
+ element,
+ element,
+ element,
+ element
+], b: [
+ element,
+ element,
+ element,
+ element
+], c: argument, d: argument);
+>>> trailing named arguments that do split
+longFunctionName(a: [element, element, element, element],
+b: [element, element, element, element], c: argument, d: argument, e: argument);
+<<<
+longFunctionName(
+ a: [
+ element,
+ element,
+ element,
+ element
+ ],
+ b: [
+ element,
+ element,
+ element,
+ element
+ ],
+ c: argument,
+ d: argument,
+ e: argument);
>>> leading named arguments
longFunctionName(name1: [element, element], name2: [element, element], name3: argument, name4: argument);
<<<
@@ -339,4 +399,39 @@
element,
element,
element
- ]));
\ No newline at end of file
+ ]));
+>>> leading positional collections indent if their args split
+function([[element]],
+[[element]], argument, // comment
+argument);
+<<<
+function(
+ [
+ [element]
+ ],
+ [
+ [element]
+ ],
+ argument, // comment
+ argument);
+>>> trailing positional collections indent if their args split
+function(argument, // comment
+argument, [[element]], [[element]]);
+<<<
+function(
+ argument, // comment
+ argument,
+ [
+ [element]
+ ],
+ [
+ [element]
+ ]);
+>>> comment before collection
+function(argument, // comment
+[[element]]);
+<<<
+function(argument, // comment
+ [
+ [element]
+ ]);
\ No newline at end of file
diff --git a/packages/dart_style/test/splitting/loops.stmt b/packages/dart_style/test/splitting/loops.stmt
index 800e01d..cb62b75 100644
--- a/packages/dart_style/test/splitting/loops.stmt
+++ b/packages/dart_style/test/splitting/loops.stmt
@@ -83,4 +83,31 @@
longCondition(expression);
a += 1, b += 1) {
;
-}
\ No newline at end of file
+}
+>>> single line for without curlies
+for (i = 0; i < 10; i++) something(i);
+<<<
+for (i = 0; i < 10; i++) something(i);
+>>> multi-line for without curlies
+for (i = 0; i < 10; i++) somethingLonger(i);
+<<<
+for (i = 0; i < 10; i++)
+ somethingLonger(i);
+>>> single line for-in without curlies
+for (i in sequence) something(i);
+<<<
+for (i in sequence) something(i);
+>>> multi-line for-in without curlies
+for (i in sequence) somethingMuchLonger(i);
+<<<
+for (i in sequence)
+ somethingMuchLonger(i);
+>>> single line while without curlies
+while (condition) something(i);
+<<<
+while (condition) something(i);
+>>> multi-line while without curlies
+while (condition) somethingMuchLonger(i);
+<<<
+while (condition)
+ somethingMuchLonger(i);
\ No newline at end of file
diff --git a/packages/dart_style/test/splitting/members.unit b/packages/dart_style/test/splitting/members.unit
index 26ca129..cb59d75 100644
--- a/packages/dart_style/test/splitting/members.unit
+++ b/packages/dart_style/test/splitting/members.unit
@@ -7,4 +7,22 @@
class Foo {
Stream methodName(AssetId id) =>
methodBodyHereItIs;
+}
+>>> can split on getter
+class Foo {
+ VeryLongTypeAnnotation get veryLongGetter => null;
+}
+<<<
+class Foo {
+ VeryLongTypeAnnotation
+ get veryLongGetter => null;
+}
+>>> can split on setter
+class Foo {
+ VeryLongTypeAnnotation set veryLongSetter(v) {}
+}
+<<<
+class Foo {
+ VeryLongTypeAnnotation
+ set veryLongSetter(v) {}
}
\ No newline at end of file
diff --git a/packages/dart_style/test/splitting/mixed.stmt b/packages/dart_style/test/splitting/mixed.stmt
index 0ef02fd..33cdba4 100644
--- a/packages/dart_style/test/splitting/mixed.stmt
+++ b/packages/dart_style/test/splitting/mixed.stmt
@@ -54,8 +54,10 @@
.method(argument)
.method(argument)
.method(() {
- body;
- }).another().another()
+ body;
+ })
+ .another()
+ .another()
];
>>> function inside an argument list
function(argument, obj.method(argument).method(argument).method(() {body;}).another().another());
@@ -66,8 +68,10 @@
.method(argument)
.method(argument)
.method(() {
- body;
- }).another().another());
+ body;
+ })
+ .another()
+ .another());
>>> unnested function inside nested expression
function(argument, function(() {;}));
<<<
@@ -91,14 +95,15 @@
receiver.firstMethod().next((parameter) => longIdentifier == veryLongIdentifier);
<<<
receiver.firstMethod().next(
- (parameter) => longIdentifier ==
+ (parameter) =>
+ longIdentifier ==
veryLongIdentifier);
>>> wrap after =>
receiver.firstMethod().next(() => veryveryveryverylongIdentifier == veryLongIdentifier);
<<<
receiver.firstMethod().next(() =>
veryveryveryverylongIdentifier ==
- veryLongIdentifier);
+ veryLongIdentifier);
>>> wrap at nested binary operator
receiver.firstMethod().next(longIdentifier == veryLongIdentifier);
<<<
@@ -193,4 +198,16 @@
<<<
longIdentifier +
(longIdentifier ? 0 : 1) ==
- identifier;
\ No newline at end of file
+ identifier;
+>>> normal indent before unsplit binary operators in => body
+veryLongFunction() => extremelyLongArgument + argument;
+<<<
+veryLongFunction() =>
+ extremelyLongArgument + argument;
+>>> no extra indent before binary operators in => body
+veryLongFunction() => longArgument + longArgument + longArgument;
+<<<
+veryLongFunction() =>
+ longArgument +
+ longArgument +
+ longArgument;
\ No newline at end of file
diff --git a/packages/dart_style/test/utils.dart b/packages/dart_style/test/utils.dart
index 2c555d0..a2bacc5 100644
--- a/packages/dart_style/test/utils.dart
+++ b/packages/dart_style/test/utils.dart
@@ -5,6 +5,7 @@
library dart_style.test.utils;
import 'dart:io';
+import 'dart:mirrors';
import 'package:path/path.dart' as p;
import 'package:scheduled_test/descriptor.dart' as d;
@@ -18,7 +19,12 @@
ScheduledProcess runFormatter([List<String> args]) {
if (args == null) args = [];
- var formatterPath = p.join("bin", "format.dart");
+ // Locate the "test" directory. Use mirrors so that this works with the test
+ // package, which loads this suite into an isolate.
+ var testDir = p.dirname(
+ currentMirrorSystem().findLibrary(#dart_style.test.utils).uri.path);
+
+ var formatterPath = p.normalize(p.join(testDir, "../bin/format.dart"));
args.insert(0, formatterPath);
diff --git a/packages/dart_style/test/whitespace/._blocks.stmt b/packages/dart_style/test/whitespace/._blocks.stmt
deleted file mode 100644
index bc02ea0..0000000
--- a/packages/dart_style/test/whitespace/._blocks.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._constructors.unit b/packages/dart_style/test/whitespace/._constructors.unit
deleted file mode 100644
index 3e816c7..0000000
--- a/packages/dart_style/test/whitespace/._constructors.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._directives.unit b/packages/dart_style/test/whitespace/._directives.unit
deleted file mode 100644
index 812c891..0000000
--- a/packages/dart_style/test/whitespace/._directives.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._do.stmt b/packages/dart_style/test/whitespace/._do.stmt
deleted file mode 100644
index 7e57633..0000000
--- a/packages/dart_style/test/whitespace/._do.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._enums.unit b/packages/dart_style/test/whitespace/._enums.unit
deleted file mode 100644
index bd3d96f..0000000
--- a/packages/dart_style/test/whitespace/._enums.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._for.stmt b/packages/dart_style/test/whitespace/._for.stmt
deleted file mode 100644
index e045bd1..0000000
--- a/packages/dart_style/test/whitespace/._for.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._methods.unit b/packages/dart_style/test/whitespace/._methods.unit
deleted file mode 100644
index 1f0c542..0000000
--- a/packages/dart_style/test/whitespace/._methods.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._script.unit b/packages/dart_style/test/whitespace/._script.unit
deleted file mode 100644
index 5a17885..0000000
--- a/packages/dart_style/test/whitespace/._script.unit
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._switch.stmt b/packages/dart_style/test/whitespace/._switch.stmt
deleted file mode 100644
index 3fb5c99..0000000
--- a/packages/dart_style/test/whitespace/._switch.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._try.stmt b/packages/dart_style/test/whitespace/._try.stmt
deleted file mode 100644
index 4e6803c..0000000
--- a/packages/dart_style/test/whitespace/._try.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/._while.stmt b/packages/dart_style/test/whitespace/._while.stmt
deleted file mode 100644
index 8fac1ae..0000000
--- a/packages/dart_style/test/whitespace/._while.stmt
+++ /dev/null
Binary files differ
diff --git a/packages/dart_style/test/whitespace/if.stmt b/packages/dart_style/test/whitespace/if.stmt
index 0403400..56019a6 100644
--- a/packages/dart_style/test/whitespace/if.stmt
+++ b/packages/dart_style/test/whitespace/if.stmt
@@ -44,8 +44,10 @@
>>> single-expression else body
if(true)print(1);else print(0);
<<<
-if (true) print(1);
-else print(0);
+if (true)
+ print(1);
+else
+ print(0);
>>> chained else if
if (0 == 0) {
zero = 0;
@@ -61,4 +63,29 @@
zero = 1;
} else if (0 == 2) {
zero = 2;
-}
\ No newline at end of file
+}
+>>> long if without curlies
+if (condition) someLong(argument, another);
+<<<
+if (condition)
+ someLong(argument, another);
+>>> long if else without curlies
+if (condition)
+ someLong(argument, another);
+else
+ anotherLong(argument, another);
+<<<
+if (condition)
+ someLong(argument, another);
+else
+ anotherLong(argument, another);
+>>> long if long else without curlies
+if (condition)
+ someLong(argument, another);
+else
+ anotherLong(argument, another, arg);
+<<<
+if (condition)
+ someLong(argument, another);
+else
+ anotherLong(argument, another, arg);
\ No newline at end of file
diff --git a/packages/dart_style/test/whitespace/metadata.unit b/packages/dart_style/test/whitespace/metadata.unit
index 3932180..292e8c8 100644
--- a/packages/dart_style/test/whitespace/metadata.unit
+++ b/packages/dart_style/test/whitespace/metadata.unit
@@ -14,6 +14,16 @@
@deprecated
export 'dart:io';
+>>> metadata on part
+@foo part "part.dart";
+<<<
+@foo
+part "part.dart";
+>>> metadata on part of
+@foo part of bar;
+<<<
+@foo
+part of bar;
>>> force newline before types
@meta class X {}
@@ -215,4 +225,10 @@
named({@foo bar}) {}
<<<
positional([@foo bar]) {}
-named({@foo bar}) {}
\ No newline at end of file
+named({@foo bar}) {}
+>>> split between metadata and parameter indents
+function(@VeryLongMetadataAnnotation longParameter) {}
+<<<
+function(
+ @VeryLongMetadataAnnotation
+ longParameter) {}
\ No newline at end of file
diff --git a/packages/dart_style/tool/grind.dart b/packages/dart_style/tool/grind.dart
new file mode 100644
index 0000000..5a9b1c6
--- /dev/null
+++ b/packages/dart_style/tool/grind.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:grinder/grinder.dart';
+import 'package:pub_semver/pub_semver.dart';
+import 'package:yaml/yaml.dart' as yaml;
+
+/// Matches the version line in dart_style's pubspec.
+final _versionPattern = new RegExp(r"^version: .*$", multiLine: true);
+
+main(args) => grind(args);
+
+@DefaultTask()
+@Task()
+validate() async {
+ // Test it.
+ await new TestRunner().testAsync();
+
+ // Make sure it's warning clean.
+ Analyzer.analyze("bin/format.dart", fatalWarnings: true);
+
+ // Format it.
+ Dart.run("bin/format.dart", arguments: ["-w", "."]);
+}
+
+/// Gets ready to publish a new version of the package.
+///
+/// To publish a version, you need to:
+///
+/// 1. Make sure the version in the pubspec is a "-dev" number. This should
+/// already be the case since you've already landed patches that change
+/// the formatter and bumped to that as a consequence.
+///
+/// 2. Run this task:
+///
+/// pub run grinder bump
+///
+/// 3. Commit the change to a branch.
+///
+/// 4. Send it out for review:
+///
+/// git cl upload
+///
+/// 5. After the review is complete, land it:
+///
+/// git cl land
+///
+/// 6. Tag the commit:
+///
+/// git tag -a "<version>" -m "<version>"
+/// git push origin <version>
+///
+/// 7. Publish the package:
+///
+/// pub lish
+@Task()
+@Depends(validate)
+bump() async {
+ // Read the version from the pubspec.
+ var pubspecFile = getFile("pubspec.yaml");
+ var pubspec = pubspecFile.readAsStringSync();
+ var version = new Version.parse(yaml.loadYaml(pubspec)["version"]);
+
+ // Require a "-dev" version since we don't otherwise know what to bump it to.
+ if (!version.isPreRelease) throw "Cannot publish non-dev version $version.";
+
+ // Don't allow versions like "1.2.3-dev+4" because it's not clear if the
+ // user intended the "+4" to be discarded or not.
+ if (version.build.isNotEmpty) throw "Cannot publish build version $version.";
+
+ var bumped = new Version(version.major, version.minor, version.patch);
+
+ // Update the version in the pubspec.
+ pubspec = pubspec.replaceAll(_versionPattern, "version: $bumped");
+ pubspecFile.writeAsStringSync(pubspec);
+
+ // Update the version constant in bin/format.dart.
+ var binFormatFile = getFile("bin/format.dart");
+ var binFormat = binFormatFile.readAsStringSync().replaceAll(
+ new RegExp(r'const version = "[^"]+";'), 'const version = "$bumped";');
+ binFormatFile.writeAsStringSync(binFormat);
+ log("Updated version to '$bumped'.");
+}
diff --git a/packages/intl/CHANGELOG.md b/packages/intl/CHANGELOG.md
index 0469e49..6266a06 100644
--- a/packages/intl/CHANGELOG.md
+++ b/packages/intl/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 0.12.4+3
+ * update analyzer to '<0.28.0' and fixnum to '<0.11.0'
+
## 0.12.4+2
* update analyzer to '<0.27.0'
diff --git a/packages/intl/pubspec.yaml b/packages/intl/pubspec.yaml
index 671a788..33b6ac5 100644
--- a/packages/intl/pubspec.yaml
+++ b/packages/intl/pubspec.yaml
@@ -1,5 +1,5 @@
name: intl
-version: 0.12.4+2
+version: 0.12.4+3
author: Dart Team <misc@dartlang.org>
description: Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.
homepage: https://github.com/dart-lang/intl
@@ -7,12 +7,12 @@
sdk: '>=1.4.0 <2.0.0'
documentation: http://www.dartdocs.org/documentation/intl/latest
dependencies:
- analyzer: '>=0.13.2 <0.27.0'
+ analyzer: '>=0.13.2 <0.28.0'
args: '>=0.12.1 <0.14.0'
path: '>=0.9.0 <2.0.0'
petitparser: '>=1.1.3 <2.0.0'
dev_dependencies:
- fixnum: '>=0.9.0 <0.10.0'
+ fixnum: '>=0.9.0 <0.11.0'
unittest: '>=0.10.0 <0.12.0'
transformers:
- $dart2js:
diff --git a/packages/observe/.packages b/packages/observe/.packages
deleted file mode 100644
index c013549..0000000
--- a/packages/observe/.packages
+++ /dev/null
@@ -1,36 +0,0 @@
-# Generate by pub on 2015-08-19 15:03:37.242.
-# This file contains a map from Dart package names to Dart package locations.
-# Dart tools, including the Dart VM and Dart analyzer, rely on the content.
-# AUTO GENERATED - DO NOT EDIT
-analyzer:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/analyzer-0.26.1/lib/
-args:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/args-0.13.2/lib/
-barback:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/barback-0.15.2+6/lib/
-benchmark_harness:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/benchmark_harness-1.0.4/lib/
-browser:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/browser-0.10.0+2/lib/
-charcode:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.0/lib/
-chart:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/chart-1.0.8/lib/
-cli_util:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/cli_util-0.0.1+2/lib/
-code_transformers:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/code_transformers-0.2.9+1/lib/
-collection:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/collection-1.1.1/lib/
-csslib:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/csslib-0.12.1/lib/
-glob:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/glob-1.0.5/lib/
-html:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/html-0.12.1+2/lib/
-intl:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/intl-0.8.10+4/lib/
-logging:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/logging-0.11.1+1/lib/
-meta:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/meta-0.8.8/lib/
-observe:lib/
-package_config:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/package_config-0.1.1/lib/
-path:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/path-1.3.6/lib/
-plugin:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/plugin-0.1.0/lib/
-pool:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/pool-1.1.0/lib/
-smoke:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/smoke-0.3.5/lib/
-source_maps:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.1/lib/
-source_span:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/source_span-1.1.3/lib/
-stack_trace:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.4.1/lib/
-string_scanner:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/string_scanner-0.1.3+1/lib/
-unittest:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/unittest-0.11.6+1/lib/
-utf:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+2/lib/
-watcher:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7/lib/
-when:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/when-0.2.0/lib/
-which:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/which-0.1.3/lib/
-yaml:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.3/lib/
diff --git a/packages/observe/CHANGELOG.md b/packages/observe/CHANGELOG.md
index 3f8a16a..9b7a559 100644
--- a/packages/observe/CHANGELOG.md
+++ b/packages/observe/CHANGELOG.md
@@ -1,3 +1,7 @@
+#### 0.13.1+3
+
+ * Sorting an already sorted list will no longer yield new change notifications.
+
#### 0.13.1+2
* Update to analyzer '<0.27.0'
diff --git a/packages/observe/lib/src/observable_list.dart b/packages/observe/lib/src/observable_list.dart
index 912c4fc..9d09b38 100644
--- a/packages/observe/lib/src/observable_list.dart
+++ b/packages/observe/lib/src/observable_list.dart
@@ -91,7 +91,7 @@
@reflectable void operator []=(int index, E value) {
var oldValue = _list[index];
- if (hasListObservers) {
+ if (hasListObservers && oldValue != value) {
_recordChange(new ListChangeRecord(this, index, addedCount: 1,
removed: [oldValue]));
}
diff --git a/packages/observe/pubspec.yaml b/packages/observe/pubspec.yaml
index b9c5c84..29829e2 100644
--- a/packages/observe/pubspec.yaml
+++ b/packages/observe/pubspec.yaml
@@ -1,5 +1,5 @@
name: observe
-version: 0.13.1+2
+version: 0.13.1+3
author: Polymer.dart Authors <web-ui-dev@dartlang.org>
description: >
Observable properties and objects for use in template_binding.
diff --git a/packages/observe/test/observable_list_test.dart b/packages/observe/test/observable_list_test.dart
index 81ca45f..adc6e40 100644
--- a/packages/observe/test/observable_list_test.dart
+++ b/packages/observe/test/observable_list_test.dart
@@ -269,6 +269,19 @@
});
});
+ test('sort of 2 elements', () {
+ var list = toObservable([3, 1]);
+ // Dummy listener to record changes.
+ // TODO(jmesserly): should we just record changes always, to support the sync api?
+ sub = list.listChanges.listen((records) => null);
+ list.sort();
+ expect(list.deliverListChanges(), true);
+ list.sort();
+ expect(list.deliverListChanges(), false);
+ list.sort();
+ expect(list.deliverListChanges(), false);
+ });
+
test('clear', () {
list.clear();
expect(list, []);
@@ -292,3 +305,4 @@
_change(index, {removed: const [], addedCount: 0}) => new ListChangeRecord(
list, index, removed: removed, addedCount: addedCount);
+
diff --git a/packages/path/.gitignore b/packages/path/.gitignore
index 388eff0..7dbf035 100644
--- a/packages/path/.gitignore
+++ b/packages/path/.gitignore
@@ -3,6 +3,7 @@
.pub/
build/
packages
+.packages
# Or the files created by dart2js.
*.dart.js
diff --git a/packages/path/.status b/packages/path/.status
deleted file mode 100644
index bfc58fc..0000000
--- a/packages/path/.status
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-
-# Skip non-test files ending with "_test".
-packages/*: Skip
-*/packages/*: Skip
-*/*/packages/*: Skip
-*/*/*/packages/*: Skip
-*/*/*/*packages/*: Skip
-*/*/*/*/*packages/*: Skip
-
-# Only run tests from the build directory, since we don't care about the
-# difference between transformed an untransformed code.
-test/*: Skip
-
-[ $browser ]
-build/test/io_test: Fail, OK # Uses dart:io.
-
-[ $runtime == vm ]
-build/test/browser_test: Fail, OK # Uses dart:html
diff --git a/packages/path/.test_config b/packages/path/.test_config
new file mode 100644
index 0000000..412fc5c
--- /dev/null
+++ b/packages/path/.test_config
@@ -0,0 +1,3 @@
+{
+ "test_package": true
+}
\ No newline at end of file
diff --git a/packages/path/CHANGELOG.md b/packages/path/CHANGELOG.md
index 457be80..44090f6 100644
--- a/packages/path/CHANGELOG.md
+++ b/packages/path/CHANGELOG.md
@@ -1,3 +1,23 @@
+## 1.3.9
+
+* Further improve the performance of `isWithin()` when paths contain `/.`
+ sequences that aren't `/../`.
+
+## 1.3.8
+
+* Improve the performance of `isWithin()` when the paths don't contain
+ asymmetrical `.` or `..` components.
+
+* Improve the performance of `relative()` when `from` is `null` and the path is
+ already relative.
+
+* Improve the performance of `current` when the current directory hasn't
+ changed.
+
+## 1.3.7
+
+* Improve the performance of `absolute()` and `normalize()`.
+
## 1.3.6
* Ensure that `path.toUri` preserves trailing slashes for relative paths.
diff --git a/packages/path/benchmark/benchmark.dart b/packages/path/benchmark/benchmark.dart
index 419eee0..4285e65 100644
--- a/packages/path/benchmark/benchmark.dart
+++ b/packages/path/benchmark/benchmark.dart
@@ -21,18 +21,48 @@
print("$name: ${count / sw.elapsedMicroseconds} iter/us (${sw.elapsed})");
}
+void runBenchmarkTwoArgs(String name, Function func, List files) {
+ // Warmup.
+ for (int i = 0; i < 1000; i++) {
+ for (var file1 in files) {
+ for (var file2 in files) {
+ func(file1, file2);
+ }
+ }
+ }
+
+ var count = 10000;
+ var sw = new Stopwatch()..start();
+ for (int i = 0; i < count; i++) {
+ for (var file1 in files) {
+ for (var file2 in files) {
+ func(file1, file2);
+ }
+ }
+ }
+ print("$name: ${count / sw.elapsedMicroseconds} iter/us (${sw.elapsed})");
+}
+
main(args) {
for (var style in [path.Style.posix, path.Style.url, path.Style.windows]) {
var context = new path.Context(style: style);
var files = COMMON_PATHS.toList()..addAll(STYLE_PATHS[style]);
- void benchmark(name, func) {
+ benchmark(name, func) {
name = style.name + '-' + name;
if (args.isEmpty || args.any((arg) => name.contains(arg))) {
runBenchmark(name, func, files);
}
}
+ benchmarkTwoArgs(name, func) {
+ name = style.name + '-' + name + '-two';
+ if (args.isEmpty || args.any((arg) => name.contains(arg))) {
+ runBenchmarkTwoArgs(name, func, files);
+ }
+ }
+
+ benchmark('absolute', context.absolute);
benchmark('basename', context.basename);
benchmark('basenameWithoutExtension', context.basenameWithoutExtension);
benchmark('dirname', context.dirname);
@@ -43,8 +73,14 @@
benchmark('isRootRelative', context.isRootRelative);
benchmark('normalize', context.normalize);
benchmark('relative', context.relative);
+ benchmarkTwoArgs('relative', context.relative);
benchmark('toUri', context.toUri);
benchmark('prettyUri', context.prettyUri);
+ benchmarkTwoArgs('isWithin', context.isWithin);
+ }
+
+ if (args.isEmpty || args.any((arg) => arg == 'current')) {
+ runBenchmark('current', (_) => path.current, [null]);
}
}
diff --git a/packages/path/lib/path.dart b/packages/path/lib/path.dart
index af9efe5..93fe67e 100644
--- a/packages/path/lib/path.dart
+++ b/packages/path/lib/path.dart
@@ -79,17 +79,36 @@
/// In the browser, this means the current URL, without the last file segment.
String get current {
var uri = Uri.base;
+
+ // Converting the base URI to a file path is pretty slow, and the base URI
+ // rarely changes in practice, so we cache the result here.
+ if (uri == _currentUriBase) return _current;
+ _currentUriBase = uri;
+
if (Style.platform == Style.url) {
- return uri.resolve('.').toString();
+ _current = uri.resolve('.').toString();
+ return _current;
} else {
var path = uri.toFilePath();
// Remove trailing '/' or '\'.
- int lastIndex = path.length - 1;
+ var lastIndex = path.length - 1;
assert(path[lastIndex] == '/' || path[lastIndex] == '\\');
- return path.substring(0, lastIndex);
+ _current = path.substring(0, lastIndex);
+ return _current;
}
}
+/// The last value returned by [Uri.base].
+///
+/// This is used to cache the current working directory.
+Uri _currentUriBase;
+
+/// The last known value of the current working directory.
+///
+/// This is cached because [current] is called frequently but rarely actually
+/// changes.
+String _current;
+
/// Gets the path separator for the current platform. This is `\` on Windows
/// and `/` on other platforms (including the browser).
String get separator => context.separator;
diff --git a/packages/path/lib/src/context.dart b/packages/path/lib/src/context.dart
index db055a1..d10a29f 100644
--- a/packages/path/lib/src/context.dart
+++ b/packages/path/lib/src/context.dart
@@ -4,6 +4,7 @@
library path.context;
+import 'characters.dart' as chars;
import 'internal_style.dart';
import 'style.dart';
import 'parsed_path.dart';
@@ -73,6 +74,15 @@
/// If [current] isn't absolute, this won't return an absolute path.
String absolute(String part1, [String part2, String part3, String part4,
String part5, String part6, String part7]) {
+ _validateArgList(
+ "absolute", [part1, part2, part3, part4, part5, part6, part7]);
+
+ // If there's a single absolute path, just return it. This is a lot faster
+ // for the common case of `p.absolute(path)`.
+ if (part2 == null && isAbsolute(part1) && !isRootRelative(part1)) {
+ return part1;
+ }
+
return join(current, part1, part2, part3, part4, part5, part6, part7);
}
@@ -295,11 +305,79 @@
///
/// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
String normalize(String path) {
+ if (!_needsNormalization(path)) return path;
+
var parsed = _parse(path);
parsed.normalize();
return parsed.toString();
}
+ /// Returns whether [path] needs to be normalized.
+ bool _needsNormalization(String path) {
+ var start = 0;
+ var codeUnits = path.codeUnits;
+ var previousPrevious;
+ var previous;
+
+ // Skip past the root before we start looking for snippets that need
+ // normalization. We want to normalize "//", but not when it's part of
+ // "http://".
+ var root = style.rootLength(path);
+ if (root != 0) {
+ start = root;
+ previous = chars.SLASH;
+
+ // On Windows, the root still needs to be normalized if it contains a
+ // forward slash.
+ if (style == Style.windows) {
+ for (var i = 0; i < root; i++) {
+ if (codeUnits[i] == chars.SLASH) return true;
+ }
+ }
+ }
+
+ for (var i = start; i < codeUnits.length; i++) {
+ var codeUnit = codeUnits[i];
+ if (style.isSeparator(codeUnit)) {
+ // Forward slashes in Windows paths are normalized to backslashes.
+ if (style == Style.windows && codeUnit == chars.SLASH) return true;
+
+ // Multiple separators are normalized to single separators.
+ if (previous != null && style.isSeparator(previous)) return true;
+
+ // Single dots and double dots are normalized to directory traversals.
+ //
+ // This can return false positives for ".../", but that's unlikely
+ // enough that it's probably not going to cause performance issues.
+ if (previous == chars.PERIOD &&
+ (previousPrevious == null ||
+ previousPrevious == chars.PERIOD ||
+ style.isSeparator(previousPrevious))) {
+ return true;
+ }
+ }
+
+ previousPrevious = previous;
+ previous = codeUnit;
+ }
+
+ // Empty paths are normalized to ".".
+ if (previous == null) return true;
+
+ // Trailing separators are removed.
+ if (style.isSeparator(previous)) return true;
+
+ // Single dots and double dots are normalized to directory traversals.
+ if (previous == chars.PERIOD &&
+ (previousPrevious == null ||
+ previousPrevious == chars.SLASH ||
+ previousPrevious == chars.PERIOD)) {
+ return true;
+ }
+
+ return false;
+ }
+
/// Attempts to convert [path] to an equivalent relative path relative to
/// [root].
///
@@ -333,13 +411,10 @@
/// "/", no path can be determined. In this case, a [PathException] will be
/// thrown.
String relative(String path, {String from}) {
- // Avoid calling [current] since it is slow and calling join() when
- // [from] is absolute does nothing.
- if (from == null) {
- from = current;
- } else if (this.isRelative(from) || this.isRootRelative(from)) {
- from = this.join(current, from);
- }
+ // Avoid expensive computation if the path is already relative.
+ if (from == null && this.isRelative(path)) return this.normalize(path);
+
+ from = from == null ? current : absolute(from);
// We can't determine the path from a relative path to an absolute path.
if (this.isRelative(from) && this.isAbsolute(path)) {
@@ -425,6 +500,31 @@
/// path.isWithin('/root/path', '/root/other'); // -> false
/// path.isWithin('/root/path', '/root/path'); // -> false
bool isWithin(String parent, String child) {
+ // Make both paths the same level of relative. We're only able to do the
+ // quick comparison if both paths are in the same format, and making a path
+ // absolute is faster than making it relative.
+ var parentIsAbsolute = isAbsolute(parent);
+ var childIsAbsolute = isAbsolute(child);
+ if (parentIsAbsolute && !childIsAbsolute) {
+ child = absolute(child);
+ if (style.isRootRelative(parent)) parent = absolute(parent);
+ } else if (childIsAbsolute && !parentIsAbsolute) {
+ parent = absolute(parent);
+ if (style.isRootRelative(child)) child = absolute(child);
+ } else if (childIsAbsolute && parentIsAbsolute) {
+ var childIsRootRelative = style.isRootRelative(child);
+ var parentIsRootRelative = style.isRootRelative(parent);
+
+ if (childIsRootRelative && !parentIsRootRelative) {
+ child = absolute(child);
+ } else if (parentIsRootRelative && !childIsRootRelative) {
+ parent = absolute(parent);
+ }
+ }
+
+ var fastResult = _isWithinFast(parent, child);
+ if (fastResult != null) return fastResult;
+
var relative;
try {
relative = this.relative(child, from: parent);
@@ -440,6 +540,259 @@
parts.first != '.';
}
+ /// An optimized implementation of [isWithin] that doesn't handle a few
+ /// complex cases.
+ bool _isWithinFast(String parent, String child) {
+ // Normally we just bail when we see "." path components, but we can handle
+ // a single dot easily enough.
+ if (parent == '.') parent = '';
+
+ var parentRootLength = style.rootLength(parent);
+ var childRootLength = style.rootLength(child);
+
+ // If the roots aren't the same length, we know both paths are absolute or
+ // both are root-relative, and thus that the roots are meaningfully
+ // different.
+ //
+ // isWithin("C:/bar", "//foo/bar/baz") //=> false
+ // isWithin("http://example.com/", "http://google.com/bar") //=> false
+ if (parentRootLength != childRootLength) return false;
+
+ var parentCodeUnits = parent.codeUnits;
+ var childCodeUnits = child.codeUnits;
+
+ // Make sure that the roots are textually the same as well.
+ //
+ // isWithin("C:/bar", "D:/bar/baz") //=> false
+ // isWithin("http://example.com/", "http://example.org/bar") //=> false
+ for (var i = 0; i < parentRootLength; i++) {
+ var parentCodeUnit = parentCodeUnits[i];
+ var childCodeUnit = childCodeUnits[i];
+ if (parentCodeUnit == childCodeUnit) continue;
+
+ // If both code units are separators, that's fine too.
+ //
+ // isWithin("C:/", r"C:\foo") //=> true
+ if (!style.isSeparator(parentCodeUnit) ||
+ !style.isSeparator(childCodeUnit)) {
+ return false;
+ }
+ }
+
+ // Start by considering the last code unit as a separator, since
+ // semantically we're starting at a new path component even if we're
+ // comparing relative paths.
+ var lastCodeUnit = chars.SLASH;
+
+ // Iterate through both paths as long as they're semantically identical.
+ var parentIndex = parentRootLength;
+ var childIndex = childRootLength;
+ while (parentIndex < parent.length && childIndex < child.length) {
+ var parentCodeUnit = parentCodeUnits[parentIndex];
+ var childCodeUnit = childCodeUnits[childIndex];
+ if (parentCodeUnit == childCodeUnit) {
+ lastCodeUnit = parentCodeUnit;
+ parentIndex++;
+ childIndex++;
+ continue;
+ }
+
+ // Different separators are considered identical.
+ var parentIsSeparator = style.isSeparator(parentCodeUnit);
+ var childIsSeparator = style.isSeparator(childCodeUnit);
+ if (parentIsSeparator && childIsSeparator) {
+ lastCodeUnit = parentCodeUnit;
+ parentIndex++;
+ childIndex++;
+ continue;
+ }
+
+ // Ignore multiple separators in a row.
+ if (parentIsSeparator && style.isSeparator(lastCodeUnit)) {
+ parentIndex++;
+ continue;
+ } else if (childIsSeparator && style.isSeparator(lastCodeUnit)) {
+ childIndex++;
+ continue;
+ }
+
+ if (parentCodeUnit == chars.PERIOD) {
+ // If a dot comes after a separator, it may be a directory traversal
+ // operator. To check that, we need to know if it's followed by either
+ // "/" or "./". Otherwise, it's just a normal non-matching character.
+ //
+ // isWithin("foo/./bar", "foo/bar/baz") //=> true
+ // isWithin("foo/bar/../baz", "foo/bar/.foo") //=> false
+ if (style.isSeparator(lastCodeUnit)) {
+ parentIndex++;
+
+ // We've hit "/." at the end of the parent path, which we can ignore,
+ // since the paths were equivalent up to this point.
+ if (parentIndex == parent.length) break;
+ parentCodeUnit = parentCodeUnits[parentIndex];
+
+ // We've hit "/./", which we can ignore.
+ if (style.isSeparator(parentCodeUnit)) {
+ parentIndex++;
+ continue;
+ }
+
+ // We've hit "/..", which may be a directory traversal operator that
+ // we can't handle on the fast track.
+ if (parentCodeUnit == chars.PERIOD) {
+ parentIndex++;
+ if (parentIndex == parent.length ||
+ style.isSeparator(parentCodeUnits[parentIndex])) {
+ return null;
+ }
+ }
+ }
+
+ // If this isn't a directory traversal, fall through so we hit the
+ // normal handling for mismatched paths.
+ }
+
+ // This is the same logic as above, but for the child path instead of the
+ // parent.
+ if (childCodeUnit == chars.PERIOD) {
+ if (style.isSeparator(lastCodeUnit)) {
+ childIndex++;
+ if (childIndex == child.length) break;
+ childCodeUnit = childCodeUnits[childIndex];
+
+ if (style.isSeparator(childCodeUnit)) {
+ childIndex++;
+ continue;
+ }
+
+ if (childCodeUnit == chars.PERIOD) {
+ childIndex++;
+ if (childIndex == child.length ||
+ style.isSeparator(childCodeUnits[childIndex])) {
+ return null;
+ }
+ }
+ }
+ }
+
+ // If we're here, we've hit two non-matching, non-significant characters.
+ // As long as the remainders of the two paths don't have any unresolved
+ // ".." components, we can be confident that [child] is not within
+ // [parent].
+ var childDirection = _pathDirection(childCodeUnits, childIndex);
+ if (childDirection != _PathDirection.belowRoot) return null;
+ var parentDirection = _pathDirection(parentCodeUnits, parentIndex);
+ if (parentDirection != _PathDirection.belowRoot) return null;
+
+ return false;
+ }
+
+ // If the child is shorter than the parent, it's probably not within the
+ // parent. The only exception is if the parent has some weird ".." stuff
+ // going on, in which case we do the slow check.
+ //
+ // isWithin("foo/bar/baz", "foo/bar") //=> false
+ // isWithin("foo/bar/baz/../..", "foo/bar") //=> true
+ if (childIndex == child.length) {
+ var direction = _pathDirection(parentCodeUnits, parentIndex);
+ return direction == _PathDirection.aboveRoot ? null : false;
+ }
+
+ // We've reached the end of the parent path, which means it's time to make a
+ // decision. Before we do, though, we'll check the rest of the child to see
+ // what that tells us.
+ var direction = _pathDirection(childCodeUnits, childIndex);
+
+ // If there are no more components in the child, then it's the same as
+ // the parent, not within it.
+ //
+ // isWithin("foo/bar", "foo/bar") //=> false
+ // isWithin("foo/bar", "foo/bar//") //=> false
+ if (direction == _PathDirection.atRoot) return false;
+
+ // If there are unresolved ".." components in the child, no decision we make
+ // will be valid. We'll abort and do the slow check instead.
+ //
+ // isWithin("foo/bar", "foo/bar/..") //=> false
+ // isWithin("foo/bar", "foo/bar/baz/bang/../../..") //=> false
+ // isWithin("foo/bar", "foo/bar/baz/bang/../../../bar/baz") //=> true
+ if (direction == _PathDirection.aboveRoot) return null;
+
+ // The child is within the parent if and only if we're on a separator
+ // boundary.
+ //
+ // isWithin("foo/bar", "foo/bar/baz") //=> true
+ // isWithin("foo/bar/", "foo/bar/baz") //=> true
+ // isWithin("foo/bar", "foo/barbaz") //=> false
+ return style.isSeparator(childCodeUnits[childIndex]) ||
+ style.isSeparator(lastCodeUnit);
+ }
+
+ // Returns a [_PathDirection] describing the path represented by [codeUnits]
+ // after [index].
+ //
+ // This ignores leading separators.
+ //
+ // pathDirection("foo") //=> below root
+ // pathDirection("foo/bar/../baz") //=> below root
+ // pathDirection("//foo/bar/baz") //=> below root
+ // pathDirection("/") //=> at root
+ // pathDirection("foo/..") //=> at root
+ // pathDirection("foo/../baz") //=> reaches root
+ // pathDirection("foo/../..") //=> above root
+ // pathDirection("foo/../../foo/bar/baz") //=> above root
+ _PathDirection _pathDirection(List<int> codeUnits, int index) {
+ var depth = 0;
+ var reachedRoot = false;
+ var i = index;
+ while (i < codeUnits.length) {
+ // Ignore initial separators or doubled separators.
+ while (i < codeUnits.length && style.isSeparator(codeUnits[i])) {
+ i++;
+ }
+
+ // If we're at the end, stop.
+ if (i == codeUnits.length) break;
+
+ // Move through the path component to the next separator.
+ var start = i;
+ while (i < codeUnits.length && !style.isSeparator(codeUnits[i])) {
+ i++;
+ }
+
+ // See if the path component is ".", "..", or a name.
+ if (i - start == 1 && codeUnits[start] == chars.PERIOD) {
+ // Don't change the depth.
+ } else if (i - start == 2 &&
+ codeUnits[start] == chars.PERIOD &&
+ codeUnits[start + 1] == chars.PERIOD) {
+ // ".." backs out a directory.
+ depth--;
+
+ // If we work back beyond the root, stop.
+ if (depth < 0) break;
+
+ // Record that we reached the root so we don't return
+ // [_PathDirection.belowRoot].
+ if (depth == 0) reachedRoot = true;
+ } else {
+ // Step inside a directory.
+ depth++;
+ }
+
+ // If we're at the end, stop.
+ if (i == codeUnits.length) break;
+
+ // Move past the separator.
+ i++;
+ }
+
+ if (depth < 0) return _PathDirection.aboveRoot;
+ if (depth == 0) return _PathDirection.atRoot;
+ if (reachedRoot) return _PathDirection.reachesRoot;
+ return _PathDirection.belowRoot;
+ }
+
/// Removes a trailing extension from the last part of [path].
///
/// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
@@ -572,3 +925,30 @@
throw new ArgumentError(message.toString());
}
}
+
+/// An enum of possible return values for [Context._pathDirection].
+class _PathDirection {
+ /// The path contains enough ".." components that at some point it reaches
+ /// above its original root.
+ ///
+ /// Note that this applies even if the path ends beneath its original root. It
+ /// takes precendence over any other return values that may apple.
+ static const aboveRoot = const _PathDirection("above root");
+
+ /// The path contains enough ".." components that it ends at its original
+ /// root.
+ static const atRoot = const _PathDirection("at root");
+
+ /// The path contains enough ".." components that at some point it reaches its
+ /// original root, but it ends beneath that root.
+ static const reachesRoot = const _PathDirection("reaches root");
+
+ /// The path never reaches to or above its original root.
+ static const belowRoot = const _PathDirection("below root");
+
+ final String name;
+
+ const _PathDirection(this.name);
+
+ String toString() => name;
+}
diff --git a/packages/path/pubspec.yaml b/packages/path/pubspec.yaml
index 6ba3a15..fb21d20 100644
--- a/packages/path/pubspec.yaml
+++ b/packages/path/pubspec.yaml
@@ -1,5 +1,5 @@
name: path
-version: 1.3.6
+version: 1.3.9
author: Dart Team <misc@dartlang.org>
description: >
A string-based path manipulation library. All of the path operations you know
@@ -7,6 +7,6 @@
machines.
homepage: http://github.com/dart-lang/path
dev_dependencies:
- unittest: ">=0.9.0 <0.12.0"
+ test: ">=0.12.0 <0.13.0"
environment:
sdk: ">=1.0.0 <2.0.0"
diff --git a/packages/path/test/browser_test.dart b/packages/path/test/browser_test.dart
index 0b56c7a..b329146 100644
--- a/packages/path/test/browser_test.dart
+++ b/packages/path/test/browser_test.dart
@@ -2,15 +2,14 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+@TestOn('browser')
+
import 'dart:html';
-import 'package:unittest/unittest.dart';
-import 'package:unittest/html_config.dart';
+import 'package:test/test.dart';
import 'package:path/path.dart' as path;
main() {
- useHtmlConfiguration();
-
group('new Context()', () {
test('uses the window location if root and style are omitted', () {
var context = new path.Context();
diff --git a/packages/path/test/io_test.dart b/packages/path/test/io_test.dart
index e855522..f15f82d 100644
--- a/packages/path/test/io_test.dart
+++ b/packages/path/test/io_test.dart
@@ -2,9 +2,11 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+@TestOn('vm')
+
import 'dart:io' as io;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:path/path.dart' as path;
main() {
diff --git a/packages/path/test/path_test.dart b/packages/path/test/path_test.dart
index 19397cf..b45f8dc 100644
--- a/packages/path/test/path_test.dart
+++ b/packages/path/test/path_test.dart
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:path/path.dart' as path;
main() {
diff --git a/packages/path/test/posix_test.dart b/packages/path/test/posix_test.dart
index 2368d99..afd6b94 100644
--- a/packages/path/test/posix_test.dart
+++ b/packages/path/test/posix_test.dart
@@ -4,7 +4,7 @@
library path.test.posix_test;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:path/path.dart' as path;
import 'utils.dart';
@@ -419,6 +419,19 @@
expect(context.isWithin('baz', '/root/path/bang/baz'), isFalse);
});
+ test('complex cases', () {
+ expect(context.isWithin('foo/./bar', 'foo/bar/baz'), isTrue);
+ expect(context.isWithin('foo//bar', 'foo/bar/baz'), isTrue);
+ expect(context.isWithin('foo/qux/../bar', 'foo/bar/baz'), isTrue);
+ expect(context.isWithin('foo/bar', 'foo/bar/baz/../..'), isFalse);
+ expect(context.isWithin('foo/bar', 'foo/bar///'), isFalse);
+ expect(context.isWithin('foo/.bar', 'foo/.bar/baz'), isTrue);
+ expect(context.isWithin('foo/./bar', 'foo/.bar/baz'), isFalse);
+ expect(context.isWithin('foo/..bar', 'foo/..bar/baz'), isTrue);
+ expect(context.isWithin('foo/bar', 'foo/bar/baz/..'), isFalse);
+ expect(context.isWithin('foo/bar', 'foo/bar/baz/../qux'), isTrue);
+ });
+
test('from a relative root', () {
var r = new path.Context(style: path.Style.posix, current: 'foo/bar');
expect(r.isWithin('.', 'a/b/c'), isTrue);
diff --git a/packages/path/test/relative_test.dart b/packages/path/test/relative_test.dart
index 57a9046..3cf02dc 100644
--- a/packages/path/test/relative_test.dart
+++ b/packages/path/test/relative_test.dart
@@ -4,7 +4,7 @@
//
// Test "relative" on all styles of path.Context, on all platforms.
-import "package:unittest/unittest.dart";
+import "package:test/test.dart";
import "package:path/path.dart" as path;
import "utils.dart";
diff --git a/packages/path/test/url_test.dart b/packages/path/test/url_test.dart
index 854e19a..c81893a 100644
--- a/packages/path/test/url_test.dart
+++ b/packages/path/test/url_test.dart
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:path/path.dart' as path;
main() {
@@ -629,6 +629,31 @@
isFalse);
});
+ test('complex cases', () {
+ expect(context.isWithin('foo/./bar', 'foo/bar/baz'), isTrue);
+ expect(context.isWithin('foo//bar', 'foo/bar/baz'), isTrue);
+ expect(context.isWithin('foo/qux/../bar', 'foo/bar/baz'), isTrue);
+ expect(context.isWithin('foo/bar', 'foo/bar/baz/../..'), isFalse);
+ expect(context.isWithin('foo/bar', 'foo/bar///'), isFalse);
+ expect(context.isWithin('foo/.bar', 'foo/.bar/baz'), isTrue);
+ expect(context.isWithin('foo/./bar', 'foo/.bar/baz'), isFalse);
+ expect(context.isWithin('foo/..bar', 'foo/..bar/baz'), isTrue);
+ expect(context.isWithin('foo/bar', 'foo/bar/baz/..'), isFalse);
+ expect(context.isWithin('foo/bar', 'foo/bar/baz/../qux'), isTrue);
+ expect(context.isWithin('http://example.org/', 'http://example.com/foo'),
+ isFalse);
+ expect(context.isWithin('http://example.org/', 'http://dartlang.org/foo'),
+ isFalse);
+ });
+
+ test('with root-relative paths', () {
+ expect(context.isWithin('/foo', 'http://dartlang.org/foo/bar'), isTrue);
+ expect(context.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue);
+ expect(context.isWithin('/root', 'foo/bar'), isTrue);
+ expect(context.isWithin('foo', '/root/path/foo/bar'), isTrue);
+ expect(context.isWithin('/foo', '/foo/bar'), isTrue);
+ });
+
test('from a relative root', () {
var r = new path.Context(style: path.Style.url, current: 'foo/bar');
expect(r.isWithin('.', 'a/b/c'), isTrue);
diff --git a/packages/path/test/utils.dart b/packages/path/test/utils.dart
index 1730798..7d917a0 100644
--- a/packages/path/test/utils.dart
+++ b/packages/path/test/utils.dart
@@ -4,7 +4,7 @@
library path.test.utils;
-import "package:unittest/unittest.dart";
+import "package:test/test.dart";
import "package:path/path.dart" as path;
/// A matcher for a closure that throws a [path.PathException].
diff --git a/packages/path/test/windows_test.dart b/packages/path/test/windows_test.dart
index 6942837..717043d 100644
--- a/packages/path/test/windows_test.dart
+++ b/packages/path/test/windows_test.dart
@@ -4,7 +4,7 @@
library path.test.windows_test;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:path/path.dart' as path;
import 'utils.dart';
@@ -13,7 +13,7 @@
var context =
new path.Context(style: path.Style.windows, current: r'C:\root\path');
- group('separator', () {
+ test('separator', () {
expect(context.separator, '\\');
});
@@ -537,6 +537,30 @@
expect(context.isWithin(r'baz', r'C:\root\path\bang\baz'), isFalse);
});
+ test('complex cases', () {
+ expect(context.isWithin(r'foo\.\bar', r'foo\bar\baz'), isTrue);
+ expect(context.isWithin(r'foo\\bar', r'foo\bar\baz'), isTrue);
+ expect(context.isWithin(r'foo\qux\..\bar', r'foo\bar\baz'), isTrue);
+ expect(context.isWithin(r'foo\bar', r'foo\bar\baz\..\..'), isFalse);
+ expect(context.isWithin(r'foo\bar', r'foo\bar\\\'), isFalse);
+ expect(context.isWithin(r'foo\.bar', r'foo\.bar\baz'), isTrue);
+ expect(context.isWithin(r'foo\.\bar', r'foo\.bar\baz'), isFalse);
+ expect(context.isWithin(r'foo\..bar', r'foo\..bar\baz'), isTrue);
+ expect(context.isWithin(r'foo\bar', r'foo\bar\baz\..'), isFalse);
+ expect(context.isWithin(r'foo\bar', r'foo\bar\baz\..\qux'), isTrue);
+ expect(context.isWithin(r'C:\', 'C:/foo'), isTrue);
+ expect(context.isWithin(r'C:\', r'D:\foo'), isFalse);
+ expect(context.isWithin(r'C:\', r'\\foo\bar'), isFalse);
+ });
+
+ test('with root-relative paths', () {
+ expect(context.isWithin(r'\foo', r'C:\foo\bar'), isTrue);
+ expect(context.isWithin(r'C:\foo', r'\foo\bar'), isTrue);
+ expect(context.isWithin(r'\root', r'foo\bar'), isTrue);
+ expect(context.isWithin(r'foo', r'\root\path\foo\bar'), isTrue);
+ expect(context.isWithin(r'\foo', r'\foo\bar'), isTrue);
+ });
+
test('from a relative root', () {
var r = new path.Context(style: path.Style.windows, current: r'foo\bar');
expect(r.isWithin('.', r'a\b\c'), isTrue);
diff --git a/packages/plugin/._AUTHORS b/packages/plugin/._AUTHORS
deleted file mode 100755
index 8fb8ff1..0000000
--- a/packages/plugin/._AUTHORS
+++ /dev/null
Binary files differ
diff --git a/packages/plugin/._CHANGELOG.md b/packages/plugin/._CHANGELOG.md
deleted file mode 100755
index 8fb8ff1..0000000
--- a/packages/plugin/._CHANGELOG.md
+++ /dev/null
Binary files differ
diff --git a/packages/plugin/._CONTRIBUTING.md b/packages/plugin/._CONTRIBUTING.md
deleted file mode 100755
index 8fb8ff1..0000000
--- a/packages/plugin/._CONTRIBUTING.md
+++ /dev/null
Binary files differ
diff --git a/packages/plugin/._LICENSE b/packages/plugin/._LICENSE
deleted file mode 100755
index 8fb8ff1..0000000
--- a/packages/plugin/._LICENSE
+++ /dev/null
Binary files differ
diff --git a/packages/plugin/._README.md b/packages/plugin/._README.md
deleted file mode 100755
index 8fb8ff1..0000000
--- a/packages/plugin/._README.md
+++ /dev/null
Binary files differ
diff --git a/packages/pool/CHANGELOG.md b/packages/pool/CHANGELOG.md
index 9f74d07..dc852aa 100644
--- a/packages/pool/CHANGELOG.md
+++ b/packages/pool/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.2.1
+
+* Internal changes only.
+
## 1.2.0
* Add `Pool.close()`, which forbids new resource requests and releases all
diff --git a/packages/pool/lib/pool.dart b/packages/pool/lib/pool.dart
index 59b949e..ef38614 100644
--- a/packages/pool/lib/pool.dart
+++ b/packages/pool/lib/pool.dart
@@ -46,12 +46,14 @@
/// The timeout timer.
///
- /// If [_timeout] isn't null, this timer is set as soon as the resource limit
- /// is reached and is reset every time an resource is released or a new
- /// resource is requested. If it fires, that indicates that the caller became
- /// deadlocked, likely due to files waiting for additional files to be read
- /// before they could be closed.
- Timer _timer;
+ /// This timer is canceled as long as the pool is below the resource limit.
+ /// It's reset once the resource limit is reached and again every time an
+ /// resource is released or a new resource is requested. If it fires, that
+ /// indicates that the caller became deadlocked, likely due to files waiting
+ /// for additional files to be read before they could be closed.
+ ///
+ /// This is `null` if this pool shouldn't time out.
+ RestartableTimer _timer;
/// The amount of time to wait before timing out the pending resources.
final Duration _timeout;
@@ -72,7 +74,13 @@
/// all pending [request] futures will throw a [TimeoutException]. This is
/// intended to avoid deadlocks.
Pool(this._maxAllocatedResources, {Duration timeout})
- : _timeout = timeout;
+ : _timeout = timeout {
+ if (timeout != null) {
+ // Start the timer canceled since we only want to start counting down once
+ // we've run out of available resources.
+ _timer = new RestartableTimer(timeout, _onTimeout)..cancel();
+ }
+ }
/// Request a [PoolResource].
///
@@ -190,11 +198,12 @@
/// A resource has been requested, allocated, or released.
void _resetTimer() {
- if (_timer != null) _timer.cancel();
- if (_timeout == null || _requestedResources.isEmpty) {
- _timer = null;
+ if (_timer == null) return;
+
+ if (_requestedResources.isEmpty) {
+ _timer.cancel();
} else {
- _timer = new Timer(_timeout, _onTimeout);
+ _timer.reset();
}
}
diff --git a/packages/pool/pubspec.yaml b/packages/pool/pubspec.yaml
index 0013e9e..9635620 100644
--- a/packages/pool/pubspec.yaml
+++ b/packages/pool/pubspec.yaml
@@ -1,10 +1,10 @@
name: pool
-version: 1.2.0
+version: 1.2.1
author: Dart Team <misc@dartlang.org>
description: A class for managing a finite pool of resources.
homepage: https://github.com/dart-lang/pool
dependencies:
- async: "^1.3.0"
+ async: "^1.4.0"
stack_trace: ">=0.9.2 <2.0.0"
environment:
sdk: ">=1.9.0 <2.0.0"
diff --git a/packages/smoke/.packages b/packages/smoke/.packages
deleted file mode 100644
index 1d75e7a..0000000
--- a/packages/smoke/.packages
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generate by pub on 2015-08-19 14:18:52.210.
-# This file contains a map from Dart package names to Dart package locations.
-# Dart tools, including the Dart VM and Dart analyzer, rely on the content.
-# AUTO GENERATED - DO NOT EDIT
-analyzer:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/analyzer-0.26.1/lib/
-args:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/args-0.13.2/lib/
-barback:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/barback-0.15.2+6/lib/
-charcode:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.0/lib/
-cli_util:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/cli_util-0.0.1+2/lib/
-code_transformers:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/code_transformers-0.2.9+1/lib/
-collection:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/collection-1.1.1/lib/
-csslib:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/csslib-0.12.1/lib/
-glob:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/glob-1.0.5/lib/
-html:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/html-0.12.1+2/lib/
-logging:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/logging-0.11.1+1/lib/
-package_config:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/package_config-0.1.1/lib/
-path:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/path-1.3.6/lib/
-plugin:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/plugin-0.1.0/lib/
-pool:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/pool-1.1.0/lib/
-smoke:lib/
-source_maps:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.1/lib/
-source_span:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/source_span-1.1.3/lib/
-stack_trace:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.4.1/lib/
-string_scanner:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/string_scanner-0.1.3+1/lib/
-unittest:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/unittest-0.11.6+1/lib/
-utf:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+2/lib/
-watcher:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7/lib/
-when:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/when-0.2.0/lib/
-which:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/which-0.1.3/lib/
-yaml:file:///usr/local/google/home/jakemac/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.3/lib/
diff --git a/packages/smoke/CHANGELOG.md b/packages/smoke/CHANGELOG.md
index 83bf042..ba83d54 100644
--- a/packages/smoke/CHANGELOG.md
+++ b/packages/smoke/CHANGELOG.md
@@ -1,3 +1,6 @@
+#### 0.3.6
+ * Update to analyzer '^0.27.0' and update to the test package.
+
#### 0.3.5
* Update to analyzer '<0.27.0'
diff --git a/packages/smoke/lib/codegen/recorder.dart b/packages/smoke/lib/codegen/recorder.dart
index 545455a..8a9ebf1 100644
--- a/packages/smoke/lib/codegen/recorder.dart
+++ b/packages/smoke/lib/codegen/recorder.dart
@@ -253,7 +253,7 @@
/// Copy metadata associated with the declaration of [target].
List<ConstExpression> _copyAnnotations(Element target) {
- var node = target.node;
+ var node = target.computeNode();
// [node] is the initialization expression, we walk up to get to the actual
// member declaration where the metadata is attached to.
while (node is! ClassMember) node = node.parent;
diff --git a/packages/smoke/lib/mirrors.dart b/packages/smoke/lib/mirrors.dart
index a84f081..ef018aa 100644
--- a/packages/smoke/lib/mirrors.dart
+++ b/packages/smoke/lib/mirrors.dart
@@ -222,7 +222,7 @@
t = _objectType;
}
return t;
- } on UnsupportedError catch (e) {
+ } on UnsupportedError catch (_) {
// Note: dart2js throws UnsupportedError when the type is not reflectable.
return _objectType;
}
diff --git a/packages/smoke/lib/static.dart b/packages/smoke/lib/static.dart
index aad1542..2b5d065 100644
--- a/packages/smoke/lib/static.dart
+++ b/packages/smoke/lib/static.dart
@@ -145,7 +145,7 @@
}
try {
return Function.apply(method, args);
- } on NoSuchMethodError catch (e) {
+ } on NoSuchMethodError catch (_) {
// TODO(sigmund): consider whether this should just be in a logger or if
// we should wrap `e` as a new exception (what's the best way to let users
// know about this tentativeError?)
diff --git a/packages/smoke/pubspec.yaml b/packages/smoke/pubspec.yaml
index 5e7cd83..58f0a80 100644
--- a/packages/smoke/pubspec.yaml
+++ b/packages/smoke/pubspec.yaml
@@ -1,5 +1,5 @@
name: smoke
-version: 0.3.5
+version: 0.3.6
author: Polymer.dart Authors <web-ui-dev@dartlang.org>
homepage: https://github.com/dart-lang/smoke
description: >
@@ -9,7 +9,7 @@
dependencies:
barback: ">=0.9.0 <0.16.0"
logging: ">=0.9.0 <0.12.0"
- analyzer: ">=0.13.0 <0.27.0"
+ analyzer: "^0.27.0"
# TODO(sigmund): once we have some easier way to do global app-level
# transformers, we might want to remove this transformer here and only apply it
# in apps that need it.
@@ -17,8 +17,8 @@
- smoke/src/default_transformer:
$include: lib/src/implementation.dart
dev_dependencies:
- unittest: ">=0.10.0 <0.12.0"
- path: ">=1.0.0 <2.0.0"
- code_transformers: ">=0.2.0 <0.3.0"
+ test: "^0.12.0"
+ path: "^1.0.0"
+ code_transformers: "^0.3.0"
environment:
- sdk: ">=1.3.0-dev.7.9 <2.0.0"
+ sdk: ">=1.12.0 <2.0.0"
diff --git a/packages/smoke/test/args_test.dart b/packages/smoke/test/args_test.dart
index 44c8079..4b5b089 100644
--- a/packages/smoke/test/args_test.dart
+++ b/packages/smoke/test/args_test.dart
@@ -7,7 +7,7 @@
import 'package:smoke/smoke.dart'
show minArgs, maxArgs, canAcceptNArgs, SUPPORTED_ARGS;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
main() {
var a = new A();
diff --git a/packages/smoke/test/codegen/common.dart b/packages/smoke/test/codegen/common.dart
index 7c80d6c..b595816 100644
--- a/packages/smoke/test/codegen/common.dart
+++ b/packages/smoke/test/codegen/common.dart
@@ -5,7 +5,7 @@
library smoke.test.codegen.common;
import 'package:smoke/codegen/generator.dart';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
checkResults(SmokeCodeGenerator generator,
{List<String> imports: const [], String topLevel: '', String initCall}) {
diff --git a/packages/smoke/test/codegen/end_to_end_test.dart b/packages/smoke/test/codegen/end_to_end_test.dart
index 62999a8..174d61a 100644
--- a/packages/smoke/test/codegen/end_to_end_test.dart
+++ b/packages/smoke/test/codegen/end_to_end_test.dart
@@ -15,13 +15,14 @@
import 'package:analyzer/src/generated/element.dart';
import 'package:smoke/codegen/generator.dart';
import 'package:smoke/codegen/recorder.dart';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:path/path.dart' as path;
import 'testing_resolver_utils.dart' show initAnalyzer;
-void main(List<String> args) {
- final updateStaticTest = args.length > 0 && args[0] == '--update_static_test';
+void main([List<String> args]) {
+ final updateStaticTest =
+ args != null && args.length > 0 && args[0] == '--update_static_test';
test('static_test is up to date', () {
var scriptPath = path.fromUri(Platform.script);
@@ -136,7 +137,7 @@
staticTestFile.writeAsStringSync(code);
print('static_test.dart has been updated.');
}
- });
+ }, skip: 'https://github.com/dart-lang/smoke/issues/26');
}
String _createEntrypoint(SmokeCodeGenerator generator) {
diff --git a/packages/smoke/test/codegen/generator_test.dart b/packages/smoke/test/codegen/generator_test.dart
index 75860bb..3d3110f 100644
--- a/packages/smoke/test/codegen/generator_test.dart
+++ b/packages/smoke/test/codegen/generator_test.dart
@@ -5,7 +5,7 @@
library smoke.test.codegen.generator_test;
import 'package:smoke/codegen/generator.dart';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'common.dart' show checkResults;
diff --git a/packages/smoke/test/codegen/recorder_test.dart b/packages/smoke/test/codegen/recorder_test.dart
index cc6d54a..f4c777a 100644
--- a/packages/smoke/test/codegen/recorder_test.dart
+++ b/packages/smoke/test/codegen/recorder_test.dart
@@ -7,7 +7,7 @@
import 'package:analyzer/src/generated/element.dart';
import 'package:smoke/codegen/generator.dart';
import 'package:smoke/codegen/recorder.dart';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'common.dart' show checkResults;
import 'testing_resolver_utils.dart' show initAnalyzer;
diff --git a/packages/smoke/test/codegen/testing_resolver_utils.dart b/packages/smoke/test/codegen/testing_resolver_utils.dart
index d824227..a2e62dc 100644
--- a/packages/smoke/test/codegen/testing_resolver_utils.dart
+++ b/packages/smoke/test/codegen/testing_resolver_utils.dart
@@ -24,6 +24,7 @@
}
LibraryProvider initAnalyzer(Map<String, String> contents) {
+ AnalysisEngine.instance.processRequiredPlugins();
var analyzer = AnalysisEngine.instance.createAnalysisContext();
var options = new AnalysisOptionsImpl()
..cacheSize = 256
diff --git a/packages/smoke/test/common.dart b/packages/smoke/test/common.dart
index 5400e74..6c511b2 100644
--- a/packages/smoke/test/common.dart
+++ b/packages/smoke/test/common.dart
@@ -7,7 +7,7 @@
library smoke.test.common;
import 'package:smoke/smoke.dart' as smoke;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
main() {
test('read value', () {
diff --git a/packages/smoke/test/common_utils_test.dart b/packages/smoke/test/common_utils_test.dart
index e62f1d1..e9818c9 100644
--- a/packages/smoke/test/common_utils_test.dart
+++ b/packages/smoke/test/common_utils_test.dart
@@ -5,7 +5,7 @@
library smoke.test.common_utils;
import 'package:smoke/src/common.dart';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
main() {
test('adjustList', () {
diff --git a/packages/smoke/test/mirrors_test.dart b/packages/smoke/test/mirrors_test.dart
index cac643a..ad93c87 100644
--- a/packages/smoke/test/mirrors_test.dart
+++ b/packages/smoke/test/mirrors_test.dart
@@ -5,7 +5,7 @@
library smoke.test.mirrors_test;
import 'package:smoke/mirrors.dart';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'common.dart' as common show main;
main() {
diff --git a/packages/smoke/test/mirrors_used_test.dart b/packages/smoke/test/mirrors_used_test.dart
index e2b6fc6..c2f542e 100644
--- a/packages/smoke/test/mirrors_used_test.dart
+++ b/packages/smoke/test/mirrors_used_test.dart
@@ -5,7 +5,7 @@
library smoke.test.mirrors_test;
import 'package:smoke/mirrors.dart';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'common.dart' hide main;
import 'common.dart' as common show main;
diff --git a/packages/smoke/test/static_in_pieces_test.dart b/packages/smoke/test/static_in_pieces_test.dart
index a18fbd2..b0682d7 100644
--- a/packages/smoke/test/static_in_pieces_test.dart
+++ b/packages/smoke/test/static_in_pieces_test.dart
@@ -6,7 +6,7 @@
/// deferred imports.
library smoke.test.static_in_pieces_test;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:smoke/smoke.dart' show Declaration, PROPERTY, METHOD;
import 'package:smoke/static.dart' show useGeneratedCode, StaticConfiguration;
import 'piece1.dart' as p1;
@@ -97,15 +97,18 @@
main() {
useGeneratedCode(configuration);
- expect(configuration.getters[#j], isNull);
+ test('smoke', () async {
+ expect(configuration.getters[#j], isNull);
- configuration.addAll(p1.configuration);
- expect(configuration.getters[#j], isNotNull);
+ configuration.addAll(p1.configuration);
+ expect(configuration.getters[#j], isNotNull);
- p2.loadLibrary().then((_) {
+ await p2.loadLibrary();
+
expect(configuration.names[#i], isNull);
configuration.addAll(p2.configuration);
expect(configuration.names[#i], 'i');
- common.main();
});
+
+ common.main();
}
diff --git a/packages/smoke/test/static_test.dart b/packages/smoke/test/static_test.dart
index 6c485e4..9078225 100644
--- a/packages/smoke/test/static_test.dart
+++ b/packages/smoke/test/static_test.dart
@@ -5,7 +5,7 @@
library smoke.test.static_test;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:smoke/smoke.dart' show Declaration, PROPERTY, METHOD;
import 'package:smoke/static.dart' show useGeneratedCode, StaticConfiguration;
import 'common.dart' as smoke_0;
diff --git a/packages/stack_trace/CHANGELOG.md b/packages/stack_trace/CHANGELOG.md
index dae0db6..624a934 100644
--- a/packages/stack_trace/CHANGELOG.md
+++ b/packages/stack_trace/CHANGELOG.md
@@ -1,3 +1,12 @@
+## 1.5.0
+
+* `new Chain.parse()` now parses all the stack trace formats supported by `new
+ Trace.parse()`. Formats other than that emitted by `Chain.toString()` will
+ produce single-element chains.
+
+* `new Trace.parse()` now parses the output of `Chain.toString()`. It produces
+ the same result as `Chain.parse().toTrace()`.
+
## 1.4.2
* Improve the display of `data:` URIs in stack traces.
diff --git a/packages/stack_trace/lib/src/chain.dart b/packages/stack_trace/lib/src/chain.dart
index acd17e9..2e07d4b 100644
--- a/packages/stack_trace/lib/src/chain.dart
+++ b/packages/stack_trace/lib/src/chain.dart
@@ -16,10 +16,6 @@
/// A function that handles errors in the zone wrapped by [Chain.capture].
typedef void ChainHandler(error, Chain chain);
-/// The line used in the string representation of stack chains to represent
-/// the gap between traces.
-const _gap = '===== asynchronous gap ===========================\n';
-
/// A chain of stack traces.
///
/// A stack chain is a collection of one or more stack traces that collectively
@@ -119,11 +115,15 @@
/// Parses a string representation of a stack chain.
///
- /// Specifically, this parses the output of [Chain.toString].
+ /// If [chain] is the output of a call to [Chain.toString], it will be parsed
+ /// as a full stack chain. Otherwise, it will be parsed as in [Trace.parse]
+ /// and returned as a single-trace chain.
factory Chain.parse(String chain) {
if (chain.isEmpty) return new Chain([]);
+ if (!chain.contains(chainGap)) return new Chain([new Trace.parse(chain)]);
+
return new Chain(
- chain.split(_gap).map((trace) => new Trace.parseFriendly(trace)));
+ chain.split(chainGap).map((trace) => new Trace.parseFriendly(trace)));
}
/// Returns a new [Chain] comprised of [traces].
@@ -191,6 +191,6 @@
return trace.frames.map((frame) {
return '${padRight(frame.location, longest)} ${frame.member}\n';
}).join();
- }).join(_gap);
+ }).join(chainGap);
}
}
diff --git a/packages/stack_trace/lib/src/trace.dart b/packages/stack_trace/lib/src/trace.dart
index f615cd2..7f2c662 100644
--- a/packages/stack_trace/lib/src/trace.dart
+++ b/packages/stack_trace/lib/src/trace.dart
@@ -111,7 +111,8 @@
/// Parses a string representation of a stack trace.
///
/// [trace] should be formatted in the same way as a Dart VM or browser stack
- /// trace.
+ /// trace. If it's formatted as a stack chain, this will return the equivalent
+ /// of [Chain.toTrace].
factory Trace.parse(String trace) {
try {
if (trace.isEmpty) return new Trace(<Frame>[]);
@@ -120,6 +121,7 @@
if (trace.contains(_firefoxSafariTrace)) {
return new Trace.parseFirefox(trace);
}
+ if (trace.contains(chainGap)) return new Chain.parse(trace).toTrace();
if (trace.contains(_friendlyTrace)) {
return new Trace.parseFriendly(trace);
}
diff --git a/packages/stack_trace/lib/src/utils.dart b/packages/stack_trace/lib/src/utils.dart
index 62a2820..1d09443 100644
--- a/packages/stack_trace/lib/src/utils.dart
+++ b/packages/stack_trace/lib/src/utils.dart
@@ -4,6 +4,10 @@
library stack_trace.src.utils;
+/// The line used in the string representation of stack chains to represent
+/// the gap between traces.
+const chainGap = '===== asynchronous gap ===========================\n';
+
/// Returns [string] with enough spaces added to the end to make it [length]
/// characters long.
String padRight(String string, int length) {
diff --git a/packages/stack_trace/pubspec.yaml b/packages/stack_trace/pubspec.yaml
index 5c8b720..932f648 100644
--- a/packages/stack_trace/pubspec.yaml
+++ b/packages/stack_trace/pubspec.yaml
@@ -7,7 +7,7 @@
#
# When the major version is upgraded, you *must* update that version constraint
# in pub to stay in sync with this.
-version: 1.4.2
+version: 1.5.0
author: "Dart Team <misc@dartlang.org>"
homepage: http://github.com/dart-lang/stack_trace
description: >
diff --git a/packages/usage/.gitignore b/packages/usage/.gitignore
index 2a3df19..f8192d2 100644
--- a/packages/usage/.gitignore
+++ b/packages/usage/.gitignore
@@ -1,4 +1,6 @@
build/
+doc/api/
+.packages
packages
.buildlog
pubspec.lock
diff --git a/packages/usage/.travis.yml b/packages/usage/.travis.yml
index d1eaab0..75b3aac 100644
--- a/packages/usage/.travis.yml
+++ b/packages/usage/.travis.yml
@@ -1,14 +1,12 @@
language: dart
-before_install:
- - "export CHROME_ARGS=--no-sandbox"
- - "export DISPLAY=:99.0"
- - "sh -e /etc/init.d/xvfb start"
-script: ./tool/travis.sh
-
-# Use container based builds; enable caching.
+dart:
+ - stable
+ - dev
sudo: false
-cache:
- apt: true
- directories:
- - .pub
- - .pub-cache
+
+# before_install:
+# - "export CHROME_ARGS=--no-sandbox"
+# - "export DISPLAY=:99.0"
+# - "sh -e /etc/init.d/xvfb start"
+
+script: ./tool/travis.sh
diff --git a/packages/usage/changelog.md b/packages/usage/changelog.md
index 6d49030..b20ef31 100644
--- a/packages/usage/changelog.md
+++ b/packages/usage/changelog.md
@@ -1,5 +1,9 @@
# Changelog
+## 1.0.1
+- make strong mode compliant
+- update some dev package dependencies
+
## 1.0.0
- Rev'd to 1.0.0!
- No other changes from the `0.0.6` release
diff --git a/packages/usage/lib/src/usage_impl.dart b/packages/usage/lib/src/usage_impl.dart
index fc2c8a7..307b6ba 100644
--- a/packages/usage/lib/src/usage_impl.dart
+++ b/packages/usage/lib/src/usage_impl.dart
@@ -91,14 +91,14 @@
bool get hasSetOptIn => properties['optIn'] != null;
Future sendScreenView(String viewName) {
- Map args = {'cd': viewName};
+ Map<String, dynamic> args = {'cd': viewName};
return _sendPayload('screenview', args);
}
Future sendEvent(String category, String action, {String label, int value}) {
if (!optIn) return new Future.value();
- Map args = {'ec': category, 'ea': action};
+ Map<String, dynamic> args = {'ec': category, 'ea': action};
if (label != null) args['el'] = label;
if (value != null) args['ev'] = value;
return _sendPayload('event', args);
@@ -107,7 +107,7 @@
Future sendSocial(String network, String action, String target) {
if (!optIn) return new Future.value();
- Map args = {'sn': network, 'sa': action, 'st': target};
+ Map<String, dynamic> args = {'sn': network, 'sa': action, 'st': target};
return _sendPayload('social', args);
}
@@ -115,7 +115,7 @@
String label}) {
if (!optIn) return new Future.value();
- Map args = {'utv': variableName, 'utt': time};
+ Map<String, dynamic> args = {'utv': variableName, 'utt': time};
if (label != null) args['utl'] = label;
if (category != null) args['utc'] = category;
return _sendPayload('timing', args);
@@ -140,7 +140,7 @@
description = description.substring(0, _MAX_EXCEPTION_LENGTH);
}
- Map args = {'exd': description};
+ Map<String, dynamic> args = {'exd': description};
if (fatal != null && fatal) args['exf'] = '1';
return _sendPayload('exception', args);
}
@@ -176,7 +176,7 @@
// Valid values for [hitType] are: 'pageview', 'screenview', 'event',
// 'transaction', 'item', 'social', 'exception', and 'timing'.
- Future _sendPayload(String hitType, Map args) {
+ Future _sendPayload(String hitType, Map<String, dynamic> args) {
if (_bucket.removeDrop()) {
_initClientId();
@@ -228,5 +228,5 @@
* send the information should be silent.
*/
abstract class PostHandler {
- Future sendPost(String url, Map<String, String> parameters);
+ Future sendPost(String url, Map<String, dynamic> parameters);
}
diff --git a/packages/usage/lib/src/usage_impl_html.dart b/packages/usage/lib/src/usage_impl_html.dart
index fafd77a..e3f6496 100644
--- a/packages/usage/lib/src/usage_impl_html.dart
+++ b/packages/usage/lib/src/usage_impl_html.dart
@@ -15,7 +15,7 @@
HtmlPostHandler({Function this.mockRequestor});
- Future sendPost(String url, Map<String, String> parameters) {
+ Future sendPost(String url, Map<String, dynamic> parameters) {
int viewportWidth = document.documentElement.clientWidth;
int viewportHeight = document.documentElement.clientHeight;
diff --git a/packages/usage/lib/src/usage_impl_io.dart b/packages/usage/lib/src/usage_impl_io.dart
index 66fe0cb..d59793e 100644
--- a/packages/usage/lib/src/usage_impl_io.dart
+++ b/packages/usage/lib/src/usage_impl_io.dart
@@ -39,7 +39,7 @@
IOPostHandler({HttpClient this.mockClient}) : _userAgent = _createUserAgent();
- Future sendPost(String url, Map<String, String> parameters) {
+ Future sendPost(String url, Map<String, dynamic> parameters) {
// Add custom parameters for OS and the Dart version.
parameters['cd1'] = Platform.operatingSystem;
parameters['cd2'] = 'dart ${_dartVersion()}';
diff --git a/packages/usage/pubspec.yaml b/packages/usage/pubspec.yaml
index 28905c5..4d6ca7b 100644
--- a/packages/usage/pubspec.yaml
+++ b/packages/usage/pubspec.yaml
@@ -3,7 +3,7 @@
# BSD-style license that can be found in the LICENSE file.
name: usage
-version: 1.0.0+1
+version: 1.0.1
description: A Google Analytics wrapper for both command-line and web apps.
homepage: https://github.com/dart-lang/usage
author: Dart Team <misc@dartlang.org>
@@ -12,6 +12,6 @@
sdk: '>=1.0.0 <2.0.0'
dev_dependencies:
- browser: any
- grinder: '>=0.6.6+3 <0.7.0'
- unittest: any
+ browser: ^0.10.0
+ grinder: ^0.7.0
+ test: ^0.12.0
diff --git a/packages/usage/test/hit_types_test.dart b/packages/usage/test/hit_types_test.dart
index 6cfa321..4bfb608 100644
--- a/packages/usage/test/hit_types_test.dart
+++ b/packages/usage/test/hit_types_test.dart
@@ -6,11 +6,13 @@
import 'dart:async';
+import 'package:test/test.dart';
import 'package:usage/usage.dart';
-import 'package:unittest/unittest.dart';
import 'src/common.dart';
+main() => defineTests();
+
void defineTests() {
group('screenView', () {
test('simple', () {
@@ -81,8 +83,6 @@
AnalyticsTimer timer =
mock.startTimer('compile', category: 'Build', label: 'Compile');
- int time;
-
return new Future.delayed(new Duration(milliseconds: 20), () {
return timer.finish().then((_) {
expect(mock.mockPostHandler.sentValues, isNot(isEmpty));
diff --git a/packages/usage/test/src/common.dart b/packages/usage/test/src/common.dart
index 44c9960..238ce6d 100644
--- a/packages/usage/test/src/common.dart
+++ b/packages/usage/test/src/common.dart
@@ -6,7 +6,7 @@
import 'dart:async';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:usage/src/usage_impl.dart';
AnalyticsImplMock createMock({bool setOptIn: true}) =>
@@ -42,9 +42,9 @@
}
class MockPostHandler extends PostHandler {
- List<Map> sentValues = [];
+ List<Map<String, dynamic>> sentValues = [];
- Future sendPost(String url, Map<String, String> parameters) {
+ Future sendPost(String url, Map<String, dynamic> parameters) {
sentValues.add(parameters);
return new Future.value();
diff --git a/packages/usage/test/usage_impl_io_test.dart b/packages/usage/test/usage_impl_io_test.dart
index a0eec18..e41f241 100644
--- a/packages/usage/test/usage_impl_io_test.dart
+++ b/packages/usage/test/usage_impl_io_test.dart
@@ -7,15 +7,17 @@
import 'dart:async';
import 'dart:io';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:usage/src/usage_impl_io.dart';
+main() => defineTests();
+
void defineTests() {
group('IOPostHandler', () {
test('sendPost', () {
var httpClient = new MockHttpClient();
IOPostHandler postHandler = new IOPostHandler(mockClient: httpClient);
- Map args = {'utv': 'varName', 'utt': 123};
+ Map<String, dynamic> args = {'utv': 'varName', 'utt': 123};
return postHandler.sendPost('http://www.google.com', args).then((_) {
expect(httpClient.sendCount, 1);
});
diff --git a/packages/usage/test/usage_impl_test.dart b/packages/usage/test/usage_impl_test.dart
index 8601cee..b8c0f05 100644
--- a/packages/usage/test/usage_impl_test.dart
+++ b/packages/usage/test/usage_impl_test.dart
@@ -4,11 +4,13 @@
library usage.impl_test;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:usage/src/usage_impl.dart';
import 'src/common.dart';
+main() => defineTests();
+
void defineTests() {
group('ThrottlingBucket', () {
test('can send', () {
@@ -64,7 +66,7 @@
group('postEncode', () {
test('simple', () {
- Map map = {'foo': 'bar', 'baz': 'qux norf'};
+ Map<String, dynamic> map = {'foo': 'bar', 'baz': 'qux norf'};
expect(postEncode(map), 'foo=bar&baz=qux%20norf');
});
});
diff --git a/packages/usage/test/usage_test.dart b/packages/usage/test/usage_test.dart
index 6184f09..bdbd6f1 100644
--- a/packages/usage/test/usage_test.dart
+++ b/packages/usage/test/usage_test.dart
@@ -4,9 +4,11 @@
library usage.usage_test;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:usage/usage.dart';
+main() => defineTests();
+
void defineTests() {
group('AnalyticsMock', () {
test('simple', () {
diff --git a/packages/usage/test/uuid_test.dart b/packages/usage/test/uuid_test.dart
index cdde2b6..335d878 100644
--- a/packages/usage/test/uuid_test.dart
+++ b/packages/usage/test/uuid_test.dart
@@ -4,9 +4,11 @@
library usage.uuid_test;
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart';
import 'package:usage/src/uuid.dart';
+main() => defineTests();
+
void defineTests() {
group('uuid', () {
// xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
diff --git a/packages/usage/test/web_test.dart b/packages/usage/test/web_test.dart
index 0b79e89..5a9cd98 100644
--- a/packages/usage/test/web_test.dart
+++ b/packages/usage/test/web_test.dart
@@ -2,23 +2,20 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+@TestOn("browser")
library usage.web_test;
import 'dart:async';
-import 'package:grinder/src/webtest.dart';
+import 'package:test/test.dart';
import 'package:usage/src/usage_impl_html.dart';
-import 'package:unittest/unittest.dart';
import 'hit_types_test.dart' as hit_types_test;
-import 'usage_test.dart' as usage_test;
import 'usage_impl_test.dart' as usage_impl_test;
+import 'usage_test.dart' as usage_test;
import 'uuid_test.dart' as uuid_test;
void main() {
- // Set up the test environment.
- WebTestConfiguration.setupTestEnvironment();
-
// Define the tests.
hit_types_test.defineTests();
usage_test.defineTests();
diff --git a/packages/usage/tool/grind.dart b/packages/usage/tool/grind.dart
index da63dc2..f1d8121 100644
--- a/packages/usage/tool/grind.dart
+++ b/packages/usage/tool/grind.dart
@@ -8,40 +8,35 @@
import 'package:grinder/grinder.dart';
-final Directory BUILD_DIR = new Directory('build');
-final Directory BUILD_TEST_DIR = new Directory('build/test');
+final Directory _buildExampleDir = new Directory('build/example');
-void main(List<String> args) {
- task('init', init);
- task('build', build, ['init']);
- task('clean', clean);
+main(List<String> args) => grind(args);
- startGrinder(args);
-}
-
-/// Do any necessary build set up.
-void init(GrinderContext context) {
+@Task('Do any necessary build set up')
+void init() {
// Verify we're running in the project root.
if (!getDir('lib').existsSync() || !getFile('pubspec.yaml').existsSync()) {
context.fail('This script must be run from the project root.');
}
- BUILD_TEST_DIR.createSync(recursive: true);
+ _buildExampleDir.createSync(recursive: true);
}
-void build(GrinderContext context) {
+@Task()
+@Depends(init)
+void build() {
// Compile `test/web_test.dart` to the `build/test` dir; measure its size.
- File srcFile = new File('test/web_test.dart');
- Dart2js.compile(context, srcFile, outDir: BUILD_TEST_DIR, minify: true);
- File outFile = joinFile(BUILD_TEST_DIR, ['web_test.dart.js']);
+ File srcFile = new File('example/example.dart');
+ Dart2js.compile(srcFile, outDir: _buildExampleDir, minify: true);
+ File outFile = joinFile(_buildExampleDir, ['example.dart.js']);
context.log('${outFile.path} compiled to ${_printSize(outFile)}');
}
-/// Delete all generated artifacts.
-void clean(GrinderContext context) {
+@Task('Delete all generated artifacts')
+void clean() {
// Delete the build/ dir.
- deleteEntity(BUILD_DIR, context);
+ delete(buildDir);
}
String _printSize(File file) => '${(file.lengthSync() + 1023) ~/ 1024}k';
diff --git a/packages/usage/tool/travis.sh b/packages/usage/tool/travis.sh
index 699e808..5b6e2dd 100755
--- a/packages/usage/tool/travis.sh
+++ b/packages/usage/tool/travis.sh
@@ -15,11 +15,15 @@
test/all.dart
# Run the tests.
-dart test/all.dart
+dart -c test/all.dart
# Run the UI/web tests as well.
-pub build test
-pub run grinder:test build/test/web.html
+#pub build test
+#pub run grinder:test build/test/web.html
+
+# Verify against DDC.
+pub global activate dev_compiler
+pub global run dev_compiler lib/usage_html.dart
# Measure the size of the compiled JS, for the dart:html version of the library.
dart tool/grind.dart build
diff --git a/pubspec.yaml b/pubspec.yaml
index 85abc62..e964459 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -7,15 +7,17 @@
inline_stylesheets:
lib/src/elements/css/shared.css: false
packages/charted/charts/themes/quantum_theme.css: false
+ $exclude: [web/third_party/*.html, web/timeline.html]
- $dart2js:
$include: "**/*.polymer.bootstrap.dart"
suppressWarnings: false
commandLineOptions: [--show-package-warnings]
dependencies:
args: any
- charted: ^0.2.9
+ charted: ^0.3.0
polymer: ^0.16.3
unittest: < 0.12.0
usage: any
dependency_overrides:
- analyzer: ^0.26.1
+ analyzer: '>=0.26.1 <0.26.1+15'
+ dart_style: <0.2.2