| // Copyright (c) 2020, 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/protocol/protocol.dart'; | |
| import 'package:analysis_server/protocol/protocol_constants.dart'; | |
| import 'package:analysis_server/protocol/protocol_generated.dart' | |
| hide AnalysisOptions; | |
| import 'package:analyzer/src/test_utilities/test_code_format.dart'; | |
| import 'package:analyzer_plugin/protocol/protocol_common.dart'; | |
| import 'package:meta/meta.dart'; | |
| import 'package:test/test.dart'; | |
| import '../../analysis_server_base.dart'; | |
| import 'expect_mixin.dart'; | |
| class CompletionDriver with ExpectMixin { | |
| final PubPackageAnalysisServerTest server; | |
| Map<String, Completer<void>> receivedSuggestionsCompleters = {}; | |
| List<CompletionSuggestion> suggestions = []; | |
| Map<String, List<CompletionSuggestion>> allSuggestions = {}; | |
| final Map<String, ExistingImports> fileToExistingImports = {}; | |
| final Map<String, List<AnalysisError>> filesErrors = {}; | |
| late String completionId; | |
| late int completionOffset; | |
| late int replacementOffset; | |
| late int replacementLength; | |
| new({required this.server}) { | |
| server.serverChannel.notifications.listen(processNotification); | |
| } | |
| void addTestFile(String content) { | |
| var code = TestCode.parse(content); | |
| completionOffset = code.position.offset; | |
| server.newFile(server.testFilePath, code.code); | |
| } | |
| void assertValidId(String id) { | |
| expect(id, isNotNull); | |
| expect(id.isNotEmpty, isTrue); | |
| } | |
| Future<void> createProject() async { | |
| await server.setRoots(included: [server.workspaceRootPath], excluded: []); | |
| } | |
| Future<List<CompletionSuggestion>> getSuggestions() async { | |
| var request = CompletionGetSuggestions2Params( | |
| server.convertPath(server.testFilePath), | |
| completionOffset, | |
| 1 << 16, | |
| timeout: 60 * 1000, | |
| ).toRequest('0', clientUriConverter: null); | |
| var response = await server.handleRequest(request); | |
| if (response.error case var error?) { | |
| fail('${request.method} failed: ${error.code}: ${error.message}'); | |
| } | |
| var result = CompletionGetSuggestions2Result.fromResponse( | |
| response, | |
| clientUriConverter: null, | |
| ); | |
| replacementOffset = result.replacementOffset; | |
| replacementLength = result.replacementLength; | |
| return result.suggestions; | |
| } | |
| @mustCallSuper | |
| Future<void> processNotification(Notification notification) async { | |
| if (notification.event == completionNotificationExistingImports) { | |
| var params = CompletionExistingImportsParams.fromNotification( | |
| notification, | |
| clientUriConverter: null, | |
| ); | |
| fileToExistingImports[params.file] = params.imports; | |
| } else if (notification.event == analysisNotificationErrors) { | |
| var decoded = AnalysisErrorsParams.fromNotification( | |
| notification, | |
| clientUriConverter: null, | |
| ); | |
| filesErrors[decoded.file] = decoded.errors; | |
| } else if (notification.event == analysisNotificationFlushResults) { | |
| // Ignored. | |
| } else if (notification.event == serverNotificationError) { | |
| throw Exception('server error: ${notification.toJson()}'); | |
| } else if (notification.event == serverNotificationConnected) { | |
| // Ignored. | |
| } else { | |
| print('Unhandled notification: ${notification.event}'); | |
| } | |
| } | |
| } |