Skip to content

Commit 95d3b69

Browse files
authored
Merge pull request #1013 from ahoppen/ahoppen/simplify-basicfomat
Simplify SwiftBasicFormat by introducing structural information of the syntax tree as static properties
2 parents f6db3c5 + 53b7888 commit 95d3b69

22 files changed

+4527
-4983
lines changed

CodeGeneration/Sources/generate-swiftbasicformat/BasicFormatFile.swift

Lines changed: 137 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,151 @@ let basicFormatFile = SourceFile {
2222
path: [AccessPathComponent(name: "SwiftSyntax")]
2323
)
2424

25-
ClassDecl(modifiers: [DeclModifier(name: .open)], identifier: "BasicFormat", inheritanceClause: TypeInheritanceClause { InheritedType(typeName: Type("SyntaxRewriter")) }) {
25+
ClassDecl("open class BasicFormat: SyntaxRewriter") {
2626
VariableDecl("public var indentationLevel: Int = 0")
2727
VariableDecl("open var indentation: TriviaPiece { .spaces(indentationLevel * 4) }")
2828
VariableDecl("public var indentedNewline: Trivia { Trivia(pieces: [.newlines(1), indentation]) }")
2929
VariableDecl("private var lastRewrittenToken: TokenSyntax?")
30+
VariableDecl("private var putNextTokenOnNewLine: Bool = false")
3031

31-
for node in SYNTAX_NODES where !node.isBase {
32-
if node.isSyntaxCollection {
33-
makeSyntaxCollectionRewriteFunc(node: node)
34-
} else {
35-
makeLayoutNodeRewriteFunc(node: node)
32+
FunctionDecl("""
33+
open override func visitPre(_ node: Syntax) {
34+
if let keyPath = getKeyPath(node), shouldIndent(keyPath) {
35+
indentationLevel += 1
36+
}
37+
if let parent = node.parent, childrenSeparatedByNewline(parent) {
38+
putNextTokenOnNewLine = true
39+
}
40+
}
41+
"""
42+
)
43+
FunctionDecl("""
44+
open override func visitPost(_ node: Syntax) {
45+
if let keyPath = getKeyPath(node), shouldIndent(keyPath) {
46+
indentationLevel -= 1
47+
}
48+
}
49+
"""
50+
)
51+
52+
FunctionDecl("""
53+
open override func visit(_ node: TokenSyntax) -> TokenSyntax {
54+
var leadingTrivia = node.leadingTrivia
55+
var trailingTrivia = node.trailingTrivia
56+
if requiresLeadingSpace(node.tokenKind) && leadingTrivia.isEmpty && lastRewrittenToken?.trailingTrivia.isEmpty != false {
57+
leadingTrivia += .space
58+
}
59+
if requiresTrailingSpace(node.tokenKind) && trailingTrivia.isEmpty {
60+
trailingTrivia += .space
61+
}
62+
if let keyPath = getKeyPath(Syntax(node)), requiresLeadingNewline(keyPath), !(leadingTrivia.first?.isNewline ?? false) {
63+
leadingTrivia = .newline + leadingTrivia
64+
}
65+
leadingTrivia = leadingTrivia.indented(indentation: indentation)
66+
trailingTrivia = trailingTrivia.indented(indentation: indentation)
67+
let rewritten = TokenSyntax(
68+
node.tokenKind,
69+
leadingTrivia: leadingTrivia,
70+
trailingTrivia: trailingTrivia,
71+
presence: node.presence
72+
)
73+
lastRewrittenToken = rewritten
74+
putNextTokenOnNewLine = false
75+
return rewritten
76+
}
77+
"""
78+
)
79+
80+
FunctionDecl("open func shouldIndent(_ keyPath: AnyKeyPath) -> Bool") {
81+
SwitchStmt(expression: Expr("keyPath")) {
82+
for node in SYNTAX_NODES where !node.isBase {
83+
for child in node.children where child.isIndented {
84+
SwitchCase("case \\\(node.type.syntaxBaseName).\(child.swiftName):") {
85+
ReturnStmt("return true")
86+
}
87+
}
88+
}
89+
SwitchCase("default:") {
90+
ReturnStmt("return false")
91+
}
92+
}
93+
}
94+
95+
FunctionDecl("open func requiresLeadingNewline(_ keyPath: AnyKeyPath) -> Bool") {
96+
SwitchStmt(expression: Expr("keyPath")) {
97+
for node in SYNTAX_NODES where !node.isBase {
98+
for child in node.children where child.requiresLeadingNewline {
99+
SwitchCase("case \\\(node.type.syntaxBaseName).\(child.swiftName):") {
100+
ReturnStmt("return true")
101+
}
102+
}
103+
}
104+
SwitchCase("default:") {
105+
ReturnStmt("return putNextTokenOnNewLine")
106+
}
107+
}
108+
}
109+
110+
FunctionDecl("open func childrenSeparatedByNewline(_ node: Syntax) -> Bool") {
111+
SwitchStmt(expression: Expr("node.as(SyntaxEnum.self)")) {
112+
for node in SYNTAX_NODES where !node.isBase {
113+
if node.elementsSeparatedByNewline {
114+
SwitchCase("case .\(node.swiftSyntaxKind):") {
115+
ReturnStmt("return true")
116+
}
117+
}
118+
}
119+
SwitchCase("default:") {
120+
ReturnStmt("return false")
121+
}
122+
}
123+
}
124+
125+
FunctionDecl("open func requiresLeadingSpace(_ tokenKind: TokenKind) -> Bool") {
126+
SwitchStmt(expression: Expr("tokenKind")) {
127+
for token in SYNTAX_TOKENS {
128+
if token.requiresLeadingSpace {
129+
SwitchCase("case .\(token.swiftKind):") {
130+
ReturnStmt("return true")
131+
}
132+
}
133+
}
134+
SwitchCase("default:") {
135+
ReturnStmt("return false")
136+
}
137+
}
138+
}
139+
140+
FunctionDecl("open func requiresTrailingSpace(_ tokenKind: TokenKind) -> Bool") {
141+
SwitchStmt(expression: Expr("tokenKind")) {
142+
for token in SYNTAX_TOKENS {
143+
if token.requiresTrailingSpace {
144+
SwitchCase("case .\(token.swiftKind):") {
145+
ReturnStmt("return true")
146+
}
147+
}
148+
}
149+
SwitchCase(#"case .contextualKeyword("async"):"#) {
150+
ReturnStmt("return true")
151+
}
152+
SwitchCase("default:") {
153+
ReturnStmt("return false")
154+
}
36155
}
37156
}
38157

39-
createTokenFormatFunction()
158+
FunctionDecl("""
159+
private func getKeyPath(_ node: Syntax) -> AnyKeyPath? {
160+
guard let parent = node.parent else {
161+
return nil
162+
}
163+
guard case .layout(let childrenKeyPaths) = parent.kind.syntaxNodeType.structure else {
164+
return nil
165+
}
166+
return childrenKeyPaths[node.indexInParent]
167+
}
168+
"""
169+
)
40170
}
41171
}
42172

@@ -55,52 +185,6 @@ private func createChildVisitCall(childType: SyntaxBuildableType, rewrittenExpr:
55185
}
56186
}
57187

58-
private func makeLayoutNodeRewriteFunc(node: Node) -> FunctionDecl {
59-
let rewriteResultType: String
60-
if node.type.baseType?.syntaxKind == "Syntax" && node.type.syntaxKind != "Missing" {
61-
rewriteResultType = node.type.syntaxBaseName
62-
} else {
63-
rewriteResultType = node.type.baseType?.syntaxBaseName ?? node.type.syntaxBaseName
64-
}
65-
return FunctionDecl("""
66-
67-
open override func visit(_ node: \(node.type.syntaxBaseName)) -> \(rewriteResultType)
68-
""") {
69-
for child in node.children {
70-
if child.isIndented {
71-
SequenceExpr("indentationLevel += 1")
72-
}
73-
let variableLetVar = child.requiresLeadingNewline ? "var" : "let"
74-
VariableDecl("\(variableLetVar) \(child.swiftName) = \(createChildVisitCall(childType: child.type, rewrittenExpr: MemberAccessExpr("node.\(child.swiftName)")))")
75-
if child.requiresLeadingNewline {
76-
IfStmt(
77-
"""
78-
if \(child.swiftName).leadingTrivia.first?.isNewline != true {
79-
\(child.swiftName).leadingTrivia = indentedNewline + \(child.swiftName).leadingTrivia
80-
}
81-
"""
82-
)
83-
}
84-
if child.isIndented {
85-
SequenceExpr("indentationLevel -= 1")
86-
}
87-
}
88-
let reconstructed = FunctionCallExpr(callee: node.type.syntaxBaseName) {
89-
for child in node.children {
90-
TupleExprElement(
91-
label: child.isUnexpectedNodes ? nil : child.swiftName,
92-
expression: Expr(child.swiftName)
93-
)
94-
}
95-
}
96-
if rewriteResultType != node.type.syntaxBaseName {
97-
ReturnStmt("return \(rewriteResultType)(\(reconstructed))")
98-
} else {
99-
ReturnStmt("return \(reconstructed)")
100-
}
101-
}
102-
}
103-
104188
private func makeSyntaxCollectionRewriteFunc(node: Node) -> FunctionDecl {
105189
let rewriteResultType = node.type.syntaxBaseName
106190
return FunctionDecl("""
@@ -132,70 +216,3 @@ private func makeSyntaxCollectionRewriteFunc(node: Node) -> FunctionDecl {
132216
}
133217
}
134218

135-
private func createTokenFormatFunction() -> FunctionDecl {
136-
return FunctionDecl("""
137-
138-
open override func visit(_ node: TokenSyntax) -> TokenSyntax
139-
""") {
140-
VariableDecl("var leadingTrivia = node.leadingTrivia")
141-
VariableDecl("var trailingTrivia = node.trailingTrivia")
142-
SwitchStmt(expression: MemberAccessExpr(base: "node", name: "tokenKind")) {
143-
for token in SYNTAX_TOKENS where token.name != "ContextualKeyword" {
144-
SwitchCase("case .\(token.swiftKind):") {
145-
if token.requiresLeadingSpace {
146-
IfStmt(
147-
"""
148-
if leadingTrivia.isEmpty && lastRewrittenToken?.trailingTrivia.isEmpty != false {
149-
leadingTrivia += .space
150-
}
151-
"""
152-
)
153-
}
154-
if token.requiresTrailingSpace {
155-
IfStmt(
156-
"""
157-
if trailingTrivia.isEmpty {
158-
trailingTrivia += .space
159-
}
160-
"""
161-
)
162-
}
163-
if !token.requiresLeadingSpace && !token.requiresTrailingSpace {
164-
BreakStmt("break")
165-
}
166-
}
167-
}
168-
SwitchCase("case .eof:") {
169-
BreakStmt("break")
170-
}
171-
SwitchCase("case .contextualKeyword:") {
172-
SwitchStmt(
173-
"""
174-
switch node.text {
175-
case "async":
176-
if trailingTrivia.isEmpty {
177-
trailingTrivia += .space
178-
}
179-
default:
180-
break
181-
}
182-
"""
183-
)
184-
}
185-
}
186-
SequenceExpr("leadingTrivia = leadingTrivia.indented(indentation: indentation)")
187-
SequenceExpr("trailingTrivia = trailingTrivia.indented(indentation: indentation)")
188-
VariableDecl(
189-
"""
190-
let rewritten = TokenSyntax(
191-
node.tokenKind,
192-
leadingTrivia: leadingTrivia,
193-
trailingTrivia: trailingTrivia,
194-
presence: node.presence
195-
)
196-
"""
197-
)
198-
SequenceExpr("lastRewrittenToken = rewritten")
199-
ReturnStmt("return rewritten")
200-
}
201-
}

0 commit comments

Comments
 (0)