| // 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/ast/standard_resolution_map.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, |
| resolutionMap.elementDeclaredByClassDeclaration(classDecl), 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 = resolutionMap.elementDeclaredByClassDeclaration(classDecl); |
| } |
| 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.type, 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; |
| } |
| } |