Index: pkg/analyzer/lib/src/task/incremental_element_builder.dart |
diff --git a/pkg/analyzer/lib/src/task/incremental_element_builder.dart b/pkg/analyzer/lib/src/task/incremental_element_builder.dart |
index fdf8717b7ffabebc7755fc3f95e8d02dee354d61..15f02dc33abd328efbd9fa5c78537da41d87b493 100644 |
--- a/pkg/analyzer/lib/src/task/incremental_element_builder.dart |
+++ b/pkg/analyzer/lib/src/task/incremental_element_builder.dart |
@@ -13,29 +13,54 @@ import 'package:analyzer/src/generated/scanner.dart'; |
import 'package:analyzer/src/generated/source.dart'; |
/** |
- * Incrementally updates the existing [oldUnitElement] and builds elements for |
+ * Incrementally updates the existing [unitElement] and builds elements for |
* the [newUnit]. |
*/ |
class IncrementalCompilationUnitElementBuilder { |
final Source source; |
final CompilationUnit oldUnit; |
- final CompilationUnitElement oldUnitElement; |
+ final CompilationUnitElementImpl unitElement; |
final CompilationUnit newUnit; |
+ final ElementHolder holder = new ElementHolder(); |
IncrementalCompilationUnitElementBuilder( |
CompilationUnit oldUnit, this.newUnit) |
: oldUnit = oldUnit, |
- oldUnitElement = oldUnit.element, |
+ unitElement = oldUnit.element, |
source = oldUnit.element.source; |
void build() { |
new CompilationUnitBuilder().buildCompilationUnit(source, newUnit); |
_processDirectives(); |
+ _processUnitMembers(); |
+ newUnit.element = unitElement; |
+ } |
+ |
+ void _addElementsToHolder(CompilationUnitMember node) { |
+ List<Element> elements = _getElements(node); |
+ elements.forEach(_addElementToHolder); |
+ } |
+ |
+ void _addElementToHolder(Element element) { |
+ if (element is PropertyAccessorElement) { |
+ holder.addAccessor(element); |
+ } else if (element is ClassElement) { |
+ if (element.isEnum) { |
+ holder.addEnum(element); |
+ } else { |
+ holder.addType(element); |
+ } |
+ } else if (element is FunctionElement) { |
+ holder.addFunction(element); |
+ } else if (element is FunctionTypeAliasElement) { |
+ holder.addTypeAlias(element); |
+ } else if (element is TopLevelVariableElement) { |
+ holder.addTopLevelVariable(element); |
+ } |
} |
void _processDirectives() { |
- Map<String, Directive> oldDirectiveMap = <String, Directive>{}; |
- // Fill the old directives map. |
+ Map<String, Directive> oldDirectiveMap = new HashMap<String, Directive>(); |
for (Directive oldDirective in oldUnit.directives) { |
String code = TokenUtils.getFullCode(oldDirective); |
oldDirectiveMap[code] = oldDirective; |
@@ -56,15 +81,45 @@ class IncrementalCompilationUnitElementBuilder { |
} |
} |
// Do replacement. |
- _replaceNode(newDirective, oldDirective, oldDirective.element); |
+ _replaceNode(newDirective, oldDirective); |
+ } |
+ } |
+ |
+ void _processUnitMembers() { |
+ Map<String, CompilationUnitMember> oldNodeMap = |
+ new HashMap<String, CompilationUnitMember>(); |
+ for (CompilationUnitMember oldNode in oldUnit.declarations) { |
+ String code = TokenUtils.getFullCode(oldNode); |
+ oldNodeMap[code] = oldNode; |
+ } |
+ // Replace new nodes with the identical old nodes. |
+ for (CompilationUnitMember newNode in newUnit.declarations) { |
+ String code = TokenUtils.getFullCode(newNode); |
+ // Prepare an old node. |
+ CompilationUnitMember oldNode = oldNodeMap[code]; |
+ if (oldNode == null) { |
+ _addElementsToHolder(newNode); |
+ continue; |
+ } |
+ // Do replacement. |
+ _replaceNode(newNode, oldNode); |
+ _addElementsToHolder(oldNode); |
} |
+ // Update CompilationUnitElement. |
+ unitElement.accessors = holder.accessors; |
+ unitElement.enums = holder.enums; |
+ unitElement.functions = holder.functions; |
+ unitElement.typeAliases = holder.typeAliases; |
+ unitElement.types = holder.types; |
+ unitElement.topLevelVariables = holder.topLevelVariables; |
+ holder.validate(); |
} |
/** |
* Replaces [newNode] with [oldNode], updates tokens and elements. |
* The nodes must have the same tokens, but offsets may be different. |
*/ |
- void _replaceNode(AstNode newNode, AstNode oldNode, Element oldElement) { |
+ void _replaceNode(AstNode newNode, AstNode oldNode) { |
// Replace node. |
NodeReplacer.replace(newNode, oldNode); |
// Replace tokens. |
@@ -79,7 +134,44 @@ class IncrementalCompilationUnitElementBuilder { |
TokenUtils.copyTokenOffsets(offsetMap, oldNode.beginToken, |
newNode.beginToken, oldNode.endToken, newNode.endToken, true); |
// Change elements offsets. |
- oldElement.accept(new _UpdateElementOffsetsVisitor(offsetMap)); |
+ { |
+ var visitor = new _UpdateElementOffsetsVisitor(offsetMap); |
+ List<Element> elements = _getElements(oldNode); |
+ for (Element element in elements) { |
+ element.accept(visitor); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Returns [Element]s that are declared directly by the given [node]. |
+ * This does not include any child elements - parameters, local variables. |
+ * |
+ * Usually just one [Element] is returned, but [VariableDeclarationList] |
+ * nodes may declare more than one. |
+ */ |
+ static List<Element> _getElements(AstNode node) { |
+ List<Element> elements = <Element>[]; |
+ if (node is TopLevelVariableDeclaration) { |
+ VariableDeclarationList variableList = node.variables; |
+ if (variableList != null) { |
+ for (VariableDeclaration variable in variableList.variables) { |
+ TopLevelVariableElement element = variable.element; |
+ elements.add(element); |
+ elements.add(element.getter); |
+ elements.add(element.setter); |
+ } |
+ } |
+ } else if (node is Directive && node.element != null) { |
+ elements.add(node.element); |
+ } else if (node is Declaration && node.element != null) { |
+ Element element = node.element; |
+ elements.add(element); |
+ if (element is PropertyAccessorElement) { |
+ elements.add(element.variable); |
+ } |
+ } |
+ return elements; |
} |
} |
@@ -100,16 +192,19 @@ class TokenUtils { |
copyTokenOffsets(offsetMap, (oldToken as CommentToken).parent, |
(newToken as CommentToken).parent, oldEndToken, newEndToken); |
} |
- while (oldToken.type != TokenType.EOF) { |
+ while (oldToken != null) { |
offsetMap[oldToken.offset] = newToken.offset; |
oldToken.offset = newToken.offset; |
oldToken = oldToken.next; |
newToken = newToken.next; |
} |
+ assert(oldToken == null); |
+ assert(newToken == null); |
+ return; |
} |
while (true) { |
if (oldToken.precedingComments != null) { |
- assert(newToken.precedingComments == null); |
+ assert(newToken.precedingComments != null); |
copyTokenOffsets(offsetMap, oldToken.precedingComments, |
newToken.precedingComments, oldEndToken, newEndToken); |
} |
@@ -140,9 +235,12 @@ class TokenUtils { |
return joinTokens(tokens); |
} |
+ /** |
+ * Returns all tokends (including comments) of the given [node]. |
+ */ |
static List<Token> getTokens(AstNode node) { |
List<Token> tokens = <Token>[]; |
- Token token = node.beginToken; |
+ Token token = getBeginTokenNotComment(node); |
Token endToken = node.endToken; |
while (true) { |
// append comment tokens |
@@ -177,9 +275,11 @@ class _UpdateElementOffsetsVisitor extends GeneralizingElementVisitor { |
visitElement(Element element) { |
int oldOffset = element.nameOffset; |
- int newOffset = map[oldOffset]; |
- assert(newOffset != null); |
- (element as ElementImpl).nameOffset = newOffset; |
+ if (oldOffset != -1) { |
+ int newOffset = map[oldOffset]; |
+ assert(newOffset != null); |
+ (element as ElementImpl).nameOffset = newOffset; |
+ } |
if (element is! LibraryElement) { |
super.visitElement(element); |
} |