blob: 3669bd43ddb62d6915128eae3cdfdb9e2a58d404 [file] [log] [blame]
// 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/suggestion_builder.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/scanner.dart';
/**
* A computer for calculating invocation / access suggestions
* `completion.getSuggestions` request results.
*/
class InvocationComputer extends DartCompletionComputer {
@override
bool computeFast(DartCompletionRequest request) {
// TODO: implement computeFast
return false;
}
@override
Future<bool> computeFull(DartCompletionRequest request) {
return request.node.accept(new _InvocationAstVisitor(request));
}
}
/**
* An [AstNode] vistor for determining the appropriate invocation/access
* suggestions based upon the node in which the completion is requested.
*/
class _InvocationAstVisitor extends GeneralizingAstVisitor<Future<bool>> {
final DartCompletionRequest request;
_InvocationAstVisitor(this.request);
@override
Future<bool> visitConstructorName(ConstructorName node) {
// SimpleIdentifier PrefixedIdentifier TypeName ConstructorName
Token period = node.period;
if (period != null && period.end <= request.offset) {
return _addNamedConstructorSuggestions(node);
}
return super.visitConstructorName(node);
}
@override
Future<bool> visitMethodInvocation(MethodInvocation node) {
Token period = node.period;
if (period == null || period.offset < request.offset) {
_addExpressionSuggestions(node.target);
}
return new Future.value(false);
}
@override
Future<bool> visitNode(AstNode node) {
return new Future.value(false);
}
@override
Future<bool> visitPrefixedIdentifier(PrefixedIdentifier node) {
if (request.offset > node.period.offset) {
SimpleIdentifier prefix = node.prefix;
if (prefix != null) {
return _addElementSuggestions(prefix.bestElement);
}
}
return super.visitPrefixedIdentifier(node);
}
@override
Future<bool> visitPropertyAccess(PropertyAccess node) {
Token operator = node.operator;
if (operator != null && operator.offset < request.offset) {
return _addExpressionSuggestions(node.realTarget);
}
return super.visitPropertyAccess(node);
}
@override
Future<bool> visitSimpleIdentifier(SimpleIdentifier node) {
return node.parent.accept(this);
}
/**
* Add invocation / access suggestions for the given element.
*/
Future<bool> _addElementSuggestions(Element element) {
if (element != null) {
return element.accept(new _InvocationElementVisitor(request));
}
return new Future.value(false);
}
/**
* Add invocation / access suggestions for the given expression.
*/
Future<bool> _addExpressionSuggestions(Expression target) {
if (target != null) {
DartType type = target.bestType;
if (type != null) {
ClassElementSuggestionBuilder.suggestionsFor(request, type.element);
return new Future.value(true);
}
}
return new Future.value(false);
}
Future<bool> _addNamedConstructorSuggestions(ConstructorName node) {
TypeName typeName = node.type;
if (typeName != null) {
DartType type = typeName.type;
if (type != null) {
NamedConstructorSuggestionBuilder.suggestionsFor(request, type.element);
return new Future.value(true);
}
}
return new Future.value(false);
}
}
/**
* An [Element] visitor for determining the appropriate invocation/access
* suggestions based upon the element for which the completion is requested.
*/
class _InvocationElementVisitor extends GeneralizingElementVisitor<Future<bool>>
{
final DartCompletionRequest request;
_InvocationElementVisitor(this.request);
@override
Future<bool> visitClassElement(ClassElement element) {
if (element != null) {
InterfaceType type = element.type;
if (type != null) {
ClassElementSuggestionBuilder.staticSuggestionsFor(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, library);
modified = true;
}
}
}
});
return new Future.value(modified);
}
@override
Future<bool> visitPropertyAccessorElement(PropertyAccessorElement element) {
if (element != null) {
PropertyInducingElement elemVar = element.variable;
if (elemVar != null) {
DartType type = elemVar.type;
if (type != null) {
ClassElementSuggestionBuilder.suggestionsFor(request, type.element);
}
}
return new Future.value(true);
}
return new Future.value(false);
}
@override
Future<bool> visitVariableElement(VariableElement element) {
DartType type = element.type;
if (type != null) {
ClassElementSuggestionBuilder.suggestionsFor(request, type.element);
}
return new Future.value(true);
}
}