handle<TArg, TResp> method

Future<void> handle<TArg, TResp>(
  1. Request request,
  2. RequestHandler<TArg, TResp> handler,
  3. TArg fromJson(
    1. Map<String, Object?>
    ),
  4. void responseWriter(
    1. Response
    ),
)
inherited

Calls handler for an incoming request, using fromJson to parse its arguments from the request.

handler will be provided a function sendResponse that it can use to sends its response without needing to build a Response from fields on the request.

handler must always call sendResponse, even if the response does not require a body.

responseWriter is a function that will be provided the response to be sent on the stream. It is critical that this sends the response synchronously because additional events may be sent by handlers that must come after it.

If handler throws, its exception will be sent as an error response.

Implementation

Future<void> handle<TArg, TResp>(
  Request request,
  RequestHandler<TArg, TResp> handler,
  TArg Function(Map<String, Object?>) fromJson,
  void Function(Response) responseWriter,
) async {
  try {
    final args = request.arguments != null
        ? fromJson(request.arguments as Map<String, Object?>)
        // arguments are only valid to be null then TArg is nullable.
        : null as TArg;

    // Because handlers may need to send responses before they have finished
    // executing (for example, initializeRequest needs to send its response
    // before sending InitializedEvent()), we pass in a function `sendResponse`
    // rather than using a return value.
    var sendResponseCalled = false;
    void sendResponse(TResp responseBody) {
      assert(!sendResponseCalled,
          'sendResponse was called multiple times by ${request.command}');
      sendResponseCalled = true;
      final response = Response(
        success: true,
        requestSeq: request.seq,
        seq: _sequence++,
        command: request.command,
        body: responseBody,
      );
      responseWriter(response);
    }

    await handler(request, args, sendResponse);
    assert(sendResponseCalled,
        'sendResponse was not called in ${request.command}');
  } catch (e, s) {
    // TODO(helin24): Consider adding an error type to DebugAdapterException.
    final messageText = e is DebugAdapterException ? e.message : '$e';
    final errorMessage = Message(
      id: ErrorMessageType.general,
      format: '{message}',
      // We include stack in the payload for debugging, but we don't include
      // it in format above because we don't want it used to build the error
      // shown to the user.
      variables: {'message': messageText, 'stack': '$s'},
      // DAP specification did not specify how to handle the case where
      // showUser does not exist. VSCode defaults to true, but some other
      // systems might default it to false.
      // Always pass true to be consistent.
      showUser: true,
    );
    final response = Response(
      success: false,
      requestSeq: request.seq,
      seq: _sequence++,
      command: request.command,
      message: messageText,
      body: ErrorResponseBody(error: errorMessage),
    );
    responseWriter(response);
  }
}