From 83ba9f3a695753f727ccdd624610f7d8fdf80971 Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Mon, 29 Jan 2024 18:14:26 +0000 Subject: [PATCH 1/6] Update renderer - add blank line after imports, delete blank line from docs Motivation: We want to have a blank line between the imports and the generated code and no blank line between the docs and the protocols/methods declarations. Modifications: Updated the TextBasedRenderer and the tests. Result: The generated code will have the right format. --- .../Internal/Renderer/TextBasedRenderer.swift | 15 ++++++++++++--- .../Renderer/TextBasedRendererTests.swift | 14 ++++++++++++++ ...ructuredSwiftTranslatorSnippetBasedTests.swift | 3 +++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift index 3fd683720..af032fa8b 100644 --- a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift +++ b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift @@ -147,7 +147,7 @@ struct TextBasedRenderer: RendererProtocol { /// Renders the specified comment. func renderComment(_ comment: Comment) { let prefix: String - let commentString: String + var commentString: String switch comment { case .inline(let string): prefix = "//" @@ -166,6 +166,7 @@ struct TextBasedRenderer: RendererProtocol { commentString = string } if prefix.isEmpty { + while commentString.hasSuffix("\n") { commentString.removeLast() } writer.writeLine(commentString) } else { let lines = commentString.transformingLines { line in @@ -177,10 +178,18 @@ struct TextBasedRenderer: RendererProtocol { } /// Renders the specified import statements. - func renderImports(_ imports: [ImportDescription]?) { (imports ?? []).forEach(renderImport) } + func renderImports(_ imports: [ImportDescription]?) { + for (`import`, isLast) in (imports ?? []).enumeratedWithLastMarker() { + renderImport(`import`, isLast: isLast) + if isLast { + writer.nextLineAppendsToLastLine() + writer.writeLine("\n") + } + } + } /// Renders a single import statement. - func renderImport(_ description: ImportDescription) { + func renderImport(_ description: ImportDescription, isLast: Bool) { func render(preconcurrency: Bool) { let spiPrefix = description.spi.map { "@_spi(\($0)) " } ?? "" let preconcurrencyPrefix = preconcurrency ? "@preconcurrency " : "" diff --git a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift index bae3b2632..fc2a8ecf5 100644 --- a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift @@ -89,6 +89,13 @@ final class Test_TextBasedRenderer: XCTestCase { // Also, bar """# ) + try _test( + .preFormatted("/// Lorem ipsum\n"), + renderedBy: TextBasedRenderer.renderComment, + rendersAs: """ + /// Lorem ipsum + """ + ) } func testImports() throws { @@ -99,6 +106,7 @@ final class Test_TextBasedRenderer: XCTestCase { rendersAs: #""" import Foo import Bar + """# ) try _test( @@ -106,6 +114,7 @@ final class Test_TextBasedRenderer: XCTestCase { renderedBy: TextBasedRenderer.renderImports, rendersAs: #""" @_spi(Secret) import Foo + """# ) try _test( @@ -117,6 +126,7 @@ final class Test_TextBasedRenderer: XCTestCase { #else import Foo #endif + """# ) try _test( @@ -128,6 +138,7 @@ final class Test_TextBasedRenderer: XCTestCase { rendersAs: #""" @preconcurrency import Foo @preconcurrency @_spi(Secret) import Bar + """# ) @@ -182,6 +193,7 @@ final class Test_TextBasedRenderer: XCTestCase { import func Foo.Bak @_spi(Secret) import func Foo.SecretBar @preconcurrency import func Foo.PreconcurrencyBar + """# ) } @@ -826,6 +838,7 @@ final class Test_TextBasedRenderer: XCTestCase { rendersAs: #""" // hi import Foo + struct Bar {} """# @@ -848,6 +861,7 @@ final class Test_TextBasedRenderer: XCTestCase { rendersAs: #""" // hi import Foo + struct Bar { struct Baz {} } diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift index 3dfec1358..aefe6a864 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift @@ -66,6 +66,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { import let Foo.Baq import var Foo.Bag import func Foo.Bak + """ try self.assertIDLToStructuredSwiftTranslation( codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies), @@ -101,6 +102,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { #else import Baz #endif + """ try self.assertIDLToStructuredSwiftTranslation( codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies), @@ -126,6 +128,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { import GRPCCore @_spi(Secret) import Foo @_spi(Secret) import enum Foo.Bar + """ try self.assertIDLToStructuredSwiftTranslation( codeGenerationRequest: makeCodeGenerationRequest(dependencies: dependencies), From 1f5c9b68a5f2fc7a2a341c0167193c44a989d2d1 Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Tue, 30 Jan 2024 15:24:18 +0000 Subject: [PATCH 2/6] CodeBlockItem for empty line + drop blank line in preformatted documentation --- .../Internal/Renderer/TextBasedRenderer.swift | 44 +++++++++++-------- .../StructuredSwiftRepresentation.swift | 10 +++++ .../Translator/TypealiasTranslator.swift | 2 +- .../Renderer/TextBasedRendererTests.swift | 7 --- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift index af032fa8b..32540c0f4 100644 --- a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift +++ b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift @@ -102,6 +102,11 @@ final class StringCodeWriter { /// /// Safe to call repeatedly, it gets reset by `writeLine`. func nextLineAppendsToLastLine() { nextWriteAppendsToLastLine = true } + + /// Sets a flag on the writer so that the next call to `writeLine` starts a new line. + /// + /// Safe to call repeatedly, it gets reset by `writeLine`. + func nextLineOnNewLine() { nextWriteAppendsToLastLine = false } } /// A renderer that uses string interpolation and concatenation @@ -165,31 +170,29 @@ struct TextBasedRenderer: RendererProtocol { prefix = "" commentString = string } - if prefix.isEmpty { - while commentString.hasSuffix("\n") { commentString.removeLast() } - writer.writeLine(commentString) - } else { - let lines = commentString.transformingLines { line in - if line.isEmpty { return prefix } - return "\(prefix) \(line)" + + let lines = commentString.transformingLines { line in + if line.isEmpty { + if !prefix.isEmpty { + return prefix + } else { + // A blank line that should be dropped - pre formatted documentation + // might contain such lines. + return nil + } + } else { + let formattedPrefix = !prefix.isEmpty ? "\(prefix) " : prefix + return "\(formattedPrefix)\(line)" } - lines.forEach(writer.writeLine) } + lines.forEach(writer.writeLine) } /// Renders the specified import statements. - func renderImports(_ imports: [ImportDescription]?) { - for (`import`, isLast) in (imports ?? []).enumeratedWithLastMarker() { - renderImport(`import`, isLast: isLast) - if isLast { - writer.nextLineAppendsToLastLine() - writer.writeLine("\n") - } - } - } + func renderImports(_ imports: [ImportDescription]?) { (imports ?? []).forEach(renderImport) } /// Renders a single import statement. - func renderImport(_ description: ImportDescription, isLast: Bool) { + func renderImport(_ description: ImportDescription) { func render(preconcurrency: Bool) { let spiPrefix = description.spi.map { "@_spi(\($0)) " } ?? "" let preconcurrencyPrefix = preconcurrency ? "@preconcurrency " : "" @@ -1058,6 +1061,7 @@ struct TextBasedRenderer: RendererProtocol { switch description { case .declaration(let declaration): renderDeclaration(declaration) case .expression(let expression): renderExpression(expression) + case .emptyLine: writer.nextLineOnNewLine() } } @@ -1104,7 +1108,9 @@ extension String { /// The closure takes a string representing one line as a parameter. /// - Parameter work: The closure that transforms each line. /// - Returns: A new string where each line has been transformed using the given closure. - fileprivate func transformingLines(_ work: (String) -> String) -> [String] { asLines().map(work) } + fileprivate func transformingLines(_ work: (String) -> String?) -> [String] { + asLines().compactMap(work) + } } extension TextBasedRenderer { diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift index 7a91fcd94..1b273beb0 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift @@ -1141,6 +1141,9 @@ enum CodeBlockItem: Equatable, Codable { /// An expression, such as a call of a declared function. case expression(Expression) + + /// A blank line between code blocks. + case emptyLine } /// A code block, with an optional comment. @@ -1437,6 +1440,13 @@ extension CodeBlock { static func expression(_ expression: Expression) -> Self { CodeBlock(item: .expression(expression)) } + + /// Returns a new code block wrapping an empty line. + /// - Returns: A new `CodeBlock` instance containing a blank line. + static func emptyLine() -> Self { + CodeBlock(item: .emptyLine) + } + } extension Expression { diff --git a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift index 308aa83a9..d75d8611b 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift @@ -64,7 +64,7 @@ struct TypealiasTranslator: SpecializedTranslator { } func translate(from codeGenerationRequest: CodeGenerationRequest) throws -> [CodeBlock] { - var codeBlocks: [CodeBlock] = [] + var codeBlocks: [CodeBlock] = [.emptyLine()] let services = codeGenerationRequest.services let servicesByNamespace = Dictionary( grouping: services, diff --git a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift index fc2a8ecf5..492cfcc95 100644 --- a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift @@ -106,7 +106,6 @@ final class Test_TextBasedRenderer: XCTestCase { rendersAs: #""" import Foo import Bar - """# ) try _test( @@ -114,7 +113,6 @@ final class Test_TextBasedRenderer: XCTestCase { renderedBy: TextBasedRenderer.renderImports, rendersAs: #""" @_spi(Secret) import Foo - """# ) try _test( @@ -126,7 +124,6 @@ final class Test_TextBasedRenderer: XCTestCase { #else import Foo #endif - """# ) try _test( @@ -138,7 +135,6 @@ final class Test_TextBasedRenderer: XCTestCase { rendersAs: #""" @preconcurrency import Foo @preconcurrency @_spi(Secret) import Bar - """# ) @@ -193,7 +189,6 @@ final class Test_TextBasedRenderer: XCTestCase { import func Foo.Bak @_spi(Secret) import func Foo.SecretBar @preconcurrency import func Foo.PreconcurrencyBar - """# ) } @@ -838,7 +833,6 @@ final class Test_TextBasedRenderer: XCTestCase { rendersAs: #""" // hi import Foo - struct Bar {} """# @@ -861,7 +855,6 @@ final class Test_TextBasedRenderer: XCTestCase { rendersAs: #""" // hi import Foo - struct Bar { struct Baz {} } From 884cd4982ad0b0c6b515ba1b7a63562899f58553 Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Tue, 30 Jan 2024 15:58:37 +0000 Subject: [PATCH 3/6] small fixtures --- Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift | 2 +- .../GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift index 32540c0f4..36d1e1b34 100644 --- a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift +++ b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift @@ -103,7 +103,7 @@ final class StringCodeWriter { /// Safe to call repeatedly, it gets reset by `writeLine`. func nextLineAppendsToLastLine() { nextWriteAppendsToLastLine = true } - /// Sets a flag on the writer so that the next call to `writeLine` starts a new line. + /// Sets a flag on the writer so that the next call to `writeLine` starts on a new line. /// /// Safe to call repeatedly, it gets reset by `writeLine`. func nextLineOnNewLine() { nextWriteAppendsToLastLine = false } diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift index 1b273beb0..a36453af0 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift @@ -1446,7 +1446,6 @@ extension CodeBlock { static func emptyLine() -> Self { CodeBlock(item: .emptyLine) } - } extension Expression { From 9e6ca51fb771b1d8ec83c6cfcacaaef8bdc8a7b5 Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Tue, 30 Jan 2024 17:29:17 +0000 Subject: [PATCH 4/6] implemented feedback --- .../Internal/Renderer/TextBasedRenderer.swift | 33 +++++++------------ .../StructuredSwiftRepresentation.swift | 5 ++- .../IDLToStructuredSwiftTranslator.swift | 2 +- .../Translator/TypealiasTranslator.swift | 2 +- .../Renderer/TextBasedRendererTests.swift | 19 +++++++++++ 5 files changed, 35 insertions(+), 26 deletions(-) diff --git a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift index 36d1e1b34..aad543331 100644 --- a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift +++ b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift @@ -98,15 +98,10 @@ final class StringCodeWriter { } /// Sets a flag on the writer so that the next call to `writeLine` continues - /// the last stored line instead of starting a new line. + /// the last stored line or starts on a new line. /// /// Safe to call repeatedly, it gets reset by `writeLine`. - func nextLineAppendsToLastLine() { nextWriteAppendsToLastLine = true } - - /// Sets a flag on the writer so that the next call to `writeLine` starts on a new line. - /// - /// Safe to call repeatedly, it gets reset by `writeLine`. - func nextLineOnNewLine() { nextWriteAppendsToLastLine = false } + func nextLineAppendsToLastLine(_ append: Bool = true) { nextWriteAppendsToLastLine = append } } /// A renderer that uses string interpolation and concatenation @@ -152,7 +147,7 @@ struct TextBasedRenderer: RendererProtocol { /// Renders the specified comment. func renderComment(_ comment: Comment) { let prefix: String - var commentString: String + let commentString: String switch comment { case .inline(let string): prefix = "//" @@ -171,17 +166,13 @@ struct TextBasedRenderer: RendererProtocol { commentString = string } - let lines = commentString.transformingLines { line in - if line.isEmpty { - if !prefix.isEmpty { - return prefix - } else { - // A blank line that should be dropped - pre formatted documentation - // might contain such lines. - return nil - } + let lines = commentString.transformingLines { line, isLast in + // The last line of a comment that is blank should be dropped. + // Pre formatted documentation might contain such lines. + if line.isEmpty && prefix.isEmpty && isLast { + return nil } else { - let formattedPrefix = !prefix.isEmpty ? "\(prefix) " : prefix + let formattedPrefix = !prefix.isEmpty && !line.isEmpty ? "\(prefix) " : prefix return "\(formattedPrefix)\(line)" } } @@ -1061,7 +1052,7 @@ struct TextBasedRenderer: RendererProtocol { switch description { case .declaration(let declaration): renderDeclaration(declaration) case .expression(let expression): renderExpression(expression) - case .emptyLine: writer.nextLineOnNewLine() + case .emptyLine: writer.nextLineAppendsToLastLine(false) } } @@ -1108,8 +1099,8 @@ extension String { /// The closure takes a string representing one line as a parameter. /// - Parameter work: The closure that transforms each line. /// - Returns: A new string where each line has been transformed using the given closure. - fileprivate func transformingLines(_ work: (String) -> String?) -> [String] { - asLines().compactMap(work) + fileprivate func transformingLines(_ work: (String, Bool) -> String?) -> [String] { + asLines().enumeratedWithLastMarker().compactMap(work) } } diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift index a36453af0..4a6e04ac3 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift @@ -1441,9 +1441,8 @@ extension CodeBlock { CodeBlock(item: .expression(expression)) } - /// Returns a new code block wrapping an empty line. - /// - Returns: A new `CodeBlock` instance containing a blank line. - static func emptyLine() -> Self { + /// A new code block wrapping an empty line. + static var emptyLine: Self { CodeBlock(item: .emptyLine) } } diff --git a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift index b35a85d1b..d42cd9107 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift @@ -38,7 +38,7 @@ struct IDLToStructuredSwiftTranslator: Translator { try partialResult.append(translateImport(dependency: newDependency)) } - var codeBlocks: [CodeBlock] = [] + var codeBlocks: [CodeBlock] = [.emptyLine] codeBlocks.append( contentsOf: try typealiasTranslator.translate(from: codeGenerationRequest) ) diff --git a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift index d75d8611b..49a92fa33 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift @@ -64,7 +64,7 @@ struct TypealiasTranslator: SpecializedTranslator { } func translate(from codeGenerationRequest: CodeGenerationRequest) throws -> [CodeBlock] { - var codeBlocks: [CodeBlock] = [.emptyLine()] + var codeBlocks = [CodeBlock]() let services = codeGenerationRequest.services let servicesByNamespace = Dictionary( grouping: services, diff --git a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift index 492cfcc95..a7e9a8000 100644 --- a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift @@ -96,6 +96,15 @@ final class Test_TextBasedRenderer: XCTestCase { /// Lorem ipsum """ ) + try _test( + .preFormatted("/// Lorem ipsum\n\n/// Lorem ipsum\n"), + renderedBy: TextBasedRenderer.renderComment, + rendersAs: """ + /// Lorem ipsum + + /// Lorem ipsum + """ + ) } func testImports() throws { @@ -363,6 +372,16 @@ final class Test_TextBasedRenderer: XCTestCase { ) } + func testEmptyLine() throws { + try _test( + .emptyLine, + renderedBy: TextBasedRenderer.renderCodeBlockItem(_:), + rendersAs: #""" + + """# + ) + } + func testFunctionKind() throws { try _test( .initializer, From c2c36d4587c08f6a1c4a5c808579538cae9745e9 Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Wed, 31 Jan 2024 11:19:24 +0000 Subject: [PATCH 5/6] moved empty line logic to the renderer - changed how it is added --- .../Internal/Renderer/TextBasedRenderer.swift | 11 ++- .../StructuredSwiftRepresentation.swift | 8 -- .../IDLToStructuredSwiftTranslator.swift | 2 +- .../Renderer/TextBasedRendererTests.swift | 14 +--- ...uredSwiftTranslatorSnippetBasedTests.swift | 78 ++++++++++++++++++- .../Internal/Translator/TestFunctions.swift | 24 +----- 6 files changed, 91 insertions(+), 46 deletions(-) diff --git a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift index aad543331..fd1100011 100644 --- a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift +++ b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift @@ -136,8 +136,14 @@ struct TextBasedRenderer: RendererProtocol { /// Renders the specified Swift file. func renderFile(_ description: FileDescription) { - if let topComment = description.topComment { renderComment(topComment) } - if let imports = description.imports { renderImports(imports) } + if let topComment = description.topComment { + renderComment(topComment) + writer.writeLine("") + } + if let imports = description.imports { + renderImports(imports) + writer.writeLine("") + } for codeBlock in description.codeBlocks { renderCodeBlock(codeBlock) writer.writeLine("") @@ -1052,7 +1058,6 @@ struct TextBasedRenderer: RendererProtocol { switch description { case .declaration(let declaration): renderDeclaration(declaration) case .expression(let expression): renderExpression(expression) - case .emptyLine: writer.nextLineAppendsToLastLine(false) } } diff --git a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift index 4a6e04ac3..7a91fcd94 100644 --- a/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift +++ b/Sources/GRPCCodeGen/Internal/StructuredSwiftRepresentation.swift @@ -1141,9 +1141,6 @@ enum CodeBlockItem: Equatable, Codable { /// An expression, such as a call of a declared function. case expression(Expression) - - /// A blank line between code blocks. - case emptyLine } /// A code block, with an optional comment. @@ -1440,11 +1437,6 @@ extension CodeBlock { static func expression(_ expression: Expression) -> Self { CodeBlock(item: .expression(expression)) } - - /// A new code block wrapping an empty line. - static var emptyLine: Self { - CodeBlock(item: .emptyLine) - } } extension Expression { diff --git a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift index d42cd9107..e195b45b4 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift @@ -38,7 +38,7 @@ struct IDLToStructuredSwiftTranslator: Translator { try partialResult.append(translateImport(dependency: newDependency)) } - var codeBlocks: [CodeBlock] = [.emptyLine] + var codeBlocks = [CodeBlock]() codeBlocks.append( contentsOf: try typealiasTranslator.translate(from: codeGenerationRequest) ) diff --git a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift index a7e9a8000..6d40aa56e 100644 --- a/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Renderer/TextBasedRendererTests.swift @@ -372,16 +372,6 @@ final class Test_TextBasedRenderer: XCTestCase { ) } - func testEmptyLine() throws { - try _test( - .emptyLine, - renderedBy: TextBasedRenderer.renderCodeBlockItem(_:), - rendersAs: #""" - - """# - ) - } - func testFunctionKind() throws { try _test( .initializer, @@ -851,7 +841,9 @@ final class Test_TextBasedRenderer: XCTestCase { renderedBy: TextBasedRenderer.renderFile, rendersAs: #""" // hi + import Foo + struct Bar {} """# @@ -873,7 +865,9 @@ final class Test_TextBasedRenderer: XCTestCase { renderedBy: TextBasedRenderer.renderFile, rendersAs: #""" // hi + import Foo + struct Bar { struct Baz {} } diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift index aefe6a864..58a10cb3d 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/IDLToStructuredSwiftTranslatorSnippetBasedTests.swift @@ -56,6 +56,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { let expectedSwift = """ /// Some really exciting license header 2023. + import GRPCCore import Foo import typealias Foo.Bar @@ -94,6 +95,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { let expectedSwift = """ /// Some really exciting license header 2023. + import GRPCCore @preconcurrency import Foo @preconcurrency import enum Foo.Bar @@ -125,6 +127,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { let expectedSwift = """ /// Some really exciting license header 2023. + import GRPCCore @_spi(Secret) import Foo @_spi(Secret) import enum Foo.Bar @@ -137,17 +140,84 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { ) } + func testGeneration() throws { + var dependencies = [CodeGenerationRequest.Dependency]() + dependencies.append(CodeGenerationRequest.Dependency(module: "Foo", spi: "Secret")) + dependencies.append( + CodeGenerationRequest.Dependency( + item: .init(kind: .enum, name: "Bar"), + module: "Foo", + spi: "Secret" + ) + ) + + let serviceA = ServiceDescriptor( + documentation: "/// Documentation for AService\n", + name: Name(base: "ServiceA", generatedUpperCase: "ServiceA", generatedLowerCase: "serviceA"), + namespace: Name( + base: "namespaceA", + generatedUpperCase: "NamespaceA", + generatedLowerCase: "namespaceA" + ), + methods: [] + ) + + let expectedSwift = + """ + /// Some really exciting license header 2023. + + import GRPCCore + @_spi(Secret) import Foo + @_spi(Secret) import enum Foo.Bar + + public enum NamespaceA { + public enum ServiceA { + public enum Methods {} + public static let methods: [MethodDescriptor] = [] + public typealias StreamingServiceProtocol = NamespaceA_ServiceAServiceStreamingProtocol + public typealias ServiceProtocol = NamespaceA_ServiceAServiceProtocol + } + } + + /// Documentation for AService + public protocol NamespaceA_ServiceAStreamingServiceProtocol: GRPCCore.RegistrableRPCService {} + + /// Conformance to `GRPCCore.RegistrableRPCService`. + extension NamespaceA.ServiceA.StreamingServiceProtocol { + public func registerMethods(with router: inout GRPCCore.RPCRouter) {} + } + + /// Documentation for AService + public protocol NamespaceA_ServiceAServiceProtocol: NamespaceA.ServiceA.StreamingServiceProtocol {} + + /// Partial conformance to `NamespaceA_ServiceAStreamingServiceProtocol`. + extension NamespaceA.ServiceA.ServiceProtocol { + } + + """ + try self.assertIDLToStructuredSwiftTranslation( + codeGenerationRequest: makeCodeGenerationRequest( + services: [serviceA], + dependencies: dependencies + ), + expectedSwift: expectedSwift, + accessLevel: .public, + server: true + ) + } + private func assertIDLToStructuredSwiftTranslation( codeGenerationRequest: CodeGenerationRequest, expectedSwift: String, - accessLevel: SourceGenerator.Configuration.AccessLevel + accessLevel: SourceGenerator.Configuration.AccessLevel, + server: Bool = false ) throws { let translator = IDLToStructuredSwiftTranslator() let structuredSwift = try translator.translate( codeGenerationRequest: codeGenerationRequest, accessLevel: accessLevel, client: false, - server: false + server: server ) let renderer = TextBasedRenderer.default let sourceFile = try renderer.render(structured: structuredSwift) @@ -265,7 +335,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { func testSameGeneratedNameServicesSameNamespaceError() throws { let serviceA = ServiceDescriptor( - documentation: "Documentation for AService", + documentation: "/// Documentation for AService\n", name: Name(base: "AService", generatedUpperCase: "AService", generatedLowerCase: "aService"), namespace: Name( base: "namespacea", @@ -275,7 +345,7 @@ final class IDLToStructuredSwiftTranslatorSnippetBasedTests: XCTestCase { methods: [] ) let serviceB = ServiceDescriptor( - documentation: "Documentation for BService", + documentation: "/// Documentation for BService\n", name: Name(base: "BService", generatedUpperCase: "AService", generatedLowerCase: "aService"), namespace: Name( base: "namespacea", diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/TestFunctions.swift b/Tests/GRPCCodeGenTests/Internal/Translator/TestFunctions.swift index 800ff7393..fd5808816 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/TestFunctions.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/TestFunctions.swift @@ -72,30 +72,14 @@ internal func XCTAssertEqualWithDiff( } internal func makeCodeGenerationRequest( - services: [CodeGenerationRequest.ServiceDescriptor] + services: [CodeGenerationRequest.ServiceDescriptor] = [], + dependencies: [CodeGenerationRequest.Dependency] = [] ) -> CodeGenerationRequest { return CodeGenerationRequest( fileName: "test.grpc", - leadingTrivia: "/// Some really exciting license header 2023.", - dependencies: [], - services: services, - lookupSerializer: { - "ProtobufSerializer<\($0)>()" - }, - lookupDeserializer: { - "ProtobufDeserializer<\($0)>()" - } - ) -} - -internal func makeCodeGenerationRequest( - dependencies: [CodeGenerationRequest.Dependency] -) -> CodeGenerationRequest { - return CodeGenerationRequest( - fileName: "test.grpc", - leadingTrivia: "/// Some really exciting license header 2023.", + leadingTrivia: "/// Some really exciting license header 2023.\n", dependencies: dependencies, - services: [], + services: services, lookupSerializer: { "ProtobufSerializer<\($0)>()" }, From 403942e24d7a977ea952a3ddec6d8511486931d9 Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Wed, 31 Jan 2024 13:32:25 +0000 Subject: [PATCH 6/6] removed unnecessary change --- Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift index fd1100011..3c9b1845c 100644 --- a/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift +++ b/Sources/GRPCCodeGen/Internal/Renderer/TextBasedRenderer.swift @@ -98,10 +98,10 @@ final class StringCodeWriter { } /// Sets a flag on the writer so that the next call to `writeLine` continues - /// the last stored line or starts on a new line. + /// the last stored line instead of starting a new line. /// /// Safe to call repeatedly, it gets reset by `writeLine`. - func nextLineAppendsToLastLine(_ append: Bool = true) { nextWriteAppendsToLastLine = append } + func nextLineAppendsToLastLine() { nextWriteAppendsToLastLine = true } } /// A renderer that uses string interpolation and concatenation