blob: b7c92ea32f986d797971709d75f13a27c2475285 [file] [log] [blame]
// Copyright (c) 2022, 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:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'server_abstract.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(TypeDefinitionTest);
});
}
@reflectiveTest
class TypeDefinitionTest extends AbstractLspAnalysisServerTest {
Uri get sdkCoreUri {
final sdkCorePath = convertPath('/sdk/lib/core/core.dart');
return Uri.file(sdkCorePath);
}
Future<void> test_currentFile() async {
final contents = '''
class [[A]] {}
final [[a^]] = A();
''';
final ranges = rangesFromMarkers(contents);
final targetRange = ranges[0];
final originRange = ranges[1];
final result = await _getResult(contents);
expect(result.originSelectionRange, originRange);
expect(result.targetUri, mainFileUri.toString());
expect(result.targetSelectionRange, targetRange);
expect(result.targetRange, rangeOfString(contents, 'class A {}'));
}
Future<void> test_doubleLiteral() async {
final contents = '''
const a = [[12^.3]];
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'double');
}
Future<void> test_getter() async {
final contents = '''
class A {
String get aaa => '';
}
void f() {
final a = A();
print(a.[[a^aa]]);
}
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_intLiteral() async {
final contents = '''
const a = [[12^3]];
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'int');
}
/// Checks a result when the client does not support [LocationLink], only
/// the original LSP [Location].
Future<void> test_location() async {
final contents = '''
const a^ = 'test string';
''';
final result = await _getLocationResult(contents);
expect(result.uri, '$sdkCoreUri');
_expectNameRange(result.range, 'String');
}
Future<void> test_nonDartFile() async {
final contents = '''
const a = '^';
''';
newFile2(pubspecFilePath, withoutMarkers(contents));
await initialize();
final results = await getTypeDefinitionAsLocation(
mainFileUri, positionFromMarker(contents));
expect(results, isEmpty);
}
Future<void> test_otherFile() async {
final otherFilePath = join(projectFolderPath, 'lib', 'other.dart');
final otherFileUri = Uri.file(otherFilePath);
final contents = '''
import 'other.dart';
final [[a^]] = A();
''';
final otherContents = '''
class [[A]] {}
''';
newFile2(otherFilePath, withoutMarkers(otherContents));
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
expect(result.targetUri, otherFileUri.toString());
expect(result.targetSelectionRange, rangeFromMarkers(otherContents));
expect(result.targetRange, rangeOfString(otherContents, 'class A {}'));
}
Future<void> test_parameter() async {
final contents = '''
void f(String a) {
void f([['te^st']]);
}
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_parameterName() async {
final contents = '''
void f({String a}) {
void f([[a^]]: 'test');
}
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_setter() async {
final contents = '''
class A {
set aaa(String value) {}
}
void f() {
final a = A();
a.[[a^aa]] = '';
}
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_stringLiteral() async {
final contents = '''
const a = [['te^st string']];
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_type() async {
final contents = '''
[[St^ring]] a = '';
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_unopenedFile() async {
final contents = '''
const a = [['^']];
''';
newFile2(mainFilePath, withoutMarkers(contents));
final result = await _getResult(contents, inOpenFile: false);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableDeclaration() async {
final contents = '''
const [[a^]] = 'test string';
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableDeclaration_inferredType() async {
final contents = '''
var [[a^]] = 'test string';
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableReference() async {
final contents = '''
void f() {
const a = 'test string';
print([[a^]]);
}
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
Future<void> test_variableReference_inferredType() async {
final contents = '''
void f() {
var a = 'test string';
print([[a^]]);
}
''';
final result = await _getResult(contents);
expect(result.originSelectionRange, rangeFromMarkers(contents));
_expectSdkCoreType(result, 'String');
}
/// Expects [range] looks consistent with a range of an elements code.
///
/// This is used for SDK sources where the exact location is not known to the
/// test.
void _expectCodeRange(Range range) {
expect(range.start.line, isPositive);
expect(range.end.line, isPositive);
// And a range that spans multiple lines.
expect(range.start.line, lessThan(range.end.line));
}
/// Expects [range] looks consistent with a range of an elements name.
///
/// This is used for SDK sources where the exact location is not known to the
/// test.
void _expectNameRange(Range range, String name) {
expect(range.start.line, isPositive);
expect(range.end.line, isPositive);
// Expect a single line, with the length matching `name`.
expect(range.start.line, range.end.line);
expect(
range.end.character - range.start.character,
name.length,
);
}
/// Expects [range] looks consistent with a range of an elements code.
///
/// This is used for SDK sources where the exact location is not known to the
/// test.
void _expectSdkCoreType(LocationLink result, String typeName) {
expect(result.targetUri, '$sdkCoreUri');
_expectNameRange(result.targetSelectionRange, typeName);
_expectCodeRange(result.targetRange);
}
/// Gets the type definition as an LSP Location object.
Future<Location> _getLocationResult(String contents) async {
await initialize();
await openFile(mainFileUri, withoutMarkers(contents));
final results = await getTypeDefinitionAsLocation(
mainFileUri, positionFromMarker(contents));
return results.single;
}
/// Advertises support for the LSP LocationLink type and gets the type
/// definition using that.
Future<LocationLink> _getResult(String contents,
{Uri? fileUri, bool inOpenFile = true}) async {
fileUri ??= mainFileUri;
await initialize(
textDocumentCapabilities:
withLocationLinkSupport(emptyTextDocumentClientCapabilities),
);
if (inOpenFile) {
await openFile(fileUri, withoutMarkers(contents));
}
final results = await getTypeDefinitionAsLocationLinks(
mainFileUri,
positionFromMarker(contents),
);
return results.single;
}
}