Chromium Code Reviews| Index: pkg/code_transformers/lib/src/resolver_impl.dart |
| diff --git a/pkg/code_transformers/lib/src/resolver_impl.dart b/pkg/code_transformers/lib/src/resolver_impl.dart |
| index 9322ccd5f8af4f7d409342c36fff0b9141478acc..e7820e6c71a955086b71e6a20ad3a22f5f3c3260 100644 |
| --- a/pkg/code_transformers/lib/src/resolver_impl.dart |
| +++ b/pkg/code_transformers/lib/src/resolver_impl.dart |
| @@ -8,7 +8,10 @@ import 'dart:async'; |
| import 'package:analyzer/src/generated/ast.dart'; |
| import 'package:analyzer/src/generated/element.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| +import 'package:analyzer/src/generated/error.dart'; |
| import 'package:analyzer/src/generated/java_io.dart'; |
| +import 'package:analyzer/src/generated/parser.dart'; |
| +import 'package:analyzer/src/generated/scanner.dart'; |
| import 'package:analyzer/src/generated/sdk.dart' show DartSdk; |
| import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk; |
| import 'package:analyzer/src/generated/source.dart'; |
| @@ -111,6 +114,7 @@ class ResolverImpl implements Resolver { |
| // and see if it changed, then walk all files accessed by it. |
| var visited = new Set<AssetId>(); |
| var visiting = new FutureGroup(); |
| + var toUpdate = []; |
| void processAsset(AssetId assetId) { |
| visited.add(assetId); |
| @@ -121,12 +125,10 @@ class ResolverImpl implements Resolver { |
| source = new _AssetBasedSource(assetId, this); |
| sources[assetId] = source; |
| } |
| - source.updateContents(contents); |
| - |
| - source.dependentAssets |
| - .where((id) => !visited.contains(id)) |
| + source.updateDependencies(contents); |
| + toUpdate.add(new _PendingUpdate(source, contents)); |
| + source.dependentAssets.where((id) => !visited.contains(id)) |
| .forEach(processAsset); |
| - |
| }, onError: (e) { |
| _context.applyChanges(new ChangeSet()..removedSource(sources[assetId])); |
| sources.remove(assetId); |
| @@ -138,6 +140,7 @@ class ResolverImpl implements Resolver { |
| // resolve everything. |
| return visiting.future.then((_) { |
| var changeSet = new ChangeSet(); |
| + toUpdate.forEach((pending) => pending.apply(changeSet)); |
| var unreachableAssets = new Set.from(sources.keys).difference(visited); |
| for (var unreachable in unreachableAssets) { |
| changeSet.removedSource(sources[unreachable]); |
| @@ -275,30 +278,26 @@ class _AssetBasedSource extends Source { |
| _AssetBasedSource(this.assetId, this._resolver); |
| + /// Update the dependencies of this source. This parses [contents] but avoids |
| + /// any analyzer resolution. |
| + void updateDependencies(String contents) { |
| + if (contents == _contents) return; |
| + var unit = _parseCompilationUnit(contents); |
| + _dependentAssets = unit.directives |
| + .where((d) => (d is ImportDirective || d is PartDirective || |
| + d is ExportDirective)) |
| + .map((d) => _resolve(assetId, d.uri.stringValue, _logger, |
| + _getSpan(d, contents))) |
| + .where((id) => id != null).toSet(); |
| + } |
| + |
| /// Update the contents of this file with [contents]. |
| /// |
| /// Returns true if the contents of this asset have changed. |
| bool updateContents(String contents) { |
| if (contents == _contents) return false; |
| - var added = _contents == null; |
| _contents = contents; |
| ++_revision; |
| - // Invalidate the imports so we only parse the AST when needed. |
| - _dependentAssets = null; |
| - |
| - if (added) { |
| - _resolver._context.applyChanges(new ChangeSet()..addedSource(this)); |
| - } else { |
| - _resolver._context.applyChanges(new ChangeSet()..changedSource(this)); |
| - } |
| - |
| - var compilationUnit = _resolver._context.parseCompilationUnit(this); |
| - _dependentAssets = compilationUnit.directives |
| - .where((d) => (d is ImportDirective || d is PartDirective || |
| - d is ExportDirective)) |
| - .map((d) => _resolve(assetId, d.uri.stringValue, |
| - _logger, _getSpan(d))) |
| - .where((id) => id != null).toSet(); |
| return true; |
| } |
| @@ -357,13 +356,13 @@ class _AssetBasedSource extends Source { |
| } |
| /// For logging errors. |
| - Span _getSpan(AstNode node) => _sourceFile.span(node.offset, node.end); |
| + Span _getSpan(AstNode node, [String contents]) => |
| + _getSourceFile(contents).span(node.offset, node.end); |
| /// For logging errors. |
| - SourceFile get _sourceFile { |
| + SourceFile _getSourceFile([String contents]) { |
| var uri = getSourceUri(_resolver.entryPoint); |
| var path = uri != null ? uri.toString() : assetId.path; |
| - |
| - return new SourceFile.text(path, rawContents); |
| + return new SourceFile.text(path, contents != null ? contents : rawContents); |
| } |
| /// Gets a URI which would be appropriate for importing this file. |
| @@ -574,3 +573,39 @@ class FutureGroup<E> { |
| */ |
| Future<List<E>> get future => _completer.future; |
| } |
| + |
| +/// Parse [code] using just the analyzer's parser, without actually getting the |
| +/// resolver to run. |
| +CompilationUnit _parseCompilationUnit(String code) { |
|
blois
2014/03/17 16:29:46
Should be able to use package:analyzer/analyzer.da
Siggi Cherem (dart-lang)
2014/03/17 17:58:09
Done.
|
| + var errorListener = new _ErrorCollector(); |
| + var reader = new CharSequenceReader(code); |
| + var scanner = new Scanner(null, reader, errorListener); |
| + var token = scanner.tokenize(); |
| + var parser = new Parser(null, errorListener); |
| + return parser.parseCompilationUnit(token); |
| +} |
| + |
| +class _ErrorCollector extends AnalysisErrorListener { |
| + final errors = <AnalysisError>[]; |
| + onError(error) => errors.add(error); |
| +} |
| + |
| +/// A pending update to notify the resolver that a [Source] has been added or |
| +/// changed. This is used by the `_performResolve` algorithm above to apply all |
| +/// changes after it first discovers the transitive closure of files that are |
| +/// reachable from the sources. |
| +class _PendingUpdate { |
| + _AssetBasedSource source; |
| + String content; |
| + |
| + _PendingUpdate(this.source, this.content); |
| + |
| + void apply(ChangeSet changeSet) { |
| + if (!source.updateContents(content)) return; |
| + if (source._revision == 1 && source._contents != null) { |
| + changeSet.addedSource(source); |
| + } else { |
| + changeSet.changedSource(source); |
| + } |
| + } |
| +} |