| // Copyright (c) 2015, 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:analysis_server/src/provisional/completion/dart/completion_dart.dart'; |
| import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/visitor.dart'; |
| |
| import '../../../protocol_server.dart' show CompletionSuggestion; |
| |
| /** |
| * A contributor for calculating static member invocation / access suggestions |
| * `completion.getSuggestions` request results. |
| */ |
| class StaticMemberContributor extends DartCompletionContributor { |
| @override |
| Future<List<CompletionSuggestion>> computeSuggestions( |
| DartCompletionRequest request) async { |
| // TODO(brianwilkerson) Determine whether this await is necessary. |
| await null; |
| Expression targetId = request.dotTarget; |
| if (targetId is Identifier && !request.target.isCascade) { |
| Element elem = targetId.staticElement; |
| if (elem is ClassElement || elem is ExtensionElement) { |
| LibraryElement containingLibrary = request.libraryElement; |
| // Gracefully degrade if the library could not be determined |
| // e.g. detached part file or source change |
| if (containingLibrary == null) { |
| return const <CompletionSuggestion>[]; |
| } |
| |
| _SuggestionBuilder builder = new _SuggestionBuilder(containingLibrary); |
| elem.accept(builder); |
| return builder.suggestions; |
| } |
| } |
| return const <CompletionSuggestion>[]; |
| } |
| } |
| |
| /** |
| * This class visits elements in a class and provides suggestions based upon |
| * the visible static members in that class. |
| */ |
| class _SuggestionBuilder extends GeneralizingElementVisitor { |
| /** |
| * The library containing the unit in which the completion is requested. |
| */ |
| final LibraryElement containingLibrary; |
| |
| /** |
| * A collection of completion suggestions. |
| */ |
| final List<CompletionSuggestion> suggestions = <CompletionSuggestion>[]; |
| |
| _SuggestionBuilder(this.containingLibrary); |
| |
| @override |
| visitClassElement(ClassElement element) { |
| element.visitChildren(this); |
| } |
| |
| @override |
| visitConstructorElement(ConstructorElement element) { |
| _addSuggestion(element); |
| } |
| |
| @override |
| visitElement(Element element) { |
| // ignored |
| } |
| |
| @override |
| visitExtensionElement(ExtensionElement element) { |
| element.visitChildren(this); |
| } |
| |
| @override |
| visitFieldElement(FieldElement element) { |
| if (element.isStatic) { |
| _addSuggestion(element); |
| } |
| } |
| |
| @override |
| visitMethodElement(MethodElement element) { |
| if (element.isStatic && !element.isOperator) { |
| _addSuggestion(element); |
| } |
| } |
| |
| @override |
| visitPropertyAccessorElement(PropertyAccessorElement element) { |
| if (element.isStatic) { |
| _addSuggestion(element); |
| } |
| } |
| |
| /** |
| * Add a suggestion based upon the given element. |
| */ |
| void _addSuggestion(Element element) { |
| if (element.isPrivate) { |
| if (element.library != containingLibrary) { |
| // Do not suggest private members for imported libraries |
| return; |
| } |
| } |
| if (element.isSynthetic) { |
| if ((element is PropertyAccessorElement) || |
| element is FieldElement && !_isSpecialEnumField(element)) { |
| return; |
| } |
| } |
| String completion = element.displayName; |
| if (completion == null || completion.isEmpty) { |
| return; |
| } |
| CompletionSuggestion suggestion = |
| createSuggestion(element, completion: completion); |
| if (suggestion != null) { |
| suggestions.add(suggestion); |
| } |
| } |
| |
| /** |
| * Determine if the given element is one of the synthetic enum accessors |
| * for which we should generate a suggestion. |
| */ |
| bool _isSpecialEnumField(FieldElement element) { |
| Element parent = element.enclosingElement; |
| if (parent is ClassElement && parent.isEnum) { |
| if (element.name == 'values') { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |