TextBasedRenderer.swift 38 KB

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