2f706c4290
Work towards https://github.com/dart-lang/sdk/issues/55660 Change-Id: I9c5aa920e9f96f99ea00d6993b5f4dafd415f831 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/426986 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
1004 lines
22 KiB
Dart
1004 lines
22 KiB
Dart
// 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.dart';
|
|
import 'package:analysis_server/src/legacy_analysis_server.dart';
|
|
import 'package:analysis_server/src/lsp/constants.dart';
|
|
import 'package:analyzer/src/test_utilities/test_code_format.dart';
|
|
import 'package:analyzer_testing/experiments/experiments.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import '../tool/lsp_spec/matchers.dart';
|
|
import '../utils/test_code_extensions.dart';
|
|
import 'server_abstract.dart';
|
|
|
|
void main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(HoverTest);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class HoverTest extends AbstractLspAnalysisServerTest {
|
|
@override
|
|
AnalysisServerOptions get serverOptions =>
|
|
AnalysisServerOptions()..enabledExperiments = experimentsForTests;
|
|
|
|
/// Checks whether the correct types of documentation are returned in a Hover
|
|
/// based on [preference].
|
|
Future<void> assertDocumentation(
|
|
String? preference, {
|
|
required bool includesSummary,
|
|
required bool includesFull,
|
|
}) async {
|
|
var code = TestCode.parse('''
|
|
/// Summary.
|
|
///
|
|
/// Full.
|
|
class ^A {}
|
|
''');
|
|
|
|
await provideConfig(initialize, {
|
|
if (preference != null) 'documentation': preference,
|
|
});
|
|
await openFile(mainFileUri, code.code);
|
|
await initialAnalysis;
|
|
var hover = await getHover(mainFileUri, code.position.position);
|
|
var hoverContents = _getStringContents(hover!);
|
|
|
|
if (includesSummary) {
|
|
expect(hoverContents, contains('Summary.'));
|
|
} else {
|
|
expect(hoverContents, isNot(contains('Summary.')));
|
|
}
|
|
|
|
if (includesFull) {
|
|
expect(hoverContents, contains('Full.'));
|
|
} else {
|
|
expect(hoverContents, isNot(contains('Full.')));
|
|
}
|
|
}
|
|
|
|
Future<void> assertMarkdownContents(String content, Matcher matcher) async {
|
|
setHoverContentFormat([MarkupKind.Markdown]);
|
|
|
|
var code = TestCode.parse(content);
|
|
|
|
await initialize();
|
|
await openFile(mainFileUri, code.code);
|
|
await initialAnalysis;
|
|
var hover = await getHover(mainFileUri, code.position.position);
|
|
expect(hover, isNotNull);
|
|
expect(hover!.range, equals(code.range.range));
|
|
expect(hover.contents, isNotNull);
|
|
var markup = _getMarkupContents(hover);
|
|
expect(markup.kind, equals(MarkupKind.Markdown));
|
|
expect(markup.value, matcher);
|
|
}
|
|
|
|
Future<void> assertNullHover(
|
|
String content, {
|
|
bool waitForAnalysis = false,
|
|
bool withOpenFile = true,
|
|
}) async {
|
|
var code = TestCode.parse(content);
|
|
|
|
var initialAnalysis = waitForAnalysis ? waitForAnalysisComplete() : null;
|
|
await initialize();
|
|
if (withOpenFile) {
|
|
await openFile(mainFileUri, code.code);
|
|
} else {
|
|
newFile(mainFilePath, code.code);
|
|
}
|
|
await initialAnalysis;
|
|
var hover = await getHover(mainFileUri, code.position.position);
|
|
expect(hover, isNull);
|
|
}
|
|
|
|
Future<void> assertPlainTextContents(String content, Matcher matcher) async {
|
|
setHoverContentFormat([MarkupKind.PlainText]);
|
|
var code = TestCode.parse(content);
|
|
|
|
await initialize();
|
|
await openFile(mainFileUri, code.code);
|
|
await initialAnalysis;
|
|
var hover = await getHover(mainFileUri, code.position.position);
|
|
expect(hover, isNotNull);
|
|
expect(hover!.range, equals(code.range.range));
|
|
expect(hover.contents, isNotNull);
|
|
var markup = _getMarkupContents(hover);
|
|
expect(markup.kind, equals(MarkupKind.PlainText));
|
|
expect(markup.value, matcher);
|
|
}
|
|
|
|
Future<void> assertStringContents(
|
|
String content,
|
|
Matcher matcher, {
|
|
bool waitForAnalysis = false,
|
|
bool withOpenFile = true,
|
|
}) async {
|
|
var code = TestCode.parse(content);
|
|
|
|
var initialAnalysis = waitForAnalysis ? waitForAnalysisComplete() : null;
|
|
await initialize();
|
|
if (withOpenFile) {
|
|
await openFile(mainFileUri, code.code);
|
|
} else {
|
|
newFile(mainFilePath, code.code);
|
|
}
|
|
await initialAnalysis;
|
|
var hover = await getHover(mainFileUri, code.position.position);
|
|
expect(hover, isNotNull);
|
|
expect(hover!.range, equals(code.range.range));
|
|
expect(hover.contents, isNotNull);
|
|
var contents = _getStringContents(hover);
|
|
expect(contents, matcher);
|
|
}
|
|
|
|
Future<void> test_dartDoc_macros() =>
|
|
assertStringContents(waitForAnalysis: true, '''
|
|
/// {@template template_name}
|
|
/// This is shared content.
|
|
/// {@endtemplate}
|
|
const String? foo = null;
|
|
|
|
/// {@macro template_name}
|
|
const String? [!f^oo2!] = null;
|
|
''', endsWith('This is shared content.'));
|
|
|
|
Future<void> test_dartDocPreference_full() =>
|
|
assertDocumentation('full', includesSummary: true, includesFull: true);
|
|
|
|
Future<void> test_dartDocPreference_none() =>
|
|
assertDocumentation('none', includesSummary: false, includesFull: false);
|
|
|
|
Future<void> test_dartDocPreference_summary() => assertDocumentation(
|
|
'summary',
|
|
includesSummary: true,
|
|
includesFull: false,
|
|
);
|
|
|
|
/// No preference should result in full docs.
|
|
Future<void> test_dartDocPreference_unset() =>
|
|
assertDocumentation(null, includesSummary: true, includesFull: true);
|
|
|
|
Future<void> test_enum_member() async {
|
|
var content = '''
|
|
enum MyEnum { one }
|
|
|
|
void f() {
|
|
MyEnum.[!o^ne!];
|
|
}
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
MyEnum get one
|
|
```
|
|
Type: `MyEnum`
|
|
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_enum_values() async {
|
|
var content = '''
|
|
enum MyEnum { one }
|
|
|
|
void f() {
|
|
MyEnum.[!va^lues!];
|
|
}
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
List<MyEnum> get values
|
|
```
|
|
Type: `List<MyEnum>`
|
|
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_field_underscore() async {
|
|
var content = '''
|
|
class A {
|
|
int _ = 1;
|
|
int f() => [!^_!];
|
|
}
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
int get _
|
|
```
|
|
Type: `int`
|
|
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_forLoop_declaredVariable() async {
|
|
var content = '''
|
|
void f() {
|
|
for (var [!ii^i!] in <String>[]) {}
|
|
}
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
String iii
|
|
```
|
|
Type: `String`''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_forLoop_variableReference() async {
|
|
var content = '''
|
|
void f() {
|
|
for (var iii in <String>[]) {
|
|
print([!ii^i!]);
|
|
}
|
|
}
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
String iii
|
|
```
|
|
Type: `String`''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_function_startOfParameterList() => assertStringContents('''
|
|
/// This is a function.
|
|
void [!abc!]^() {}
|
|
''', contains('This is a function.'));
|
|
|
|
Future<void> test_function_startOfTypeParameterList() =>
|
|
assertStringContents('''
|
|
/// This is a function.
|
|
void [!abc!]^<T>(T a) {}
|
|
''', contains('This is a function.'));
|
|
|
|
Future<void> test_hover_bad_position() async {
|
|
await initialize();
|
|
await openFile(mainFileUri, '');
|
|
await expectLater(
|
|
() => getHover(mainFileUri, Position(line: 999, character: 999)),
|
|
throwsA(isResponseError(ServerErrorCodes.InvalidFileLineCol)),
|
|
);
|
|
}
|
|
|
|
Future<void> test_import_alias_dart() async {
|
|
var content = '''
|
|
import 'dart:core' as [!l^ib!];
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
import 'dart:core' as lib;
|
|
```
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_import_alias_interpolation() async {
|
|
var content = '''
|
|
// ignore: uri_with_interpolation
|
|
import '\$bug.dart' as [!l^ib!];
|
|
const bug = 'bug';
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
import '<unknown>' as lib;
|
|
```
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_import_alias_multiple_equal() async {
|
|
newFile('/home/my_project/lib/lib.dart', '''
|
|
/// This is a string.
|
|
library my.lib;
|
|
''');
|
|
var content = '''
|
|
import 'dart:math' as lib;
|
|
import 'lib.dart' as [!l^ib!];
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
import 'dart:math' as lib;
|
|
import 'lib.dart' as lib;
|
|
```
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_import_alias_package() async {
|
|
newFile('/home/my_project/lib/lib.dart', '''
|
|
/// This is a string.
|
|
library my.lib;
|
|
''');
|
|
var content = '''
|
|
import 'package:test/lib.dart' as [!l^ib!];
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
import 'package:test/lib.dart' as lib;
|
|
```
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_import_alias_project() async {
|
|
newFile('/home/my_project/lib/lib.dart', '''
|
|
''');
|
|
var content = '''
|
|
import 'lib.dart' as [!l^ib!];
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
import 'lib.dart' as lib;
|
|
```
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_import_prefix() async {
|
|
newFile('/home/my_project/lib/lib.dart', '''
|
|
/// This is a string.
|
|
library my.lib;
|
|
class C {}
|
|
''');
|
|
var content = '''
|
|
import 'lib.dart' as lib;
|
|
[!li^b!].C? c;
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
import 'lib.dart' as lib;
|
|
```
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_localVariable_wildcard() async {
|
|
var content = '''
|
|
f() {
|
|
int [!^_!] = 0;
|
|
}
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
int _
|
|
```
|
|
Type: `int`''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_markdown_isFormattedForDisplay() async {
|
|
var content = '''
|
|
/// This is a string.
|
|
///
|
|
/// {@template foo}
|
|
/// With some [refs] and some
|
|
/// [links](https://www.dartlang.org/)
|
|
/// {@endtemplate foo}
|
|
///
|
|
/// ```dart sample
|
|
/// print();
|
|
/// ```
|
|
String? [!a^bc!];
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
String? abc
|
|
```
|
|
Type: `String?`
|
|
|
|
*package:test/main.dart*
|
|
|
|
---
|
|
This is a string.
|
|
|
|
With some [refs] and some
|
|
[links](https://www.dartlang.org/)
|
|
|
|
```dart
|
|
print();
|
|
```
|
|
'''.trim();
|
|
|
|
await assertMarkdownContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_markdown_simple() => assertMarkdownContents('''
|
|
/// This is a string.
|
|
String [!a^bc!] = '';
|
|
''', contains('This is a string.'));
|
|
|
|
Future<void> test_method_mixin_onImplementation() async {
|
|
var content = '''
|
|
abstract class A {
|
|
/// Documentation for [f].
|
|
void f();
|
|
}
|
|
|
|
abstract class B implements A {}
|
|
|
|
mixin M on B {
|
|
@override
|
|
void [!f^!]() {}
|
|
}
|
|
''';
|
|
await assertStringContents(content, contains('Documentation for [f].'));
|
|
}
|
|
|
|
Future<void> test_method_startOfParameterList() => assertStringContents('''
|
|
class A {
|
|
/// This is a method.
|
|
void [!abc!]^() {}
|
|
}
|
|
''', contains('This is a method.'));
|
|
|
|
Future<void> test_method_startOfTypeParameterList() =>
|
|
assertStringContents('''
|
|
class A {
|
|
/// This is a method.
|
|
String [!abc!]^<T>(T a) => '';
|
|
}
|
|
''', contains('This is a method.'));
|
|
|
|
Future<void> test_method_underscore() async {
|
|
var content = '''
|
|
class A {
|
|
int _() => 1;
|
|
int f() => [!^_!]();
|
|
}
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
int _()
|
|
```
|
|
Type: `int Function()`
|
|
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_noElement() async {
|
|
var code = TestCode.parse('''
|
|
String? abc;
|
|
|
|
^
|
|
|
|
int? a;
|
|
''');
|
|
|
|
await initialize();
|
|
await openFile(mainFileUri, code.code);
|
|
var hover = await getHover(mainFileUri, code.position.position);
|
|
expect(hover, isNull);
|
|
}
|
|
|
|
Future<void> test_nonDartFile() async {
|
|
await initialize();
|
|
await openFile(pubspecFileUri, simplePubspecContent);
|
|
var hover = await getHover(pubspecFileUri, startOfDocPos);
|
|
expect(hover, isNull);
|
|
}
|
|
|
|
Future<void> test_nullableTypes() async {
|
|
var content = '''
|
|
String? [!a^bc!];
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
String? abc
|
|
```
|
|
Type: `String?`
|
|
|
|
*package:test/main.dart*
|
|
'''.trim();
|
|
|
|
await assertStringContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_parameter_wildcard() async {
|
|
var content = '''
|
|
f(int [!^_!]) { }
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
int _
|
|
```
|
|
Type: `int`''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_pattern_assignment_left() => assertStringContents('''
|
|
void f(String a, String b) {
|
|
(b, [!a^!]) = (a, b);
|
|
}
|
|
''', contains('Type: `String`'));
|
|
|
|
Future<void> test_pattern_assignment_list() => assertStringContents('''
|
|
void f(List<int> x, num a) {
|
|
[[!a^!]] = x;
|
|
}
|
|
''', contains('num a'));
|
|
|
|
Future<void> test_pattern_assignment_right() => assertStringContents('''
|
|
void f(String a, String b) {
|
|
(b, a) = ([!a^!], b);
|
|
}
|
|
''', contains('Type: `String`'));
|
|
|
|
Future<void> test_pattern_cast_typeName() => assertStringContents('''
|
|
void f((num, Object) record) {
|
|
var (i as int, s as [!St^ring!]) = record;
|
|
}
|
|
''', contains('class String'));
|
|
|
|
Future<void> test_pattern_map() => assertStringContents('''
|
|
void f(x) {
|
|
switch (x) {
|
|
case {0: [!Str^ing!] a}:
|
|
break;
|
|
}
|
|
}
|
|
''', contains('class String'));
|
|
|
|
Future<void> test_pattern_map_typeArguments() => assertStringContents('''
|
|
void f(x) {
|
|
switch (x) {
|
|
case <int, [!Str^ing!]>{0: var a}:
|
|
break;
|
|
}
|
|
}
|
|
''', contains('class String'));
|
|
|
|
Future<void> test_pattern_nullAssert() => assertStringContents('''
|
|
void f((int?, int?) position) {
|
|
var ([!x^!]!, y!) = position;
|
|
}
|
|
''', contains('Type: `int`'));
|
|
|
|
Future<void> test_pattern_nullCheck() => assertStringContents('''
|
|
void f(String? maybeString) {
|
|
switch (maybeString) {
|
|
case var [!s^!]?:
|
|
}
|
|
}
|
|
''', contains('Type: `String`'));
|
|
|
|
Future<void> test_pattern_object_fieldName() => assertStringContents('''
|
|
double calculateArea(Shape shape) =>
|
|
switch (shape) {
|
|
Square([!leng^th!]: var l) => l * l,
|
|
};
|
|
|
|
sealed class Shape { }
|
|
class Square extends Shape {
|
|
/// The length.
|
|
double get length => 0;
|
|
}
|
|
''', allOf([contains('double get length'), contains('The length.')]));
|
|
|
|
Future<void> test_pattern_object_typeName() => assertStringContents('''
|
|
double calculateArea(Shape shape) =>
|
|
switch (shape) {
|
|
[!Squ^are!](length: var l) => l * l,
|
|
};
|
|
|
|
sealed class Shape { }
|
|
/// A square.
|
|
class Square extends Shape {
|
|
double get length => 0;
|
|
}
|
|
''', contains('A square.'));
|
|
|
|
Future<void> test_pattern_record_fieldName() => assertStringContents('''
|
|
void f(({int foo}) x, num a) {
|
|
([!fo^o!]: a,) = x;
|
|
}
|
|
''', contains('Type: `int`'));
|
|
|
|
Future<void> test_pattern_record_fieldValue() => assertStringContents('''
|
|
void f(({int foo}) x, num a) {
|
|
(foo: [!a^!],) = x;
|
|
}
|
|
''', contains('Type: `num`'));
|
|
|
|
Future<void> test_pattern_record_variable() => assertStringContents('''
|
|
void f(({int foo}) x, num a) {
|
|
(foo: a,) = [!x^!];
|
|
}
|
|
''', contains('Type: `({int foo})`'));
|
|
|
|
Future<void> test_pattern_relational_variable() => assertStringContents('''
|
|
String f(int char) {
|
|
const zero = 0;
|
|
return switch (char) {
|
|
== [!ze^ro!] => 'zero',
|
|
_ => '',
|
|
};
|
|
}
|
|
''', contains('Type: `int`'));
|
|
|
|
Future<void> test_pattern_variable_wildcard() => assertStringContents('''
|
|
void f() {
|
|
var a = (1, 2);
|
|
var ([!^_!], _) = a;
|
|
}
|
|
''', contains('Type: `int`'));
|
|
|
|
Future<void> test_pattern_variable_wildcard_annotated() =>
|
|
assertStringContents('''
|
|
void f() {
|
|
var a = (1, 2);
|
|
var (int [!^_!], _) = a;
|
|
}
|
|
''', contains('Type: `int`'));
|
|
|
|
Future<void> test_plainText_simple() => assertPlainTextContents('''
|
|
/// This is a string.
|
|
String? [!a^bc!];
|
|
''', contains('This is a string.'));
|
|
|
|
Future<void> test_promotedTypes() async {
|
|
var content = '''
|
|
void f(aaa) {
|
|
if (aaa is String) {
|
|
print([!aa^a!]);
|
|
}
|
|
}
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
dynamic aaa
|
|
```
|
|
Type: `String`
|
|
'''.trim();
|
|
|
|
await assertStringContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_range_multiLineConstructorCall() => assertStringContents('''
|
|
final a = new [!Str^ing.fromCharCodes!]([
|
|
1,
|
|
2,
|
|
]);
|
|
''', contains('String String.fromCharCodes('));
|
|
|
|
Future<void> test_recordLiteral_named() => assertStringContents(r'''
|
|
void f(({int f1, int f2}) r) {
|
|
r.[!f^1!];
|
|
}
|
|
''', contains('Type: `int`'));
|
|
|
|
Future<void> test_recordLiteral_positional() => assertStringContents(r'''
|
|
void f((int, int) r) {
|
|
r.[!$^1!];
|
|
}
|
|
''', contains('Type: `int`'));
|
|
|
|
Future<void> test_recordType_parameter() => assertStringContents('''
|
|
Object f(([!dou^ble!], double) param) {
|
|
return (1.0, 1.0);
|
|
}
|
|
''', contains('class double'));
|
|
|
|
Future<void> test_recordType_return() => assertStringContents('''
|
|
([!dou^ble!], double) f() {
|
|
return (1.0, 1.0);
|
|
}
|
|
''', contains('class double'));
|
|
|
|
Future<void> test_signatureFormatting_multiLine() => assertStringContents(
|
|
'''
|
|
class Foo {
|
|
Foo(String arg1, String arg2, [String? arg3]);
|
|
}
|
|
|
|
void f() {
|
|
var a = [!Fo^o!]('', '');
|
|
}
|
|
''',
|
|
startsWith('''
|
|
```dart
|
|
(new) Foo Foo(
|
|
String arg1,
|
|
String arg2, [
|
|
String? arg3,
|
|
])
|
|
```'''),
|
|
);
|
|
|
|
Future<void> test_signatureFormatting_singleLine() => assertStringContents(
|
|
'''
|
|
class Foo {
|
|
Foo(String a, String b);
|
|
}
|
|
|
|
void f() {
|
|
var a = [!Fo^o!]('', '');
|
|
}
|
|
''',
|
|
startsWith('''
|
|
```dart
|
|
(new) Foo Foo(String a, String b)
|
|
```'''),
|
|
);
|
|
|
|
Future<void> test_staticType_field() async {
|
|
var content = '''
|
|
class A<T> {
|
|
late final T? myField;
|
|
}
|
|
|
|
final data = A<String?>().[!myF^ield!];
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
String? get myField
|
|
```
|
|
Type: `String?`
|
|
|
|
*package:test/main.dart*
|
|
'''.trim();
|
|
|
|
await assertStringContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_staticType_getter() async {
|
|
var content = '''
|
|
class A {
|
|
String get myGetter => '';
|
|
}
|
|
|
|
final data = A().[!myG^etter!];
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
String get myGetter
|
|
```
|
|
Type: `String`
|
|
|
|
*package:test/main.dart*
|
|
'''.trim();
|
|
|
|
await assertStringContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_staticType_getter_generic() async {
|
|
var content = '''
|
|
class A<T> {
|
|
late final T? myField;
|
|
T? get myGetter => myField;
|
|
}
|
|
|
|
final data = A<String?>().[!myG^etter!];
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
String? get myGetter
|
|
```
|
|
Type: `String?`
|
|
|
|
*package:test/main.dart*
|
|
'''.trim();
|
|
|
|
await assertStringContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_staticType_setter() async {
|
|
var content = '''
|
|
class A {
|
|
set mySetter(String value) {}
|
|
}
|
|
|
|
void f() {
|
|
A().[!myS^etter!] = '';
|
|
}
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
set mySetter(String value)
|
|
```
|
|
Type: `String`
|
|
|
|
*package:test/main.dart*
|
|
'''.trim();
|
|
|
|
await assertStringContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_staticType_setter_generic() async {
|
|
var content = '''
|
|
class A<T> {
|
|
set mySetter(T value) {}
|
|
}
|
|
|
|
void f() {
|
|
A<String>().[!myS^etter!] = '';
|
|
}
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
set mySetter(String value)
|
|
```
|
|
Type: `String`
|
|
|
|
*package:test/main.dart*
|
|
'''.trim();
|
|
|
|
await assertStringContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_string_noDocComment() async {
|
|
var content = '''
|
|
String? [!a^bc!];
|
|
''';
|
|
|
|
var expectedHoverContent =
|
|
'''
|
|
```dart
|
|
String? abc
|
|
```
|
|
Type: `String?`
|
|
|
|
*package:test/main.dart*
|
|
'''.trim();
|
|
|
|
await assertStringContents(content, equals(expectedHoverContent));
|
|
}
|
|
|
|
Future<void> test_string_reflectsLatestEdits() async {
|
|
var original = TestCode.parse('''
|
|
/// Original string.
|
|
String? [!a^bc!];
|
|
''');
|
|
var updated = TestCode.parse('''
|
|
/// Updated string.
|
|
String? [!a^bc!];
|
|
''');
|
|
|
|
await initialize();
|
|
await openFile(mainFileUri, original.code);
|
|
var hover = await getHover(mainFileUri, original.position.position);
|
|
expect(hover, isNotNull);
|
|
var contents = _getStringContents(hover!);
|
|
expect(contents, contains('Original'));
|
|
|
|
await replaceFile(222, mainFileUri, updated.code);
|
|
hover = await getHover(mainFileUri, updated.position.position);
|
|
expect(hover, isNotNull);
|
|
contents = _getStringContents(hover!);
|
|
expect(contents, contains('Updated'));
|
|
}
|
|
|
|
Future<void> test_string_simple() async {
|
|
var content = '''
|
|
/// This is a string.
|
|
String? [!a^bc!];
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
String? abc
|
|
```
|
|
Type: `String?`
|
|
|
|
*package:test/main.dart*
|
|
|
|
---
|
|
This is a string.''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_topLevelFunction_underscore() async {
|
|
var content = '''
|
|
int _() => 1;
|
|
int f() => [!^_!]();
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
int _()
|
|
```
|
|
Type: `int Function()`
|
|
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_topLevelVariable_underscore() async {
|
|
var content = '''
|
|
int _ = 1;
|
|
int f() => [!^_!];
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
int get _
|
|
```
|
|
Type: `int`
|
|
|
|
*package:test/main.dart*''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_typeParameter() async {
|
|
var content = '''
|
|
class C<[!^T!]> {}
|
|
''';
|
|
await assertNullHover(content);
|
|
}
|
|
|
|
Future<void> test_typeParameter_wildcard() async {
|
|
var content = '''
|
|
class C<[!^_!]> {}
|
|
''';
|
|
await assertNullHover(content);
|
|
}
|
|
|
|
Future<void> test_unnamed_library_directive() async {
|
|
var content = '''
|
|
/// This is a string.
|
|
[!lib^rary!];
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
library package:test/main.dart
|
|
```
|
|
*package:test/main.dart*
|
|
|
|
---
|
|
This is a string.''';
|
|
await assertStringContents(content, equals(expected));
|
|
}
|
|
|
|
Future<void> test_unopenFile() async {
|
|
var content = '''
|
|
/// This is a string.
|
|
String? [!a^bc!];
|
|
''';
|
|
var expected = '''
|
|
```dart
|
|
String? abc
|
|
```
|
|
Type: `String?`
|
|
|
|
*package:test/main.dart*
|
|
|
|
---
|
|
This is a string.''';
|
|
await assertStringContents(withOpenFile: false, content, equals(expected));
|
|
}
|
|
|
|
MarkupContent _getMarkupContents(Hover hover) {
|
|
return hover.contents.map(
|
|
(t1) => t1,
|
|
(t2) => throw 'Hover contents were String, not MarkupContent',
|
|
);
|
|
}
|
|
|
|
String _getStringContents(Hover hover) {
|
|
return hover.contents.map(
|
|
(t1) => throw 'Hover contents were MarkupContent, not String',
|
|
(t2) => t2,
|
|
);
|
|
}
|
|
}
|