| // 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 = 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<FormalParameterElement> requiredParams, |
| Iterable<FormalParameterElement> namedParams, |
| ) { |
| // Copied from analysis_server/lib/src/services/completion/dart/suggestion_builder.dart |
| var buffer = StringBuffer(); |
| var ranges = <int>[]; |
| |
| int offset; |
| |
| for (var param in requiredParams) { |
| if (buffer.isNotEmpty) { |
| buffer.write(', '); |
| } |
| offset = buffer.length; |
| var name = param.name ?? ''; |
| buffer.write(name); |
| ranges.addAll([offset, name.length]); |
| } |
| |
| for (var param in namedParams) { |
| if (param.metadata.hasRequired || param.isRequiredNamed) { |
| if (buffer.isNotEmpty) { |
| buffer.write(', '); |
| } |
| var name = param.name ?? ''; |
| buffer.write('$name: '); |
| offset = buffer.length; |
| buffer.write(name); |
| ranges.addAll([offset, name.length]); |
| } |
| } |
| |
| suggestion.defaultArgumentListString = buffer.isNotEmpty |
| ? buffer.toString() |
| : null; |
| suggestion.defaultArgumentListTextRanges = ranges.isNotEmpty |
| ? ranges |
| : null; |
| } |
| |
| @override |
| CompletionSuggestion? forElement( |
| Element? element, { |
| String? completion, |
| CompletionSuggestionKind? kind, |
| 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 MethodElement && element.isOperator) { |
| // Do not include operators in suggestions |
| return null; |
| } |
| completion ??= element.displayName; |
| |
| var isDeprecated = element.metadata.hasDeprecated; |
| var suggestion = CompletionSuggestion( |
| kind ?? CompletionSuggestionKind.INVOCATION, |
| isDeprecated ? DART_RELEVANCE_LOW : relevance, |
| completion, |
| completion.length, |
| 0, |
| isDeprecated, |
| false, |
| ); |
| |
| // Attach docs. |
| var doc = removeDartDocDelimiters(element.documentationComment); |
| suggestion.docComplete = doc; |
| suggestion.docSummary = getDartDocSummary(doc); |
| |
| suggestion.element = converter.convertElement(element); |
| var 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.formalParameters |
| .map((parameter) => parameter.name ?? '') |
| .toList(); |
| suggestion.parameterTypes = element.formalParameters.map((parameter) { |
| return parameter.type.getDisplayString(); |
| }).toList(); |
| |
| var requiredParameters = element.formalParameters.where( |
| (param) => param.isRequiredPositional, |
| ); |
| suggestion.requiredParameterCount = requiredParameters.length; |
| |
| var namedParameters = element.formalParameters.where( |
| (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.getDisplayString(); |
| } |
| } else if (element is VariableElement) { |
| var type = element.type; |
| return type.getDisplayString(); |
| } else if (element is TypeAliasElement) { |
| if (element.aliasedType case FunctionType aliasedType) { |
| return aliasedType.returnType.getDisplayString(); |
| } |
| return null; |
| } else { |
| return null; |
| } |
| } |
| } |