| // 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/lsp_protocol/protocol_generated.dart'; |
| import 'package:analysis_server/src/analysis_server.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import 'server_abstract.dart'; |
| |
| void main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(SignatureHelpTest); |
| defineReflectiveTests(SignatureHelpWithNullSafetyTest); |
| }); |
| } |
| |
| mixin SignatureHelpMixin on AbstractLspAnalysisServerTest { |
| Future<void> testSignature( |
| String fileContent, |
| String expectedLabel, |
| String expectedDoc, |
| List<ParameterInformation> expectedParams, { |
| MarkupKind expectedFormat = MarkupKind.Markdown, |
| SignatureHelpContext context, |
| }) async { |
| final res = await getSignatureHelp( |
| mainFileUri, positionFromMarker(fileContent), context); |
| |
| // TODO(dantup): Update this when there is clarification on how to handle |
| // no valid selected parameter. |
| expect(res.activeParameter, -1); |
| expect(res.activeSignature, equals(0)); |
| expect(res.signatures, hasLength(1)); |
| final sig = res.signatures.first; |
| expect(sig.label, equals(expectedLabel)); |
| expect(sig.parameters, equals(expectedParams)); |
| |
| // Test the format matches the tests expectation. |
| // For clients that don't support MarkupContent it'll be a plain string, |
| // but otherwise it'll be a MarkupContent of type PlainText or Markdown. |
| final doc = sig.documentation; |
| if (expectedFormat == null) { |
| // Plain string. |
| expect(doc.valueEquals(expectedDoc), isTrue); |
| } else { |
| final expected = MarkupContent(kind: expectedFormat, value: expectedDoc); |
| expect(doc.valueEquals(expected), isTrue); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class SignatureHelpTest extends AbstractLspAnalysisServerTest |
| with SignatureHelpMixin { |
| Future<void> test_dartDocMacro() async { |
| final content = ''' |
| /// {@template template_name} |
| /// This is shared content. |
| /// {@endtemplate} |
| const String bar = null; |
| |
| /// {@macro template_name} |
| foo(String s, int i) { |
| foo(^); |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'This is shared content.'; |
| |
| final initialAnalysis = waitForAnalysisComplete(); |
| await initialize(); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await initialAnalysis; |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| expectedFormat: null, |
| ); |
| } |
| |
| Future<void> test_formats_markdown() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo(^); |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| expectedFormat: MarkupKind.Markdown, |
| ); |
| } |
| |
| Future<void> test_formats_notSupported() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo(^); |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize(); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| expectedFormat: null, |
| ); |
| } |
| |
| Future<void> test_formats_plainTextOnly() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo(^); |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.PlainText])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| expectedFormat: MarkupKind.PlainText, |
| ); |
| } |
| |
| Future<void> test_formats_plainTextPreferred() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo(^); |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'Does foo.'; |
| |
| // We say we prefer PlainText as a client, but since we only really |
| // support Markdown and the client supports it, we expect the server |
| // to provide Markdown. |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, |
| [MarkupKind.PlainText, MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| expectedFormat: MarkupKind.Markdown, |
| ); |
| } |
| |
| 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). |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo('this is a (^test'); |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| expectedFormat: MarkupKind.Markdown, |
| context: SignatureHelpContext( |
| triggerKind: SignatureHelpTriggerKind.Invoked, |
| isRetrigger: false, |
| )); |
| } |
| |
| Future<void> test_nonDartFile() async { |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.PlainText])); |
| await openFile(pubspecFileUri, simplePubspecContent); |
| final res = await getSignatureHelp(pubspecFileUri, startOfDocPos); |
| expect(res, isNull); |
| } |
| |
| Future<void> test_params_multipleNamed() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, {bool b = true, bool a}) { |
| foo(^); |
| } |
| '''; |
| |
| final expectedLabel = 'foo(String s, {bool b = true, bool a})'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'bool b = true'), |
| ParameterInformation(label: 'bool a'), |
| ], |
| ); |
| } |
| |
| Future<void> test_params_multipleOptional() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, [bool b = true, bool a]) { |
| foo(^); |
| } |
| '''; |
| |
| final expectedLabel = 'foo(String s, [bool b = true, bool a])'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'bool b = true'), |
| ParameterInformation(label: 'bool a'), |
| ], |
| ); |
| } |
| |
| Future<void> test_params_named() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, {bool b = true}) { |
| foo(^); |
| } |
| '''; |
| |
| final expectedLabel = 'foo(String s, {bool b = true})'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'bool b = true'), |
| ], |
| ); |
| } |
| |
| Future<void> test_params_optional() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, [bool b = true]) { |
| foo(^); |
| } |
| '''; |
| |
| final expectedLabel = 'foo(String s, [bool b = true])'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'bool b = true'), |
| ], |
| ); |
| } |
| |
| Future<void> test_simple() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo(^); |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| ); |
| } |
| |
| Future<void> test_triggerCharacter_invalidLocation() async { |
| // The client will automatically trigger when the user types ( so we need to |
| // ignore it when we're not in a suitable location. |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo('this is a (^test'); |
| } |
| '''; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| |
| // Expect no result. |
| final res = await getSignatureHelp( |
| mainFileUri, |
| positionFromMarker(content), |
| SignatureHelpContext( |
| triggerKind: SignatureHelpTriggerKind.TriggerCharacter, |
| isRetrigger: false, |
| ), |
| ); |
| expect(res, isNull); |
| } |
| |
| Future<void> test_triggerCharacter_validLocation() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo(^ |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| expectedFormat: MarkupKind.Markdown, |
| context: SignatureHelpContext( |
| triggerKind: SignatureHelpTriggerKind.Invoked, |
| isRetrigger: false, |
| )); |
| } |
| |
| Future<void> test_unopenFile() async { |
| final content = ''' |
| /// Does foo. |
| foo(String s, int i) { |
| foo(^); |
| } |
| '''; |
| final expectedLabel = 'foo(String s, int i)'; |
| final expectedDoc = 'Does foo.'; |
| |
| newFile(mainFilePath, content: withoutMarkers(content)); |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'int i'), |
| ], |
| ); |
| } |
| } |
| |
| @reflectiveTest |
| class SignatureHelpWithNullSafetyTest extends AbstractLspAnalysisServerTest |
| with SignatureHelpMixin { |
| @override |
| AnalysisServerOptions get serverOptions => |
| AnalysisServerOptions()..enabledExperiments = ['non-nullable']; |
| |
| Future<void> test_params_requiredNamed() async { |
| // This test requires support for the "required" keyword. |
| final content = ''' |
| /// Does foo. |
| foo(String s, {bool b = true, required bool a}) { |
| foo(^); |
| } |
| '''; |
| |
| final expectedLabel = 'foo(String s, {bool b = true, required bool a})'; |
| final expectedDoc = 'Does foo.'; |
| |
| await initialize( |
| textDocumentCapabilities: withSignatureHelpContentFormat( |
| emptyTextDocumentClientCapabilities, [MarkupKind.Markdown])); |
| await openFile(mainFileUri, withoutMarkers(content)); |
| await testSignature( |
| content, |
| expectedLabel, |
| expectedDoc, |
| [ |
| ParameterInformation(label: 'String s'), |
| ParameterInformation(label: 'bool b = true'), |
| ParameterInformation(label: 'required bool a'), |
| ], |
| ); |
| } |
| } |