blob: 7489716436538e55e2eff9154b1ec5e941d286ef [file] [log] [blame]
// Copyright (c) 2018, 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:analysis_server/src/computer/computer_documentation.dart';
import 'package:analysis_server/src/protocol_server.dart' hide Element;
import 'package:analysis_server/src/utilities/extensions/ast.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element2.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/element_locator.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
/// A computer for the signature at the specified offset of a Dart
/// [CompilationUnit].
class DartUnitSignatureComputer {
final AstNode? _node;
late ArgumentList _argumentList;
final DocumentationPreference documentationPreference;
final DartDocumentationComputer _documentationComputer;
DartUnitSignatureComputer(
DartdocDirectiveInfo dartdocInfo,
CompilationUnit unit,
int offset, {
this.documentationPreference = DocumentationPreference.full,
}) : _documentationComputer = DartDocumentationComputer(dartdocInfo),
_node = NodeLocator(offset).searchWithin(unit);
/// The [ArgumentList] node located by [compute].
ArgumentList get argumentList => _argumentList;
bool get offsetIsValid => _node != null;
/// Returns the computed signature information, maybe `null`.
AnalysisGetSignatureResult? compute() {
var argumentList = _findArgumentList();
if (argumentList == null) {
return null;
}
String? name;
Element2? element;
List<FormalParameterElement>? parameters;
var parent = argumentList.parent;
if (parent is MethodInvocation) {
name = parent.methodName.name;
element = ElementLocator.locate2(parent);
parameters =
element is FunctionTypedElement2 ? element.formalParameters : null;
} else if (parent is InstanceCreationExpression) {
name = parent.constructorName.type.qualifiedName;
var constructorName = parent.constructorName.name;
if (constructorName != null) {
name += '.${constructorName.name}';
}
element = ElementLocator.locate2(parent);
parameters =
element is FunctionTypedElement2 ? element.formalParameters : null;
} else if (parent
case FunctionExpressionInvocation(function: Identifier function)) {
name = function.name;
if (function.staticType case FunctionType functionType) {
// Standard function expression.
element = function.element;
parameters = functionType.formalParameters;
} else if (parent.element case ExecutableElement2 executableElement) {
// Callable class instance (where we'll look at the `call` method).
element = executableElement;
parameters = executableElement.formalParameters;
}
}
if (name == null || element == null || parameters == null) {
return null;
}
_argumentList = argumentList;
var convertedParameters = parameters.map((p) => _convertParam(p)).toList();
var dartdoc = _documentationComputer.computePreferred2(
element,
documentationPreference,
);
return AnalysisGetSignatureResult(
name,
convertedParameters,
dartdoc: dartdoc,
);
}
ParameterInfo _convertParam(FormalParameterElement param) {
return ParameterInfo(
param.isOptionalNamed
? ParameterKind.OPTIONAL_NAMED
: param.isOptionalPositional
? ParameterKind.OPTIONAL_POSITIONAL
: param.isRequiredNamed
? ParameterKind.REQUIRED_NAMED
: ParameterKind.REQUIRED_POSITIONAL,
param.displayName,
param.type.getDisplayString(),
defaultValue: param.defaultValueCode);
}
/// Return the closest argument list surrounding the [_node].
ArgumentList? _findArgumentList() {
var node = _node;
while (node != null && node is! ArgumentList) {
// Certain nodes don't make sense to search above for an argument list
// (for example when inside a function expression).
if (node is FunctionExpression) {
return null;
}
node = node.parent;
}
return node as ArgumentList?;
}
}