TextBasedRenderer.swift 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096
  1. /*
  2. * Copyright 2023, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. //===----------------------------------------------------------------------===//
  17. //
  18. // This source file is part of the SwiftOpenAPIGenerator open source project
  19. //
  20. // Copyright (c) 2023 Apple Inc. and the SwiftOpenAPIGenerator project authors
  21. // Licensed under Apache License v2.0
  22. //
  23. // See LICENSE.txt for license information
  24. // See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
  25. //
  26. // SPDX-License-Identifier: Apache-2.0
  27. //
  28. //===----------------------------------------------------------------------===//
  29. import Foundation
  30. /// An object for building up a generated file line-by-line.
  31. ///
  32. /// After creation, make calls such as `writeLine` to build up the file,
  33. /// and call `rendered` at the end to get the full file contents.
  34. final class StringCodeWriter {
  35. /// The stored lines of code.
  36. private var lines: [String]
  37. /// The current nesting level.
  38. private var level: Int
  39. /// Whether the next call to `writeLine` will continue writing to the last
  40. /// stored line. Otherwise a new line is appended.
  41. private var nextWriteAppendsToLastLine: Bool = false
  42. /// Creates a new empty writer.
  43. init() {
  44. self.level = 0
  45. self.lines = []
  46. }
  47. /// Concatenates the stored lines of code into a single string.
  48. /// - Returns: The contents of the full file in a single string.
  49. func rendered() -> String { lines.joined(separator: "\n") }
  50. /// Writes a line of code.
  51. ///
  52. /// By default, a new line is appended to the file.
  53. ///
  54. /// To continue the last line, make a call to `nextLineAppendsToLastLine`
  55. /// before calling `writeLine`.
  56. /// - Parameter line: The contents of the line to write.
  57. func writeLine(_ line: String) {
  58. let newLine: String
  59. if nextWriteAppendsToLastLine && !lines.isEmpty {
  60. let existingLine = lines.removeLast()
  61. newLine = existingLine + line
  62. } else {
  63. let indentation = Array(repeating: " ", count: 4 * level).joined()
  64. newLine = indentation + line
  65. }
  66. lines.append(newLine)
  67. nextWriteAppendsToLastLine = false
  68. }
  69. /// Increases the indentation level by 1.
  70. func push() { level += 1 }
  71. /// Decreases the indentation level by 1.
  72. /// - Precondition: Current level must be greater than 0.
  73. func pop() {
  74. precondition(level > 0, "Cannot pop below 0")
  75. level -= 1
  76. }
  77. /// Executes the provided closure with one level deeper indentation.
  78. /// - Parameter work: The closure to execute.
  79. /// - Returns: The result of the closure execution.
  80. func withNestedLevel<R>(_ work: () -> R) -> R {
  81. push()
  82. defer { pop() }
  83. return work()
  84. }
  85. /// Sets a flag on the writer so that the next call to `writeLine` continues
  86. /// the last stored line instead of starting a new line.
  87. ///
  88. /// Safe to call repeatedly, it gets reset by `writeLine`.
  89. func nextLineAppendsToLastLine() { nextWriteAppendsToLastLine = true }
  90. }
  91. /// A renderer that uses string interpolation and concatenation
  92. /// to convert the provided structure code into raw string form.
  93. struct TextBasedRenderer: RendererProtocol {
  94. func render(
  95. structured: StructuredSwiftRepresentation
  96. ) throws
  97. -> SourceFile
  98. {
  99. let namedFile = structured.file
  100. renderFile(namedFile.contents)
  101. let string = writer.rendered()
  102. return SourceFile(name: namedFile.name, contents: string)
  103. }
  104. /// The underlying writer.
  105. private let writer: StringCodeWriter
  106. /// Creates a new empty renderer.
  107. static var `default`: TextBasedRenderer { .init(writer: StringCodeWriter()) }
  108. // MARK: - Internals
  109. /// Returns the current contents of the writer as a string.
  110. func renderedContents() -> String { writer.rendered() }
  111. /// Renders the specified Swift file.
  112. func renderFile(_ description: FileDescription) {
  113. if let topComment = description.topComment { renderComment(topComment) }
  114. if let imports = description.imports { renderImports(imports) }
  115. for codeBlock in description.codeBlocks {
  116. renderCodeBlock(codeBlock)
  117. writer.writeLine("")
  118. }
  119. }
  120. /// Renders the specified comment.
  121. func renderComment(_ comment: Comment) {
  122. let prefix: String
  123. let commentString: String
  124. switch comment {
  125. case .inline(let string):
  126. prefix = "//"
  127. commentString = string
  128. case .doc(let string):
  129. prefix = "///"
  130. commentString = string
  131. case .mark(let string, sectionBreak: true):
  132. prefix = "// MARK: -"
  133. commentString = string
  134. case .mark(let string, sectionBreak: false):
  135. prefix = "// MARK:"
  136. commentString = string
  137. }
  138. let lines = commentString.transformingLines { line in
  139. if line.isEmpty { return prefix }
  140. return "\(prefix) \(line)"
  141. }
  142. lines.forEach(writer.writeLine)
  143. }
  144. /// Renders the specified import statements.
  145. func renderImports(_ imports: [ImportDescription]?) { (imports ?? []).forEach(renderImport) }
  146. /// Renders a single import statement.
  147. func renderImport(_ description: ImportDescription) {
  148. func render(preconcurrency: Bool) {
  149. let spiPrefix = description.spi.map { "@_spi(\($0)) " } ?? ""
  150. let preconcurrencyPrefix = preconcurrency ? "@preconcurrency " : ""
  151. if let item = description.item {
  152. writer.writeLine(
  153. "\(preconcurrencyPrefix)\(spiPrefix)import \(item.kind) \(description.moduleName).\(item.name)"
  154. )
  155. } else if let moduleTypes = description.moduleTypes {
  156. for type in moduleTypes {
  157. writer.writeLine("\(preconcurrencyPrefix)\(spiPrefix)import \(type)")
  158. }
  159. } else {
  160. writer.writeLine("\(preconcurrencyPrefix)\(spiPrefix)import \(description.moduleName)")
  161. }
  162. }
  163. switch description.preconcurrency {
  164. case .always: render(preconcurrency: true)
  165. case .never: render(preconcurrency: false)
  166. case .onOS(let operatingSystems):
  167. writer.writeLine("#if \(operatingSystems.map { "os(\($0))" }.joined(separator: " || "))")
  168. render(preconcurrency: true)
  169. writer.writeLine("#else")
  170. render(preconcurrency: false)
  171. writer.writeLine("#endif")
  172. }
  173. }
  174. /// Renders the specified access modifier.
  175. func renderedAccessModifier(_ accessModifier: AccessModifier) -> String {
  176. switch accessModifier {
  177. case .public: return "public"
  178. case .package: return "package"
  179. case .internal: return "internal"
  180. case .fileprivate: return "fileprivate"
  181. case .private: return "private"
  182. }
  183. }
  184. /// Renders the specified identifier.
  185. func renderIdentifier(_ identifier: IdentifierDescription) {
  186. switch identifier {
  187. case .pattern(let string): writer.writeLine(string)
  188. case .type(let existingTypeDescription):
  189. renderExistingTypeDescription(existingTypeDescription)
  190. }
  191. }
  192. /// Renders the specified member access expression.
  193. func renderMemberAccess(_ memberAccess: MemberAccessDescription) {
  194. if let left = memberAccess.left {
  195. renderExpression(left)
  196. writer.nextLineAppendsToLastLine()
  197. }
  198. writer.writeLine(".\(memberAccess.right)")
  199. }
  200. /// Renders the specified function call argument.
  201. func renderFunctionCallArgument(_ arg: FunctionArgumentDescription) {
  202. if let left = arg.label {
  203. writer.writeLine("\(left): ")
  204. writer.nextLineAppendsToLastLine()
  205. }
  206. renderExpression(arg.expression)
  207. }
  208. /// Renders the specified function call.
  209. func renderFunctionCall(_ functionCall: FunctionCallDescription) {
  210. renderExpression(functionCall.calledExpression)
  211. writer.nextLineAppendsToLastLine()
  212. writer.writeLine("(")
  213. let arguments = functionCall.arguments
  214. if arguments.count > 1 {
  215. writer.withNestedLevel {
  216. for (argument, isLast) in arguments.enumeratedWithLastMarker() {
  217. renderFunctionCallArgument(argument)
  218. if !isLast {
  219. writer.nextLineAppendsToLastLine()
  220. writer.writeLine(",")
  221. }
  222. }
  223. }
  224. } else {
  225. writer.nextLineAppendsToLastLine()
  226. if let argument = arguments.first { renderFunctionCallArgument(argument) }
  227. writer.nextLineAppendsToLastLine()
  228. }
  229. writer.writeLine(")")
  230. if let trailingClosure = functionCall.trailingClosure {
  231. writer.nextLineAppendsToLastLine()
  232. writer.writeLine(" ")
  233. renderClosureInvocation(trailingClosure)
  234. }
  235. }
  236. /// Renders the specified assignment expression.
  237. func renderAssignment(_ assignment: AssignmentDescription) {
  238. renderExpression(assignment.left)
  239. writer.nextLineAppendsToLastLine()
  240. writer.writeLine(" = ")
  241. writer.nextLineAppendsToLastLine()
  242. renderExpression(assignment.right)
  243. }
  244. /// Renders the specified switch case kind.
  245. func renderSwitchCaseKind(_ kind: SwitchCaseKind) {
  246. switch kind {
  247. case let .`case`(expression, associatedValueNames):
  248. let associatedValues: String
  249. let maybeLet: String
  250. if !associatedValueNames.isEmpty {
  251. associatedValues = "(" + associatedValueNames.joined(separator: ", ") + ")"
  252. maybeLet = "let "
  253. } else {
  254. associatedValues = ""
  255. maybeLet = ""
  256. }
  257. writer.writeLine("case \(maybeLet)")
  258. writer.nextLineAppendsToLastLine()
  259. renderExpression(expression)
  260. writer.nextLineAppendsToLastLine()
  261. writer.writeLine(associatedValues)
  262. case .multiCase(let expressions):
  263. writer.writeLine("case ")
  264. writer.nextLineAppendsToLastLine()
  265. for (expression, isLast) in expressions.enumeratedWithLastMarker() {
  266. renderExpression(expression)
  267. writer.nextLineAppendsToLastLine()
  268. if !isLast { writer.writeLine(", ") }
  269. writer.nextLineAppendsToLastLine()
  270. }
  271. case .`default`: writer.writeLine("default")
  272. }
  273. }
  274. /// Renders the specified switch case.
  275. func renderSwitchCase(_ switchCase: SwitchCaseDescription) {
  276. renderSwitchCaseKind(switchCase.kind)
  277. writer.nextLineAppendsToLastLine()
  278. writer.writeLine(":")
  279. writer.withNestedLevel { renderCodeBlocks(switchCase.body) }
  280. }
  281. /// Renders the specified switch expression.
  282. func renderSwitch(_ switchDesc: SwitchDescription) {
  283. writer.writeLine("switch ")
  284. writer.nextLineAppendsToLastLine()
  285. renderExpression(switchDesc.switchedExpression)
  286. writer.nextLineAppendsToLastLine()
  287. writer.writeLine(" {")
  288. for caseDesc in switchDesc.cases { renderSwitchCase(caseDesc) }
  289. writer.writeLine("}")
  290. }
  291. /// Renders the specified if statement.
  292. func renderIf(_ ifDesc: IfStatementDescription) {
  293. let ifBranch = ifDesc.ifBranch
  294. writer.writeLine("if ")
  295. writer.nextLineAppendsToLastLine()
  296. renderExpression(ifBranch.condition)
  297. writer.nextLineAppendsToLastLine()
  298. writer.writeLine(" {")
  299. writer.withNestedLevel { renderCodeBlocks(ifBranch.body) }
  300. writer.writeLine("}")
  301. for branch in ifDesc.elseIfBranches {
  302. writer.nextLineAppendsToLastLine()
  303. writer.writeLine(" else if ")
  304. writer.nextLineAppendsToLastLine()
  305. renderExpression(branch.condition)
  306. writer.nextLineAppendsToLastLine()
  307. writer.writeLine(" {")
  308. writer.withNestedLevel { renderCodeBlocks(branch.body) }
  309. writer.writeLine("}")
  310. }
  311. if let elseBody = ifDesc.elseBody {
  312. writer.nextLineAppendsToLastLine()
  313. writer.writeLine(" else {")
  314. writer.withNestedLevel { renderCodeBlocks(elseBody) }
  315. writer.writeLine("}")
  316. }
  317. }
  318. /// Renders the specified switch expression.
  319. func renderDoStatement(_ description: DoStatementDescription) {
  320. writer.writeLine("do {")
  321. writer.withNestedLevel { renderCodeBlocks(description.doStatement) }
  322. if let catchBody = description.catchBody {
  323. writer.writeLine("} catch {")
  324. if !catchBody.isEmpty {
  325. writer.withNestedLevel { renderCodeBlocks(catchBody) }
  326. } else {
  327. writer.nextLineAppendsToLastLine()
  328. }
  329. }
  330. writer.writeLine("}")
  331. }
  332. /// Renders the specified value binding expression.
  333. func renderValueBinding(_ valueBinding: ValueBindingDescription) {
  334. writer.writeLine("\(renderedBindingKind(valueBinding.kind)) ")
  335. writer.nextLineAppendsToLastLine()
  336. renderFunctionCall(valueBinding.value)
  337. }
  338. /// Renders the specified keyword.
  339. func renderedKeywordKind(_ kind: KeywordKind) -> String {
  340. switch kind {
  341. case .return: return "return"
  342. case .try(hasPostfixQuestionMark: let hasPostfixQuestionMark):
  343. return "try\(hasPostfixQuestionMark ? "?" : "")"
  344. case .await: return "await"
  345. case .throw: return "throw"
  346. case .yield: return "yield"
  347. }
  348. }
  349. /// Renders the specified unary keyword expression.
  350. func renderUnaryKeywordExpression(_ expression: UnaryKeywordDescription) {
  351. writer.writeLine(renderedKeywordKind(expression.kind))
  352. guard let expr = expression.expression else { return }
  353. writer.nextLineAppendsToLastLine()
  354. writer.writeLine(" ")
  355. writer.nextLineAppendsToLastLine()
  356. renderExpression(expr)
  357. }
  358. /// Renders the specified closure invocation.
  359. func renderClosureInvocation(_ invocation: ClosureInvocationDescription) {
  360. writer.writeLine("{")
  361. if !invocation.argumentNames.isEmpty {
  362. writer.nextLineAppendsToLastLine()
  363. writer.writeLine(" \(invocation.argumentNames.joined(separator: ", ")) in")
  364. }
  365. if let body = invocation.body { writer.withNestedLevel { renderCodeBlocks(body) } }
  366. writer.writeLine("}")
  367. }
  368. /// Renders the specified binary operator.
  369. func renderedBinaryOperator(_ op: BinaryOperator) -> String { op.rawValue }
  370. /// Renders the specified binary operation.
  371. func renderBinaryOperation(_ operation: BinaryOperationDescription) {
  372. renderExpression(operation.left)
  373. writer.nextLineAppendsToLastLine()
  374. writer.writeLine(" \(renderedBinaryOperator(operation.operation)) ")
  375. writer.nextLineAppendsToLastLine()
  376. renderExpression(operation.right)
  377. }
  378. /// Renders the specified inout expression.
  379. func renderInOutDescription(_ description: InOutDescription) {
  380. writer.writeLine("&")
  381. writer.nextLineAppendsToLastLine()
  382. renderExpression(description.referencedExpr)
  383. }
  384. /// Renders the specified optional chaining expression.
  385. func renderOptionalChainingDescription(_ description: OptionalChainingDescription) {
  386. renderExpression(description.referencedExpr)
  387. writer.nextLineAppendsToLastLine()
  388. writer.writeLine("?")
  389. }
  390. /// Renders the specified tuple expression.
  391. func renderTupleDescription(_ description: TupleDescription) {
  392. writer.writeLine("(")
  393. writer.nextLineAppendsToLastLine()
  394. let members = description.members
  395. for (member, isLast) in members.enumeratedWithLastMarker() {
  396. renderExpression(member)
  397. if !isLast {
  398. writer.nextLineAppendsToLastLine()
  399. writer.writeLine(", ")
  400. }
  401. writer.nextLineAppendsToLastLine()
  402. }
  403. writer.writeLine(")")
  404. }
  405. /// Renders the specified expression.
  406. func renderExpression(_ expression: Expression) {
  407. switch expression {
  408. case .literal(let literalDescription): renderLiteral(literalDescription)
  409. case .identifier(let identifierDescription):
  410. renderIdentifier(identifierDescription)
  411. case .memberAccess(let memberAccessDescription): renderMemberAccess(memberAccessDescription)
  412. case .functionCall(let functionCallDescription): renderFunctionCall(functionCallDescription)
  413. case .assignment(let assignment): renderAssignment(assignment)
  414. case .switch(let switchDesc): renderSwitch(switchDesc)
  415. case .ifStatement(let ifDesc): renderIf(ifDesc)
  416. case .doStatement(let doStmt): renderDoStatement(doStmt)
  417. case .valueBinding(let valueBinding): renderValueBinding(valueBinding)
  418. case .unaryKeyword(let unaryKeyword): renderUnaryKeywordExpression(unaryKeyword)
  419. case .closureInvocation(let closureInvocation): renderClosureInvocation(closureInvocation)
  420. case .binaryOperation(let binaryOperation): renderBinaryOperation(binaryOperation)
  421. case .inOut(let inOut): renderInOutDescription(inOut)
  422. case .optionalChaining(let optionalChaining):
  423. renderOptionalChainingDescription(optionalChaining)
  424. case .tuple(let tuple): renderTupleDescription(tuple)
  425. }
  426. }
  427. /// Renders the specified literal expression.
  428. func renderLiteral(_ literal: LiteralDescription) {
  429. func write(_ string: String) { writer.writeLine(string) }
  430. switch literal {
  431. case let .string(string):
  432. // Use a raw literal if the string contains a quote/backslash.
  433. if string.contains("\"") || string.contains("\\") {
  434. write("#\"\(string)\"#")
  435. } else {
  436. write("\"\(string)\"")
  437. }
  438. case let .int(int): write("\(int)")
  439. case let .bool(bool): write(bool ? "true" : "false")
  440. case .nil: write("nil")
  441. case .array(let items):
  442. writer.writeLine("[")
  443. if !items.isEmpty {
  444. writer.withNestedLevel {
  445. for (item, isLast) in items.enumeratedWithLastMarker() {
  446. renderExpression(item)
  447. if !isLast {
  448. writer.nextLineAppendsToLastLine()
  449. writer.writeLine(",")
  450. }
  451. }
  452. }
  453. } else {
  454. writer.nextLineAppendsToLastLine()
  455. }
  456. writer.writeLine("]")
  457. }
  458. }
  459. /// Renders the specified where clause requirement.
  460. func renderedWhereClauseRequirement(_ requirement: WhereClauseRequirement) -> String {
  461. switch requirement {
  462. case .conformance(let left, let right): return "\(left): \(right)"
  463. }
  464. }
  465. /// Renders the specified where clause.
  466. func renderedWhereClause(_ clause: WhereClause) -> String {
  467. let renderedRequirements = clause.requirements.map(renderedWhereClauseRequirement)
  468. return "where \(renderedRequirements.joined(separator: ", "))"
  469. }
  470. /// Renders the specified extension declaration.
  471. func renderExtension(_ extensionDescription: ExtensionDescription) {
  472. if let accessModifier = extensionDescription.accessModifier {
  473. writer.writeLine(renderedAccessModifier(accessModifier) + " ")
  474. writer.nextLineAppendsToLastLine()
  475. }
  476. writer.writeLine("extension \(extensionDescription.onType)")
  477. writer.nextLineAppendsToLastLine()
  478. if !extensionDescription.conformances.isEmpty {
  479. writer.writeLine(": \(extensionDescription.conformances.joined(separator: ", "))")
  480. writer.nextLineAppendsToLastLine()
  481. }
  482. if let whereClause = extensionDescription.whereClause {
  483. writer.writeLine(" " + renderedWhereClause(whereClause))
  484. writer.nextLineAppendsToLastLine()
  485. }
  486. writer.writeLine(" {")
  487. for declaration in extensionDescription.declarations {
  488. writer.withNestedLevel { renderDeclaration(declaration) }
  489. }
  490. writer.writeLine("}")
  491. }
  492. /// Renders the specified type reference to an existing type.
  493. func renderExistingTypeDescription(_ type: ExistingTypeDescription) {
  494. switch type {
  495. case .any(let existingTypeDescription):
  496. writer.writeLine("any ")
  497. writer.nextLineAppendsToLastLine()
  498. renderExistingTypeDescription(existingTypeDescription)
  499. case .generic(let wrapper, let wrapped):
  500. renderExistingTypeDescription(wrapper)
  501. writer.nextLineAppendsToLastLine()
  502. writer.writeLine("<")
  503. writer.nextLineAppendsToLastLine()
  504. renderExistingTypeDescription(wrapped)
  505. writer.nextLineAppendsToLastLine()
  506. writer.writeLine(">")
  507. case .optional(let existingTypeDescription):
  508. renderExistingTypeDescription(existingTypeDescription)
  509. writer.nextLineAppendsToLastLine()
  510. writer.writeLine("?")
  511. case .member(let components):
  512. writer.writeLine(components.joined(separator: "."))
  513. case .array(let existingTypeDescription):
  514. writer.writeLine("[")
  515. writer.nextLineAppendsToLastLine()
  516. renderExistingTypeDescription(existingTypeDescription)
  517. writer.nextLineAppendsToLastLine()
  518. writer.writeLine("]")
  519. case .dictionaryValue(let existingTypeDescription):
  520. writer.writeLine("[String: ")
  521. writer.nextLineAppendsToLastLine()
  522. renderExistingTypeDescription(existingTypeDescription)
  523. writer.nextLineAppendsToLastLine()
  524. writer.writeLine("]")
  525. case .some(let existingTypeDescription):
  526. writer.writeLine("some ")
  527. writer.nextLineAppendsToLastLine()
  528. renderExistingTypeDescription(existingTypeDescription)
  529. case .closure(let closureSignatureDescription):
  530. renderClosureSignature(closureSignatureDescription)
  531. }
  532. }
  533. /// Renders the specified typealias declaration.
  534. func renderTypealias(_ alias: TypealiasDescription) {
  535. var words: [String] = []
  536. if let accessModifier = alias.accessModifier {
  537. words.append(renderedAccessModifier(accessModifier))
  538. }
  539. words.append(contentsOf: [
  540. "typealias", alias.name, "=",
  541. ])
  542. writer.writeLine(words.joinedWords() + " ")
  543. writer.nextLineAppendsToLastLine()
  544. renderExistingTypeDescription(alias.existingType)
  545. }
  546. /// Renders the specified binding kind.
  547. func renderedBindingKind(_ kind: BindingKind) -> String {
  548. switch kind {
  549. case .var: return "var"
  550. case .let: return "let"
  551. }
  552. }
  553. /// Renders the specified variable declaration.
  554. func renderVariable(_ variable: VariableDescription) {
  555. do {
  556. if let accessModifier = variable.accessModifier {
  557. writer.writeLine(renderedAccessModifier(accessModifier) + " ")
  558. writer.nextLineAppendsToLastLine()
  559. }
  560. if variable.isStatic {
  561. writer.writeLine("static ")
  562. writer.nextLineAppendsToLastLine()
  563. }
  564. writer.writeLine(renderedBindingKind(variable.kind) + " ")
  565. writer.nextLineAppendsToLastLine()
  566. renderExpression(variable.left)
  567. if let type = variable.type {
  568. writer.nextLineAppendsToLastLine()
  569. writer.writeLine(": ")
  570. writer.nextLineAppendsToLastLine()
  571. renderExistingTypeDescription(type)
  572. }
  573. }
  574. if let right = variable.right {
  575. writer.nextLineAppendsToLastLine()
  576. writer.writeLine(" = ")
  577. writer.nextLineAppendsToLastLine()
  578. renderExpression(right)
  579. }
  580. if let body = variable.getter {
  581. writer.nextLineAppendsToLastLine()
  582. writer.writeLine(" {")
  583. writer.withNestedLevel {
  584. let hasExplicitGetter =
  585. !variable.getterEffects.isEmpty || variable.setter != nil || variable.modify != nil
  586. if hasExplicitGetter {
  587. let keywords = variable.getterEffects.map(renderedFunctionKeyword).joined(separator: " ")
  588. let line = "get \(keywords) {"
  589. writer.writeLine(line)
  590. writer.push()
  591. }
  592. renderCodeBlocks(body)
  593. if hasExplicitGetter {
  594. writer.pop()
  595. writer.writeLine("}")
  596. }
  597. if let modify = variable.modify {
  598. writer.writeLine("_modify {")
  599. writer.withNestedLevel { renderCodeBlocks(modify) }
  600. writer.writeLine("}")
  601. }
  602. if let setter = variable.setter {
  603. writer.writeLine("set {")
  604. writer.withNestedLevel { renderCodeBlocks(setter) }
  605. writer.writeLine("}")
  606. }
  607. }
  608. writer.writeLine("}")
  609. }
  610. }
  611. /// Renders the specified struct declaration.
  612. func renderStruct(_ structDesc: StructDescription) {
  613. if let accessModifier = structDesc.accessModifier {
  614. writer.writeLine(renderedAccessModifier(accessModifier) + " ")
  615. writer.nextLineAppendsToLastLine()
  616. }
  617. writer.writeLine("struct \(structDesc.name)")
  618. writer.nextLineAppendsToLastLine()
  619. if !structDesc.conformances.isEmpty {
  620. writer.writeLine(": \(structDesc.conformances.joined(separator: ", "))")
  621. writer.nextLineAppendsToLastLine()
  622. }
  623. writer.writeLine(" {")
  624. if !structDesc.members.isEmpty {
  625. writer.withNestedLevel { for member in structDesc.members { renderDeclaration(member) } }
  626. } else {
  627. writer.nextLineAppendsToLastLine()
  628. }
  629. writer.writeLine("}")
  630. }
  631. /// Renders the specified protocol declaration.
  632. func renderProtocol(_ protocolDesc: ProtocolDescription) {
  633. if let accessModifier = protocolDesc.accessModifier {
  634. writer.writeLine("\(renderedAccessModifier(accessModifier)) ")
  635. writer.nextLineAppendsToLastLine()
  636. }
  637. writer.writeLine("protocol \(protocolDesc.name)")
  638. writer.nextLineAppendsToLastLine()
  639. if !protocolDesc.conformances.isEmpty {
  640. let conformances = protocolDesc.conformances.joined(separator: ", ")
  641. writer.writeLine(": \(conformances)")
  642. writer.nextLineAppendsToLastLine()
  643. }
  644. writer.writeLine(" {")
  645. if !protocolDesc.members.isEmpty {
  646. writer.withNestedLevel { for member in protocolDesc.members { renderDeclaration(member) } }
  647. } else {
  648. writer.nextLineAppendsToLastLine()
  649. }
  650. writer.writeLine("}")
  651. }
  652. /// Renders the specified enum declaration.
  653. func renderEnum(_ enumDesc: EnumDescription) {
  654. if enumDesc.isFrozen {
  655. writer.writeLine("@frozen ")
  656. writer.nextLineAppendsToLastLine()
  657. }
  658. if let accessModifier = enumDesc.accessModifier {
  659. writer.writeLine("\(renderedAccessModifier(accessModifier)) ")
  660. writer.nextLineAppendsToLastLine()
  661. }
  662. if enumDesc.isIndirect {
  663. writer.writeLine("indirect ")
  664. writer.nextLineAppendsToLastLine()
  665. }
  666. writer.writeLine("enum \(enumDesc.name)")
  667. writer.nextLineAppendsToLastLine()
  668. if !enumDesc.conformances.isEmpty {
  669. writer.writeLine(": \(enumDesc.conformances.joined(separator: ", "))")
  670. writer.nextLineAppendsToLastLine()
  671. }
  672. writer.writeLine(" {")
  673. if !enumDesc.members.isEmpty {
  674. writer.withNestedLevel { for member in enumDesc.members { renderDeclaration(member) } }
  675. } else {
  676. writer.nextLineAppendsToLastLine()
  677. }
  678. writer.writeLine("}")
  679. }
  680. /// Renders the specified enum case associated value.
  681. func renderEnumCaseAssociatedValue(_ value: EnumCaseAssociatedValueDescription) {
  682. var words: [String] = []
  683. if let label = value.label { words.append(label + ":") }
  684. writer.writeLine(words.joinedWords())
  685. writer.nextLineAppendsToLastLine()
  686. renderExistingTypeDescription(value.type)
  687. }
  688. /// Renders the specified enum case declaration.
  689. func renderEnumCase(_ enumCase: EnumCaseDescription) {
  690. writer.writeLine("case \(enumCase.name)")
  691. switch enumCase.kind {
  692. case .nameOnly: break
  693. case .nameWithRawValue(let rawValue):
  694. writer.nextLineAppendsToLastLine()
  695. writer.writeLine(" = ")
  696. writer.nextLineAppendsToLastLine()
  697. renderLiteral(rawValue)
  698. case .nameWithAssociatedValues(let values):
  699. if values.isEmpty { break }
  700. for (value, isLast) in values.enumeratedWithLastMarker() {
  701. renderEnumCaseAssociatedValue(value)
  702. if !isLast {
  703. writer.nextLineAppendsToLastLine()
  704. writer.writeLine(", ")
  705. }
  706. }
  707. }
  708. }
  709. /// Renders the specified declaration.
  710. func renderDeclaration(_ declaration: Declaration) {
  711. switch declaration {
  712. case let .commentable(comment, nestedDeclaration):
  713. renderCommentableDeclaration(comment: comment, declaration: nestedDeclaration)
  714. case let .deprecated(deprecation, nestedDeclaration):
  715. renderDeprecatedDeclaration(deprecation: deprecation, declaration: nestedDeclaration)
  716. case .variable(let variableDescription): renderVariable(variableDescription)
  717. case .extension(let extensionDescription): renderExtension(extensionDescription)
  718. case .struct(let structDescription): renderStruct(structDescription)
  719. case .protocol(let protocolDescription): renderProtocol(protocolDescription)
  720. case .enum(let enumDescription): renderEnum(enumDescription)
  721. case .typealias(let typealiasDescription): renderTypealias(typealiasDescription)
  722. case .function(let functionDescription): renderFunction(functionDescription)
  723. case .enumCase(let enumCase): renderEnumCase(enumCase)
  724. }
  725. }
  726. /// Renders the specified function kind.
  727. func renderedFunctionKind(_ functionKind: FunctionKind) -> String {
  728. switch functionKind {
  729. case .initializer(let isFailable): return "init\(isFailable ? "?" : "")"
  730. case .function(let name, let isStatic):
  731. return (isStatic ? "static " : "") + "func \(name)"
  732. }
  733. }
  734. /// Renders the specified function keyword.
  735. func renderedFunctionKeyword(_ keyword: FunctionKeyword) -> String {
  736. switch keyword {
  737. case .throws: return "throws"
  738. case .async: return "async"
  739. case .rethrows: return "rethrows"
  740. }
  741. }
  742. /// Renders the specified function signature.
  743. func renderClosureSignature(_ signature: ClosureSignatureDescription) {
  744. if signature.sendable {
  745. writer.writeLine("@Sendable ")
  746. writer.nextLineAppendsToLastLine()
  747. }
  748. if signature.escaping {
  749. writer.writeLine("@escaping ")
  750. writer.nextLineAppendsToLastLine()
  751. }
  752. writer.writeLine("(")
  753. let parameters = signature.parameters
  754. let separateLines = parameters.count > 1
  755. if separateLines {
  756. writer.withNestedLevel {
  757. for (parameter, isLast) in signature.parameters.enumeratedWithLastMarker() {
  758. renderClosureParameter(parameter)
  759. if !isLast {
  760. writer.nextLineAppendsToLastLine()
  761. writer.writeLine(",")
  762. }
  763. }
  764. }
  765. } else {
  766. writer.nextLineAppendsToLastLine()
  767. if let parameter = parameters.first {
  768. renderClosureParameter(parameter)
  769. writer.nextLineAppendsToLastLine()
  770. }
  771. }
  772. writer.writeLine(")")
  773. let keywords = signature.keywords
  774. for keyword in keywords {
  775. writer.nextLineAppendsToLastLine()
  776. writer.writeLine(" " + renderedFunctionKeyword(keyword))
  777. }
  778. if let returnType = signature.returnType {
  779. writer.nextLineAppendsToLastLine()
  780. writer.writeLine(" -> ")
  781. writer.nextLineAppendsToLastLine()
  782. renderExpression(returnType)
  783. }
  784. }
  785. /// Renders the specified function signature.
  786. func renderFunctionSignature(_ signature: FunctionSignatureDescription) {
  787. do {
  788. if let accessModifier = signature.accessModifier {
  789. writer.writeLine(renderedAccessModifier(accessModifier) + " ")
  790. writer.nextLineAppendsToLastLine()
  791. }
  792. let generics = signature.generics
  793. writer.writeLine(
  794. renderedFunctionKind(signature.kind)
  795. )
  796. if !generics.isEmpty {
  797. writer.nextLineAppendsToLastLine()
  798. writer.writeLine("<")
  799. for (genericType, isLast) in generics.enumeratedWithLastMarker() {
  800. writer.nextLineAppendsToLastLine()
  801. renderExistingTypeDescription(genericType)
  802. if !isLast {
  803. writer.nextLineAppendsToLastLine()
  804. writer.writeLine(", ")
  805. }
  806. }
  807. writer.nextLineAppendsToLastLine()
  808. writer.writeLine(">")
  809. }
  810. writer.nextLineAppendsToLastLine()
  811. writer.writeLine("(")
  812. let parameters = signature.parameters
  813. let separateLines = parameters.count > 1
  814. if separateLines {
  815. writer.withNestedLevel {
  816. for (parameter, isLast) in signature.parameters.enumeratedWithLastMarker() {
  817. renderParameter(parameter)
  818. if !isLast {
  819. writer.nextLineAppendsToLastLine()
  820. writer.writeLine(",")
  821. }
  822. }
  823. }
  824. } else {
  825. writer.nextLineAppendsToLastLine()
  826. if let parameter = parameters.first { renderParameter(parameter) }
  827. writer.nextLineAppendsToLastLine()
  828. }
  829. writer.writeLine(")")
  830. }
  831. do {
  832. let keywords = signature.keywords
  833. if !keywords.isEmpty {
  834. for keyword in keywords {
  835. writer.nextLineAppendsToLastLine()
  836. writer.writeLine(" " + renderedFunctionKeyword(keyword))
  837. }
  838. }
  839. }
  840. if let returnType = signature.returnType {
  841. writer.nextLineAppendsToLastLine()
  842. writer.writeLine(" -> ")
  843. writer.nextLineAppendsToLastLine()
  844. renderExpression(returnType)
  845. }
  846. if let whereClause = signature.whereClause {
  847. writer.nextLineAppendsToLastLine()
  848. writer.writeLine(" " + renderedWhereClause(whereClause))
  849. }
  850. }
  851. /// Renders the specified function declaration.
  852. func renderFunction(_ functionDescription: FunctionDescription) {
  853. renderFunctionSignature(functionDescription.signature)
  854. guard let body = functionDescription.body else { return }
  855. writer.nextLineAppendsToLastLine()
  856. writer.writeLine(" {")
  857. if !body.isEmpty {
  858. writer.withNestedLevel { renderCodeBlocks(body) }
  859. } else {
  860. writer.nextLineAppendsToLastLine()
  861. }
  862. writer.writeLine("}")
  863. }
  864. /// Renders the specified parameter declaration.
  865. func renderParameter(_ parameterDescription: ParameterDescription) {
  866. if let label = parameterDescription.label {
  867. writer.writeLine(label)
  868. } else {
  869. writer.writeLine("_")
  870. }
  871. writer.nextLineAppendsToLastLine()
  872. if let name = parameterDescription.name, name != parameterDescription.label {
  873. // If the label and name are the same value, don't repeat it.
  874. writer.writeLine(" ")
  875. writer.nextLineAppendsToLastLine()
  876. writer.writeLine(name)
  877. writer.nextLineAppendsToLastLine()
  878. }
  879. writer.writeLine(": ")
  880. writer.nextLineAppendsToLastLine()
  881. if parameterDescription.inout {
  882. writer.writeLine("inout ")
  883. writer.nextLineAppendsToLastLine()
  884. }
  885. if let type = parameterDescription.type {
  886. renderExistingTypeDescription(type)
  887. }
  888. if let defaultValue = parameterDescription.defaultValue {
  889. writer.nextLineAppendsToLastLine()
  890. writer.writeLine(" = ")
  891. writer.nextLineAppendsToLastLine()
  892. renderExpression(defaultValue)
  893. }
  894. }
  895. /// Renders the specified parameter declaration for a closure.
  896. func renderClosureParameter(_ parameterDescription: ParameterDescription) {
  897. let name = parameterDescription.name
  898. let label: String
  899. if let declaredLabel = parameterDescription.label {
  900. label = declaredLabel
  901. } else {
  902. label = "_"
  903. }
  904. if let name = name {
  905. writer.writeLine(label)
  906. if name != parameterDescription.label {
  907. // If the label and name are the same value, don't repeat it.
  908. writer.writeLine(" ")
  909. writer.nextLineAppendsToLastLine()
  910. writer.writeLine(name)
  911. writer.nextLineAppendsToLastLine()
  912. }
  913. }
  914. if parameterDescription.inout {
  915. writer.writeLine("inout ")
  916. writer.nextLineAppendsToLastLine()
  917. }
  918. if let type = parameterDescription.type {
  919. renderExistingTypeDescription(type)
  920. }
  921. if let defaultValue = parameterDescription.defaultValue {
  922. writer.nextLineAppendsToLastLine()
  923. writer.writeLine(" = ")
  924. writer.nextLineAppendsToLastLine()
  925. renderExpression(defaultValue)
  926. }
  927. }
  928. /// Renders the specified declaration with a comment.
  929. func renderCommentableDeclaration(comment: Comment?, declaration: Declaration) {
  930. if let comment { renderComment(comment) }
  931. renderDeclaration(declaration)
  932. }
  933. /// Renders the specified declaration with a deprecation annotation.
  934. func renderDeprecatedDeclaration(deprecation: DeprecationDescription, declaration: Declaration) {
  935. renderDeprecation(deprecation)
  936. renderDeclaration(declaration)
  937. }
  938. func renderDeprecation(_ deprecation: DeprecationDescription) {
  939. let things: [String] = [
  940. "*", "deprecated", deprecation.message.map { "message: \"\($0)\"" },
  941. deprecation.renamed.map { "renamed: \"\($0)\"" },
  942. ]
  943. .compactMap({ $0 })
  944. let line = "@available(\(things.joined(separator: ", ")))"
  945. writer.writeLine(line)
  946. }
  947. /// Renders the specified code block item.
  948. func renderCodeBlockItem(_ description: CodeBlockItem) {
  949. switch description {
  950. case .declaration(let declaration): renderDeclaration(declaration)
  951. case .expression(let expression): renderExpression(expression)
  952. }
  953. }
  954. /// Renders the specified code block.
  955. func renderCodeBlock(_ description: CodeBlock) {
  956. if let comment = description.comment { renderComment(comment) }
  957. let item = description.item
  958. renderCodeBlockItem(item)
  959. }
  960. /// Renders the specified code blocks.
  961. func renderCodeBlocks(_ blocks: [CodeBlock]) { blocks.forEach(renderCodeBlock) }
  962. }
  963. extension Array {
  964. /// Returns a collection of tuples, where the first element is
  965. /// the collection element and the second is a Boolean value indicating
  966. /// whether it is the last element in the collection.
  967. /// - Returns: A collection of tuples.
  968. fileprivate func enumeratedWithLastMarker() -> [(Element, isLast: Bool)] {
  969. let count = count
  970. return enumerated().map { index, element in (element, index == count - 1) }
  971. }
  972. }
  973. extension Array where Element == String {
  974. /// Returns a string where the elements of the array are joined
  975. /// by a space character.
  976. /// - Returns: A string with the elements of the array joined by space characters.
  977. fileprivate func joinedWords() -> String { joined(separator: " ") }
  978. }
  979. extension String {
  980. /// Returns an array of strings, where each string represents one line
  981. /// in the current string.
  982. /// - Returns: An array of strings, each representing one line in the original string.
  983. fileprivate func asLines() -> [String] {
  984. split(omittingEmptySubsequences: false, whereSeparator: \.isNewline).map(String.init)
  985. }
  986. /// Returns a new string where the provided closure transforms each line.
  987. /// The closure takes a string representing one line as a parameter.
  988. /// - Parameter work: The closure that transforms each line.
  989. /// - Returns: A new string where each line has been transformed using the given closure.
  990. fileprivate func transformingLines(_ work: (String) -> String) -> [String] { asLines().map(work) }
  991. }
  992. extension TextBasedRenderer {
  993. /// Returns the provided expression rendered as a string.
  994. /// - Parameter expression: The expression.
  995. /// - Returns: The string representation of the expression.
  996. static func renderedExpressionAsString(_ expression: Expression) -> String {
  997. let renderer = TextBasedRenderer.default
  998. renderer.renderExpression(expression)
  999. return renderer.renderedContents()
  1000. }
  1001. }