Skip to content

Commit

Permalink
Version 3.8.0-10.0.dev
Browse files Browse the repository at this point in the history
Merge dd9a061 into dev
  • Loading branch information
Dart CI committed Jan 18, 2025
2 parents 3fc19db + dd9a061 commit cabf47d
Show file tree
Hide file tree
Showing 33 changed files with 941 additions and 821 deletions.
5 changes: 5 additions & 0 deletions pkg/analysis_server/lib/src/analysis_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,11 @@ abstract class AnalysisServer {
/// 'lsp.notification' notification.
void sendLspNotification(lsp.NotificationMessage notification);

/// Sends an LSP request with the given [params] to the client and waits for a
/// response. Completes with the raw [lsp.ResponseMessage] which could be an
/// error response.
Future<lsp.ResponseMessage> sendLspRequest(lsp.Method method, Object params);

/// Sends an error notification to the user.
void sendServerErrorNotification(
String message,
Expand Down
47 changes: 47 additions & 0 deletions pkg/analysis_server/lib/src/legacy_analysis_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import 'package:analysis_server/src/handler/legacy/server_shutdown.dart';
import 'package:analysis_server/src/handler/legacy/unsupported_request.dart';
import 'package:analysis_server/src/lsp/client_capabilities.dart' as lsp;
import 'package:analysis_server/src/lsp/client_configuration.dart' as lsp;
import 'package:analysis_server/src/lsp/constants.dart' as lsp;
import 'package:analysis_server/src/lsp/handlers/handler_states.dart';
import 'package:analysis_server/src/operation/operation_analysis.dart';
import 'package:analysis_server/src/plugin/notification_manager.dart';
Expand Down Expand Up @@ -716,6 +717,52 @@ class LegacyAnalysisServer extends AnalysisServer {
);
}

/// Sends an LSP request to the server (wrapped in 'lsp.handle') and unwraps
/// the LSP response from the result of the legacy response.
@override
Future<lsp.ResponseMessage> sendLspRequest(
lsp.Method method,
Object params,
) async {
var id = nextServerRequestId++;

// Build the complete LSP RequestMessage to send.
var lspMessage = lsp.RequestMessage(
id: lsp.Either2<int, String>.t1(id),
jsonrpc: lsp.jsonRpcVersion,
method: method,
params: params,
);

// Wrap the LSP message inside a call to lsp.handle.
var response = await sendRequest(
Request(
id.toString(),
LSP_REQUEST_HANDLE,
LspHandleParams(lspMessage).toJson(clientUriConverter: uriConverter),
),
);

// Unwrap the LSP response from the legacy response.
var result = LspHandleResult.fromResponse(
response,
clientUriConverter: uriConverter,
);
var lspResponse = result.lspResponse;

return lspResponse is Map<String, Object?>
? lsp.ResponseMessage.fromJson(lspResponse)
: lsp.ResponseMessage(
jsonrpc: lsp.jsonRpcVersion,
error: lsp.ResponseError(
code: lsp.ServerErrorCodes.UnhandledError,
message:
"The client responded to a '$method' LSP request but"
' did not include a valid response in the lspResponse field',
),
);
}

/// Send the given [notification] to the client.
void sendNotification(Notification notification) {
channel.sendNotification(notification);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ abstract class SimpleEditCommandHandler
) async {
// Send the edit to the client via a applyEdit request (this is a request
// from server -> client and the client will provide a response).
var editResponse = await server.sendRequest(
var editResponse = await server.sendLspRequest(
Method.workspace_applyEdit,
ApplyWorkspaceEditParams(label: commandName, edit: workspaceEdit),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/error_or.dart';
import 'package:analysis_server/src/lsp/handlers/custom/editable_arguments/editable_arguments_mixin.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analysis_server/src/lsp/source_edits.dart';
import 'package:analyzer/dart/analysis/results.dart';
Expand Down Expand Up @@ -286,16 +285,8 @@ class EditArgumentHandler extends SharedMessageHandler<EditArgumentParams, Null>
/// Sends [workspaceEdit] to the client and returns `null` if applied
/// successfully or an error otherwise.
Future<ErrorOr<Null>> _sendEditToClient(WorkspaceEdit workspaceEdit) async {
var server = this.server;
if (server is! LspAnalysisServer) {
return error(
ErrorCodes.RequestFailed,
'Sending edits is currently only supported for clients using LSP directly',
);
}

var editDescription = 'Edit argument';
var editResponse = await server.sendRequest(
var editResponse = await server.sendLspRequest(
Method.workspace_applyEdit,
ApplyWorkspaceEditParams(label: editDescription, edit: workspaceEdit),
);
Expand Down
10 changes: 4 additions & 6 deletions pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ class LspAnalysisServer extends AnalysisServer {
// Fetch all configuration we care about from the client. This is just
// "dart" for now, but in future this may be extended to include
// others (for example "flutter").
var response = await sendRequest(
var response = await sendLspRequest(
Method.workspace_configuration,
ConfigurationParams(
items: [
Expand Down Expand Up @@ -861,10 +861,8 @@ class LspAnalysisServer extends AnalysisServer {
channel.sendNotification(notification);
}

/// Sends a request with the given [params] to the client and wait for a
/// response. Completes with the raw [ResponseMessage] which could be an
/// error response.
Future<ResponseMessage> sendRequest(Method method, Object params) {
@override
Future<ResponseMessage> sendLspRequest(Method method, Object params) {
var requestId = nextRequestId++;
var completer = Completer<ResponseMessage>();
completers[requestId] = completer;
Expand Down Expand Up @@ -1020,7 +1018,7 @@ class LspAnalysisServer extends AnalysisServer {
List<MessageActionItem> actions,
) async {
assert(supportsShowMessageRequest);
var response = await sendRequest(
var response = await sendLspRequest(
Method.window_showMessageRequest,
ShowMessageRequestParams(
type: type.forLsp,
Expand Down
2 changes: 1 addition & 1 deletion pkg/analysis_server/lib/src/lsp/progress.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class _ServerCreatedProgressReporter extends _TokenProgressReporter {
// begin is sent (which could happen because create is async), end will
// not be sent/return too early.
_tokenBeginRequest = _server
.sendRequest(
.sendLspRequest(
Method.window_workDoneProgress_create,
WorkDoneProgressCreateParams(token: _token),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class ServerCapabilitiesComputer {
// we cannot re-enter this method until we have sent both the unregister
// and register requests to the client atomically.
// https://github.com/dart-lang/sdk/issues/47851#issuecomment-988093109
unregistrationRequest = _server.sendRequest(
unregistrationRequest = _server.sendLspRequest(
Method.client_unregisterCapability,
UnregistrationParams(unregisterations: unregistrations),
);
Expand All @@ -316,7 +316,7 @@ class ServerCapabilitiesComputer {
// otherwise we don't know that the client supports registerCapability).
if (registrationsToAdd.isNotEmpty) {
registrationRequest = _server
.sendRequest(
.sendLspRequest(
Method.client_registerCapability,
RegistrationParams(registrations: registrationsToAdd),
)
Expand Down
56 changes: 45 additions & 11 deletions pkg/analysis_server/lib/src/utilities/mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,31 @@ import 'package:watcher/watcher.dart';
class MockServerChannel implements ServerCommunicationChannel {
/// A controller for the stream of requests and responses from the client to
/// the server.
///
/// Messages added to this stream should be converted to/from JSON to ensure
/// they are fully serialized/deserialized as they would be in a real server
/// otherwise tests may receive real instances where in reality they would be
/// maps.
StreamController<RequestOrResponse> requestController =
StreamController<RequestOrResponse>();

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

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

Expand Down Expand Up @@ -67,7 +82,10 @@ class MockServerChannel implements ServerCommunicationChannel {
if (_closed) {
return;
}

notificationsReceived.add(notification);
notificationController.add(notification);

var errorCompleter = this.errorCompleter;
if (errorCompleter != null && notification.event == 'server.error') {
var params = notification.params!;
Expand All @@ -77,14 +95,17 @@ class MockServerChannel implements ServerCommunicationChannel {
StackTrace.fromString(params['stackTrace'] as String),
);
}
// Wrap send notification in future to simulate websocket
// TODO(scheglov): ask Dan why and decide what to do
// new Future(() => notificationController.add(notification));
notificationController.add(notification);
}

@override
void sendRequest(Request request) {
// Round-trip via JSON to ensure all types are fully serialized as they
// would be in a real setup.
request =
Request.fromJson(
jsonDecode(jsonEncode(request.toJson())) as Map<String, Object?>,
)!;

serverRequestsSent.add(request);
responseController.add(request);
}
Expand All @@ -95,9 +116,16 @@ class MockServerChannel implements ServerCommunicationChannel {
if (_closed) {
return;
}

// Round-trip via JSON to ensure all types are fully serialized as they
// would be in a real setup.
response =
Response.fromJson(
jsonDecode(jsonEncode(response.toJson())) as Map<String, Object?>,
)!;

responsesReceived.add(response);
// Wrap send response in future to simulate WebSocket.
Future(() => responseController.add(response));
responseController.add(response);
}

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

// Wrap send request in future to simulate WebSocket.
unawaited(Future(() => requestController.add(request)));
requestController.add(request);
var response = await waitForResponse(request);

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

/// Send the given [response] to the server as if it had been sent from the
/// client.
Future<void> simulateResponseFromClient(Response response) {
void simulateResponseFromClient(Response response) {
// No further requests should be sent after the connection is closed.
if (_closed) {
throw Exception('simulateRequestFromClient after connection closed');
}
// Wrap send request in future to simulate WebSocket.
return Future(() => requestController.add(response));

// Round-trip via JSON to ensure all types are fully serialized as they
// would be in a real setup.
response =
Response.fromJson(
jsonDecode(jsonEncode(response)) as Map<String, Object?>,
)!;

requestController.add(response);
}

/// Return a future that will complete when a response associated with the
Expand Down
1 change: 1 addition & 0 deletions pkg/analysis_server/test/domain_analysis_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,7 @@ analyzer:
newFile(path, '<manifest/>');

await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();

// No touch-screen.
assertNotificationsText(r'''
Expand Down
6 changes: 3 additions & 3 deletions pkg/analysis_server/test/domain_server_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class ServerDomainTest extends PubPackageAnalysisServerTest {

// Simulate the response.
var request = serverChannel.serverRequestsSent[0];
await serverChannel.simulateResponseFromClient(
serverChannel.simulateResponseFromClient(
ServerOpenUrlRequestResult().toResponse(
request.id,
clientUriConverter: server.uriConverter,
Expand Down Expand Up @@ -383,7 +383,7 @@ class ServerDomainTest extends PubPackageAnalysisServerTest {

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

// Simulate the response.
var request = serverChannel.serverRequestsSent[0];
await serverChannel.simulateResponseFromClient(
serverChannel.simulateResponseFromClient(
ServerShowMessageRequestResult().toResponse(
request.id,
clientUriConverter: server.uriConverter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class LegacyServerMessageSchedulerTest extends PubPackageAnalysisServerTest {

Future<void> test_initialize() async {
await setRoots(included: [workspaceRootPath], excluded: []);
await waitForTasksFinished();
_assertLogContents(messageScheduler, r'''
Incoming LegacyMessage: analysis.setAnalysisRoots
Entering process messages loop
Expand All @@ -74,6 +75,7 @@ Exit process messages loop
).toRequest('0', clientUriConverter: server.uriConverter);
futures.add(handleSuccessfulRequest(request));
await Future.wait(futures);
await waitForTasksFinished();
_assertLogContents(messageScheduler, r'''
Incoming LegacyMessage: analysis.setAnalysisRoots
Entering process messages loop
Expand Down
Loading

0 comments on commit cabf47d

Please sign in to comment.