Skip to content

Commit 5c79a88

Browse files
committed
Generate the SwiftSyntax docc index using CodeGeneration
It turns out we need this index because docc generates the left sidebar that contains from it.
1 parent 902ef0b commit 5c79a88

File tree

4 files changed

+541
-56
lines changed

4 files changed

+541
-56
lines changed

CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift

Lines changed: 72 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ import SwiftSyntax
1717
import SwiftSyntaxBuilder
1818
import Utils
1919

20-
private let swiftBasicFormatDir: String = "SwiftBasicFormat"
21-
private let ideUtilsDir: String = "IDEUtils"
22-
private let swiftParserDir: String = "SwiftParser"
23-
private let swiftSyntaxDir: String = "SwiftSyntax"
24-
private let swiftSyntaxBuilderDir: String = "SwiftSyntaxBuilder"
20+
private let generatedDirName = "generated"
21+
22+
private let swiftBasicFormatGeneratedDir = ["SwiftBasicFormat", generatedDirName]
23+
private let ideUtilsGeneratedDir = ["IDEUtils", generatedDirName]
24+
private let swiftParserGeneratedDir = ["SwiftParser", generatedDirName]
25+
private let swiftSyntaxGeneratedDir = ["SwiftSyntax", generatedDirName]
26+
private let swiftSyntaxBuilderGeneratedDir = ["SwiftSyntaxBuilder", generatedDirName]
2527
private let BASE_KIND_FILES = [
2628
"Decl": "SyntaxDeclNodes.swift",
2729
"Expr": "SyntaxExprNodes.swift",
@@ -31,6 +33,23 @@ private let BASE_KIND_FILES = [
3133
"Type": "SyntaxTypeNodes.swift",
3234
]
3335

36+
struct GeneratedFileSpec {
37+
let pathComponents: [String]
38+
private let contentsGenerator: () -> String
39+
var contents: String {
40+
return self.contentsGenerator()
41+
}
42+
43+
init(_ pathComponents: [String], _ contents: @escaping @autoclosure () -> String) {
44+
self.pathComponents = pathComponents
45+
self.contentsGenerator = contents
46+
}
47+
48+
init(_ pathComponents: [String], _ contents: @escaping @autoclosure () -> SourceFileSyntax) {
49+
self.init(pathComponents, "\(contents().formatted(using: CodeGenerationFormat()))\n")
50+
}
51+
}
52+
3453
struct TemplateSpec {
3554
let sourceFileGenerator: () -> SourceFileSyntax
3655
var sourceFile: SourceFileSyntax {
@@ -55,49 +74,51 @@ struct GenerateSwiftSyntax: ParsableCommand {
5574
var verbose: Bool = false
5675

5776
func run() throws {
58-
let templates: [TemplateSpec] =
77+
let fileSpecs: [GeneratedFileSpec] =
5978
[
6079
// SwiftBasicFormat
61-
TemplateSpec(sourceFile: basicFormatFile, module: swiftBasicFormatDir, filename: "BasicFormat.swift"),
80+
GeneratedFileSpec(swiftBasicFormatGeneratedDir + ["BasicFormat.swift"], basicFormatFile),
6281

6382
// IDEUtils
64-
TemplateSpec(sourceFile: syntaxClassificationFile, module: ideUtilsDir, filename: "SyntaxClassification.swift"),
83+
GeneratedFileSpec(ideUtilsGeneratedDir + ["SyntaxClassification.swift"], syntaxClassificationFile),
6584

6685
// SwiftParser
67-
TemplateSpec(sourceFile: declarationModifierFile, module: swiftParserDir, filename: "DeclarationModifier.swift"),
68-
TemplateSpec(sourceFile: parserEntryFile, module: swiftParserDir, filename: "Parser+Entry.swift"),
69-
TemplateSpec(sourceFile: tokenSpecStaticMembersFile, module: swiftParserDir, filename: "TokenSpecStaticMembers.swift"),
70-
TemplateSpec(sourceFile: typeAttributeFile, module: swiftParserDir, filename: "TypeAttribute.swift"),
86+
GeneratedFileSpec(swiftParserGeneratedDir + ["DeclarationModifier.swift"], declarationModifierFile),
87+
GeneratedFileSpec(swiftParserGeneratedDir + ["Parser+Entry.swift"], parserEntryFile),
88+
GeneratedFileSpec(swiftParserGeneratedDir + ["TokenSpecStaticMembers.swift"], tokenSpecStaticMembersFile),
89+
GeneratedFileSpec(swiftParserGeneratedDir + ["TypeAttribute.swift"], typeAttributeFile),
7190

7291
// SwiftSyntax
73-
TemplateSpec(sourceFile: keywordFile, module: swiftSyntaxDir, filename: "Keyword.swift"),
74-
TemplateSpec(sourceFile: miscFile, module: swiftSyntaxDir, filename: "Misc.swift"),
75-
TemplateSpec(sourceFile: rawSyntaxNodesFile, module: swiftSyntaxDir, filename: "raw/RawSyntaxNodes.swift"),
76-
TemplateSpec(sourceFile: rawSyntaxValidationFile, module: swiftSyntaxDir, filename: "raw/RawSyntaxValidation.swift"),
77-
TemplateSpec(sourceFile: syntaxAnyVisitorFile, module: swiftSyntaxDir, filename: "SyntaxAnyVisitor.swift"),
78-
TemplateSpec(sourceFile: syntaxBaseNodesFile, module: swiftSyntaxDir, filename: "SyntaxBaseNodes.swift"),
79-
TemplateSpec(sourceFile: syntaxCollectionsFile, module: swiftSyntaxDir, filename: "SyntaxCollections.swift"),
80-
TemplateSpec(sourceFile: syntaxEnumFile, module: swiftSyntaxDir, filename: "SyntaxEnum.swift"),
81-
TemplateSpec(sourceFile: syntaxKindFile, module: swiftSyntaxDir, filename: "SyntaxKind.swift"),
82-
TemplateSpec(sourceFile: syntaxRewriterFile, module: swiftSyntaxDir, filename: "SyntaxRewriter.swift"),
83-
TemplateSpec(sourceFile: syntaxTraitsFile, module: swiftSyntaxDir, filename: "SyntaxTraits.swift"),
84-
TemplateSpec(sourceFile: syntaxTransformFile, module: swiftSyntaxDir, filename: "SyntaxTransform.swift"),
85-
TemplateSpec(sourceFile: syntaxVisitorFile, module: swiftSyntaxDir, filename: "SyntaxVisitor.swift"),
86-
TemplateSpec(sourceFile: tokenKindFile, module: swiftSyntaxDir, filename: "TokenKind.swift"),
87-
TemplateSpec(sourceFile: tokensFile, module: swiftSyntaxDir, filename: "Tokens.swift"),
88-
TemplateSpec(sourceFile: triviaFile, module: swiftSyntaxDir, filename: "Trivia.swift"),
92+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["Keyword.swift"], keywordFile),
93+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["Misc.swift"], miscFile),
94+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["raw", "RawSyntaxNodes.swift"], rawSyntaxNodesFile),
95+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["raw", "RawSyntaxValidation.swift"], rawSyntaxValidationFile),
96+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxAnyVisitor.swift"], syntaxAnyVisitorFile),
97+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxBaseNodes.swift"], syntaxBaseNodesFile),
98+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxCollections.swift"], syntaxCollectionsFile),
99+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxEnum.swift"], syntaxEnumFile),
100+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxKind.swift"], syntaxKindFile),
101+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxRewriter.swift"], syntaxRewriterFile),
102+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxTraits.swift"], syntaxTraitsFile),
103+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxTransform.swift"], syntaxTransformFile),
104+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxVisitor.swift"], syntaxVisitorFile),
105+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["TokenKind.swift"], tokenKindFile),
106+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["Tokens.swift"], tokensFile),
107+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["Trivia.swift"], triviaFile),
89108

90109
// SwiftSyntaxBuilder
91-
TemplateSpec(sourceFile: buildableCollectionNodesFile, module: swiftSyntaxBuilderDir, filename: "BuildableCollectionNodes.swift"),
92-
TemplateSpec(sourceFile: buildableNodesFile, module: swiftSyntaxBuilderDir, filename: "BuildableNodes.swift"),
93-
TemplateSpec(sourceFile: resultBuildersFile, module: swiftSyntaxBuilderDir, filename: "ResultBuilders.swift"),
94-
TemplateSpec(sourceFile: syntaxExpressibleByStringInterpolationConformancesFile, module: swiftSyntaxBuilderDir, filename: "SyntaxExpressibleByStringInterpolationConformances.swift"),
110+
GeneratedFileSpec(swiftSyntaxBuilderGeneratedDir + ["BuildableCollectionNodes.swift"], buildableCollectionNodesFile),
111+
GeneratedFileSpec(swiftSyntaxBuilderGeneratedDir + ["BuildableNodes.swift"], buildableNodesFile),
112+
GeneratedFileSpec(swiftSyntaxBuilderGeneratedDir + ["ResultBuilders.swift"], resultBuildersFile),
113+
GeneratedFileSpec(swiftSyntaxBuilderGeneratedDir + ["SyntaxExpressibleByStringInterpolationConformances.swift"], syntaxExpressibleByStringInterpolationConformancesFile),
95114
]
96115
+ BASE_KIND_FILES.map { baseKind in
97-
TemplateSpec(sourceFile: syntaxNode(emitKind: baseKind.key), module: swiftSyntaxDir, filename: "syntaxNodes/\(baseKind.value)")
98-
}
116+
GeneratedFileSpec(swiftSyntaxGeneratedDir + ["syntaxNodes", baseKind.value], syntaxNode(emitKind: baseKind.key))
117+
} + [
118+
GeneratedFileSpec(["SwiftSyntax", "Documentation.docc", "generated", "SwiftSyntax.md"], swiftSyntaxDoccIndex)
119+
]
99120

100-
let modules = Set(templates.map(\.module))
121+
let modules = Set(fileSpecs.compactMap { $0.pathComponents.first })
101122

102123
let previouslyGeneratedFilesLock = NSLock()
103124
var previouslyGeneratedFiles = Set(
@@ -108,25 +129,25 @@ struct GenerateSwiftSyntax: ParsableCommand {
108129
return FileManager.default
109130
.enumerator(at: generatedDir, includingPropertiesForKeys: nil)!
110131
.compactMap { $0 as? URL }
111-
.filter { $0.pathExtension == "swift" }
132+
.filter { !$0.hasDirectoryPath }
112133
}
113134
)
114135

115136
var errors: [Error] = []
116-
DispatchQueue.concurrentPerform(iterations: templates.count) { index in
117-
let template = templates[index]
137+
DispatchQueue.concurrentPerform(iterations: fileSpecs.count) { index in
138+
let fileSpec = fileSpecs[index]
118139
do {
119-
let destination = URL(fileURLWithPath: destination)
120-
.appendingPathComponent(template.module)
121-
.appendingPathComponent("generated")
122-
.appendingPathComponent(template.filename)
140+
var destination = URL(fileURLWithPath: destination)
141+
for component in fileSpec.pathComponents {
142+
destination = destination.appendingPathComponent(component)
143+
}
123144

124145
previouslyGeneratedFilesLock.lock();
125146
_ = previouslyGeneratedFiles.remove(destination)
126147
previouslyGeneratedFilesLock.unlock()
127148

128-
try generateTemplate(
129-
sourceFile: template.sourceFile,
149+
try generateFile(
150+
contents: fileSpec.contents,
130151
destination: destination,
131152
verbose: verbose
132153
)
@@ -147,8 +168,8 @@ struct GenerateSwiftSyntax: ParsableCommand {
147168
}
148169
}
149170

150-
private func generateTemplate(
151-
sourceFile: SourceFileSyntax,
171+
private func generateFile(
172+
contents: @autoclosure () -> String,
152173
destination: URL,
153174
verbose: Bool
154175
) throws {
@@ -161,7 +182,10 @@ struct GenerateSwiftSyntax: ParsableCommand {
161182
if verbose {
162183
print("Generating \(destination.path)...")
163184
}
164-
let syntax = sourceFile.formatted(using: CodeGenerationFormat())
165-
try "\(syntax)\n".write(to: destination, atomically: true, encoding: .utf8)
185+
let start = Date()
186+
try contents().write(to: destination, atomically: true, encoding: .utf8)
187+
if verbose {
188+
print("Generated \(destination.path) in \((Date().timeIntervalSince(start) * 1000).rounded() / 1000)s")
189+
}
166190
}
167191
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Foundation
14+
import SyntaxSupport
15+
16+
let nodesSections: String = {
17+
var result = ""
18+
var handledSyntaxTypes: Set<String> = []
19+
20+
func addSection(heading: String, types: [String]) {
21+
handledSyntaxTypes.formUnion(types)
22+
result += "### \(heading)\n\n"
23+
24+
for type in types {
25+
result += "- <doc:SwiftSyntax/\(type)>\n"
26+
}
27+
result += "\n"
28+
}
29+
30+
var nodeKinds = [
31+
("Decl", "Declarations"),
32+
("Expr", "Expressions"),
33+
("Pattern", "Patterns"),
34+
("Stmt", "Statements"),
35+
("Type", "Types"),
36+
]
37+
38+
for (baseKind, heading) in nodeKinds {
39+
let baseTypes = ["\(baseKind)Syntax", "\(baseKind)SyntaxProtocol", "Missing\(baseKind)Syntax"]
40+
let leafTypes = SYNTAX_NODES.filter({ $0.baseKind == baseKind && !$0.isMissing }).map(\.name)
41+
addSection(heading: heading, types: baseTypes + leafTypes)
42+
}
43+
44+
addSection(
45+
heading: "Collections",
46+
types: [
47+
"SyntaxChildren",
48+
"SyntaxChildrenIndex",
49+
"SyntaxChildrenIndexData",
50+
]
51+
+ SYNTAX_NODES.flatMap({ (node: Node) -> [String] in
52+
guard node.isSyntaxCollection else {
53+
return []
54+
}
55+
if !handledSyntaxTypes.contains(node.collectionElement) && SYNTAX_NODE_MAP[node.collectionElement] != nil {
56+
return ["\(node.name)", "\(node.collectionElement)Syntax"]
57+
} else {
58+
return ["\(node.name)"]
59+
}
60+
})
61+
)
62+
63+
addSection(heading: "Miscellaneous Syntax", types: SYNTAX_NODES.map(\.name).filter({ !handledSyntaxTypes.contains($0) }))
64+
65+
addSection(heading: "Traits", types: TRAITS.map(\.traitName))
66+
67+
return result
68+
}()
69+
70+
let swiftSyntaxDoccIndex: String = {
71+
let templateURL = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent("SwiftSyntaxDoccIndexTemplate.md")
72+
let template = try! String(contentsOf: templateURL)
73+
74+
return template.replacingOccurrences(of: "{{Nodes}}", with: nodesSections)
75+
}()

Sources/SwiftSyntax/Documentation.docc/SwiftSyntax.md renamed to CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntax/SwiftSyntaxDoccIndexTemplate.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
1-
%{
2-
# -*- mode: Swift -*-
3-
from gyb_syntax_support import *
4-
from gyb_syntax_support.Traits import TRAITS
5-
NODE_MAP = create_node_map()
6-
# Ignore the following admonition it applies to the resulting .md file only
7-
}%
81
# ``SwiftSyntax``
92

103
@Comment {
11-
Automatically Generated From SwiftSyntax.md.gyb.
4+
Automatically Generated by CodeGeneration.
125
Do Not Edit Directly!
136
}
147

@@ -58,6 +51,8 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code.
5851
- <doc:SwiftSyntax/SyntaxVisitorContinueKind>
5952
- <doc:SwiftSyntax/ReversedTokenSequence>
6053

54+
{{Nodes}}
55+
6156
### Syntax Position
6257

6358
- <doc:SwiftSyntax/AbsolutePosition>

0 commit comments

Comments
 (0)