Chromium Code Reviews| Index: pkg/analyzer/lib/src/generated/incremental_resolver.dart |
| diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart |
| index 97a36a994f8003e96d1efad13b31890109e0d65f..009e01e77302776c8560ee801e81e484ac7e1d6c 100644 |
| --- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart |
| +++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart |
| @@ -5,6 +5,7 @@ |
| library engine.incremental_resolver; |
| import 'dart:collection'; |
| +import 'dart:math' as math; |
| import 'ast.dart'; |
| import 'element.dart'; |
| @@ -13,6 +14,151 @@ import 'java_engine.dart'; |
| import 'resolver.dart'; |
| import 'scanner.dart'; |
| import 'source.dart'; |
| +import 'parser.dart'; |
| + |
| + |
| +/** |
| + * Attempts to update [oldUnit] to the state that would correspond to [newCode]. |
| + * Returns `true` if success, or `false` otherwise. |
| + * The [oldUnit] might be damaged. |
| + */ |
| +bool poorMansIncrementalResolution(TypeProvider typeProvider, |
| + CompilationUnit oldUnit, String newCode) { |
| + try { |
| + CompilationUnit newUnit = _parseUnit(newCode); |
| + _TokenPair firstPair = |
| + _findFirstDifferentToken(oldUnit.beginToken, newUnit.beginToken); |
| + _TokenPair lastPair = |
| + _findLastDifferentToken(oldUnit.endToken, newUnit.endToken); |
| + if (firstPair != null && lastPair != null) { |
| + AstNode oldNode = _findNodeWithTokens(oldUnit, firstPair.a, lastPair.a); |
| + AstNode newNode = _findNodeWithTokens(newUnit, firstPair.b, lastPair.b); |
| + // Try to find the smallest common node, a FunctionBody currently. |
| + { |
| + List<AstNode> oldParents = _getParents(oldNode); |
|
Brian Wilkerson
2014/11/24 15:09:14
Why compute a list? Why not just walk up both pare
|
| + List<AstNode> newParents = _getParents(newNode); |
| + int length = math.min(oldParents.length, newParents.length); |
| + bool found = false; |
| + for (int i = 0; i < length; i++) { |
| + AstNode oldParent = oldParents[i]; |
| + AstNode newParent = newParents[i]; |
| + if (oldParent is FunctionBody && |
| + newParent is FunctionBody) { |
| + oldNode = oldParent; |
| + newNode = newParent; |
| + found = true; |
| + break; |
| + } |
| + } |
| + if (!found) { |
| + return false; |
| + } |
| + } |
| + // replace node |
| + NodeReplacer.replace(oldNode, newNode); |
| + // update token references |
| + firstPair.a.previous.setNext(firstPair.b); |
| + lastPair.b.setNext(firstPair.a.next); |
|
Paul Berry
2014/11/24 15:02:25
I think this should be:
lastPair.b.setNext(last
scheglov
2014/11/24 18:59:06
Done.
|
| + // perform incremental resolution |
| + // TODO(scheglov) update errors |
| + AnalysisErrorListener errorListener = new BooleanErrorListener(); |
| + CompilationUnitElement oldUnitElement = oldUnit.element; |
| + IncrementalResolver incrementalResolver = new IncrementalResolver( |
| + errorListener, |
| + typeProvider, |
| + oldUnitElement.library, |
| + oldUnitElement, |
| + oldUnitElement.source, |
| + oldNode.offset, |
| + oldNode.length, |
| + newNode.length); |
| + incrementalResolver.resolve(newNode); |
| + return true; |
| + } |
| + } catch (e) { |
| + } |
|
Paul Berry
2014/11/24 15:02:25
Can we report the exception using the "server.erro
scheglov
2014/11/24 18:59:05
We're in a wrong project to do this.
I'll add TODO
|
| + return false; |
| +} |
| + |
| + |
| +List<AstNode> _getParents(AstNode node) { |
| + List<AstNode> parents = <AstNode>[]; |
| + while (node != null) { |
| + parents.insert(0, node); |
| + node = node.parent; |
| + } |
| + return parents; |
| +} |
| + |
| +AstNode _findNodeWithTokens(AstNode root, Token first, Token last) { |
| + int offset = first.offset; |
| + int end = last.end; |
| + NodeLocator nodeLocator = new NodeLocator.con2(offset, end); |
| + return nodeLocator.searchWithin(root); |
| +} |
| + |
| + |
| +class _TokenPair { |
| + final Token a; |
|
Paul Berry
2014/11/24 15:02:25
It looks like all uses of _TokenPair use "a" to re
scheglov
2014/11/24 18:59:05
Done.
|
| + final Token b; |
| + _TokenPair(this.a, this.b); |
| +} |
| + |
| + |
| +_TokenPair _findFirstDifferentToken(Token a, Token b) { |
|
Paul Berry
2014/11/24 15:02:25
The same rename would be nice here too (and also i
scheglov
2014/11/24 18:59:05
Done.
|
| +// print('first ------------'); |
|
Paul Berry
2014/11/24 15:02:25
Commented out debug code should be removed (and al
scheglov
2014/11/24 18:59:05
I'd prefer to keep it in for some time and remove
|
| + while (true) { |
| +// print('a: $a @ ${a.offset}'); |
| +// print('b: $b @ ${b.offset}'); |
| + if (!_equalToken(a, b, 0)) { |
| + return new _TokenPair(a, b); |
| + } |
| + if (a.type == TokenType.EOF) { |
|
Brian Wilkerson
2014/11/24 15:09:13
Why not make this part of the while loop's conditi
scheglov
2014/11/24 18:59:06
Done.
|
| + return null; |
| + } |
| + a = a.next; |
| + b = b.next; |
| + } |
| + return null; |
| +} |
| + |
| + |
| +_TokenPair _findLastDifferentToken(Token a, Token b) { |
| +// print('last ------------'); |
| + int delta = b.offset - a.offset; |
| + while (a.previous != a && b.previous != b) { |
| +// print('a: $a @ ${a.offset}'); |
| +// print('b: $b @ ${b.offset}'); |
| + if (!_equalToken(a, b, delta)) { |
| + return new _TokenPair(a.next, b.next); |
| + } |
| + a.offset += delta; |
| + a = a.previous; |
| + b = b.previous; |
| + } |
|
Brian Wilkerson
2014/11/24 15:09:14
For what it's worth, I would find the code easier
scheglov
2014/11/24 18:59:06
Done.
|
| +} |
| + |
| + |
| +bool _equalToken(Token a, Token b, int delta) { |
| + if (a.type != b.type) { |
| + return false; |
| + } |
| + if (b.offset - a.offset != delta) { |
| + return false; |
| + } |
| + return a.lexeme == b.lexeme; |
| +} |
| + |
| + |
| +CompilationUnit _parseUnit(String code) { |
| + // TODO(scheglov) remember and update errors |
| + var errorListener = new BooleanErrorListener(); |
| + 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); |
| +} |
| /** |
| @@ -454,8 +600,12 @@ class DeclarationMatcher extends RecursiveAstVisitor { |
| } else if (type is TypeParameterType) { |
| _assertEquals(nodeName, type.name); |
| // TODO(scheglov) it should be possible to rename type parameters |
| + } else if (type is VoidType) { |
| + _assertEquals(nodeName, 'void'); |
| + // TODO(scheglov) add test for "void" |
| } else { |
| // TODO(scheglov) support other types |
| +// print('node: $node type: $type type.type: ${type.runtimeType}'); |
| _assertTrue(false); |
| } |
| } |
| @@ -548,8 +698,8 @@ class DeclarationMatcher extends RecursiveAstVisitor { |
| void _gatherElements(Element element) { |
| _ElementsGatherer gatherer = new _ElementsGatherer(this); |
| element.accept(gatherer); |
| - // TODO(scheglov) push into CompilationUnitElement |
| - if (identical(_enclosingUnit, _enclosingLibrary.definingCompilationUnit)) { |
| + // TODO(scheglov) what if a change in a directive? |
| + if (identical(element, _enclosingLibrary.definingCompilationUnit)) { |
| gatherer.addElements(_enclosingLibrary.imports); |
| gatherer.addElements(_enclosingLibrary.exports); |
| gatherer.addElements(_enclosingLibrary.parts); |