Skip to content

Commit fa78409

Browse files
authored
Merge pull request #1394 from ahoppen/ahoppen/index
Generate the SwiftSyntax docc index using CodeGeneration
2 parents 17d5043 + 5c79a88 commit fa78409

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)