| Index: pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
|
| diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..24726965fe8d048702db386eee7086fdab13d7d2
|
| --- /dev/null
|
| +++ b/pkg/analyzer_plugin/lib/src/utilities/completion/suggestion_builder.dart
|
| @@ -0,0 +1,193 @@
|
| +// 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 'package:analyzer/dart/element/element.dart';
|
| +import 'package:analyzer/dart/element/type.dart';
|
| +import 'package:analyzer/file_system/file_system.dart';
|
| +import 'package:analyzer/src/generated/utilities_dart.dart';
|
| +import 'package:analyzer_plugin/protocol/protocol_common.dart'
|
| + hide Element, ElementKind;
|
| +import 'package:analyzer_plugin/src/utilities/documentation.dart';
|
| +import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
|
| +import 'package:analyzer_plugin/utilities/completion/relevance.dart';
|
| +import 'package:analyzer_plugin/utilities/completion/suggestion_builder.dart';
|
| +import 'package:front_end/src/base/source.dart' show Source;
|
| +import 'package:front_end/src/base/uri_kind.dart';
|
| +
|
| +/**
|
| + * An object used to build code completion suggestions for Dart code.
|
| + */
|
| +class SuggestionBuilderImpl implements SuggestionBuilder {
|
| + /**
|
| + * The resource provider used to access the file system.
|
| + */
|
| + final ResourceProvider resourceProvider;
|
| +
|
| + /**
|
| + * The converter used to convert analyzer objects to protocol objects.
|
| + */
|
| + final AnalyzerConverter converter = new AnalyzerConverter();
|
| +
|
| + /**
|
| + * Initialize a newly created suggestion builder.
|
| + */
|
| + SuggestionBuilderImpl(this.resourceProvider);
|
| +
|
| + /**
|
| + * Add default argument list text and ranges based on the given [requiredParams]
|
| + * and [namedParams].
|
| + */
|
| + void addDefaultArgDetails(
|
| + CompletionSuggestion suggestion,
|
| + Element element,
|
| + Iterable<ParameterElement> requiredParams,
|
| + Iterable<ParameterElement> namedParams) {
|
| + // Copied from analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
|
| + StringBuffer buffer = new StringBuffer();
|
| + List<int> ranges = <int>[];
|
| +
|
| + int offset;
|
| +
|
| + for (ParameterElement param in requiredParams) {
|
| + if (buffer.isNotEmpty) {
|
| + buffer.write(', ');
|
| + }
|
| + offset = buffer.length;
|
| + String name = param.name;
|
| + buffer.write(name);
|
| + ranges.addAll([offset, name.length]);
|
| + }
|
| +
|
| + for (ParameterElement param in namedParams) {
|
| + if (param.isRequired) {
|
| + if (buffer.isNotEmpty) {
|
| + buffer.write(', ');
|
| + }
|
| + String name = param.name;
|
| + buffer.write('$name: ');
|
| + offset = buffer.length;
|
| + String defaultValue = 'null'; // originally _getDefaultValue(param)
|
| + buffer.write(defaultValue);
|
| + ranges.addAll([offset, defaultValue.length]);
|
| + }
|
| + }
|
| +
|
| + suggestion.defaultArgumentListString =
|
| + buffer.isNotEmpty ? buffer.toString() : null;
|
| + suggestion.defaultArgumentListTextRanges =
|
| + ranges.isNotEmpty ? ranges : null;
|
| + }
|
| +
|
| + @override
|
| + CompletionSuggestion forElement(Element element,
|
| + {String completion,
|
| + CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
|
| + int relevance: DART_RELEVANCE_DEFAULT,
|
| + Source importForSource}) {
|
| + // Copied from analysis_server/lib/src/services/completion/dart/suggestion_builder.dart
|
| + if (element == null) {
|
| + return null;
|
| + }
|
| + 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);
|
| +
|
| + // Attach docs.
|
| + String doc = removeDartDocDelimiters(element.documentationComment);
|
| + suggestion.docComplete = doc;
|
| + suggestion.docSummary = getDartDocSummary(doc);
|
| +
|
| + suggestion.element = converter.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) {
|
| + DartType paramType = parameter.type;
|
| + // Gracefully degrade if type not resolved yet
|
| + return paramType != null ? paramType.displayName : 'var';
|
| + }).toList();
|
| +
|
| + Iterable<ParameterElement> requiredParameters = element.parameters.where(
|
| + (ParameterElement param) =>
|
| + param.parameterKind == ParameterKind.REQUIRED);
|
| + suggestion.requiredParameterCount = requiredParameters.length;
|
| +
|
| + Iterable<ParameterElement> namedParameters = element.parameters.where(
|
| + (ParameterElement param) =>
|
| + param.parameterKind == ParameterKind.NAMED);
|
| + suggestion.hasNamedParameters = namedParameters.isNotEmpty;
|
| +
|
| + addDefaultArgDetails(
|
| + suggestion, element, requiredParameters, namedParameters);
|
| + }
|
| + if (importForSource != null) {
|
| + String srcPath =
|
| + resourceProvider.pathContext.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 = resourceProvider.pathContext
|
| + .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;
|
| + }
|
| +
|
| + String getReturnTypeString(Element element) {
|
| + // Copied from analysis_server/lib/src/protocol_server.dart
|
| + if (element is ExecutableElement) {
|
| + if (element.kind == ElementKind.SETTER) {
|
| + return null;
|
| + } else {
|
| + return element.returnType?.toString();
|
| + }
|
| + } else if (element is VariableElement) {
|
| + DartType type = element.type;
|
| + return type != null ? type.displayName : 'dynamic';
|
| + } else if (element is FunctionTypeAliasElement) {
|
| + return element.returnType.toString();
|
| + } else {
|
| + return null;
|
| + }
|
| + }
|
| +}
|
|
|