Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(618)

Unified Diff: pkg/analysis_server/lib/src/services/completion/imported_computer.dart

Issue 742163002: cache import suggestions (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: merge Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/lib/src/services/completion/imported_computer.dart
diff --git a/pkg/analysis_server/lib/src/services/completion/imported_computer.dart b/pkg/analysis_server/lib/src/services/completion/imported_computer.dart
index 73f972b6684d1b9954ffab2dbeb9e52870aaa537..d9be1da3ba1bc6dc2e2612540223b600f4031ede 100644
--- a/pkg/analysis_server/lib/src/services/completion/imported_computer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/imported_computer.dart
@@ -5,6 +5,7 @@
library services.completion.computer.dart.toplevel;
import 'dart:async';
+import 'dart:collection';
import 'package:analysis_server/src/protocol_server.dart' hide Element,
ElementKind;
@@ -22,197 +23,264 @@ import 'package:analyzer/src/generated/source.dart';
* `completion.getSuggestions` request results.
*/
class ImportedComputer extends DartCompletionComputer {
+ _ImportedSuggestionBuilder builder;
@override
bool computeFast(DartCompletionRequest request) {
- // TODO: implement computeFast
- // - compute results based upon current search, then replace those results
- // during the full compute phase
- // - filter results based upon completion offset
- return false;
+ builder = request.node.accept(new _ImportedAstVisitor(request));
+ if (builder != null) {
+ return builder.computeFast();
+ }
+ return true;
}
@override
Future<bool> computeFull(DartCompletionRequest request) {
- return request.node.accept(new _ImportedVisitor(request));
+ if (builder != null) {
+ return builder.computeFull(request.node);
+ }
+ return new Future.value(false);
}
}
/**
- * A visitor for determining which imported classes and top level variables
- * should be suggested and building those suggestions.
+ * [_ImportedAstVisitor] determines whether an import suggestions are needed
+ * and instantiates the builder to create those suggestions.
*/
-class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> {
+class _ImportedAstVisitor extends
+ GeneralizingAstVisitor<_ImportedSuggestionBuilder> {
final DartCompletionRequest request;
- _ImportedVisitor(this.request);
+ _ImportedAstVisitor(this.request);
@override
- Future<bool> visitArgumentList(ArgumentList node) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ _ImportedSuggestionBuilder visitArgumentList(ArgumentList node) {
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
@override
- Future<bool> visitBlock(Block node) {
- return _addImportedElementSuggestions(node);
+ _ImportedSuggestionBuilder visitBlock(Block node) {
+ return new _ImportedSuggestionBuilder(request);
}
@override
- Future<bool> visitCascadeExpression(CascadeExpression node) {
+ _ImportedSuggestionBuilder visitCascadeExpression(CascadeExpression node) {
// Make suggestions for the target, but not for the selector
// InvocationComputer makes selector suggestions
Expression target = node.target;
if (target != null && request.offset <= target.end) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitClassDeclaration(ClassDeclaration node) {
+ _ImportedSuggestionBuilder visitClassDeclaration(ClassDeclaration node) {
// Make suggestions in the body of the class declaration
Token leftBracket = node.leftBracket;
if (leftBracket != null && request.offset >= leftBracket.end) {
- return _addImportedElementSuggestions(node);
+ return new _ImportedSuggestionBuilder(request);
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitExpression(Expression node) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ _ImportedSuggestionBuilder visitExpression(Expression node) {
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
@override
- Future<bool> visitExpressionStatement(ExpressionStatement node) {
+ _ImportedSuggestionBuilder
+ visitExpressionStatement(ExpressionStatement node) {
Expression expression = node.expression;
// A pre-variable declaration (e.g. C ^) is parsed as an expression
// statement. Do not make suggestions for the variable name.
if (expression is SimpleIdentifier && request.offset <= expression.end) {
- return _addImportedElementSuggestions(node);
+ return new _ImportedSuggestionBuilder(request);
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitFormalParameterList(FormalParameterList node) {
+ _ImportedSuggestionBuilder
+ visitFormalParameterList(FormalParameterList node) {
Token leftParen = node.leftParenthesis;
if (leftParen != null && request.offset > leftParen.offset) {
Token rightParen = node.rightParenthesis;
if (rightParen == null || request.offset <= rightParen.offset) {
- return _addImportedElementSuggestions(node);
+ return new _ImportedSuggestionBuilder(request);
}
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitForStatement(ForStatement node) {
+ _ImportedSuggestionBuilder visitForStatement(ForStatement node) {
Token leftParen = node.leftParenthesis;
if (leftParen != null && request.offset >= leftParen.end) {
- return _addImportedElementSuggestions(node);
+ return new _ImportedSuggestionBuilder(request);
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitIfStatement(IfStatement node) {
+ _ImportedSuggestionBuilder visitIfStatement(IfStatement node) {
Token leftParen = node.leftParenthesis;
if (leftParen != null && request.offset >= leftParen.end) {
Token rightParen = node.rightParenthesis;
if (rightParen == null || request.offset <= rightParen.offset) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitInterpolationExpression(InterpolationExpression node) {
+ _ImportedSuggestionBuilder
+ visitInterpolationExpression(InterpolationExpression node) {
Expression expression = node.expression;
if (expression is SimpleIdentifier) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitMethodInvocation(MethodInvocation node) {
+ _ImportedSuggestionBuilder visitMethodInvocation(MethodInvocation node) {
Token period = node.period;
if (period == null || request.offset <= period.offset) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitNode(AstNode node) {
- return new Future.value(false);
+ _ImportedSuggestionBuilder visitNode(AstNode node) {
+ return null;
}
@override
- Future<bool> visitPrefixedIdentifier(PrefixedIdentifier node) {
+ _ImportedSuggestionBuilder visitPrefixedIdentifier(PrefixedIdentifier node) {
// Make suggestions for the prefix, but not for the selector
// InvocationComputer makes selector suggestions
Token period = node.period;
if (period != null && request.offset <= period.offset) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitPropertyAccess(PropertyAccess node) {
+ _ImportedSuggestionBuilder visitPropertyAccess(PropertyAccess node) {
// Make suggestions for the target, but not for the property name
// InvocationComputer makes property name suggestions
var operator = node.operator;
if (operator != null && request.offset < operator.offset) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
- return new Future.value(false);
+ return null;
}
@override
- Future<bool> visitSimpleIdentifier(SimpleIdentifier node) {
+ _ImportedSuggestionBuilder visitSimpleIdentifier(SimpleIdentifier node) {
return node.parent.accept(this);
}
@override
- Future<bool> visitStringLiteral(StringLiteral node) {
- return new Future.value(false);
+ _ImportedSuggestionBuilder visitStringLiteral(StringLiteral node) {
+ return null;
}
@override
- Future<bool> visitTypeName(TypeName node) {
- return _addImportedElementSuggestions(node, typesOnly: true);
+ _ImportedSuggestionBuilder visitTypeName(TypeName node) {
+ return new _ImportedSuggestionBuilder(request, typesOnly: true);
}
@override
- visitVariableDeclaration(VariableDeclaration node) {
+ _ImportedSuggestionBuilder
+ visitVariableDeclaration(VariableDeclaration node) {
Token equals = node.equals;
// Make suggestions for the RHS of a variable declaration
if (equals != null && request.offset >= equals.end) {
- return _addImportedElementSuggestions(node, excludeVoidReturn: true);
+ return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true);
}
- return new Future.value(false);
+ return null;
}
+}
- void _addElementSuggestion(Element element, bool typesOnly,
- bool excludeVoidReturn, CompletionRelevance relevance) {
+/**
+ * [_ImportedSuggestionBuilder] traverses the imports and builds suggestions
+ * based upon imported elements.
+ */
+class _ImportedSuggestionBuilder {
+ final DartCompletionRequest request;
+ final bool typesOnly;
+ final bool excludeVoidReturn;
+ final HashSet<String> completions = new HashSet();
+ DartCompletionCache cache;
+ String importKey;
+
+ _ImportedSuggestionBuilder(this.request, {this.typesOnly: false,
+ this.excludeVoidReturn: false}) {
+ cache = request.cache;
+ }
+
+ /**
+ * Compute a hash of the import directives.
+ */
+ String get computeImportKey {
+ if (importKey == null) {
+ StringBuffer sb = new StringBuffer();
+ request.unit.directives.forEach((Directive directive) {
+ if (directive is ImportDirective) {
+ sb.write(directive.toSource());
+ }
+ });
+ importKey = sb.toString();
+ }
+ return importKey;
+ }
+
+ void addCachedSuggestions() {
+ DartCompletionCache cache = request.cache;
+ request.suggestions
+ ..addAll(cache.importedTypeSuggestions)
+ ..addAll(cache.libraryPrefixSuggestions);
+ if (!typesOnly) {
+ request.suggestions.addAll(cache.otherImportedSuggestions);
+ if (!excludeVoidReturn) {
+ request.suggestions.addAll(cache.importedVoidReturnSuggestions);
+ }
+ }
+ }
+
+ void addLibraryPrefixSuggestion(ImportElement importElem) {
+ CompletionSuggestion suggestion = null;
+ String completion = importElem.prefix.displayName;
+ if (completion != null && completion.length > 0) {
+ suggestion = new CompletionSuggestion(
+ CompletionSuggestionKind.INVOCATION,
+ CompletionRelevance.DEFAULT,
+ completion,
+ completion.length,
+ 0,
+ importElem.isDeprecated,
+ false);
+ LibraryElement lib = importElem.importedLibrary;
+ if (lib != null) {
+ suggestion.element = newElement_fromEngine(lib);
+ }
+ cache.libraryPrefixSuggestions.add(suggestion);
+ completions.add(suggestion.completion);
+ }
+ }
+
+ void addSuggestion(Element element, CompletionRelevance relevance) {
if (element is ExecutableElement) {
if (element.isOperator) {
return;
}
- if (excludeVoidReturn) {
- DartType returnType = element.returnType;
- if (returnType != null && returnType.isVoid) {
- return;
- }
- }
- }
- if (typesOnly && element is! ClassElement) {
- return;
}
String completion = element.displayName;
@@ -242,31 +310,57 @@ class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> {
}
}
- request.suggestions.add(suggestion);
+ if (element is ExecutableElement) {
+ DartType returnType = element.returnType;
+ if (returnType != null && returnType.isVoid) {
+ cache.importedVoidReturnSuggestions.add(suggestion);
+ } else {
+ cache.otherImportedSuggestions.add(suggestion);
+ }
+ } else if (element is ClassElement) {
+ cache.importedTypeSuggestions.add(suggestion);
+ } else {
+ cache.otherImportedSuggestions.add(suggestion);
+ }
+ completions.add(suggestion.completion);
}
- void _addElementSuggestions(List<Element> elements, bool typesOnly,
- bool excludeVoidReturn) {
+ void addSuggestions(List<Element> elements) {
elements.forEach((Element elem) {
- _addElementSuggestion(
- elem,
- typesOnly,
- excludeVoidReturn,
- CompletionRelevance.DEFAULT);
+ addSuggestion(elem, CompletionRelevance.DEFAULT);
});
}
- Future<bool> _addImportedElementSuggestions(AstNode node, {bool typesOnly:
- false, bool excludeVoidReturn: false}) {
+ /**
+ * If the needed information is cached, then add suggestions and return `true`
+ * else return `false` indicating that additional work is necessary.
+ */
+ bool computeFast() {
+ if (cache.importKey == computeImportKey) {
+ addCachedSuggestions();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compute suggested based upon imported elements.
+ */
+ computeFull(AstNode node) {
+ CompilationUnit unit = node.getAncestor((p) => p is CompilationUnit);
+ cache.importedTypeSuggestions = <CompletionSuggestion>[];
+ cache.libraryPrefixSuggestions = <CompletionSuggestion>[];
+ cache.otherImportedSuggestions = <CompletionSuggestion>[];
+ cache.importedVoidReturnSuggestions = <CompletionSuggestion>[];
// Exclude elements from local library
// because they are provided by LocalComputer
Set<LibraryElement> excludedLibs = new Set<LibraryElement>();
- excludedLibs.add(request.unit.element.enclosingElement);
+ excludedLibs.add(unit.element.enclosingElement);
// Include explicitly imported elements
Map<String, ClassElement> classMap = new Map<String, ClassElement>();
- request.unit.directives.forEach((Directive directive) {
+ unit.directives.forEach((Directive directive) {
if (directive is ImportDirective) {
ImportElement importElem = directive.element;
if (importElem != null && importElem.importedLibrary != null) {
@@ -278,17 +372,13 @@ class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> {
if (elem is ClassElement) {
classMap[name] = elem;
}
- _addElementSuggestion(
- elem,
- typesOnly,
- excludeVoidReturn,
- CompletionRelevance.DEFAULT);
+ addSuggestion(elem, CompletionRelevance.DEFAULT);
});
} else {
// Exclude elements from prefixed imports
// because they are provided by InvocationComputer
excludedLibs.add(importElem.importedLibrary);
- _addLibraryPrefixSuggestion(importElem);
+ addLibraryPrefixSuggestion(importElem);
}
}
}
@@ -303,11 +393,7 @@ class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> {
if (elem is ClassElement) {
classMap[name] = elem;
}
- _addElementSuggestion(
- elem,
- typesOnly,
- excludeVoidReturn,
- CompletionRelevance.DEFAULT);
+ addSuggestion(elem, CompletionRelevance.DEFAULT);
});
// Build a list of inherited types that are imported
@@ -325,18 +411,12 @@ class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> {
String name = inheritedTypes.removeLast();
ClassElement elem = classMap[name];
if (visited.add(name) && elem != null) {
- _addElementSuggestions(elem.accessors, typesOnly, excludeVoidReturn);
- _addElementSuggestions(elem.methods, typesOnly, excludeVoidReturn);
+ addSuggestions(elem.accessors);
+ addSuggestions(elem.methods);
elem.allSupertypes.forEach((InterfaceType type) {
if (visited.add(type.name)) {
- _addElementSuggestions(
- type.accessors,
- typesOnly,
- excludeVoidReturn);
- _addElementSuggestions(
- type.methods,
- typesOnly,
- excludeVoidReturn);
+ addSuggestions(type.accessors);
+ addSuggestions(type.methods);
}
});
}
@@ -346,46 +426,19 @@ class _ImportedVisitor extends GeneralizingAstVisitor<Future<bool>> {
// Add non-imported elements as low relevance
var future = request.searchEngine.searchTopLevelDeclarations('');
return future.then((List<SearchMatch> matches) {
- Set<String> completionSet = new Set<String>();
- request.suggestions.forEach((CompletionSuggestion suggestion) {
- completionSet.add(suggestion.completion);
- });
matches.forEach((SearchMatch match) {
if (match.kind == MatchKind.DECLARATION) {
Element element = match.element;
if (element.isPublic &&
!excludedLibs.contains(element.library) &&
- !completionSet.contains(element.displayName)) {
- if (!typesOnly || element is ClassElement) {
- _addElementSuggestion(
- element,
- typesOnly,
- excludeVoidReturn,
- CompletionRelevance.LOW);
- }
+ !completions.contains(element.displayName)) {
+ addSuggestion(element, CompletionRelevance.LOW);
}
}
});
+ cache.importKey = computeImportKey;
+ addCachedSuggestions();
return true;
});
}
-
- void _addLibraryPrefixSuggestion(ImportElement importElem) {
- String completion = importElem.prefix.displayName;
- if (completion != null && completion.length > 0) {
- CompletionSuggestion suggestion = new CompletionSuggestion(
- CompletionSuggestionKind.INVOCATION,
- CompletionRelevance.DEFAULT,
- completion,
- completion.length,
- 0,
- importElem.isDeprecated,
- false);
- LibraryElement lib = importElem.importedLibrary;
- if (lib != null) {
- suggestion.element = newElement_fromEngine(lib);
- }
- request.suggestions.add(suggestion);
- }
- }
}

Powered by Google App Engine
This is Rietveld 408576698