Skip to content

Commit be9c3f2

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 be9c3f2

File tree

4 files changed

+534
-51
lines changed

4 files changed

+534
-51
lines changed

CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ private let BASE_KIND_FILES = [
3131
"Type": "SyntaxTypeNodes.swift",
3232
]
3333

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

5774
func run() throws {
58-
let templates: [TemplateSpec] =
75+
let fileSpecs: [GeneratedFileSpec] =
5976
[
6077
// SwiftBasicFormat
61-
TemplateSpec(sourceFile: basicFormatFile, module: swiftBasicFormatDir, filename: "BasicFormat.swift"),
78+
GeneratedFileSpec([swiftBasicFormatDir, "generated", "BasicFormat.swift"], basicFormatFile),
6279

6380
// IDEUtils
64-
TemplateSpec(sourceFile: syntaxClassificationFile, module: ideUtilsDir, filename: "SyntaxClassification.swift"),
81+
GeneratedFileSpec([ideUtilsDir, "generated", "SyntaxClassification.swift"], syntaxClassificationFile),
6582

6683
// 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"),
84+
GeneratedFileSpec([swiftParserDir, "generated", "DeclarationModifier.swift"], declarationModifierFile),
85+
GeneratedFileSpec([swiftParserDir, "generated", "Parser+Entry.swift"], parserEntryFile),
86+
GeneratedFileSpec([swiftParserDir, "generated", "TokenSpecStaticMembers.swift"], tokenSpecStaticMembersFile),
87+
GeneratedFileSpec([swiftParserDir, "generated", "TypeAttribute.swift"], typeAttributeFile),
7188

7289
// 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"),
90+
GeneratedFileSpec([swiftSyntaxDir, "generated", "Keyword.swift"], keywordFile),
91+
GeneratedFileSpec([swiftSyntaxDir, "generated", "Misc.swift"], miscFile),
92+
GeneratedFileSpec([swiftSyntaxDir, "generated", "raw", "RawSyntaxNodes.swift"], rawSyntaxNodesFile),
93+
GeneratedFileSpec([swiftSyntaxDir, "generated", "raw", "RawSyntaxValidation.swift"], rawSyntaxValidationFile),
94+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxAnyVisitor.swift"], syntaxAnyVisitorFile),
95+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxBaseNodes.swift"], syntaxBaseNodesFile),
96+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxCollections.swift"], syntaxCollectionsFile),
97+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxEnum.swift"], syntaxEnumFile),
98+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxKind.swift"], syntaxKindFile),
99+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxRewriter.swift"], syntaxRewriterFile),
100+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxTraits.swift"], syntaxTraitsFile),
101+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxTransform.swift"], syntaxTransformFile),
102+
GeneratedFileSpec([swiftSyntaxDir, "generated", "SyntaxVisitor.swift"], syntaxVisitorFile),
103+
GeneratedFileSpec([swiftSyntaxDir, "generated", "TokenKind.swift"], tokenKindFile),
104+
GeneratedFileSpec([swiftSyntaxDir, "generated", "Tokens.swift"], tokensFile),
105+
GeneratedFileSpec([swiftSyntaxDir, "generated", "Trivia.swift"], triviaFile),
89106

90107
// 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"),
108+
GeneratedFileSpec([swiftSyntaxBuilderDir, "generated", "BuildableCollectionNodes.swift"], buildableCollectionNodesFile),
109+
GeneratedFileSpec([swiftSyntaxBuilderDir, "generated", "BuildableNodes.swift"], buildableNodesFile),
110+
GeneratedFileSpec([swiftSyntaxBuilderDir, "generated", "ResultBuilders.swift"], resultBuildersFile),
111+
GeneratedFileSpec([swiftSyntaxBuilderDir, "generated", "SyntaxExpressibleByStringInterpolationConformances.swift"], syntaxExpressibleByStringInterpolationConformancesFile),
95112
]
96113
+ BASE_KIND_FILES.map { baseKind in
97-
TemplateSpec(sourceFile: syntaxNode(emitKind: baseKind.key), module: swiftSyntaxDir, filename: "syntaxNodes/\(baseKind.value)")
98-
}
114+
GeneratedFileSpec([swiftSyntaxDir, "generated", "syntaxNodes", baseKind.value], syntaxNode(emitKind: baseKind.key))
115+
} + [
116+
GeneratedFileSpec([swiftSyntaxDir, "Documentation.docc", "generated", "SwiftSyntax.md"], swiftSyntaxDoccIndex)
117+
]
99118

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

102121
let previouslyGeneratedFilesLock = NSLock()
103122
var previouslyGeneratedFiles = Set(
@@ -108,25 +127,25 @@ struct GenerateSwiftSyntax: ParsableCommand {
108127
return FileManager.default
109128
.enumerator(at: generatedDir, includingPropertiesForKeys: nil)!
110129
.compactMap { $0 as? URL }
111-
.filter { $0.pathExtension == "swift" }
130+
.filter { !$0.hasDirectoryPath }
112131
}
113132
)
114133

115134
var errors: [Error] = []
116-
DispatchQueue.concurrentPerform(iterations: templates.count) { index in
117-
let template = templates[index]
135+
DispatchQueue.concurrentPerform(iterations: fileSpecs.count) { index in
136+
let fileSpec = fileSpecs[index]
118137
do {
119-
let destination = URL(fileURLWithPath: destination)
120-
.appendingPathComponent(template.module)
121-
.appendingPathComponent("generated")
122-
.appendingPathComponent(template.filename)
138+
var destination = URL(fileURLWithPath: destination)
139+
for component in fileSpec.pathComponents {
140+
destination = destination.appendingPathComponent(component)
141+
}
123142

124143
previouslyGeneratedFilesLock.lock();
125144
_ = previouslyGeneratedFiles.remove(destination)
126145
previouslyGeneratedFilesLock.unlock()
127146

128-
try generateTemplate(
129-
sourceFile: template.sourceFile,
147+
try generateFile(
148+
contents: fileSpec.contents,
130149
destination: destination,
131150
verbose: verbose
132151
)
@@ -147,8 +166,8 @@ struct GenerateSwiftSyntax: ParsableCommand {
147166
}
148167
}
149168

150-
private func generateTemplate(
151-
sourceFile: SourceFileSyntax,
169+
private func generateFile(
170+
contents: @autoclosure () -> String,
152171
destination: URL,
153172
verbose: Bool
154173
) throws {
@@ -161,7 +180,10 @@ struct GenerateSwiftSyntax: ParsableCommand {
161180
if verbose {
162181
print("Generating \(destination.path)...")
163182
}
164-
let syntax = sourceFile.formatted(using: CodeGenerationFormat())
165-
try "\(syntax)\n".write(to: destination, atomically: true, encoding: .utf8)
183+
let start = Date()
184+
try contents().write(to: destination, atomically: true, encoding: .utf8)
185+
if verbose {
186+
print("Generated \(destination.path) in \((Date().timeIntervalSince(start) * 1000).rounded() / 1000)s")
187+
}
166188
}
167189
}
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)