Skip to content

Commit

Permalink
[analysis_server] Handle callable classes and function expressions fo…
Browse files Browse the repository at this point in the history
…r LSP Signature Help

Fixes Dart-Code/Dart-Code#5153.

Change-Id: I4335c2378c4e863a38d2f802ff1618b23917ba7d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/377460
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
  • Loading branch information
DanTup authored and Commit Queue committed Jul 24, 2024
1 parent 973b4ce commit 3fffa71
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 14 deletions.
9 changes: 6 additions & 3 deletions pkg/analysis_server/lib/src/computer/computer_hover.dart
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,14 @@ class DartUnitHoverComputer {

// Look for documentation comments of overridden members
var overridden = findOverriddenElements(element);
for (var candidate in [
var candidates = [
element,
...overridden.superElements,
...overridden.interfaceElements
]) {
...overridden.interfaceElements,
if (element case PropertyAccessorElement(variable2: var variable?))
variable
];
for (var candidate in candidates) {
if (candidate.documentationComment != null) {
documentedElement = candidate;
break;
Expand Down
43 changes: 32 additions & 11 deletions pkg/analysis_server/lib/src/computer/computer_signature.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ 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/element.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';
Expand Down Expand Up @@ -38,33 +39,53 @@ class DartUnitSignatureComputer {
return null;
}
String? name;
ExecutableElement? execElement;
Element? element;
List<ParameterElement>? parameters;
var parent = argumentList.parent;
if (parent is MethodInvocation) {
name = parent.methodName.name;
var element = ElementLocator.locate(parent);
execElement = element is ExecutableElement ? element : null;
element = ElementLocator.locate(parent);
parameters = element is FunctionTypedElement ? element.parameters : null;
} else if (parent is InstanceCreationExpression) {
name = parent.constructorName.type.qualifiedName;
var constructorName = parent.constructorName.name;
if (constructorName != null) {
name += '.${constructorName.name}';
}
execElement = ElementLocator.locate(parent) as ExecutableElement?;
element = ElementLocator.locate(parent);
parameters = element is FunctionTypedElement ? element.parameters : null;
} else if (parent
case FunctionExpressionInvocation(function: Identifier function)) {
name = function.name;

if (function.staticType case FunctionType functionType) {
// Standard function expression.
element = function.staticElement;
parameters = functionType.parameters;
} else if (parent.staticElement case ExecutableElement staticElement) {
// Callable class instance (where we'll look at the `call` method).
element = staticElement;
parameters = staticElement.parameters;
}
}

if (name == null || execElement == null) {
if (name == null || element == null || parameters == null) {
return null;
}

_argumentList = argumentList;
var convertedParameters = parameters.map((p) => _convertParam(p)).toList();
var dartdoc = DartUnitHoverComputer.computePreferredDocumentation(
_dartdocInfo,
element,
documentationPreference,
);

var parameters =
execElement.parameters.map((p) => _convertParam(p)).toList();

return AnalysisGetSignatureResult(name, parameters,
dartdoc: DartUnitHoverComputer.computePreferredDocumentation(
_dartdocInfo, execElement, documentationPreference));
return AnalysisGetSignatureResult(
name,
convertedParameters,
dartdoc: dartdoc,
);
}

ParameterInfo _convertParam(ParameterElement param) {
Expand Down
68 changes: 68 additions & 0 deletions pkg/analysis_server/test/lsp/signature_help_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,30 @@ augment class Foo {
);
}

Future<void> test_callableClass() async {
var content = '''
class Foo {
/// Does foo.
int call(String s, int i) {
var foo = Foo();
return foo(^);
}
}
''';
var expectedLabel = 'foo(String s, int i)';
var expectedDoc = 'Does foo.';

await _expectSignature(
content,
expectedLabel,
expectedDoc,
[
ParameterInformation(label: 'String s'),
ParameterInformation(label: 'int i'),
],
);
}

Future<void> test_dartDocMacro() async {
setSignatureHelpContentFormat(null);

Expand Down Expand Up @@ -367,6 +391,50 @@ foo(String s, int i) {
);
}

Future<void> test_functionExpression_local() async {
var content = '''
void f() {
var foo = (String s, int i) => s;
foo(^);
}
''';
var expectedLabel = 'foo(String s, int i)';

await _expectSignature(
content,
expectedLabel,
null, // expectedDoc, not dartDocs on local vars.
[
ParameterInformation(label: 'String s'),
ParameterInformation(label: 'int i'),
],
);
}

Future<void> test_functionExpression_topLevel() async {
var content = '''
/// Does foo.
var foo = (String s, int i) => s;
void f() {
foo(^);
}
''';
var expectedLabel = 'foo(String s, int i)';
var expectedDoc = 'Does foo.';

await _expectSignature(
content,
expectedLabel,
expectedDoc,
[
ParameterInformation(label: 'String s'),
ParameterInformation(label: 'int i'),
],
);
}

Future<void> test_manualTrigger_invalidLocation() async {
// If the user invokes signature help, we should show it even if it's a
// location where we wouldn't automatically trigger (for example in a string).
Expand Down

0 comments on commit 3fffa71

Please sign in to comment.