mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:59:47 +00:00
completion driver suggestions
The absolute minimum to see suggestions wired through. Lots of work to flesh out the client-implemented algorithms. For reference: * https://github.com/Dart-Code/Dart-Code/blob/master/src/extension/providers/dart_completion_item_provider.ts * https://github.com/JetBrains/intellij-plugins/blob/master/Dart/src/com/jetbrains/lang/dart/ide/completion/DartServerCompletionContributor.java Change-Id: Idc78fca787b095e5b6236481b89ddb21c0ed9c94 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134877 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
160614929a
commit
3d97d22fe5
|
@ -2,30 +2,28 @@
|
|||
// 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:analyzer/src/test_utilities/resource_provider_mixin.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
|
||||
|
||||
import '../services/completion/dart/completion_contributor_util.dart';
|
||||
import 'impl/completion_driver.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(BasicCompletionTest);
|
||||
defineReflectiveTests(CompletionWithSuggestionsTest);
|
||||
});
|
||||
}
|
||||
|
||||
abstract class AbstractCompletionDriverTest with ResourceProviderMixin {
|
||||
CompletionDriver driver;
|
||||
|
||||
Map<String, String> packageRoots;
|
||||
|
||||
List<CompletionSuggestion> suggestions;
|
||||
|
||||
bool get supportsAvailableDeclarations;
|
||||
bool get supportsAvailableSuggestions;
|
||||
|
||||
String addTestFile(String content, {int offset}) =>
|
||||
driver.addTestFile(content, offset: offset);
|
||||
|
@ -38,7 +36,7 @@ abstract class AbstractCompletionDriverTest with ResourceProviderMixin {
|
|||
@mustCallSuper
|
||||
void setUp() {
|
||||
driver = CompletionDriver(
|
||||
supportsAvailableDeclarations: supportsAvailableDeclarations,
|
||||
supportsAvailableSuggestions: supportsAvailableSuggestions,
|
||||
resourceProvider: resourceProvider);
|
||||
driver.createProject(packageRoots: packageRoots);
|
||||
}
|
||||
|
@ -74,9 +72,9 @@ abstract class AbstractCompletionDriverTest with ResourceProviderMixin {
|
|||
@reflectiveTest
|
||||
class BasicCompletionTest extends AbstractCompletionDriverTest {
|
||||
@override
|
||||
bool get supportsAvailableDeclarations => false;
|
||||
bool get supportsAvailableSuggestions => false;
|
||||
|
||||
/// Duplicates (and potentially replaces DeprecatedMemberRelevanceTest.
|
||||
/// Duplicates (and potentially replaces DeprecatedMemberRelevanceTest).
|
||||
Future<void> test_deprecated_member_relevance() async {
|
||||
addTestFile('''
|
||||
class A {
|
||||
|
@ -105,3 +103,26 @@ void main() {
|
|||
.relevance));
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class CompletionWithSuggestionsTest extends AbstractCompletionDriverTest {
|
||||
@override
|
||||
bool get supportsAvailableSuggestions => true;
|
||||
|
||||
Future<void> test_sanity() async {
|
||||
addTestFile('''
|
||||
void main() {
|
||||
^
|
||||
}
|
||||
''');
|
||||
|
||||
await getSuggestions();
|
||||
|
||||
// todo (pq): replace with a "real test"; this just proves we're getting end to end.
|
||||
expect(
|
||||
// from dart:math
|
||||
suggestionWith(
|
||||
completion: 'tan', kind: CompletionSuggestionKind.INVOCATION),
|
||||
isNotNull);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:analysis_server/protocol/protocol_generated.dart'
|
|||
hide AnalysisOptions;
|
||||
import 'package:analysis_server/src/analysis_server.dart';
|
||||
import 'package:analysis_server/src/domain_analysis.dart';
|
||||
import 'package:analysis_server/src/domain_completion.dart';
|
||||
import 'package:analysis_server/src/utilities/mocks.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/instrumentation/instrumentation.dart';
|
||||
|
@ -52,6 +53,9 @@ abstract class AbstractClient {
|
|||
AnalysisDomainHandler get analysisHandler => server.handlers
|
||||
.singleWhere((handler) => handler is AnalysisDomainHandler);
|
||||
|
||||
CompletionDomainHandler get completionHandler =>
|
||||
server.handlers.whereType<CompletionDomainHandler>().single;
|
||||
|
||||
AnalysisOptions get analysisOptions => testDiver.analysisOptions;
|
||||
|
||||
ResourceProvider get resourceProvider;
|
||||
|
@ -105,6 +109,7 @@ abstract class AbstractClient {
|
|||
}
|
||||
|
||||
/// Create a project at [projectPath].
|
||||
@mustCallSuper
|
||||
void createProject({Map<String, String> packageRoots}) {
|
||||
newFolder(projectPath);
|
||||
var request = AnalysisSetAnalysisRootsParams([projectPath], [],
|
||||
|
|
|
@ -18,8 +18,23 @@ import '../../constants.dart';
|
|||
import 'abstract_client.dart';
|
||||
import 'expect_mixin.dart';
|
||||
|
||||
CompletionSuggestion _createCompletionSuggestionFromAvailableSuggestion(
|
||||
AvailableSuggestion suggestion) {
|
||||
// todo (pq): IMPLEMENT
|
||||
// com.jetbrains.lang.dart.ide.completion.DartServerCompletionContributor#createCompletionSuggestionFromAvailableSuggestion
|
||||
return CompletionSuggestion(
|
||||
// todo (pq): in IDEA, this is "UNKNOWN" but here we need a value; figure out what's up.
|
||||
CompletionSuggestionKind.INVOCATION,
|
||||
0,
|
||||
suggestion.label,
|
||||
0,
|
||||
0,
|
||||
suggestion.element.isDeprecated,
|
||||
false);
|
||||
}
|
||||
|
||||
class CompletionDriver extends AbstractClient with ExpectMixin {
|
||||
final bool supportsAvailableDeclarations;
|
||||
final bool supportsAvailableSuggestions;
|
||||
final MemoryResourceProvider _resourceProvider;
|
||||
|
||||
Map<String, Completer<void>> receivedSuggestionsCompleters = {};
|
||||
|
@ -27,13 +42,18 @@ class CompletionDriver extends AbstractClient with ExpectMixin {
|
|||
bool suggestionsDone = false;
|
||||
Map<String, List<CompletionSuggestion>> allSuggestions = {};
|
||||
|
||||
final Map<int, AvailableSuggestionSet> idToSetMap = {};
|
||||
final Map<String, AvailableSuggestionSet> uriToSetMap = {};
|
||||
final Map<String, CompletionResultsParams> idToSuggestions = {};
|
||||
final Map<String, ExistingImports> fileToExistingImports = {};
|
||||
|
||||
String completionId;
|
||||
int completionOffset;
|
||||
int replacementOffset;
|
||||
int replacementLength;
|
||||
|
||||
CompletionDriver({
|
||||
@required this.supportsAvailableDeclarations,
|
||||
@required this.supportsAvailableSuggestions,
|
||||
@required MemoryResourceProvider resourceProvider,
|
||||
}) : _resourceProvider = resourceProvider,
|
||||
super(
|
||||
|
@ -60,6 +80,16 @@ class CompletionDriver extends AbstractClient with ExpectMixin {
|
|||
content.substring(completionOffset + 1));
|
||||
}
|
||||
|
||||
@override
|
||||
void createProject({Map<String, String> packageRoots}) {
|
||||
super.createProject(packageRoots: packageRoots);
|
||||
if (supportsAvailableSuggestions) {
|
||||
var request = CompletionSetSubscriptionsParams(
|
||||
[CompletionService.AVAILABLE_SUGGESTION_SETS]).toRequest('0');
|
||||
handleSuccessfulRequest(request, handler: completionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<CompletionSuggestion>> getSuggestions() async {
|
||||
await waitForTasksFinished();
|
||||
|
||||
|
@ -71,6 +101,20 @@ class CompletionDriver extends AbstractClient with ExpectMixin {
|
|||
assertValidId(completionId);
|
||||
await _getResultsCompleter(completionId).future;
|
||||
expect(suggestionsDone, isTrue);
|
||||
|
||||
if (supportsAvailableSuggestions) {
|
||||
// todo(pq): limit set(s)
|
||||
for (var suggestionSet in idToSetMap.values) {
|
||||
for (var suggestion in suggestionSet.items) {
|
||||
var completionSuggestion =
|
||||
_createCompletionSuggestionFromAvailableSuggestion(suggestion
|
||||
//, includedSet.getRelevance(), includedRelevanceTags
|
||||
);
|
||||
suggestions.add(completionSuggestion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
|
@ -82,6 +126,7 @@ class CompletionDriver extends AbstractClient with ExpectMixin {
|
|||
Folder newFolder(String path) => resourceProvider.newFolder(path);
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
Future<void> processNotification(Notification notification) async {
|
||||
if (notification.event == COMPLETION_RESULTS) {
|
||||
var params = CompletionResultsParams.fromNotification(notification);
|
||||
|
@ -95,6 +140,24 @@ class CompletionDriver extends AbstractClient with ExpectMixin {
|
|||
expect(allSuggestions.containsKey(id), isFalse);
|
||||
allSuggestions[id] = params.results;
|
||||
_getResultsCompleter(id).complete(null);
|
||||
} else if (notification.event ==
|
||||
COMPLETION_NOTIFICATION_AVAILABLE_SUGGESTIONS) {
|
||||
var params = CompletionAvailableSuggestionsParams.fromNotification(
|
||||
notification,
|
||||
);
|
||||
for (var set in params.changedLibraries) {
|
||||
idToSetMap[set.id] = set;
|
||||
uriToSetMap[set.uri] = set;
|
||||
}
|
||||
for (var id in params.removedLibraries) {
|
||||
var set = idToSetMap.remove(id);
|
||||
uriToSetMap.remove(set?.uri);
|
||||
}
|
||||
} else if (notification.event == COMPLETION_NOTIFICATION_EXISTING_IMPORTS) {
|
||||
var params = CompletionExistingImportsParams.fromNotification(
|
||||
notification,
|
||||
);
|
||||
fileToExistingImports[params.file] = params.imports;
|
||||
} else if (notification.event == SERVER_NOTIFICATION_ERROR) {
|
||||
throw Exception('server error: ${notification.toJson()}');
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue