| Index: pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
|
| diff --git a/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a46b96b0208dd776c8724dbeadaebe4e6b7cc4d7
|
| --- /dev/null
|
| +++ b/pkg/analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
|
| @@ -0,0 +1,259 @@
|
| +// 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.
|
| +
|
| +library services.completion.dart.suggestion.builder;
|
| +
|
| +import 'package:analysis_server/src/protocol_server.dart' as protocol;
|
| +import 'package:analysis_server/src/protocol_server.dart'
|
| + hide Element, ElementKind;
|
| +import 'package:analyzer/src/generated/element.dart';
|
| +import 'package:analyzer/src/generated/source.dart';
|
| +import 'package:analyzer/src/generated/utilities_dart.dart';
|
| +import 'package:path/path.dart' as path;
|
| +import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
|
| +
|
| +const String DYNAMIC = 'dynamic';
|
| +
|
| +/**
|
| + * Return a suggestion based upon the given element
|
| + * or `null` if a suggestion is not appropriate for the given element.
|
| + * If the suggestion is not currently in scope, then specify
|
| + * importForSource as the source to which an import should be added.
|
| + */
|
| +CompletionSuggestion createSuggestion(Element element,
|
| + {String completion,
|
| + CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
|
| + int relevance: DART_RELEVANCE_DEFAULT,
|
| + Source importForSource}) {
|
| + if (element is ExecutableElement && element.isOperator) {
|
| + // Do not include operators in suggestions
|
| + return null;
|
| + }
|
| + if (completion == null) {
|
| + completion = element.displayName;
|
| + }
|
| + bool isDeprecated = element.isDeprecated;
|
| + CompletionSuggestion suggestion = new CompletionSuggestion(
|
| + kind,
|
| + isDeprecated ? DART_RELEVANCE_LOW : relevance,
|
| + completion,
|
| + completion.length,
|
| + 0,
|
| + isDeprecated,
|
| + false);
|
| + suggestion.element = protocol.convertElement(element);
|
| + Element enclosingElement = element.enclosingElement;
|
| + if (enclosingElement is ClassElement) {
|
| + suggestion.declaringType = enclosingElement.displayName;
|
| + }
|
| + suggestion.returnType = getReturnTypeString(element);
|
| + if (element is ExecutableElement && element is! PropertyAccessorElement) {
|
| + suggestion.parameterNames = element.parameters
|
| + .map((ParameterElement parameter) => parameter.name)
|
| + .toList();
|
| + suggestion.parameterTypes = element.parameters
|
| + .map((ParameterElement parameter) => parameter.type.displayName)
|
| + .toList();
|
| + suggestion.requiredParameterCount = element.parameters
|
| + .where((ParameterElement parameter) =>
|
| + parameter.parameterKind == ParameterKind.REQUIRED)
|
| + .length;
|
| + suggestion.hasNamedParameters = element.parameters.any(
|
| + (ParameterElement parameter) =>
|
| + parameter.parameterKind == ParameterKind.NAMED);
|
| + }
|
| + if (importForSource != null) {
|
| + String srcPath = path.dirname(importForSource.fullName);
|
| + LibraryElement libElem = element.library;
|
| + if (libElem != null) {
|
| + Source libSource = libElem.source;
|
| + if (libSource != null) {
|
| + UriKind uriKind = libSource.uriKind;
|
| + if (uriKind == UriKind.DART_URI) {
|
| + suggestion.importUri = libSource.uri.toString();
|
| + } else if (uriKind == UriKind.PACKAGE_URI) {
|
| + suggestion.importUri = libSource.uri.toString();
|
| + } else if (uriKind == UriKind.FILE_URI &&
|
| + element.source.uriKind == UriKind.FILE_URI) {
|
| + try {
|
| + suggestion.importUri =
|
| + path.relative(libSource.fullName, from: srcPath);
|
| + } catch (_) {
|
| + // ignored
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (suggestion.importUri == null) {
|
| + // Do not include out of scope suggestions
|
| + // for which we cannot determine an import
|
| + return null;
|
| + }
|
| + }
|
| + return suggestion;
|
| +}
|
| +
|
| +/**
|
| + * Common mixin for sharing behavior
|
| + */
|
| +abstract class ElementSuggestionBuilder {
|
| + /**
|
| + * A collection of completion suggestions.
|
| + */
|
| + final List<CompletionSuggestion> suggestions = <CompletionSuggestion>[];
|
| +
|
| + /**
|
| + * Return the kind of suggestions that should be built.
|
| + */
|
| + CompletionSuggestionKind get kind;
|
| +
|
| + /**
|
| + * Return the request on which the builder is operating.
|
| + */
|
| + DartCompletionRequest get request;
|
| +
|
| + /**
|
| + * Add a suggestion based upon the given element.
|
| + */
|
| + void addSuggestion(Element element,
|
| + {String prefix, int relevance: DART_RELEVANCE_DEFAULT}) {
|
| + if (element.isPrivate) {
|
| + LibraryElement elementLibrary = element.library;
|
| + CompilationUnitElement unitElem = request.target.unit.element;
|
| + if (unitElem == null) {
|
| + return;
|
| + }
|
| + LibraryElement unitLibrary = unitElem.library;
|
| + if (elementLibrary != unitLibrary) {
|
| + return;
|
| + }
|
| + }
|
| + if (prefix == null && element.isSynthetic) {
|
| + if ((element is PropertyAccessorElement) ||
|
| + element is FieldElement && !_isSpecialEnumField(element)) {
|
| + return;
|
| + }
|
| + }
|
| + String completion = element.displayName;
|
| + if (prefix != null && prefix.length > 0) {
|
| + if (completion == null || completion.length <= 0) {
|
| + completion = prefix;
|
| + } else {
|
| + completion = '$prefix.$completion';
|
| + }
|
| + }
|
| + if (completion == null || completion.length <= 0) {
|
| + return;
|
| + }
|
| + CompletionSuggestion suggestion = createSuggestion(element,
|
| + completion: completion, kind: kind, relevance: relevance);
|
| + 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;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * This class visits elements in a library and provides suggestions based upon
|
| + * the visible members in that library. Clients should call
|
| + * [LibraryElementSuggestionBuilder.suggestionsFor].
|
| + */
|
| +class LibraryElementSuggestionBuilder extends GeneralizingElementVisitor
|
| + with ElementSuggestionBuilder {
|
| + final DartCompletionRequest request;
|
| + final CompletionSuggestionKind kind;
|
| + final bool typesOnly;
|
| + final bool instCreation;
|
| +
|
| + LibraryElementSuggestionBuilder(
|
| + this.request, this.kind, this.typesOnly, this.instCreation);
|
| +
|
| + @override
|
| + visitClassElement(ClassElement element) {
|
| + if (instCreation) {
|
| + element.visitChildren(this);
|
| + } else {
|
| + addSuggestion(element);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + visitCompilationUnitElement(CompilationUnitElement element) {
|
| + element.visitChildren(this);
|
| + LibraryElement containingLibrary = element.library;
|
| + if (containingLibrary != null) {
|
| + for (var lib in containingLibrary.exportedLibraries) {
|
| + lib.visitChildren(this);
|
| + }
|
| + }
|
| + }
|
| +
|
| + @override
|
| + visitConstructorElement(ConstructorElement element) {
|
| + if (instCreation) {
|
| + ClassElement classElem = element.enclosingElement;
|
| + if (classElem != null) {
|
| + String prefix = classElem.name;
|
| + if (prefix != null && prefix.length > 0) {
|
| + addSuggestion(element, prefix: prefix);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + @override
|
| + visitElement(Element element) {
|
| + // ignored
|
| + }
|
| +
|
| + @override
|
| + visitFunctionElement(FunctionElement element) {
|
| + if (!typesOnly) {
|
| + addSuggestion(element);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
|
| + if (!instCreation) {
|
| + addSuggestion(element);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + visitTopLevelVariableElement(TopLevelVariableElement element) {
|
| + if (!typesOnly) {
|
| + addSuggestion(element);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Add suggestions for the visible members in the given library
|
| + */
|
| + static void suggestionsFor(
|
| + DartCompletionRequest request,
|
| + CompletionSuggestionKind kind,
|
| + LibraryElement library,
|
| + bool typesOnly,
|
| + bool instCreation) {
|
| + if (library != null) {
|
| + library.visitChildren(new LibraryElementSuggestionBuilder(
|
| + request, kind, typesOnly, instCreation));
|
| + }
|
| + }
|
| +}
|
|
|