blob: 228f53d894f2da854f780df5516bb832178dfdfc [file] [log] [blame]
// 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_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';
/**
* 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.hasRequired) {
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}) {
// 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.hasDeprecated;
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.isRequiredPositional);
suggestion.requiredParameterCount = requiredParameters.length;
Iterable<ParameterElement> namedParameters =
element.parameters.where((ParameterElement param) => param.isNamed);
suggestion.hasNamedParameters = namedParameters.isNotEmpty;
addDefaultArgDetails(
suggestion, element, requiredParameters, namedParameters);
}
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;
}
}
}