blob: bef2ef66b1b14fd5ba39e62ee3e035f3c18660d2 [file] [log] [blame]
// 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;
}
}