Skip to content

Commit f7f3d2d

Browse files
[CodeGenLib] Render preformatted comments (#1770)
Motivation: Some IDL representations (SwiftProtobuf) store the methods and services documentation as preformatted strings containing the "///" and new lines. So we need a case in the SwiftStructuredRepresentation and in the TextRenderer. The responsability of providing already formatted comments for the services and methods should be of the user and not of the CodeGenLibrary. Modifications: - created the new case in StructuredSwiiftrepresentation - handled the rendering of this case - modified tests Result: Documentation for methoda and services should pe preformatted in any CodeGenerationRequest object so we avoid generating commenyts with double `///`.
1 parent 91f9bd8 commit f7f3d2d

File tree

9 files changed

+75
-46
lines changed

9 files changed

+75
-46
lines changed

Sources/GRPCCodeGen/CodeGenerationRequest.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ public struct CodeGenerationRequest {
2121
public var fileName: String
2222

2323
/// Any comments at the top of the file such as documentation and copyright headers.
24-
/// They will be placed at the top of the generated file.
24+
/// They will be placed at the top of the generated file. They are already formatted,
25+
/// meaning they contain "///" and new lines.
2526
public var leadingTrivia: String
2627

2728
/// The Swift imports that the generated file depends on. The gRPC specific imports aren't required
@@ -218,6 +219,7 @@ public struct CodeGenerationRequest {
218219
/// Represents a service described in an IDL file.
219220
public struct ServiceDescriptor: Hashable {
220221
/// Documentation from comments above the IDL service description.
222+
/// It is already formatted, meaning it contains "///" and new lines.
221223
public var documentation: String
222224

223225
/// The service name in different formats.
@@ -252,6 +254,7 @@ public struct CodeGenerationRequest {
252254
/// Represents a method described in an IDL file.
253255
public struct MethodDescriptor: Hashable {
254256
/// Documentation from comments above the IDL method description.
257+
/// It is already formatted, meaning it contains "///" and new lines.
255258
public var documentation: String
256259

257260
/// Method name in different formats.

Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,19 @@ struct TextBasedRenderer: RendererProtocol {
161161
case .mark(let string, sectionBreak: false):
162162
prefix = "// MARK:"
163163
commentString = string
164+
case .preFormatted(let string):
165+
prefix = ""
166+
commentString = string
164167
}
165-
let lines = commentString.transformingLines { line in
166-
if line.isEmpty { return prefix }
167-
return "\(prefix) \(line)"
168+
if prefix.isEmpty {
169+
writer.writeLine(commentString)
170+
} else {
171+
let lines = commentString.transformingLines { line in
172+
if line.isEmpty { return prefix }
173+
return "\(prefix) \(line)"
174+
}
175+
lines.forEach(writer.writeLine)
168176
}
169-
lines.forEach(writer.writeLine)
170177
}
171178

172179
/// Renders the specified import statements.

Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ enum Comment: Equatable, Codable {
130130
/// For example: `// MARK: - Public methods`, with the optional
131131
/// section break (`-`).
132132
case mark(String, sectionBreak: Bool)
133+
134+
/// A comment that is already formatted,
135+
/// meaning that it already has the `///` and
136+
/// can contain multiple lines
137+
///
138+
/// For example both the string and the comment
139+
/// can look like `/// Important type`.
140+
case preFormatted(String)
133141
}
134142

135143
/// A description of a literal.

Sources/GRPCCodeGen/Internal/Translator/ClientCodeTranslator.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ struct ClientCodeTranslator: SpecializedTranslator {
7777
codeBlocks.append(
7878
.declaration(
7979
.commentable(
80-
.doc(service.documentation),
80+
.preFormatted(service.documentation),
8181
self.makeClientProtocol(for: service, in: codeGenerationRequest)
8282
)
8383
)
@@ -88,7 +88,7 @@ struct ClientCodeTranslator: SpecializedTranslator {
8888
codeBlocks.append(
8989
.declaration(
9090
.commentable(
91-
.doc(service.documentation),
91+
.preFormatted(service.documentation),
9292
self.makeClientStruct(for: service, in: codeGenerationRequest)
9393
)
9494
)
@@ -179,7 +179,10 @@ extension ClientCodeTranslator {
179179
)
180180
return .function(signature: functionSignature, body: body)
181181
} else {
182-
return .commentable(.doc(method.documentation), .function(signature: functionSignature))
182+
return .commentable(
183+
.preFormatted(method.documentation),
184+
.function(signature: functionSignature)
185+
)
183186
}
184187
}
185188

@@ -324,7 +327,7 @@ extension ClientCodeTranslator {
324327
let initializer = self.makeClientVariable()
325328
let methods = service.methods.map {
326329
Declaration.commentable(
327-
.doc($0.documentation),
330+
.preFormatted($0.documentation),
328331
self.makeClientMethod(for: $0, in: service, from: codeGenerationRequest)
329332
)
330333
}

Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ struct IDLToStructuredSwiftTranslator: Translator {
3131
accessLevel: accessLevel
3232
)
3333

34-
let topComment = Comment.doc(codeGenerationRequest.leadingTrivia)
34+
let topComment = Comment.preFormatted(codeGenerationRequest.leadingTrivia)
3535
let imports = try codeGenerationRequest.dependencies.reduce(
3636
into: [ImportDescription(moduleName: "GRPCCore")]
3737
) { partialResult, newDependency in

Sources/GRPCCodeGen/Internal/Translator/ServerCodeTranslator.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ extension ServerCodeTranslator {
107107
) -> Declaration {
108108
let methods = service.methods.compactMap {
109109
Declaration.commentable(
110-
.doc($0.documentation),
110+
.preFormatted($0.documentation),
111111
.function(
112112
FunctionDescription(
113113
signature: self.makeStreamingMethodSignature(for: $0, in: service)
@@ -125,7 +125,7 @@ extension ServerCodeTranslator {
125125
)
126126
)
127127

128-
return .commentable(.doc(service.documentation), streamingProtocol)
128+
return .commentable(.preFormatted(service.documentation), streamingProtocol)
129129
}
130130

131131
private func makeStreamingMethodSignature(
@@ -290,7 +290,7 @@ extension ServerCodeTranslator {
290290
let streamingProtocol = self.protocolNameTypealias(service: service, streaming: true)
291291

292292
return .commentable(
293-
.doc(service.documentation),
293+
.preFormatted(service.documentation),
294294
.protocol(
295295
ProtocolDescription(
296296
accessModifier: self.accessModifier,
@@ -344,7 +344,7 @@ extension ServerCodeTranslator {
344344
)
345345

346346
return .commentable(
347-
.doc(method.documentation),
347+
.preFormatted(method.documentation),
348348
.function(FunctionDescription(signature: functionSignature))
349349
)
350350
}

Tests/GRPCCodeGenTests/Internal/Translator/ClientCodeTranslatorSnippetBasedTests.swift

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
2727

2828
func testClientCodeTranslatorUnaryMethod() throws {
2929
let method = MethodDescriptor(
30-
documentation: "Documentation for MethodA",
30+
documentation: "/// Documentation for MethodA",
3131
name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
3232
isInputStreaming: false,
3333
isOutputStreaming: false,
3434
inputType: "NamespaceA_ServiceARequest",
3535
outputType: "NamespaceA_ServiceAResponse"
3636
)
3737
let service = ServiceDescriptor(
38-
documentation: "Documentation for ServiceA",
38+
documentation: "/// Documentation for ServiceA",
3939
name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
4040
namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
4141
methods: [method]
@@ -98,15 +98,15 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
9898

9999
func testClientCodeTranslatorClientStreamingMethod() throws {
100100
let method = MethodDescriptor(
101-
documentation: "Documentation for MethodA",
101+
documentation: "/// Documentation for MethodA",
102102
name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
103103
isInputStreaming: true,
104104
isOutputStreaming: false,
105105
inputType: "NamespaceA_ServiceARequest",
106106
outputType: "NamespaceA_ServiceAResponse"
107107
)
108108
let service = ServiceDescriptor(
109-
documentation: "Documentation for ServiceA",
109+
documentation: "/// Documentation for ServiceA",
110110
name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
111111
namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
112112
methods: [method]
@@ -169,15 +169,15 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
169169

170170
func testClientCodeTranslatorServerStreamingMethod() throws {
171171
let method = MethodDescriptor(
172-
documentation: "Documentation for MethodA",
172+
documentation: "/// Documentation for MethodA",
173173
name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
174174
isInputStreaming: false,
175175
isOutputStreaming: true,
176176
inputType: "NamespaceA_ServiceARequest",
177177
outputType: "NamespaceA_ServiceAResponse"
178178
)
179179
let service = ServiceDescriptor(
180-
documentation: "Documentation for ServiceA",
180+
documentation: "/// Documentation for ServiceA",
181181
name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
182182
namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
183183
methods: [method]
@@ -240,15 +240,15 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
240240

241241
func testClientCodeTranslatorBidirectionalStreamingMethod() throws {
242242
let method = MethodDescriptor(
243-
documentation: "Documentation for MethodA",
243+
documentation: "/// Documentation for MethodA",
244244
name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
245245
isInputStreaming: true,
246246
isOutputStreaming: true,
247247
inputType: "NamespaceA_ServiceARequest",
248248
outputType: "NamespaceA_ServiceAResponse"
249249
)
250250
let service = ServiceDescriptor(
251-
documentation: "Documentation for ServiceA",
251+
documentation: "/// Documentation for ServiceA",
252252
name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
253253
namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
254254
methods: [method]
@@ -311,23 +311,23 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
311311

312312
func testClientCodeTranslatorMultipleMethods() throws {
313313
let methodA = MethodDescriptor(
314-
documentation: "Documentation for MethodA",
314+
documentation: "/// Documentation for MethodA",
315315
name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
316316
isInputStreaming: true,
317317
isOutputStreaming: false,
318318
inputType: "NamespaceA_ServiceARequest",
319319
outputType: "NamespaceA_ServiceAResponse"
320320
)
321321
let methodB = MethodDescriptor(
322-
documentation: "Documentation for MethodB",
322+
documentation: "/// Documentation for MethodB",
323323
name: Name(base: "MethodB", generatedUpperCase: "MethodB", generatedLowerCase: "methodB"),
324324
isInputStreaming: false,
325325
isOutputStreaming: true,
326326
inputType: "NamespaceA_ServiceARequest",
327327
outputType: "NamespaceA_ServiceAResponse"
328328
)
329329
let service = ServiceDescriptor(
330-
documentation: "Documentation for ServiceA",
330+
documentation: "/// Documentation for ServiceA",
331331
name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
332332
namespace: Name(base: "namespaceA", generatedUpperCase: "NamespaceA", generatedLowerCase: ""),
333333
methods: [methodA, methodB]
@@ -423,15 +423,15 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
423423

424424
func testClientCodeTranslatorNoNamespaceService() throws {
425425
let method = MethodDescriptor(
426-
documentation: "Documentation for MethodA",
426+
documentation: "/// Documentation for MethodA",
427427
name: Name(base: "MethodA", generatedUpperCase: "MethodA", generatedLowerCase: "methodA"),
428428
isInputStreaming: false,
429429
isOutputStreaming: false,
430430
inputType: "ServiceARequest",
431431
outputType: "ServiceAResponse"
432432
)
433433
let service = ServiceDescriptor(
434-
documentation: "Documentation for ServiceA",
434+
documentation: "/// Documentation for ServiceA",
435435
name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
436436
namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""),
437437
methods: [method]
@@ -494,7 +494,7 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
494494

495495
func testClientCodeTranslatorMultipleServices() throws {
496496
let serviceA = ServiceDescriptor(
497-
documentation: "Documentation for ServiceA",
497+
documentation: "/// Documentation for ServiceA",
498498
name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: ""),
499499
namespace: Name(
500500
base: "nammespaceA",
@@ -504,7 +504,11 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
504504
methods: []
505505
)
506506
let serviceB = ServiceDescriptor(
507-
documentation: "Documentation for ServiceB",
507+
documentation: """
508+
/// Documentation for ServiceB
509+
///
510+
/// Line 2
511+
""",
508512
name: Name(base: "ServiceB", generatedUpperCase: "ServiceB", generatedLowerCase: ""),
509513
namespace: Name(base: "", generatedUpperCase: "", generatedLowerCase: ""),
510514
methods: []
@@ -523,10 +527,14 @@ final class ClientCodeTranslatorSnippetBasedTests: XCTestCase {
523527
}
524528
}
525529
/// Documentation for ServiceB
530+
///
531+
/// Line 2
526532
public protocol ServiceBClientProtocol: Sendable {}
527533
extension ServiceB.ClientProtocol {
528534
}
529535
/// Documentation for ServiceB
536+
///
537+
/// Line 2
530538
public struct ServiceBClient: ServiceB.ClientProtocol {
531539
private let client: GRPCCore.GRPCClient
532540
public init(client: GRPCCore.GRPCClient) {

0 commit comments

Comments
 (0)