| // 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. |
| |
| library services.completion.computer.dart.invocation; |
| |
| import 'dart:async'; |
| |
| import 'package:analysis_server/src/services/completion/dart_completion_manager.dart'; |
| import 'package:analysis_server/src/services/completion/optype.dart'; |
| import 'package:analysis_server/src/services/completion/suggestion_builder.dart'; |
| import 'package:analyzer/src/generated/ast.dart'; |
| import 'package:analyzer/src/generated/element.dart'; |
| |
| import '../../protocol_server.dart' show CompletionSuggestionKind; |
| |
| /** |
| * A computer for calculating invocation / access suggestions |
| * `completion.getSuggestions` request results. |
| */ |
| class InvocationComputer extends DartCompletionComputer { |
| SuggestionBuilder builder; |
| |
| @override |
| bool computeFast(DartCompletionRequest request) { |
| OpType optype = request.optype; |
| if (optype.includeInvocationSuggestions) { |
| builder = request.node.accept(new _InvocationAstVisitor(request)); |
| if (builder != null) { |
| return builder.computeFast(request.node); |
| } |
| } |
| |
| return true; |
| } |
| |
| @override |
| Future<bool> computeFull(DartCompletionRequest request) { |
| if (builder != null) { |
| return builder.computeFull(request.node); |
| } |
| return new Future.value(false); |
| } |
| } |
| |
| class _ExpressionSuggestionBuilder implements SuggestionBuilder { |
| final DartCompletionRequest request; |
| |
| _ExpressionSuggestionBuilder(this.request); |
| |
| @override |
| bool computeFast(AstNode node) { |
| return false; |
| } |
| |
| @override |
| Future<bool> computeFull(AstNode node) { |
| if (node is SimpleIdentifier) { |
| node = node.parent; |
| } |
| if (node is MethodInvocation) { |
| node = (node as MethodInvocation).realTarget; |
| } else if (node is PropertyAccess) { |
| node = (node as PropertyAccess).realTarget; |
| } |
| if (node is Identifier && node.bestElement is ClassElement) { |
| node.bestElement.accept( |
| new _PrefixedIdentifierSuggestionBuilder(request)); |
| return new Future.value(true); |
| } |
| if (node is Expression) { |
| InterfaceTypeSuggestionBuilder.suggestionsFor(request, node.bestType); |
| return new Future.value(true); |
| } |
| return new Future.value(false); |
| } |
| } |
| |
| /** |
| * An [AstNode] vistor for determining which suggestion builder |
| * should be used to build invocation/access suggestions. |
| */ |
| class _InvocationAstVisitor extends GeneralizingAstVisitor<SuggestionBuilder> { |
| final DartCompletionRequest request; |
| |
| _InvocationAstVisitor(this.request); |
| |
| @override |
| visitConstructorName(ConstructorName node) { |
| // some PrefixedIdentifier nodes are transformed into |
| // ConstructorName nodes during the resolution process. |
| return new _PrefixedIdentifierSuggestionBuilder(request); |
| } |
| |
| @override |
| SuggestionBuilder visitMethodInvocation(MethodInvocation node) { |
| return new _ExpressionSuggestionBuilder(request); |
| } |
| |
| @override |
| SuggestionBuilder visitNode(AstNode node) { |
| return null; |
| } |
| |
| @override |
| SuggestionBuilder visitPrefixedIdentifier(PrefixedIdentifier node) { |
| // some PrefixedIdentifier nodes are transformed into |
| // ConstructorName nodes during the resolution process. |
| return new _PrefixedIdentifierSuggestionBuilder(request); |
| } |
| |
| @override |
| SuggestionBuilder visitPropertyAccess(PropertyAccess node) { |
| return new _ExpressionSuggestionBuilder(request); |
| } |
| |
| @override |
| SuggestionBuilder visitSimpleIdentifier(SimpleIdentifier node) { |
| return node.parent.accept(this); |
| } |
| } |
| |
| /** |
| * An [Element] visitor for determining the appropriate invocation/access |
| * suggestions based upon the element for which the completion is requested. |
| */ |
| class _PrefixedIdentifierSuggestionBuilder extends |
| GeneralizingElementVisitor<Future<bool>> implements SuggestionBuilder { |
| |
| final DartCompletionRequest request; |
| |
| _PrefixedIdentifierSuggestionBuilder(this.request); |
| |
| @override |
| bool computeFast(AstNode node) { |
| return false; |
| } |
| |
| @override |
| Future<bool> computeFull(AstNode node) { |
| if (node is SimpleIdentifier) { |
| node = node.parent; |
| } |
| if (node is ConstructorName) { |
| // some PrefixedIdentifier nodes are transformed into |
| // ConstructorName nodes during the resolution process. |
| return new NamedConstructorSuggestionBuilder(request).computeFull(node); |
| } |
| if (node is PrefixedIdentifier) { |
| SimpleIdentifier prefix = node.prefix; |
| if (prefix != null) { |
| if (prefix.propagatedType != null) { |
| InterfaceTypeSuggestionBuilder.suggestionsFor( |
| request, |
| prefix.propagatedType); |
| } else { |
| Element element = prefix.bestElement; |
| if (element != null) { |
| return element.accept(this); |
| } |
| } |
| } |
| } |
| return new Future.value(false); |
| } |
| |
| @override |
| Future<bool> visitClassElement(ClassElement element) { |
| if (element != null) { |
| InterfaceType type = element.type; |
| if (type != null) { |
| StaticClassElementSuggestionBuilder.suggestionsFor( |
| request, |
| type.element); |
| } |
| } |
| return new Future.value(false); |
| } |
| |
| @override |
| Future<bool> visitElement(Element element) { |
| return new Future.value(false); |
| } |
| |
| @override |
| Future<bool> visitPrefixElement(PrefixElement element) { |
| //TODO (danrubel) reimplement to use prefixElement.importedLibraries |
| // once that accessor is implemented and available in Dart |
| bool modified = false; |
| // Find the import directive with the given prefix |
| request.unit.directives.forEach((Directive directive) { |
| if (directive is ImportDirective) { |
| if (directive.prefix != null) { |
| if (directive.prefix.name == element.name) { |
| // Suggest elements from the imported library |
| LibraryElement library = directive.uriElement; |
| LibraryElementSuggestionBuilder.suggestionsFor( |
| request, |
| CompletionSuggestionKind.INVOCATION, |
| library); |
| modified = true; |
| } |
| } |
| } |
| }); |
| return new Future.value(modified); |
| } |
| |
| @override |
| Future<bool> visitPropertyAccessorElement(PropertyAccessorElement element) { |
| if (element != null) { |
| PropertyInducingElement elemVar = element.variable; |
| if (elemVar != null) { |
| InterfaceTypeSuggestionBuilder.suggestionsFor(request, elemVar.type); |
| } |
| return new Future.value(true); |
| } |
| return new Future.value(false); |
| } |
| |
| @override |
| Future<bool> visitVariableElement(VariableElement element) { |
| InterfaceTypeSuggestionBuilder.suggestionsFor(request, element.type); |
| return new Future.value(true); |
| } |
| } |