blob: 9e16b8ffc41d5fc1c58e9320588a81bc69dbf21b [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'dart:async';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
import 'package:analyzer_plugin/src/utilities/completion/element_suggestion_builder.dart';
import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
import 'package:analyzer_plugin/utilities/completion/completion_core.dart';
/**
* A contributor for calculating suggestions for inherited references.
*
* Plugin developers should extend this function and primarily
* overload `computeSuggestions` (if needed).
*/
class InheritedReferenceContributor
with ElementSuggestionBuilder
implements CompletionContributor {
@override
LibraryElement containingLibrary;
@override
CompletionSuggestionKind kind;
@override
ResourceProvider resourceProvider;
/**
* Plugin contributors should primarily overload this function.
* Should more parameters be needed for autocompletion needs, the
* overloaded function should define those parameters and
* call on `computeSuggestionsForClass`.
*/
@override
Future<void> computeSuggestions(
DartCompletionRequest request, CompletionCollector collector) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
CompletionTarget target =
new CompletionTarget.forOffset(request.result.unit, request.offset);
OpType optype = new OpType.forCompletion(target, request.offset);
if (!optype.includeIdentifiers) {
return;
}
ClassDeclaration classDecl = _enclosingClass(target);
if (classDecl == null || classDecl.declaredElement == null) {
return;
}
containingLibrary = request.result.libraryElement;
_computeSuggestionsForClass2(
collector, target, classDecl.declaredElement, optype);
}
/**
* Clients should not overload this function.
*/
Future<void> computeSuggestionsForClass(
DartCompletionRequest request,
CompletionCollector collector,
ClassElement classElement, {
AstNode entryPoint,
bool skipChildClass,
CompletionTarget target,
OpType optype,
}) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
target ??= new CompletionTarget.forOffset(
request.result.unit, request.offset,
entryPoint: entryPoint);
optype ??= new OpType.forCompletion(target, request.offset);
if (!optype.includeIdentifiers) {
return;
}
if (classElement == null) {
ClassDeclaration classDecl = _enclosingClass(target);
if (classDecl == null || classDecl.declaredElement == null) {
return;
}
classElement = classDecl.declaredElement;
}
containingLibrary = request.result.libraryElement;
_computeSuggestionsForClass2(collector, target, classElement, optype,
skipChildClass: skipChildClass);
}
_addSuggestionsForType(InterfaceType type, OpType optype,
{bool isFunctionalArgument: false}) {
if (!isFunctionalArgument) {
for (PropertyAccessorElement elem in type.accessors) {
if (elem.isGetter) {
if (optype.includeReturnValueSuggestions) {
addSuggestion(elem);
}
} else {
if (optype.includeVoidReturnSuggestions) {
addSuggestion(elem);
}
}
}
}
for (MethodElement elem in type.methods) {
if (elem.returnType == null) {
addSuggestion(elem);
} else if (!elem.returnType.isVoid) {
if (optype.includeReturnValueSuggestions) {
addSuggestion(elem);
}
} else {
if (optype.includeVoidReturnSuggestions) {
addSuggestion(elem);
}
}
}
}
void _computeSuggestionsForClass2(CompletionCollector collector,
CompletionTarget target, ClassElement classElement, OpType optype,
{bool skipChildClass: true}) {
bool isFunctionalArgument = target.isFunctionalArgument();
kind = isFunctionalArgument
? CompletionSuggestionKind.IDENTIFIER
: CompletionSuggestionKind.INVOCATION;
if (!skipChildClass) {
_addSuggestionsForType(classElement.thisType, optype,
isFunctionalArgument: isFunctionalArgument);
}
for (InterfaceType type in classElement.allSupertypes) {
_addSuggestionsForType(type, optype,
isFunctionalArgument: isFunctionalArgument);
}
for (CompletionSuggestion suggestion in suggestions) {
collector.addSuggestion(suggestion);
}
}
/**
* Return the class containing the target
* or `null` if the target is in a static method or field
* or not in a class.
*/
ClassDeclaration _enclosingClass(CompletionTarget target) {
AstNode node = target.containingNode;
while (node != null) {
if (node is ClassDeclaration) {
return node;
}
if (node is MethodDeclaration) {
if (node.isStatic) {
return null;
}
}
if (node is FieldDeclaration) {
if (node.isStatic) {
return null;
}
}
node = node.parent;
}
return null;
}
}