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; |
+ } |
+ } |
+} |