afcfbbeba8
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the packages owned by the developer experience team to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) bumping the packages' SDK constraints to `3.13.0-0`, (b) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint failures using `dart fix`, and then (d) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Since this change requires bumping SDK constaints to `3.13.0-0`, it was only performed on packages that are *not* published on pub. (Packages that *are* published on pub should remain on lower language versions until at least after the stable version of 3.13 is released, so that we don't block users on the stable channel from receiving updates to those packages.) Change-Id: Ibb4daebafd239da58251e838ea6a3f336a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505046 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
2392 lines
49 KiB
Dart
2392 lines
49 KiB
Dart
// Copyright (c) 2014, 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 'dart:async';
|
|
|
|
import 'package:analysis_server/src/protocol_server.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/src/test_utilities/platform.dart';
|
|
import 'package:analyzer/src/test_utilities/test_code_format.dart';
|
|
import 'package:analyzer_testing/package_config_file_builder.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import 'analysis_server_base.dart';
|
|
import 'mocks.dart';
|
|
import 'services/completion/dart/completion_check.dart';
|
|
import 'services/completion/dart/completion_printer.dart' as printer;
|
|
import 'services/completion/dart/text_expectations.dart';
|
|
|
|
void main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(CompletionDomainHandlerGetSuggestionDetails2Test);
|
|
defineReflectiveTests(CompletionDomainHandlerGetSuggestions2Test);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class CompletionDomainHandlerGetSuggestionDetails2Test
|
|
extends PubPackageAnalysisServerTest {
|
|
String get testEolEscaped =>
|
|
testEol.replaceAll('\r', r'\r').replaceAll('\n', r'\n');
|
|
|
|
void assertDetailsText(
|
|
CompletionGetSuggestionDetails2Result result,
|
|
String expected, {
|
|
bool printIfFailed = true,
|
|
}) {
|
|
var buffer = StringBuffer();
|
|
_SuggestionDetailsPrinter(
|
|
resourceProvider: resourceProvider,
|
|
fileDisplayMap: {testFile: 'testFile'},
|
|
buffer: buffer,
|
|
result: result,
|
|
).writeResult();
|
|
var actual = buffer.toString();
|
|
|
|
if (actual != expected) {
|
|
if (printIfFailed) {
|
|
print(actual);
|
|
}
|
|
TextExpectationsCollector.add(actual);
|
|
}
|
|
expect(actual, expected);
|
|
}
|
|
|
|
Future<void> test_alreadyImported() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var details = await _getTestCodeDetails(
|
|
'''
|
|
import 'dart:math';
|
|
void f() {
|
|
Rand^
|
|
}
|
|
''',
|
|
completion: 'Random',
|
|
libraryUri: 'dart:math',
|
|
);
|
|
|
|
assertDetailsText(details, r'''
|
|
completion: Random
|
|
change
|
|
''');
|
|
}
|
|
|
|
Future<void> test_import_dart() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var details = await _getTestCodeDetails(
|
|
'''
|
|
void f() {
|
|
R^
|
|
}
|
|
''',
|
|
completion: 'Random',
|
|
libraryUri: 'dart:math',
|
|
);
|
|
|
|
assertDetailsText(details, '''
|
|
completion: Random
|
|
change
|
|
testFile
|
|
offset: 0
|
|
length: 0
|
|
replacement: import 'dart:math';$testEolEscaped$testEolEscaped
|
|
''');
|
|
}
|
|
|
|
Future<void> test_import_package_dependencies() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
dependencies:
|
|
aaa: any
|
|
''');
|
|
|
|
var aaaRoot = getFolder('$workspaceRootPath/packages/aaa');
|
|
newFile('${aaaRoot.path}/lib/f.dart', '''
|
|
class Test {}
|
|
''');
|
|
|
|
writeTestPackageConfig(
|
|
config: PackageConfigFileBuilder()..add(name: 'aaa', rootFolder: aaaRoot),
|
|
);
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var details = await _getTestCodeDetails(
|
|
'''
|
|
void f() {
|
|
T^
|
|
}
|
|
''',
|
|
completion: 'Test',
|
|
libraryUri: 'package:aaa/a.dart',
|
|
);
|
|
|
|
assertDetailsText(details, '''
|
|
completion: Test
|
|
change
|
|
testFile
|
|
offset: 0
|
|
length: 0
|
|
replacement: import 'package:aaa/a.dart';$testEolEscaped$testEolEscaped
|
|
''');
|
|
}
|
|
|
|
Future<void> test_import_package_this() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class Test {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var details = await _getTestCodeDetails(
|
|
'''
|
|
void f() {
|
|
T^
|
|
}
|
|
''',
|
|
completion: 'Test',
|
|
libraryUri: 'package:test/a.dart',
|
|
);
|
|
|
|
assertDetailsText(details, '''
|
|
completion: Test
|
|
change
|
|
testFile
|
|
offset: 0
|
|
length: 0
|
|
replacement: import 'package:test/a.dart';$testEolEscaped$testEolEscaped
|
|
''');
|
|
}
|
|
|
|
Future<void> test_invalidLibraryUri() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var request = CompletionGetSuggestionDetails2Params(
|
|
testFile.path,
|
|
0,
|
|
'Random',
|
|
'[foo]:bar',
|
|
).toRequest('0', clientUriConverter: server.uriConverter);
|
|
|
|
var response = await handleRequest(request);
|
|
expect(response.error?.code, RequestErrorCode.INVALID_PARAMETER);
|
|
// TODO(scheglov): Check that says "libraryUri".
|
|
}
|
|
|
|
Future<void> test_invalidPath() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var request = CompletionGetSuggestionDetails2Params(
|
|
'foo',
|
|
0,
|
|
'Random',
|
|
'dart:math',
|
|
).toRequest('0', clientUriConverter: server.uriConverter);
|
|
|
|
var response = await handleRequest(request);
|
|
expect(response.error?.code, RequestErrorCode.INVALID_FILE_PATH_FORMAT);
|
|
}
|
|
|
|
Future<void> _configureWithWorkspaceRoot() async {
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
}
|
|
|
|
Future<CompletionGetSuggestionDetails2Result> _getCodeDetails({
|
|
required String path,
|
|
required String content,
|
|
required String completion,
|
|
required String libraryUri,
|
|
}) async {
|
|
var code = TestCode.parse(content);
|
|
var completionOffset = code.position.offset;
|
|
|
|
newFile(path, code.code);
|
|
|
|
return await _getDetails(
|
|
path: path,
|
|
completionOffset: completionOffset,
|
|
completion: completion,
|
|
libraryUri: libraryUri,
|
|
);
|
|
}
|
|
|
|
Future<CompletionGetSuggestionDetails2Result> _getDetails({
|
|
required String path,
|
|
required int completionOffset,
|
|
required String completion,
|
|
required String libraryUri,
|
|
}) async {
|
|
var request = CompletionGetSuggestionDetails2Params(
|
|
path,
|
|
completionOffset,
|
|
completion,
|
|
libraryUri,
|
|
).toRequest('0', clientUriConverter: server.uriConverter);
|
|
|
|
var response = await handleSuccessfulRequest(request);
|
|
return CompletionGetSuggestionDetails2Result.fromResponse(
|
|
response,
|
|
clientUriConverter: server.uriConverter,
|
|
);
|
|
}
|
|
|
|
Future<CompletionGetSuggestionDetails2Result> _getTestCodeDetails(
|
|
String content, {
|
|
required String completion,
|
|
required String libraryUri,
|
|
}) {
|
|
return _getCodeDetails(
|
|
path: convertPath(testFilePath),
|
|
content: content,
|
|
completion: completion,
|
|
libraryUri: libraryUri,
|
|
);
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class CompletionDomainHandlerGetSuggestions2Test
|
|
extends PubPackageAnalysisServerTest {
|
|
printer.Configuration printerConfiguration = printer.Configuration(
|
|
filter: (suggestion) {
|
|
var completion = suggestion.completion;
|
|
if (completion.startsWith('A0')) {
|
|
return suggestion.isClass;
|
|
}
|
|
return const {'foo0'}.any(completion.startsWith);
|
|
},
|
|
withIsNotImported: true,
|
|
withLibraryUri: true,
|
|
);
|
|
|
|
/// Asserts that the [response] has the [expected] textual dump produced
|
|
/// using [printerConfiguration].
|
|
void assertResponseText(
|
|
CompletionResponseForTesting response,
|
|
String expected, {
|
|
bool printIfFailed = true,
|
|
}) {
|
|
// The generated response will always be using the EOLs being tested so
|
|
// normalize the expected test to the same to ensure that all actual output
|
|
// uses the expected line endings.
|
|
expected = normalizeNewlinesForPlatform(expected);
|
|
|
|
var buffer = StringBuffer();
|
|
printer.CompletionResponsePrinter(
|
|
buffer: buffer,
|
|
configuration: printerConfiguration,
|
|
response: response,
|
|
).writeResponse();
|
|
var actual = buffer.toString();
|
|
|
|
// The source code in our test files will always be written with only `\n`
|
|
// (due to git settings) so strip any `\r` that might be in the actual
|
|
// result for any output intended to go into source files.
|
|
var actualForSourceCode = actual.replaceAll('\r', '');
|
|
|
|
if (actual != expected) {
|
|
if (printIfFailed) {
|
|
print(actualForSourceCode);
|
|
}
|
|
TextExpectationsCollector.add(actualForSourceCode);
|
|
}
|
|
expect(actual, expected);
|
|
}
|
|
|
|
@override
|
|
void setUp() {
|
|
super.setUp();
|
|
server.completionState.budgetDuration = const Duration(seconds: 30);
|
|
}
|
|
|
|
Future<void> test_abort_onAnotherCompletionRequest() async {
|
|
var abortedIdSet = <String>{};
|
|
server.discardedRequests.stream.listen((request) {
|
|
abortedIdSet.add(request.id);
|
|
});
|
|
|
|
newFile(testFilePath, '');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
// Send three requests, the first two should be aborted.
|
|
var request0 = _sendTestCompletionRequest('0', 0);
|
|
var request1 = _sendTestCompletionRequest('1', 0);
|
|
var request2 = _sendTestCompletionRequest('2', 0);
|
|
|
|
// Wait for all three.
|
|
var response0 = await request0.toResponse();
|
|
var response1 = await request1.toResponse();
|
|
var response2 = await request2.toResponse();
|
|
|
|
// The first two should be aborted.
|
|
expect(abortedIdSet, {'0', '1'});
|
|
|
|
printerConfiguration.filter = (suggestion) {
|
|
if (suggestion.isClass) {
|
|
return const {'int'}.contains(suggestion.completion);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
assertResponseText(response0, r'''
|
|
suggestions
|
|
''');
|
|
|
|
assertResponseText(response1, r'''
|
|
suggestions
|
|
''');
|
|
|
|
assertResponseText(response2, r'''
|
|
suggestions
|
|
int
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: dart:core
|
|
''');
|
|
}
|
|
|
|
Future<void> test_abort_onUpdateContent() async {
|
|
var abortedIdSet = <String>{};
|
|
server.discardedRequests.stream.listen((request) {
|
|
abortedIdSet.add(request.id);
|
|
});
|
|
|
|
newFile(testFilePath, '');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
// Schedule a completion request.
|
|
var request = _sendTestCompletionRequest('0', 0);
|
|
|
|
// Simulate typing in the IDE.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
testFile.path: AddContentOverlay('void f() {}'),
|
|
}).toRequest('1', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
// The request should be aborted.
|
|
var response = await request.toResponse();
|
|
expect(abortedIdSet, {'0'});
|
|
|
|
assertResponseText(response, r'''
|
|
suggestions
|
|
''');
|
|
}
|
|
|
|
Future<void> test_applyPendingFileChanges() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
// Request with the empty content.
|
|
await _getTestCodeSuggestions('^');
|
|
|
|
// Change the file, and request again.
|
|
// Should apply pending file changes before resolving.
|
|
var response = await _getTestCodeSuggestions('Str^');
|
|
|
|
printerConfiguration.filter = (suggestion) {
|
|
if (suggestion.isClass) {
|
|
return const {'String'}.contains(suggestion.completion);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 3
|
|
suggestions
|
|
String
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: dart:core
|
|
''');
|
|
}
|
|
|
|
Future<void> test_isNotImportedFeature_prefixed_classInstanceMethod() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class A {
|
|
void foo01() {}
|
|
}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
import 'a.dart';
|
|
|
|
class B extends A {
|
|
void foo02() {}
|
|
}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'b.dart';
|
|
|
|
void f(B b) {
|
|
b.foo0^
|
|
}
|
|
''');
|
|
|
|
// The fact that `b.dart` is imported, and `a.dart` is not, does not affect
|
|
// the order of suggestions added with an expression prefix. We are not
|
|
// going to import anything, so this does not matter.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_dart() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
void f() {
|
|
Rand^
|
|
}
|
|
''');
|
|
|
|
printerConfiguration.filter = (suggestion) {
|
|
return suggestion.isClass;
|
|
};
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
Random
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: dart:math
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_emptyBudget() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
// Empty budget, so no not yet imported libraries.
|
|
server.completionState.budgetDuration = const Duration();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
void f() {
|
|
Rand^
|
|
}
|
|
''');
|
|
|
|
printerConfiguration.filter = (_) => true;
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_lowerRelevance_extension_getter() async {
|
|
await _configureWithWorkspaceRoot();
|
|
printerConfiguration.sorting =
|
|
printer.Sorting.relevanceThenCompletionThenKind;
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
int get foo01 => 0;
|
|
}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
extension E2 on int {
|
|
int get foo02 => 0;
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
// `foo01` relevance is decreased because it is not yet imported.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo02
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo01
|
|
kind: getter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_lowerRelevance_extension_method() async {
|
|
await _configureWithWorkspaceRoot();
|
|
printerConfiguration.sorting =
|
|
printer.Sorting.relevanceThenCompletionThenKind;
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
void foo01() {}
|
|
}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
extension E2 on int {
|
|
void foo02() {}
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
// `foo01` relevance is decreased because it is not yet imported.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo02
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo01
|
|
kind: methodInvocation
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_lowerRelevance_extension_setter() async {
|
|
await _configureWithWorkspaceRoot();
|
|
printerConfiguration.sorting =
|
|
printer.Sorting.relevanceThenCompletionThenKind;
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
set foo01(int _) {}
|
|
}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
extension E2 on int {
|
|
set foo02(int _) {}
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
// `foo01` relevance is decreased because it is not yet imported.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo02
|
|
kind: setter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo01
|
|
kind: setter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_lowerRelevance_topLevel_class() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
printerConfiguration.sorting =
|
|
printer.Sorting.relevanceThenCompletionThenKind;
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
A0^
|
|
}
|
|
''');
|
|
|
|
// `A01` relevance is decreased because it is not yet imported.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A02
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: package:test/b.dart
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_lowerRelevance_topLevel_getter() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
int get foo01 => 0;
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
int get foo02 => 0;
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
printerConfiguration.sorting =
|
|
printer.Sorting.relevanceThenCompletionThenKind;
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
foo0^
|
|
}
|
|
''');
|
|
|
|
// `foo01` relevance is decreased because it is not yet imported.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo02
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: package:test/b.dart
|
|
foo01
|
|
kind: getter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_lowerRelevance_topLevel_setter() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
set foo01(int _) {}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
set foo02(int _) {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
printerConfiguration.sorting =
|
|
printer.Sorting.relevanceThenCompletionThenKind;
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
foo0^
|
|
}
|
|
''');
|
|
|
|
// `foo01` relevance is decreased because it is not yet imported.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo02
|
|
kind: setter
|
|
isNotImported: null
|
|
libraryUri: package:test/b.dart
|
|
foo01
|
|
kind: setter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_lowerRelevance_topLevel_variable() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
var foo01 = 0;
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
var foo02 = 0;
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
printerConfiguration.sorting =
|
|
printer.Sorting.relevanceThenCompletionThenKind;
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
foo0^
|
|
}
|
|
''');
|
|
|
|
// `foo01` relevance is decreased because it is not yet imported.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo02
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: package:test/b.dart
|
|
foo01
|
|
kind: topLevelVariable
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_dependencies_inBin() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
dependencies:
|
|
aaa: any
|
|
dev_dependencies:
|
|
bbb: any
|
|
''');
|
|
|
|
var aaaRoot = getFolder('$workspaceRootPath/packages/aaa');
|
|
newFile('${aaaRoot.path}/lib/f.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
var bbbRoot = getFolder('$workspaceRootPath/packages/bbb');
|
|
newFile('${bbbRoot.path}/lib/f.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
writeTestPackageConfig(
|
|
config: PackageConfigFileBuilder()
|
|
..add(name: 'aaa', rootFolder: aaaRoot)
|
|
..add(name: 'bbb', rootFolder: bbbRoot),
|
|
);
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var binPath = convertPath('$testPackageRootPath/bin/main.dart');
|
|
var response = await _getCodeSuggestions(
|
|
path: binPath,
|
|
content: '''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''',
|
|
);
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:aaa/f.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_dependencies_inLib() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
dependencies:
|
|
aaa: any
|
|
dev_dependencies:
|
|
bbb: any
|
|
''');
|
|
|
|
var aaaRoot = getFolder('$workspaceRootPath/packages/aaa');
|
|
newFile('${aaaRoot.path}/lib/f.dart', '''
|
|
class A01 {}
|
|
''');
|
|
newFile('${aaaRoot.path}/lib/src/f.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
var bbbRoot = getFolder('$workspaceRootPath/packages/bbb');
|
|
newFile('${bbbRoot.path}/lib/f.dart', '''
|
|
class A03 {}
|
|
''');
|
|
newFile('${bbbRoot.path}/lib/src/f.dart', '''
|
|
class A04 {}
|
|
''');
|
|
|
|
writeTestPackageConfig(
|
|
config: PackageConfigFileBuilder()
|
|
..add(name: 'aaa', rootFolder: aaaRoot)
|
|
..add(name: 'bbb', rootFolder: bbbRoot),
|
|
);
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:aaa/f.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_dependencies_inTest() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
dependencies:
|
|
aaa: any
|
|
dev_dependencies:
|
|
bbb: any
|
|
''');
|
|
|
|
var aaaRoot = getFolder('$workspaceRootPath/packages/aaa');
|
|
newFile('${aaaRoot.path}/lib/f.dart', '''
|
|
class A01 {}
|
|
''');
|
|
newFile('${aaaRoot.path}/lib/src/f.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
var bbbRoot = getFolder('$workspaceRootPath/packages/bbb');
|
|
newFile('${bbbRoot.path}/lib/f.dart', '''
|
|
class A03 {}
|
|
''');
|
|
newFile('${bbbRoot.path}/lib/src/f.dart', '''
|
|
class A04 {}
|
|
''');
|
|
|
|
writeTestPackageConfig(
|
|
config: PackageConfigFileBuilder()
|
|
..add(name: 'aaa', rootFolder: aaaRoot)
|
|
..add(name: 'bbb', rootFolder: bbbRoot),
|
|
);
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var test_path = convertPath('$testPackageTestPath/test.dart');
|
|
var response = await _getCodeSuggestions(
|
|
path: test_path,
|
|
content: '''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''',
|
|
);
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:aaa/f.dart
|
|
A03
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:bbb/f.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_dependencies_inWeb() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
dependencies:
|
|
aaa: any
|
|
dev_dependencies:
|
|
bbb: any
|
|
''');
|
|
|
|
var aaaRoot = getFolder('$workspaceRootPath/packages/aaa');
|
|
newFile('${aaaRoot.path}/lib/f.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
var bbbRoot = getFolder('$workspaceRootPath/packages/bbb');
|
|
newFile('${bbbRoot.path}/lib/f.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
writeTestPackageConfig(
|
|
config: PackageConfigFileBuilder()
|
|
..add(name: 'aaa', rootFolder: aaaRoot)
|
|
..add(name: 'bbb', rootFolder: bbbRoot),
|
|
);
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var webPath = convertPath('$testPackageRootPath/web/main.dart');
|
|
var response = await _getCodeSuggestions(
|
|
path: webPath,
|
|
content: '''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''',
|
|
);
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:aaa/f.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_this() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
A02
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/b.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_this_hasImport() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class A01 {}
|
|
class A02 {}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
class A03 {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'a.dart';
|
|
|
|
void f() {
|
|
A0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: package:test/a.dart
|
|
A02
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: package:test/a.dart
|
|
A03
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/b.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_this_hasImport_hasShow() async {
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class A01 {}
|
|
class A02 {}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
class A03 {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
printerConfiguration.sorting =
|
|
printer.Sorting.relevanceThenCompletionThenKind;
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'a.dart' show A02;
|
|
|
|
void f() {
|
|
A0^
|
|
}
|
|
''');
|
|
|
|
// Note:
|
|
// 1. A02 is the first, because it is already imported.
|
|
// 2. A01 is still suggested, but with lower relevance.
|
|
// 3. A03 has the same relevance (not tested), but sorted by name.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A02
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: package:test/a.dart
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
A03
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/b.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_this_inLib_excludesTest() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
newFile('$testPackageTestPath/b.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_this_inLib_includesThisSrc() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/f.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/src/f.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/f.dart
|
|
A02
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/src/f.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_this_inTest_includesTest() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
var b = newFile('$testPackageTestPath/b.dart', '''
|
|
class A02 {}
|
|
''');
|
|
var b_uriStr = toUriStr(b.path);
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var test_path = convertPath('$testPackageTestPath/test.dart');
|
|
var response = await _getCodeSuggestions(
|
|
path: test_path,
|
|
content: '''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''',
|
|
);
|
|
|
|
assertResponseText(response, '''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
A02
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: $b_uriStr
|
|
''');
|
|
}
|
|
|
|
Future<void> test_notImported_pub_this_inTest_includesThisSrc() async {
|
|
writeTestPackagePubspecYamlFile(r'''
|
|
name: test
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/f.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/src/f.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var test_path = convertPath('$testPackageTestPath/test.dart');
|
|
var response = await _getCodeSuggestions(
|
|
path: test_path,
|
|
content: '''
|
|
void f() {
|
|
A0^
|
|
}
|
|
''',
|
|
);
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/f.dart
|
|
A02
|
|
kind: class
|
|
isNotImported: true
|
|
libraryUri: package:test/src/f.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_numResults_class_methods() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
class A {
|
|
void foo01() {}
|
|
void foo02() {}
|
|
void foo03() {}
|
|
}
|
|
|
|
void f(A a) {
|
|
a.foo0^
|
|
}
|
|
''', maxResults: 2);
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_numResults_includesLocalVariable() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
// Create many imported declarations to exceed the maxResults cap.
|
|
newFile(
|
|
'$testPackageLibPath/a.dart',
|
|
[
|
|
for (var i = 1; i <= 50; i++) 'class C$i {}',
|
|
for (var i = 1; i <= 50; i++) 'void f$i() {}',
|
|
for (var i = 1; i <= 50; i++) 'var v$i = $i;',
|
|
].join('\n'),
|
|
);
|
|
|
|
// No prefix, no filtering.
|
|
// Local variables 'foo0*' must be included.
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'a.dart';
|
|
void f() {
|
|
var foo01 = 0;
|
|
var foo02 = 0;
|
|
^
|
|
}
|
|
''', maxResults: 10);
|
|
|
|
// Only include local variables to keep the result stable.
|
|
printerConfiguration.filter = (suggestion) {
|
|
return suggestion.completion.startsWith('foo0');
|
|
};
|
|
|
|
// Note the variables order: the closer, the higher.
|
|
assertResponseText(response, r'''
|
|
suggestions
|
|
foo02
|
|
kind: localVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo01
|
|
kind: localVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_numResults_topLevelVariables() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
var foo01 = 0;
|
|
var foo02 = 0;
|
|
var foo03 = 0;
|
|
|
|
void f() {
|
|
foo0^
|
|
}
|
|
''', maxResults: 2);
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_numResults_topLevelVariables_imported_withPrefix() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
var foo01 = 0;
|
|
var foo02 = 0;
|
|
var foo03 = 0;
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'a.dart' as prefix;
|
|
|
|
void f() {
|
|
prefix.^
|
|
}
|
|
''', maxResults: 2);
|
|
|
|
assertResponseText(response, r'''
|
|
suggestions
|
|
foo01
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: package:test/a.dart
|
|
foo02
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_class_constructors() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
class A {
|
|
A.foo01();
|
|
A.foo02();
|
|
}
|
|
|
|
void f() {
|
|
A.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: constructorInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: constructorInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_class_getters() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
class A {
|
|
int get foo01 => 0;
|
|
int get foo02 => 0;
|
|
}
|
|
|
|
void f(A a) {
|
|
a.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_class_methods_instance() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
class A {
|
|
void foo01() {}
|
|
void foo02() {}
|
|
}
|
|
|
|
void f(A a) {
|
|
a.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_class_methods_static() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
class A {
|
|
static void foo01() {}
|
|
static void foo02() {}
|
|
}
|
|
|
|
void f() {
|
|
A.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_expression_extensionGetters() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
extension E1 on int {
|
|
int get foo01 => 0;
|
|
int get foo02 => 0;
|
|
int get bar => 0;
|
|
}
|
|
|
|
extension E2 on double {
|
|
int get foo03 => 0;
|
|
}
|
|
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_expression_extensionGetters_notImported() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
int get foo01 => 0;
|
|
int get bar => 0;
|
|
}
|
|
|
|
extension E2 on int {
|
|
int get foo02 => 0;
|
|
}
|
|
|
|
extension E3 on double {
|
|
int get foo03 => 0;
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: getter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
foo02
|
|
kind: getter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_prefixed_expression_extensionGetters_notImported_private() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
int get foo01 => 0;
|
|
}
|
|
|
|
extension _E2 on int {
|
|
int get foo02 => 0;
|
|
}
|
|
|
|
extension on int {
|
|
int get foo03 => 0;
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: getter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_expression_extensionMethods() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
extension E1 on int {
|
|
void foo01() {}
|
|
void foo02() {}
|
|
void bar() {}
|
|
}
|
|
|
|
extension E2 on double {
|
|
void foo03() {}
|
|
}
|
|
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_expression_extensionMethods_notImported() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
void foo01() {}
|
|
void bar() {}
|
|
}
|
|
|
|
extension E2 on int {
|
|
void foo02() {}
|
|
}
|
|
|
|
extension E3 on double {
|
|
void foo03() {}
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: methodInvocation
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
foo02
|
|
kind: methodInvocation
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_expression_extensionSetters() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
extension E1 on int {
|
|
set foo01(int _) {}
|
|
set foo02(int _) {}
|
|
set bar(int _) {}
|
|
}
|
|
|
|
extension E2 on double {
|
|
set foo03(int _) {}
|
|
}
|
|
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: setter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: setter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_expression_extensionSetters_notImported() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
set foo01(int _) {}
|
|
set bar(int _) {}
|
|
}
|
|
|
|
extension E2 on int {
|
|
set foo02(int _) {}
|
|
}
|
|
|
|
extension E3 on double {
|
|
set foo03(int _) {}
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: setter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
foo02
|
|
kind: setter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void>
|
|
test_prefixed_expression_extensionSetters_notImported_private() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
set foo01(int _) {}
|
|
}
|
|
|
|
extension _E2 on int {
|
|
set foo02(int _) {}
|
|
}
|
|
|
|
extension on int {
|
|
set foo03(int _) {}
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: setter
|
|
isNotImported: true
|
|
libraryUri: package:test/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_extensionGetters_imported() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
extension E1 on int {
|
|
int get foo01 => 0;
|
|
int get foo02 => 0;
|
|
int get bar => 0;
|
|
}
|
|
|
|
extension E2 on double {
|
|
int get foo03 => 0;
|
|
}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
import 'a.dart';
|
|
|
|
void f() {
|
|
0.foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_extensionOverride_extensionGetters() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
extension E1 on int {
|
|
int get foo01 => 0;
|
|
}
|
|
|
|
extension E2 on int {
|
|
int get foo02 => 0;
|
|
}
|
|
|
|
void f() {
|
|
E1(0).foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: getter
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_extensionOverride_extensionMethods() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
extension E1 on int {
|
|
void foo01() {}
|
|
}
|
|
|
|
extension E2 on int {
|
|
void foo01() {}
|
|
}
|
|
|
|
void f() {
|
|
E1(0).foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: methodInvocation
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_prefixed_importPrefix_class() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'dart:math' as math;
|
|
|
|
void f() {
|
|
math.Rand^
|
|
}
|
|
''');
|
|
|
|
printerConfiguration.filter = (_) => true;
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
Random
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: dart:math
|
|
Random
|
|
kind: constructorInvocation
|
|
isNotImported: null
|
|
libraryUri: dart:math
|
|
''');
|
|
}
|
|
|
|
Future<void> test_unprefixed_filters() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
var foo01 = 0;
|
|
var foo02 = 0;
|
|
var bar01 = 0;
|
|
var bar02 = 0;
|
|
|
|
void f() {
|
|
foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
foo02
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
''');
|
|
}
|
|
|
|
Future<void> test_unprefixed_imported_class() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
class A01 {}
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
class A02 {}
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'a.dart';
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
A0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 2
|
|
suggestions
|
|
A01
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: package:test/a.dart
|
|
A02
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: package:test/b.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_unprefixed_imported_topLevelVariable() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
newFile('$testPackageLibPath/a.dart', '''
|
|
var foo01 = 0;
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/b.dart', '''
|
|
var foo02 = 0;
|
|
''');
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'a.dart';
|
|
import 'b.dart';
|
|
|
|
void f() {
|
|
foo0^
|
|
}
|
|
''');
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo01
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: package:test/a.dart
|
|
foo02
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: package:test/b.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_unprefixed_imported_withPrefix_class() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions('''
|
|
import 'dart:math' as math;
|
|
|
|
void f() {
|
|
Rand^
|
|
}
|
|
''');
|
|
|
|
printerConfiguration.filter = (suggestion) {
|
|
return suggestion.isClass;
|
|
};
|
|
|
|
// No suggestion without the `math` prefix.
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
math.Random
|
|
kind: class
|
|
isNotImported: null
|
|
libraryUri: dart:math
|
|
''');
|
|
}
|
|
|
|
Future<void> test_unprefixed_sorts_byScore() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
var fooAB = 0;
|
|
var fooBB = 0;
|
|
|
|
void f() {
|
|
fooB^
|
|
}
|
|
''');
|
|
|
|
printerConfiguration
|
|
..filter = (suggestion) {
|
|
return suggestion.completion.startsWith('foo');
|
|
}
|
|
..sorting = printer.Sorting.asIs
|
|
..withRelevance = true;
|
|
|
|
// `fooBB` has better score than `fooAB` - prefix match
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
fooBB
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
relevance: 503
|
|
fooAB
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
relevance: 503
|
|
''');
|
|
}
|
|
|
|
Future<void> test_unprefixed_sorts_byType() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var response = await _getTestCodeSuggestions(r'''
|
|
var foo01 = 0.0;
|
|
var foo02 = 0;
|
|
|
|
void f() {
|
|
int v = foo0^
|
|
}
|
|
''');
|
|
|
|
printerConfiguration
|
|
..sorting = printer.Sorting.asIs
|
|
..withRelevance = true;
|
|
|
|
assertResponseText(response, r'''
|
|
replacement
|
|
left: 4
|
|
suggestions
|
|
foo02
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
relevance: 558
|
|
foo01
|
|
kind: topLevelVariable
|
|
isNotImported: null
|
|
libraryUri: null
|
|
relevance: 510
|
|
''');
|
|
}
|
|
|
|
Future<void> test_yaml_analysisOptions_formatter() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var path = convertPath('$testPackageRootPath/analysis_options.yaml');
|
|
var response = await _getCodeSuggestions(
|
|
path: path,
|
|
content: '''
|
|
formatter:
|
|
^
|
|
''',
|
|
);
|
|
|
|
printerConfiguration
|
|
..filter = ((_) => true)
|
|
..withIsNotImported = false
|
|
..withLibraryUri = false;
|
|
|
|
assertResponseText(response, r'''
|
|
suggestions
|
|
|page_width: |
|
|
kind: identifier
|
|
|trailing_commas: |
|
|
kind: identifier
|
|
''');
|
|
}
|
|
|
|
Future<void> test_yaml_analysisOptions_root() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var path = convertPath('$testPackageRootPath/analysis_options.yaml');
|
|
var response = await _getCodeSuggestions(path: path, content: '^');
|
|
|
|
printerConfiguration
|
|
..filter = ((_) => true)
|
|
..withIsNotImported = false
|
|
..withLibraryUri = false;
|
|
|
|
assertResponseText(response, r'''
|
|
suggestions
|
|
|analyzer: |
|
|
kind: identifier
|
|
|code-style: |
|
|
kind: identifier
|
|
|formatter: |
|
|
kind: identifier
|
|
|include: |
|
|
kind: identifier
|
|
|linter: |
|
|
kind: identifier
|
|
|plugins: |
|
|
kind: identifier
|
|
''');
|
|
}
|
|
|
|
Future<void> test_yaml_fixData_root() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var path = convertPath('$testPackageRootPath/fix_data.yaml');
|
|
var response = await _getCodeSuggestions(path: path, content: '^');
|
|
|
|
printerConfiguration
|
|
..filter = ((_) => true)
|
|
..withIsNotImported = false
|
|
..withLibraryUri = false;
|
|
|
|
assertResponseText(response, r'''
|
|
suggestions
|
|
transforms:
|
|
kind: identifier
|
|
|version: |
|
|
kind: identifier
|
|
''');
|
|
}
|
|
|
|
Future<void> test_yaml_pubspec_root() async {
|
|
await _configureWithWorkspaceRoot();
|
|
|
|
var path = convertPath('$testPackageRootPath/pubspec.yaml');
|
|
var response = await _getCodeSuggestions(path: path, content: '^');
|
|
|
|
printerConfiguration
|
|
..filter = ((_) => true)
|
|
..withIsNotImported = false
|
|
..withLibraryUri = false;
|
|
|
|
assertResponseText(response, r'''
|
|
suggestions
|
|
|dependencies: |
|
|
kind: identifier
|
|
|dependency_overrides: |
|
|
kind: identifier
|
|
|description: |
|
|
kind: identifier
|
|
|dev_dependencies: |
|
|
kind: identifier
|
|
|documentation: |
|
|
kind: identifier
|
|
|environment: |
|
|
kind: identifier
|
|
|executables: |
|
|
kind: identifier
|
|
|flutter: |
|
|
kind: identifier
|
|
|homepage: |
|
|
kind: identifier
|
|
|issue_tracker: |
|
|
kind: identifier
|
|
|name: |
|
|
kind: identifier
|
|
|publish_to: |
|
|
kind: identifier
|
|
|repository: |
|
|
kind: identifier
|
|
|resolution: |
|
|
kind: identifier
|
|
screenshots:
|
|
kind: identifier
|
|
|topics: |
|
|
kind: identifier
|
|
|version: |
|
|
kind: identifier
|
|
workspace:
|
|
kind: identifier
|
|
''');
|
|
}
|
|
|
|
Future<void> _configureWithWorkspaceRoot() async {
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
}
|
|
|
|
Future<CompletionResponseForTesting> _getCodeSuggestions({
|
|
required String path,
|
|
required String content,
|
|
int maxResults = 1 << 10,
|
|
}) async {
|
|
var code = TestCode.parseNormalized(content);
|
|
var completionOffset = code.position.offset;
|
|
|
|
newFile(path, code.code);
|
|
|
|
return await _getSuggestions(
|
|
path: path,
|
|
completionOffset: completionOffset,
|
|
maxResults: maxResults,
|
|
);
|
|
}
|
|
|
|
Future<CompletionResponseForTesting> _getSuggestions({
|
|
required String path,
|
|
required int completionOffset,
|
|
required int maxResults,
|
|
}) async {
|
|
var request = CompletionGetSuggestions2Params(
|
|
path,
|
|
completionOffset,
|
|
maxResults,
|
|
).toRequest('0', clientUriConverter: server.uriConverter);
|
|
|
|
var response = await handleSuccessfulRequest(request);
|
|
var result = CompletionGetSuggestions2Result.fromResponse(
|
|
response,
|
|
clientUriConverter: server.uriConverter,
|
|
);
|
|
|
|
// Extract the internal request object.
|
|
var dartRequest = server.completionState.currentRequest;
|
|
|
|
return CompletionResponseForTesting(
|
|
requestOffset: completionOffset,
|
|
requestLocationName: dartRequest?.collectorLocationName,
|
|
opTypeLocationName: dartRequest?.opType.completionLocation,
|
|
replacementOffset: result.replacementOffset,
|
|
replacementLength: result.replacementLength,
|
|
isIncomplete: result.isIncomplete,
|
|
suggestions: result.suggestions,
|
|
);
|
|
}
|
|
|
|
Future<CompletionResponseForTesting> _getTestCodeSuggestions(
|
|
String content, {
|
|
int maxResults = 1 << 10,
|
|
}) {
|
|
return _getCodeSuggestions(
|
|
path: convertPath(testFilePath),
|
|
content: content,
|
|
maxResults: maxResults,
|
|
);
|
|
}
|
|
|
|
RequestWithFutureResponse _sendTestCompletionRequest(String id, int offset) {
|
|
var request = CompletionGetSuggestions2Params(
|
|
testFile.path,
|
|
0,
|
|
1 << 10,
|
|
).toRequest(id, clientUriConverter: server.uriConverter);
|
|
var futureResponse = handleRequest(request);
|
|
return RequestWithFutureResponse(offset, request, futureResponse);
|
|
}
|
|
}
|
|
|
|
class RequestWithFutureResponse {
|
|
final int offset;
|
|
final Request request;
|
|
final Future<Response> futureResponse;
|
|
|
|
new(this.offset, this.request, this.futureResponse);
|
|
|
|
Future<CompletionResponseForTesting> toResponse() async {
|
|
var response = await futureResponse;
|
|
expect(response, isResponseSuccess(request.id));
|
|
var result = CompletionGetSuggestions2Result.fromResponse(
|
|
response,
|
|
clientUriConverter: null,
|
|
);
|
|
return CompletionResponseForTesting(
|
|
requestOffset: offset,
|
|
requestLocationName: null,
|
|
opTypeLocationName: null,
|
|
replacementOffset: result.replacementOffset,
|
|
replacementLength: result.replacementLength,
|
|
isIncomplete: result.isIncomplete,
|
|
suggestions: result.suggestions,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _SuggestionDetailsPrinter {
|
|
final StringBuffer buffer;
|
|
final CompletionGetSuggestionDetails2Result result;
|
|
final ResourceProvider resourceProvider;
|
|
final Map<File, String> fileDisplayMap;
|
|
|
|
String _indent = '';
|
|
|
|
new({
|
|
required this.buffer,
|
|
required this.result,
|
|
required this.resourceProvider,
|
|
required this.fileDisplayMap,
|
|
});
|
|
|
|
void writeResult() {
|
|
_writelnWithIndent('completion: ${result.completion}');
|
|
_withIndent(() {
|
|
_writeChange(result.change);
|
|
});
|
|
}
|
|
|
|
void _withIndent(void Function() f) {
|
|
var indent = _indent;
|
|
_indent = '$_indent ';
|
|
f();
|
|
_indent = indent;
|
|
}
|
|
|
|
void _writeChange(SourceChange change) {
|
|
_writelnWithIndent('change');
|
|
_withIndent(() {
|
|
for (var fileEdit in change.edits) {
|
|
_writeSourceFileEdit(fileEdit);
|
|
}
|
|
});
|
|
}
|
|
|
|
void _writelnWithIndent(String line) {
|
|
buffer.write(_indent);
|
|
buffer.writeln(line);
|
|
}
|
|
|
|
void _writeSourceEdit(SourceEdit edit) {
|
|
_writelnWithIndent('offset: ${edit.offset}');
|
|
_writelnWithIndent('length: ${edit.length}');
|
|
|
|
var replacementStr = edit.replacement
|
|
.replaceAll('\n', r'\n')
|
|
.replaceAll('\r', r'\r');
|
|
_writelnWithIndent('replacement: $replacementStr');
|
|
}
|
|
|
|
void _writeSourceFileEdit(SourceFileEdit fileEdit) {
|
|
var file = resourceProvider.getFile(fileEdit.file);
|
|
var fileStr = fileDisplayMap[file] ?? fail('No display name: $file');
|
|
_writelnWithIndent(fileStr);
|
|
|
|
_withIndent(() {
|
|
for (var edit in fileEdit.edits) {
|
|
_writeSourceEdit(edit);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
extension on CompletionSuggestion {
|
|
bool get isClass {
|
|
return kind == CompletionSuggestionKind.IDENTIFIER &&
|
|
element?.kind == ElementKind.CLASS;
|
|
}
|
|
}
|