Skip to content

Commit b908274

Browse files
DanTupCommit Queue
authored andcommitted
[analysis_server] Minor refactors to enable running EditArgument through legacy server
This is some minor refactoring to support the next CL that will enable the EditArgument request (the one that actually edits arguments, not the one that gets the list of arguments) over legacy. It moves some code for sending LSP reverse-requests through the legacy server from test code into the actual server (and fixes that they weren't correctly wrapped in the 'lsp.handle' protocol classes) and updates the tests to use the shared interface ready. Change-Id: I06492138645538072fb12f2fc2d424e214f8055b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404824 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 7dc5a42 commit b908274

17 files changed

+143
-82
lines changed

pkg/analysis_server/lib/src/legacy_analysis_server.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import 'package:analysis_server/src/handler/legacy/server_shutdown.dart';
7676
import 'package:analysis_server/src/handler/legacy/unsupported_request.dart';
7777
import 'package:analysis_server/src/lsp/client_capabilities.dart' as lsp;
7878
import 'package:analysis_server/src/lsp/client_configuration.dart' as lsp;
79+
import 'package:analysis_server/src/lsp/constants.dart' as lsp;
7980
import 'package:analysis_server/src/lsp/handlers/handler_states.dart';
8081
import 'package:analysis_server/src/operation/operation_analysis.dart';
8182
import 'package:analysis_server/src/plugin/notification_manager.dart';
@@ -716,6 +717,51 @@ class LegacyAnalysisServer extends AnalysisServer {
716717
);
717718
}
718719

720+
/// Sends an LSP request to the server (wrapped in 'lsp.handle') and unwraps
721+
/// the LSP response from the result of the legacy response.
722+
Future<lsp.ResponseMessage> sendLspRequest(
723+
lsp.Method method,
724+
Object params,
725+
) async {
726+
var id = nextServerRequestId++;
727+
728+
// Build the complete LSP RequestMessage to send.
729+
var lspMessage = lsp.RequestMessage(
730+
id: lsp.Either2<int, String>.t1(id),
731+
jsonrpc: lsp.jsonRpcVersion,
732+
method: method,
733+
params: params,
734+
);
735+
736+
// Wrap the LSP message inside a call to lsp.handle.
737+
var response = await sendRequest(
738+
Request(
739+
id.toString(),
740+
LSP_REQUEST_HANDLE,
741+
LspHandleParams(lspMessage).toJson(clientUriConverter: uriConverter),
742+
),
743+
);
744+
745+
// Unwrap the LSP response from the legacy response.
746+
var result = LspHandleResult.fromResponse(
747+
response,
748+
clientUriConverter: uriConverter,
749+
);
750+
var lspResponse = result.lspResponse;
751+
752+
return lspResponse is Map<String, Object?>
753+
? lsp.ResponseMessage.fromJson(lspResponse)
754+
: lsp.ResponseMessage(
755+
jsonrpc: lsp.jsonRpcVersion,
756+
error: lsp.ResponseError(
757+
code: lsp.ServerErrorCodes.UnhandledError,
758+
message:
759+
"The client responded to a '$method' LSP request but"
760+
' did not include a valid response in the lspResponse field',
761+
),
762+
);
763+
}
764+
719765
/// Send the given [notification] to the client.
720766
void sendNotification(Notification notification) {
721767
channel.sendNotification(notification);

pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ abstract class SimpleEditCommandHandler
6767
) async {
6868
// Send the edit to the client via a applyEdit request (this is a request
6969
// from server -> client and the client will provide a response).
70-
var editResponse = await server.sendRequest(
70+
var editResponse = await server.sendLspRequest(
7171
Method.workspace_applyEdit,
7272
ApplyWorkspaceEditParams(label: commandName, edit: workspaceEdit),
7373
);

pkg/analysis_server/lib/src/lsp/handlers/custom/editable_arguments/handler_edit_argument.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ class EditArgumentHandler extends SharedMessageHandler<EditArgumentParams, Null>
295295
}
296296

297297
var editDescription = 'Edit argument';
298-
var editResponse = await server.sendRequest(
298+
var editResponse = await server.sendLspRequest(
299299
Method.workspace_applyEdit,
300300
ApplyWorkspaceEditParams(label: editDescription, edit: workspaceEdit),
301301
);

pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ class LspAnalysisServer extends AnalysisServer {
329329
// Fetch all configuration we care about from the client. This is just
330330
// "dart" for now, but in future this may be extended to include
331331
// others (for example "flutter").
332-
var response = await sendRequest(
332+
var response = await sendLspRequest(
333333
Method.workspace_configuration,
334334
ConfigurationParams(
335335
items: [
@@ -864,7 +864,7 @@ class LspAnalysisServer extends AnalysisServer {
864864
/// Sends a request with the given [params] to the client and wait for a
865865
/// response. Completes with the raw [ResponseMessage] which could be an
866866
/// error response.
867-
Future<ResponseMessage> sendRequest(Method method, Object params) {
867+
Future<ResponseMessage> sendLspRequest(Method method, Object params) {
868868
var requestId = nextRequestId++;
869869
var completer = Completer<ResponseMessage>();
870870
completers[requestId] = completer;
@@ -1020,7 +1020,7 @@ class LspAnalysisServer extends AnalysisServer {
10201020
List<MessageActionItem> actions,
10211021
) async {
10221022
assert(supportsShowMessageRequest);
1023-
var response = await sendRequest(
1023+
var response = await sendLspRequest(
10241024
Method.window_showMessageRequest,
10251025
ShowMessageRequestParams(
10261026
type: type.forLsp,

pkg/analysis_server/lib/src/lsp/progress.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class _ServerCreatedProgressReporter extends _TokenProgressReporter {
6363
// begin is sent (which could happen because create is async), end will
6464
// not be sent/return too early.
6565
_tokenBeginRequest = _server
66-
.sendRequest(
66+
.sendLspRequest(
6767
Method.window_workDoneProgress_create,
6868
WorkDoneProgressCreateParams(token: _token),
6969
)

pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ class ServerCapabilitiesComputer {
305305
// we cannot re-enter this method until we have sent both the unregister
306306
// and register requests to the client atomically.
307307
// https://github.com/dart-lang/sdk/issues/47851#issuecomment-988093109
308-
unregistrationRequest = _server.sendRequest(
308+
unregistrationRequest = _server.sendLspRequest(
309309
Method.client_unregisterCapability,
310310
UnregistrationParams(unregisterations: unregistrations),
311311
);
@@ -316,7 +316,7 @@ class ServerCapabilitiesComputer {
316316
// otherwise we don't know that the client supports registerCapability).
317317
if (registrationsToAdd.isNotEmpty) {
318318
registrationRequest = _server
319-
.sendRequest(
319+
.sendLspRequest(
320320
Method.client_registerCapability,
321321
RegistrationParams(registrations: registrationsToAdd),
322322
)

pkg/analysis_server/lib/src/utilities/mocks.dart

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,31 @@ import 'package:watcher/watcher.dart';
1818
class MockServerChannel implements ServerCommunicationChannel {
1919
/// A controller for the stream of requests and responses from the client to
2020
/// the server.
21+
///
22+
/// Messages added to this stream should be converted to/from JSON to ensure
23+
/// they are fully serialized/deserialized as they would be in a real server
24+
/// otherwise tests may receive real instances where in reality they would be
25+
/// maps.
2126
StreamController<RequestOrResponse> requestController =
2227
StreamController<RequestOrResponse>();
2328

2429
/// A controller for the stream of requests and responses from the server to
2530
/// the client.
31+
///
32+
/// Messages added to this stream should be converted to/from JSON to ensure
33+
/// they are fully serialized/deserialized as they would be in a real server
34+
/// otherwise tests may receive real instances where in reality they would be
35+
/// maps.
2636
StreamController<RequestOrResponse> responseController =
2737
StreamController<RequestOrResponse>.broadcast();
2838

2939
/// A controller for the stream of notifications from the server to the
3040
/// client.
41+
///
42+
/// Unlike [requestController] and [responseController], notifications added
43+
/// here are not round-tripped through JSON but instead have real class
44+
/// instances as their `params`. This is because they are only used by tests
45+
/// which will cast and/or switch on the types for convenience.
3146
StreamController<Notification> notificationController =
3247
StreamController<Notification>.broadcast(sync: true);
3348

@@ -67,7 +82,10 @@ class MockServerChannel implements ServerCommunicationChannel {
6782
if (_closed) {
6883
return;
6984
}
85+
7086
notificationsReceived.add(notification);
87+
notificationController.add(notification);
88+
7189
var errorCompleter = this.errorCompleter;
7290
if (errorCompleter != null && notification.event == 'server.error') {
7391
var params = notification.params!;
@@ -77,14 +95,17 @@ class MockServerChannel implements ServerCommunicationChannel {
7795
StackTrace.fromString(params['stackTrace'] as String),
7896
);
7997
}
80-
// Wrap send notification in future to simulate websocket
81-
// TODO(scheglov): ask Dan why and decide what to do
82-
// new Future(() => notificationController.add(notification));
83-
notificationController.add(notification);
8498
}
8599

86100
@override
87101
void sendRequest(Request request) {
102+
// Round-trip via JSON to ensure all types are fully serialized as they
103+
// would be in a real setup.
104+
request =
105+
Request.fromJson(
106+
jsonDecode(jsonEncode(request.toJson())) as Map<String, Object?>,
107+
)!;
108+
88109
serverRequestsSent.add(request);
89110
responseController.add(request);
90111
}
@@ -95,9 +116,16 @@ class MockServerChannel implements ServerCommunicationChannel {
95116
if (_closed) {
96117
return;
97118
}
119+
120+
// Round-trip via JSON to ensure all types are fully serialized as they
121+
// would be in a real setup.
122+
response =
123+
Response.fromJson(
124+
jsonDecode(jsonEncode(response.toJson())) as Map<String, Object?>,
125+
)!;
126+
98127
responsesReceived.add(response);
99-
// Wrap send response in future to simulate WebSocket.
100-
Future(() => responseController.add(response));
128+
responseController.add(response);
101129
}
102130

103131
/// Send the given [request] to the server as if it had been sent from the
@@ -117,8 +145,7 @@ class MockServerChannel implements ServerCommunicationChannel {
117145
jsonDecode(jsonEncode(request)) as Map<String, Object?>,
118146
)!;
119147

120-
// Wrap send request in future to simulate WebSocket.
121-
unawaited(Future(() => requestController.add(request)));
148+
requestController.add(request);
122149
var response = await waitForResponse(request);
123150

124151
// Round-trip via JSON to ensure all types are fully serialized as they
@@ -133,13 +160,20 @@ class MockServerChannel implements ServerCommunicationChannel {
133160

134161
/// Send the given [response] to the server as if it had been sent from the
135162
/// client.
136-
Future<void> simulateResponseFromClient(Response response) {
163+
void simulateResponseFromClient(Response response) {
137164
// No further requests should be sent after the connection is closed.
138165
if (_closed) {
139166
throw Exception('simulateRequestFromClient after connection closed');
140167
}
141-
// Wrap send request in future to simulate WebSocket.
142-
return Future(() => requestController.add(response));
168+
169+
// Round-trip via JSON to ensure all types are fully serialized as they
170+
// would be in a real setup.
171+
response =
172+
Response.fromJson(
173+
jsonDecode(jsonEncode(response)) as Map<String, Object?>,
174+
)!;
175+
176+
requestController.add(response);
143177
}
144178

145179
/// Return a future that will complete when a response associated with the

pkg/analysis_server/test/domain_analysis_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,7 @@ analyzer:
17871787
newFile(path, '<manifest/>');
17881788

17891789
await setRoots(included: [workspaceRootPath], excluded: []);
1790+
await waitForTasksFinished();
17901791

17911792
// No touch-screen.
17921793
assertNotificationsText(r'''

pkg/analysis_server/test/domain_server_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class ServerDomainTest extends PubPackageAnalysisServerTest {
9292

9393
// Simulate the response.
9494
var request = serverChannel.serverRequestsSent[0];
95-
await serverChannel.simulateResponseFromClient(
95+
serverChannel.simulateResponseFromClient(
9696
ServerOpenUrlRequestResult().toResponse(
9797
request.id,
9898
clientUriConverter: server.uriConverter,
@@ -383,7 +383,7 @@ class ServerDomainTest extends PubPackageAnalysisServerTest {
383383

384384
// Simulate the response.
385385
var request = serverChannel.serverRequestsSent[0];
386-
await serverChannel.simulateResponseFromClient(
386+
serverChannel.simulateResponseFromClient(
387387
ServerShowMessageRequestResult(
388388
action: 'a',
389389
).toResponse(request.id, clientUriConverter: server.uriConverter),
@@ -405,7 +405,7 @@ class ServerDomainTest extends PubPackageAnalysisServerTest {
405405

406406
// Simulate the response.
407407
var request = serverChannel.serverRequestsSent[0];
408-
await serverChannel.simulateResponseFromClient(
408+
serverChannel.simulateResponseFromClient(
409409
ServerShowMessageRequestResult().toResponse(
410410
request.id,
411411
clientUriConverter: server.uriConverter,

pkg/analysis_server/test/integration/server/message_scheduler_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class LegacyServerMessageSchedulerTest extends PubPackageAnalysisServerTest {
5757

5858
Future<void> test_initialize() async {
5959
await setRoots(included: [workspaceRootPath], excluded: []);
60+
await waitForTasksFinished();
6061
_assertLogContents(messageScheduler, r'''
6162
Incoming LegacyMessage: analysis.setAnalysisRoots
6263
Entering process messages loop
@@ -74,6 +75,7 @@ Exit process messages loop
7475
).toRequest('0', clientUriConverter: server.uriConverter);
7576
futures.add(handleSuccessfulRequest(request));
7677
await Future.wait(futures);
78+
await waitForTasksFinished();
7779
_assertLogContents(messageScheduler, r'''
7880
Incoming LegacyMessage: analysis.setAnalysisRoots
7981
Entering process messages loop

0 commit comments

Comments
 (0)