Skip to content

Commit 4d0d277

Browse files
prefer async over completion handlers
1 parent bf8e86f commit 4d0d277

File tree

2 files changed

+97
-103
lines changed

2 files changed

+97
-103
lines changed

Sources/SourceKitLSP/Documentation/DocCServer.swift

Lines changed: 77 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import Foundation
1616

1717
package struct DocCServer {
1818
private let server: DocumentationServer
19-
private let jsonEncoder = JSONEncoder()
20-
private let jsonDecoder = JSONDecoder()
2119

2220
init(peer peerServer: DocumentationServer? = nil, qualityOfService: DispatchQoS) {
2321
server = DocumentationServer.createDefaultServer(qualityOfService: qualityOfService, peer: peerServer)
@@ -35,9 +33,8 @@ package struct DocCServer {
3533
emitSymbolSourceFileURIs: Bool,
3634
markupFiles: [Data],
3735
tutorialFiles: [Data],
38-
convertRequestIdentifier: String,
39-
completion: @escaping (_: Result<ConvertResponse, DocCServerError>) -> Void
40-
) {
36+
convertRequestIdentifier: String
37+
) async throws(DocCServerError) -> ConvertResponse {
4138
let request = ConvertRequest(
4239
bundleInfo: DocumentationBundle.Info(
4340
displayName: documentationBundleDisplayName,
@@ -61,90 +58,97 @@ package struct DocCServer {
6158
miscResourceURLs: [],
6259
symbolIdentifiersWithExpandedDocumentation: nil
6360
)
64-
65-
makeRequest(
61+
let response = try await makeRequest(
6662
messageType: ConvertService.convertMessageType,
6763
messageIdentifier: convertRequestIdentifier,
6864
request: request
69-
) { response in
70-
completion(
71-
response.flatMap {
72-
message -> Result<Data, DocCServerError> in
73-
guard let messagePayload = message.payload else {
74-
return .failure(.unexpectedlyNilPayload(message.type.rawValue))
75-
}
76-
77-
guard message.type != ConvertService.convertResponseErrorMessageType else {
78-
return Result {
79-
try self.jsonDecoder.decode(ConvertServiceError.self, from: messagePayload)
80-
}
81-
.flatMapError {
82-
.failure(
83-
DocCServerError.messagePayloadDecodingFailure(
84-
messageType: message.type.rawValue,
85-
decodingError: $0
86-
)
87-
)
88-
}
89-
.flatMap { .failure(.internalError($0)) }
90-
}
91-
92-
guard message.type == ConvertService.convertResponseMessageType else {
93-
return .failure(.unknownMessageType(message.type.rawValue))
94-
}
95-
96-
return .success(messagePayload)
97-
}
98-
.flatMap { convertMessagePayload -> Result<ConvertResponse, DocCServerError> in
99-
return Result {
100-
try self.jsonDecoder.decode(ConvertResponse.self, from: convertMessagePayload)
101-
}
102-
.flatMapError { decodingError -> Result<ConvertResponse, DocCServerError> in
103-
return .failure(
104-
DocCServerError.messagePayloadDecodingFailure(
105-
messageType: ConvertService.convertResponseMessageType.rawValue,
106-
decodingError: decodingError
107-
)
108-
)
109-
}
110-
}
111-
)
65+
);
66+
guard let responsePayload = response.payload else {
67+
throw .unexpectedlyNilPayload(response.type.rawValue)
68+
}
69+
// Check for an error response from SwiftDocC
70+
guard response.type != ConvertService.convertResponseErrorMessageType else {
71+
let convertServiceError: ConvertServiceError
72+
do {
73+
convertServiceError = try JSONDecoder().decode(ConvertServiceError.self, from: responsePayload)
74+
} catch {
75+
throw .messagePayloadDecodingFailure(messageType: response.type.rawValue, decodingError: error)
76+
}
77+
throw .internalError(convertServiceError)
78+
}
79+
guard response.type == ConvertService.convertResponseMessageType else {
80+
throw .unknownMessageType(response.type.rawValue)
81+
}
82+
// Decode the SwiftDocC.ConvertResponse and wrap it in our own Sendable type
83+
let doccConvertResponse: SwiftDocC.ConvertResponse
84+
do {
85+
doccConvertResponse = try JSONDecoder().decode(SwiftDocC.ConvertResponse.self, from: responsePayload)
86+
} catch {
87+
throw .decodingFailure(error)
11288
}
89+
return ConvertResponse(doccConvertResponse: doccConvertResponse)
11390
}
11491

11592
private func makeRequest<Request: Encodable & Sendable>(
11693
messageType: DocumentationServer.MessageType,
11794
messageIdentifier: String,
118-
request: Request,
119-
completion: @escaping (_: Result<DocumentationServer.Message, DocCServerError>) -> Void
120-
) {
121-
let encodedMessageResult: Result<Data, DocCServerError> = Result { try jsonEncoder.encode(request) }
122-
.mapError { .encodingFailure($0) }
123-
.flatMap { encodedPayload in
124-
Result {
125-
let message = DocumentationServer.Message(
126-
type: messageType,
127-
identifier: messageIdentifier,
128-
payload: encodedPayload
129-
)
130-
return try jsonEncoder.encode(message)
131-
}.mapError { encodingError -> DocCServerError in
132-
return .encodingFailure(encodingError)
133-
}
95+
request: Request
96+
) async throws(DocCServerError) -> DocumentationServer.Message {
97+
let result: Result<DocumentationServer.Message, DocCServerError> = await withCheckedContinuation { continuation in
98+
// Encode the request in JSON format
99+
let encodedPayload: Data
100+
do {
101+
encodedPayload = try JSONEncoder().encode(request)
102+
} catch {
103+
return continuation.resume(returning: .failure(.encodingFailure(error)))
134104
}
135-
136-
switch encodedMessageResult {
137-
case .success(let encodedMessage):
105+
// Encode the full message in JSON format
106+
let message = DocumentationServer.Message(
107+
type: messageType,
108+
identifier: messageIdentifier,
109+
payload: encodedPayload
110+
)
111+
let encodedMessage: Data
112+
do {
113+
encodedMessage = try JSONEncoder().encode(message)
114+
} catch {
115+
return continuation.resume(returning: .failure(.encodingFailure(error)))
116+
}
117+
// Send the request to the server and decode the response
138118
server.process(encodedMessage) { response in
139119
let decodeMessageResult: Result<DocumentationServer.Message, DocCServerError> = Result {
140-
try self.jsonDecoder.decode(DocumentationServer.Message.self, from: response)
120+
try JSONDecoder().decode(DocumentationServer.Message.self, from: response)
141121
}
142122
.flatMapError { .failure(.decodingFailure($0)) }
143-
completion(decodeMessageResult)
123+
continuation.resume(returning: decodeMessageResult)
144124
}
145-
case .failure(let encodingError):
146-
completion(.failure(encodingError))
147125
}
126+
return try result.get()
127+
}
128+
}
129+
130+
/// A Sendable wrapper around ``SwiftDocC.ConvertResponse``
131+
struct ConvertResponse: Sendable, Codable {
132+
/// The render nodes that were created as part of the conversion, encoded as JSON.
133+
let renderNodes: [Data]
134+
135+
/// The render reference store that was created as part of the bundle's conversion, encoded as JSON.
136+
///
137+
/// The ``RenderReferenceStore`` contains compiled information for documentation nodes that were registered as part of
138+
/// the conversion. This information can be used as a lightweight index of the available documentation content in the bundle that's
139+
/// been converted.
140+
let renderReferenceStore: Data?
141+
142+
/// Creates a conversion response given the render nodes that were created as part of the conversion.
143+
init(renderNodes: [Data], renderReferenceStore: Data? = nil) {
144+
self.renderNodes = renderNodes
145+
self.renderReferenceStore = renderReferenceStore
146+
}
147+
148+
/// Creates a conversion response given a SwiftDocC conversion response
149+
init(doccConvertResponse: SwiftDocC.ConvertResponse) {
150+
self.renderNodes = doccConvertResponse.renderNodes
151+
self.renderReferenceStore = doccConvertResponse.renderReferenceStore
148152
}
149153
}
150154

Sources/SourceKitLSP/Documentation/DocumentationManager.swift

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -99,37 +99,27 @@ package final actor DocumentationManager {
9999
throw ResponseError.requestFailed(.noDocumentation)
100100
}
101101
// Send the convert request to SwiftDocC and wait for the response
102-
return try await withCheckedThrowingContinuation { continuation in
103-
doccServer.convert(
104-
externalIDsToConvert: externalIDsToConvert,
105-
documentPathsToConvert: nil,
106-
includeRenderReferenceStore: false,
107-
documentationBundleLocation: nil,
108-
documentationBundleDisplayName: moduleName ?? "Unknown",
109-
documentationBundleIdentifier: "unknown",
110-
symbolGraphs: symbolGraphs,
111-
overridingDocumentationComments: overridingDocumentationComments,
112-
emitSymbolSourceFileURIs: false,
113-
markupFiles: [],
114-
tutorialFiles: [],
115-
convertRequestIdentifier: UUID().uuidString
116-
) { convertResponse in
117-
switch convertResponse {
118-
case .success(let convertResponse):
119-
guard let renderNodeData = convertResponse.renderNodes.first else {
120-
continuation.resume(throwing: ResponseError.internalError("SwiftDocC did not return any render nodes"))
121-
return
122-
}
123-
guard let renderNode = String(data: renderNodeData, encoding: .utf8) else {
124-
continuation.resume(throwing: ResponseError.internalError("Failed to encode render node from SwiftDocC"))
125-
return
126-
}
127-
continuation.resume(returning: DoccDocumentationResponse(renderNode: renderNode))
128-
case .failure(let serverError):
129-
continuation.resume(throwing: serverError)
130-
}
131-
}
102+
let convertResponse = try await doccServer.convert(
103+
externalIDsToConvert: externalIDsToConvert,
104+
documentPathsToConvert: nil,
105+
includeRenderReferenceStore: false,
106+
documentationBundleLocation: nil,
107+
documentationBundleDisplayName: moduleName ?? "Unknown",
108+
documentationBundleIdentifier: "unknown",
109+
symbolGraphs: symbolGraphs,
110+
overridingDocumentationComments: overridingDocumentationComments,
111+
emitSymbolSourceFileURIs: false,
112+
markupFiles: [],
113+
tutorialFiles: [],
114+
convertRequestIdentifier: UUID().uuidString
115+
)
116+
guard let renderNodeData = convertResponse.renderNodes.first else {
117+
throw ResponseError.internalError("SwiftDocC did not return any render nodes")
118+
}
119+
guard let renderNode = String(data: renderNodeData, encoding: .utf8) else {
120+
throw ResponseError.internalError("Failed to encode render node from SwiftDocC")
132121
}
122+
return DoccDocumentationResponse(renderNode: renderNode)
133123
}
134124
}
135125

0 commit comments

Comments
 (0)