Skip to content

Commit e759209

Browse files
committed
Improve implementation of BasicFormat (yet again)
This re-writes the implementation of BasicFormat once again to better handle user-indented code. Co-Authored-By: Ben Barham<[email protected]>
1 parent 011d9c1 commit e759209

26 files changed

+1121
-675
lines changed

CodeGeneration/Sources/SyntaxSupport/Trivia.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ public class Trivia {
1515
public let comment: String
1616
public let characters: [Character]
1717
public let swiftCharacters: [Character]
18-
public let isNewLine: Bool
1918
public let isComment: Bool
2019

2120
public var lowerName: String { lowercaseFirstWord(name: name) }
@@ -36,17 +35,23 @@ public class Trivia {
3635

3736
public var isCollection: Bool { charactersLen > 0 }
3837

38+
public var isBlank: Bool {
39+
characters.contains { $0.isWhitespace }
40+
}
41+
42+
public var isNewLine: Bool {
43+
characters.contains { $0.isNewline }
44+
}
45+
3946
init(
4047
name: String,
4148
comment: String,
4249
characters: [Character] = [],
4350
swiftCharacters: [Character] = [],
44-
isNewLine: Bool = false,
4551
isComment: Bool = false
4652
) {
4753
self.name = name
4854
self.comment = comment
49-
self.isNewLine = isNewLine
5055
self.isComment = isComment
5156
self.characters = characters
5257

@@ -86,8 +91,7 @@ public let TRIVIAS: [Trivia] = [
8691
],
8792
swiftCharacters: [
8893
Character("\r")
89-
],
90-
isNewLine: true
94+
]
9195
),
9296

9397
Trivia(
@@ -100,8 +104,7 @@ public let TRIVIAS: [Trivia] = [
100104
swiftCharacters: [
101105
Character("\r"),
102106
Character("\n"),
103-
],
104-
isNewLine: true
107+
]
105108
),
106109

107110
Trivia(
@@ -142,8 +145,7 @@ public let TRIVIAS: [Trivia] = [
142145
],
143146
swiftCharacters: [
144147
Character("\n")
145-
],
146-
isNewLine: true
148+
]
147149
),
148150

149151
Trivia(

CodeGeneration/Sources/generate-swiftsyntax/GenerateSwiftSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ struct GenerateSwiftSyntax: ParsableCommand {
8080
let fileSpecs: [GeneratedFileSpec] =
8181
[
8282
// SwiftBasicFormat
83-
GeneratedFileSpec(swiftBasicFormatGeneratedDir + ["BasicFormat.swift"], basicFormatFile),
83+
GeneratedFileSpec(swiftBasicFormatGeneratedDir + ["BasicFormat+Extensions.swift"], basicFormatExtensionsFile),
8484

8585
// IDEUtils
8686
GeneratedFileSpec(ideUtilsGeneratedDir + ["SyntaxClassification.swift"], syntaxClassificationFile),
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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 SwiftSyntax
14+
import SwiftSyntaxBuilder
15+
import SyntaxSupport
16+
import Utils
17+
18+
extension Child {
19+
var requiresLeadingSpace: Bool? {
20+
switch self.kind {
21+
case .token(_, let requiresLeadingSpace, _):
22+
return requiresLeadingSpace
23+
case .nodeChoices(let choices):
24+
for choice in choices {
25+
if let requiresLeadingSpace = choice.requiresLeadingSpace {
26+
return requiresLeadingSpace
27+
}
28+
}
29+
default:
30+
break
31+
}
32+
return nil
33+
}
34+
35+
var requiresTrailingSpace: Bool? {
36+
switch self.kind {
37+
case .token(choices: _, _, let requiresTrailingSpace):
38+
return requiresTrailingSpace
39+
case .nodeChoices(let choices):
40+
for choice in choices {
41+
if let requiresTrailingSpace = choice.requiresTrailingSpace {
42+
return requiresTrailingSpace
43+
}
44+
}
45+
default:
46+
break
47+
}
48+
return nil
49+
}
50+
}
51+
52+
let basicFormatExtensionsFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
53+
DeclSyntax("import SwiftSyntax")
54+
55+
try! ExtensionDeclSyntax("public extension SyntaxProtocol") {
56+
DeclSyntax(
57+
"""
58+
var requiresIndent: Bool {
59+
guard let keyPath = keyPathInParent else {
60+
return false
61+
}
62+
return keyPath.requiresIndent
63+
}
64+
"""
65+
)
66+
}
67+
68+
try! ExtensionDeclSyntax("public extension TokenSyntax") {
69+
try VariableDeclSyntax("var requiresLeadingNewline: Bool") {
70+
StmtSyntax(
71+
"""
72+
if let keyPath = keyPathInParent, keyPath.requiresLeadingNewline {
73+
return true
74+
}
75+
"""
76+
)
77+
78+
DeclSyntax("var ancestor: Syntax = Syntax(self)")
79+
try WhileStmtSyntax("while let parent = ancestor.parent, parent.firstToken(viewMode: .sourceAccurate) == self") {
80+
try SwitchExprSyntax("switch ancestor.as(SyntaxEnum.self)") {
81+
for node in SYNTAX_NODES where !node.isBase {
82+
if node.elementsSeparatedByNewline {
83+
SwitchCaseSyntax("case .\(raw: node.swiftSyntaxKind):") {
84+
StmtSyntax("return true")
85+
}
86+
}
87+
}
88+
SwitchCaseSyntax("default:") {
89+
StmtSyntax("ancestor = parent")
90+
}
91+
}
92+
}
93+
94+
StmtSyntax("return false")
95+
}
96+
97+
try VariableDeclSyntax("var requiresLeadingSpace: Bool") {
98+
StmtSyntax(
99+
"""
100+
if let keyPath = keyPathInParent, let requiresLeadingSpace = keyPath.requiresLeadingSpace {
101+
return requiresLeadingSpace
102+
}
103+
"""
104+
)
105+
106+
try SwitchExprSyntax("switch tokenKind") {
107+
for token in SYNTAX_TOKENS {
108+
if token.requiresLeadingSpace {
109+
SwitchCaseSyntax("case .\(raw: token.swiftKind):") {
110+
StmtSyntax("return true")
111+
}
112+
}
113+
}
114+
for keyword in KEYWORDS where keyword.requiresLeadingSpace {
115+
SwitchCaseSyntax("case .keyword(.\(raw: keyword.escapedName)):") {
116+
StmtSyntax("return true")
117+
}
118+
}
119+
SwitchCaseSyntax("default:") {
120+
StmtSyntax("return false")
121+
}
122+
}
123+
}
124+
125+
try VariableDeclSyntax("var requiresTrailingSpace: Bool") {
126+
StmtSyntax(
127+
"""
128+
if let keyPath = keyPathInParent, let requiresTrailingSpace = keyPath.requiresTrailingSpace {
129+
return requiresTrailingSpace
130+
}
131+
"""
132+
)
133+
134+
try SwitchExprSyntax("switch tokenKind") {
135+
for token in SYNTAX_TOKENS {
136+
if token.requiresTrailingSpace {
137+
SwitchCaseSyntax("case .\(raw: token.swiftKind):") {
138+
StmtSyntax("return true")
139+
}
140+
}
141+
}
142+
for keyword in KEYWORDS where keyword.requiresTrailingSpace {
143+
SwitchCaseSyntax("case .keyword(.\(raw: keyword.escapedName)):") {
144+
StmtSyntax("return true")
145+
}
146+
}
147+
SwitchCaseSyntax("default:") {
148+
StmtSyntax("return false")
149+
}
150+
}
151+
}
152+
}
153+
154+
try! ExtensionDeclSyntax("fileprivate extension AnyKeyPath") {
155+
try VariableDeclSyntax("var requiresIndent: Bool") {
156+
try SwitchExprSyntax("switch self") {
157+
for node in SYNTAX_NODES where !node.isBase {
158+
for child in node.children where child.isIndented {
159+
SwitchCaseSyntax("case \\\(raw: node.type.syntaxBaseName).\(raw: child.swiftName):") {
160+
StmtSyntax("return true")
161+
}
162+
}
163+
}
164+
SwitchCaseSyntax("default:") {
165+
StmtSyntax("return false")
166+
}
167+
}
168+
}
169+
170+
try VariableDeclSyntax("var requiresLeadingNewline: Bool") {
171+
try SwitchExprSyntax("switch self") {
172+
for node in SYNTAX_NODES where !node.isBase {
173+
for child in node.children where child.requiresLeadingNewline {
174+
SwitchCaseSyntax("case \\\(raw: node.type.syntaxBaseName).\(raw: child.swiftName):") {
175+
StmtSyntax("return true")
176+
}
177+
}
178+
}
179+
SwitchCaseSyntax("default:") {
180+
StmtSyntax("return false")
181+
}
182+
}
183+
}
184+
185+
try VariableDeclSyntax("var requiresLeadingSpace: Bool?") {
186+
try SwitchExprSyntax("switch self") {
187+
for node in SYNTAX_NODES where !node.isBase {
188+
for child in node.children {
189+
if let requiresLeadingSpace = child.requiresLeadingSpace {
190+
SwitchCaseSyntax("case \\\(raw: node.type.syntaxBaseName).\(raw: child.swiftName):") {
191+
StmtSyntax("return \(literal: requiresLeadingSpace)")
192+
}
193+
}
194+
}
195+
}
196+
SwitchCaseSyntax("default:") {
197+
StmtSyntax("return nil")
198+
}
199+
}
200+
}
201+
202+
try VariableDeclSyntax("var requiresTrailingSpace: Bool?") {
203+
try SwitchExprSyntax("switch self") {
204+
for node in SYNTAX_NODES where !node.isBase {
205+
for child in node.children {
206+
if let requiresTrailingSpace = child.requiresTrailingSpace {
207+
SwitchCaseSyntax("case \\\(raw: node.type.syntaxBaseName).\(raw: child.swiftName):") {
208+
StmtSyntax("return \(literal: requiresTrailingSpace)")
209+
}
210+
}
211+
}
212+
}
213+
SwitchCaseSyntax("default:") {
214+
StmtSyntax("return nil")
215+
}
216+
}
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)