Skip to content

Commit 90385e3

Browse files
committed
Move SyntaxClassification to SyntaxBuilder
1 parent 856b585 commit 90385e3

File tree

13 files changed

+449
-304
lines changed

13 files changed

+449
-304
lines changed

CodeGeneration/Package.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ let package = Package(
99
],
1010
products: [
1111
.executable(name: "generate-swiftbasicformat", targets: ["generate-swiftbasicformat"]),
12+
.executable(name: "generate-swiftideutils", targets: ["generate-swiftideutils"]),
1213
.executable(name: "generate-swiftsyntaxbuilder", targets: ["generate-swiftsyntaxbuilder"]),
1314
],
1415
dependencies: [
@@ -26,6 +27,16 @@ let package = Package(
2627
"Utils"
2728
]
2829
),
30+
.executableTarget(
31+
name: "generate-swiftideutils",
32+
dependencies: [
33+
.product(name: "SwiftSyntax", package: "swift-syntax"),
34+
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
35+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
36+
"SyntaxSupport",
37+
"Utils"
38+
]
39+
),
2940
.executableTarget(
3041
name: "generate-swiftsyntaxbuilder",
3142
dependencies: [

CodeGeneration/Sources/SyntaxSupport/Classification.swift.gyb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ public class SyntaxClassification {
3434
}
3535
}
3636

37+
public class ChildClassification {
38+
public let parent: Node
39+
public let childIndex: Int
40+
public let isToken: Bool
41+
public let classification: SyntaxClassification?
42+
public let force: Bool
43+
44+
public init(node: Node, childIndex: Int, child: Child) {
45+
self.parent = node
46+
self.childIndex = childIndex
47+
self.isToken = child.syntaxKind.hasSuffix("Token")
48+
self.classification = child.classification
49+
self.force = child.forceClassification
50+
}
51+
}
52+
53+
3754
public let SYNTAX_CLASSIFICATIONS: [SyntaxClassification] = [
3855
% for syntaxClassification in SYNTAX_CLASSIFICATIONS:
3956
SyntaxClassification(name: "${syntaxClassification.name}", description: "${syntaxClassification.description.strip()}"),

CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift.gyb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class TokenSpec {
2525
public let nameForDiagnostics: String
2626
public let unprefixedKind: String
2727
public let text: String?
28-
public let classification: String
28+
public let classification: SyntaxClassification?
2929
public let isKeyword: Bool
3030
public let requiresLeadingSpace: Bool
3131
public let requiresTrailingSpace: Bool
@@ -60,7 +60,7 @@ public class TokenSpec {
6060
self.unprefixedKind = kind
6161
}
6262
self.text = text
63-
self.classification = classification
63+
self.classification = classificationByName(classification)
6464
self.isKeyword = isKeyword
6565
self.requiresLeadingSpace = requiresLeadingSpace
6666
self.requiresTrailingSpace = requiresTrailingSpace
@@ -209,6 +209,7 @@ public class MiscSpec: TokenSpec { }
209209
public let SYNTAX_TOKENS: [TokenSpec] = [
210210
% for token in SYNTAX_TOKENS:
211211
% class_name = type(token).__name__
212+
% classification = "classification: \"%s\"" % token.classification.name if token.classification.name not in ['None', 'Keyword', 'ObjectLiteral', 'PoundDirectiveKeyword'] or class_name == 'Misc' and token.classification.name != 'None' else None
212213
% parameters = ["name: \"%s\"" % token.name]
213214
% if class_name in ['Keyword', 'SwiftKeyword', 'DeclKeyword', 'StmtKeyword', 'ExprKeyword', 'PatternKeyword', 'SilKeyword']:
214215
% parameters += ["text: \"%s\"" % token.text]
@@ -222,8 +223,15 @@ public let SYNTAX_TOKENS: [TokenSpec] = [
222223
% if token.text:
223224
% parameters += ["text: \"%s\"" % token.text]
224225
% end
226+
% if classification:
227+
% parameters += [classification]
228+
% end
225229
% elif class_name == 'PoundObjectLiteral':
226-
% parameters += ["kind: \"%s\"" % token.kind, "text: \"%s\"" % token.text, "nameForDiagnostics: \"%s\"" % token.name_for_diagnostics, "protocol: \"%s\"" % token.protocol]
230+
% parameters += ["kind: \"%s\"" % token.kind, "text: \"%s\"" % token.text]
231+
% if classification:
232+
% parameters += [classification]
233+
% end
234+
% parameters += ["nameForDiagnostics: \"%s\"" % token.name_for_diagnostics, "protocol: \"%s\"" % token.protocol]
227235
% else:
228236
% print("Unknown token `%s`", (token.name), file=sys.stderr)
229237
% sys.exit(1)

CodeGeneration/Sources/SyntaxSupport/gyb_generated/Classification.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,23 @@ public class SyntaxClassification {
2727
}
2828
}
2929

30+
public class ChildClassification {
31+
public let parent: Node
32+
public let childIndex: Int
33+
public let isToken: Bool
34+
public let classification: SyntaxClassification?
35+
public let force: Bool
36+
37+
public init(node: Node, childIndex: Int, child: Child) {
38+
self.parent = node
39+
self.childIndex = childIndex
40+
self.isToken = child.syntaxKind.hasSuffix("Token")
41+
self.classification = child.classification
42+
self.force = child.forceClassification
43+
}
44+
}
45+
46+
3047
public let SYNTAX_CLASSIFICATIONS: [SyntaxClassification] = [
3148
SyntaxClassification(name: "None", description: "The token should not receive syntax coloring."),
3249
SyntaxClassification(name: "Keyword", description: "A Swift keyword, including contextual keywords."),

CodeGeneration/Sources/SyntaxSupport/gyb_generated/TokenSpec.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class TokenSpec {
1919
public let nameForDiagnostics: String
2020
public let unprefixedKind: String
2121
public let text: String?
22-
public let classification: String
22+
public let classification: SyntaxClassification?
2323
public let isKeyword: Bool
2424
public let requiresLeadingSpace: Bool
2525
public let requiresTrailingSpace: Bool
@@ -54,7 +54,7 @@ public class TokenSpec {
5454
self.unprefixedKind = kind
5555
}
5656
self.text = text
57-
self.classification = classification
57+
self.classification = classificationByName(classification)
5858
self.isKeyword = isKeyword
5959
self.requiresLeadingSpace = requiresLeadingSpace
6060
self.requiresTrailingSpace = requiresTrailingSpace
@@ -275,7 +275,7 @@ public let SYNTAX_TOKENS: [TokenSpec] = [
275275
PunctuatorSpec(name: "Colon", kind: "colon", text: ":", requiresTrailingSpace: true),
276276
PunctuatorSpec(name: "Semicolon", kind: "semi", text: ";"),
277277
PunctuatorSpec(name: "Equal", kind: "equal", text: "=", requiresLeadingSpace: true, requiresTrailingSpace: true),
278-
PunctuatorSpec(name: "AtSign", kind: "at_sign", text: "@"),
278+
PunctuatorSpec(name: "AtSign", kind: "at_sign", text: "@", classification: "Attribute"),
279279
PunctuatorSpec(name: "Pound", kind: "pound", text: "#"),
280280
PunctuatorSpec(name: "PrefixAmpersand", kind: "amp_prefix", text: "&", requiresLeadingSpace: true, requiresTrailingSpace: true),
281281
PunctuatorSpec(name: "Arrow", kind: "arrow", text: "->", requiresLeadingSpace: true, requiresTrailingSpace: true),
@@ -284,9 +284,9 @@ public let SYNTAX_TOKENS: [TokenSpec] = [
284284
PunctuatorSpec(name: "ExclamationMark", kind: "exclaim_postfix", text: "!"),
285285
PunctuatorSpec(name: "PostfixQuestionMark", kind: "question_postfix", text: "?"),
286286
PunctuatorSpec(name: "InfixQuestionMark", kind: "question_infix", text: "?"),
287-
PunctuatorSpec(name: "StringQuote", kind: "string_quote", text: "\""),
288-
PunctuatorSpec(name: "SingleQuote", kind: "single_quote", text: "\'"),
289-
PunctuatorSpec(name: "MultilineStringQuote", kind: "multiline_string_quote", text: "\"\"\""),
287+
PunctuatorSpec(name: "StringQuote", kind: "string_quote", text: "\"", classification: "StringLiteral"),
288+
PunctuatorSpec(name: "SingleQuote", kind: "single_quote", text: "\'", classification: "StringLiteral"),
289+
PunctuatorSpec(name: "MultilineStringQuote", kind: "multiline_string_quote", text: "\"\"\"", classification: "StringLiteral"),
290290
PoundKeywordSpec(name: "PoundKeyPath", kind: "pound_keyPath", text: "#keyPath"),
291291
PoundKeywordSpec(name: "PoundLine", kind: "pound_line", text: "#line"),
292292
PoundKeywordSpec(name: "PoundSelector", kind: "pound_selector", text: "#selector"),
@@ -310,21 +310,21 @@ public let SYNTAX_TOKENS: [TokenSpec] = [
310310
PoundObjectLiteralSpec(name: "PoundImageLiteral", kind: "pound_imageLiteral", text: "#imageLiteral", nameForDiagnostics: "image", protocol: "ExpressibleByImageLiteral"),
311311
PoundObjectLiteralSpec(name: "PoundColorLiteral", kind: "pound_colorLiteral", text: "#colorLiteral", nameForDiagnostics: "color", protocol: "ExpressibleByColorLiteral"),
312312
PoundConfigSpec(name: "PoundHasSymbol", kind: "pound__hasSymbol", text: "#_hasSymbol"),
313-
LiteralSpec(name: "IntegerLiteral", kind: "integer_literal", nameForDiagnostics: "integer literal"),
314-
LiteralSpec(name: "FloatingLiteral", kind: "floating_literal", nameForDiagnostics: "floating literal"),
315-
LiteralSpec(name: "StringLiteral", kind: "string_literal", nameForDiagnostics: "string literal"),
313+
LiteralSpec(name: "IntegerLiteral", kind: "integer_literal", nameForDiagnostics: "integer literal", classification: "IntegerLiteral"),
314+
LiteralSpec(name: "FloatingLiteral", kind: "floating_literal", nameForDiagnostics: "floating literal", classification: "FloatingLiteral"),
315+
LiteralSpec(name: "StringLiteral", kind: "string_literal", nameForDiagnostics: "string literal", classification: "StringLiteral"),
316316
LiteralSpec(name: "RegexLiteral", kind: "regex_literal", nameForDiagnostics: "regex literal"),
317317
MiscSpec(name: "Unknown", kind: "unknown", nameForDiagnostics: "token"),
318-
MiscSpec(name: "Identifier", kind: "identifier", nameForDiagnostics: "identifier"),
319-
MiscSpec(name: "UnspacedBinaryOperator", kind: "oper_binary_unspaced", nameForDiagnostics: "binary operator"),
320-
MiscSpec(name: "SpacedBinaryOperator", kind: "oper_binary_spaced", nameForDiagnostics: "binary operator", requiresLeadingSpace: true, requiresTrailingSpace: true),
321-
MiscSpec(name: "PostfixOperator", kind: "oper_postfix", nameForDiagnostics: "postfix operator"),
322-
MiscSpec(name: "PrefixOperator", kind: "oper_prefix", nameForDiagnostics: "prefix operator"),
323-
MiscSpec(name: "DollarIdentifier", kind: "dollarident", nameForDiagnostics: "dollar identifier"),
324-
MiscSpec(name: "ContextualKeyword", kind: "contextual_keyword", nameForDiagnostics: "keyword"),
318+
MiscSpec(name: "Identifier", kind: "identifier", nameForDiagnostics: "identifier", classification: "Identifier"),
319+
MiscSpec(name: "UnspacedBinaryOperator", kind: "oper_binary_unspaced", nameForDiagnostics: "binary operator", classification: "OperatorIdentifier"),
320+
MiscSpec(name: "SpacedBinaryOperator", kind: "oper_binary_spaced", nameForDiagnostics: "binary operator", classification: "OperatorIdentifier", requiresLeadingSpace: true, requiresTrailingSpace: true),
321+
MiscSpec(name: "PostfixOperator", kind: "oper_postfix", nameForDiagnostics: "postfix operator", classification: "OperatorIdentifier"),
322+
MiscSpec(name: "PrefixOperator", kind: "oper_prefix", nameForDiagnostics: "prefix operator", classification: "OperatorIdentifier"),
323+
MiscSpec(name: "DollarIdentifier", kind: "dollarident", nameForDiagnostics: "dollar identifier", classification: "DollarIdentifier"),
324+
MiscSpec(name: "ContextualKeyword", kind: "contextual_keyword", nameForDiagnostics: "keyword", classification: "Keyword"),
325325
MiscSpec(name: "RawStringDelimiter", kind: "raw_string_delimiter", nameForDiagnostics: "raw string delimiter"),
326-
MiscSpec(name: "StringSegment", kind: "string_segment", nameForDiagnostics: "string segment"),
327-
MiscSpec(name: "StringInterpolationAnchor", kind: "string_interpolation_anchor", nameForDiagnostics: "string interpolation anchor", text: ")"),
326+
MiscSpec(name: "StringSegment", kind: "string_segment", nameForDiagnostics: "string segment", classification: "StringLiteral"),
327+
MiscSpec(name: "StringInterpolationAnchor", kind: "string_interpolation_anchor", nameForDiagnostics: "string interpolation anchor", text: ")", classification: "StringInterpolationAnchor"),
328328
MiscSpec(name: "Yield", kind: "kw_yield", nameForDiagnostics: "yield", text: "yield"),
329329
]
330330

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 ArgumentParser
14+
import Foundation
15+
import SwiftSyntaxBuilder
16+
import Utils
17+
18+
@main
19+
struct GenerateSwiftIDEUtils: ParsableCommand {
20+
@Argument(help: "The path to the destination directory where the source files are to be generated")
21+
var generatedPath: String
22+
23+
@Flag(help: "Enable verbose output")
24+
var verbose: Bool = false
25+
26+
func run() throws {
27+
try generateTemplates(
28+
templates: [
29+
(syntaxClassificationFile, "SyntaxClassification.swift"),
30+
],
31+
destination: URL(fileURLWithPath: generatedPath),
32+
verbose: verbose
33+
)
34+
}
35+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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 SwiftSyntax
15+
import SwiftSyntaxBuilder
16+
import SyntaxSupport
17+
import Utils
18+
19+
// Collects the list of classifications to use for contextual classification.
20+
var node_child_classifications: [ChildClassification] {
21+
var result = [ChildClassification]()
22+
for node in SYNTAX_NODES {
23+
for (index, child) in node.children.enumerated() where child.classification?.name != nil {
24+
result.append(
25+
ChildClassification(node: node, childIndex: index, child: child)
26+
)
27+
}
28+
}
29+
30+
return result
31+
}
32+
33+
let syntaxClassificationFile = SourceFile {
34+
ImportDecl(
35+
"""
36+
\(copyrightHeader)
37+
@_spi(RawSyntax) import SwiftSyntax
38+
"""
39+
)
40+
41+
EnumDeclSyntax("public enum SyntaxClassification") {
42+
for classification in SYNTAX_CLASSIFICATIONS {
43+
EnumCaseDecl(
44+
"""
45+
/// \(classification.description)
46+
case \(classification.swiftName)
47+
"""
48+
)
49+
}
50+
}
51+
52+
ExtensionDeclSyntax("extension SyntaxClassification") {
53+
FunctionDecl(
54+
"""
55+
/// Checks if a node has a classification attached via its syntax definition.
56+
/// - Parameters:
57+
/// - parentKind: The parent node syntax kind.
58+
/// - indexInParent: The index of the node in its parent.
59+
/// - childKind: The node syntax kind.
60+
/// - Returns: A pair of classification and whether it is "forced", or nil if
61+
/// no classification is attached.
62+
internal static func classify(
63+
parentKind: SyntaxKind, indexInParent: Int, childKind: SyntaxKind
64+
) -> (SyntaxClassification, Bool)?
65+
""") {
66+
IfStmt(
67+
leadingTrivia: [.docLineComment("// Separate checks for token nodes (most common checks) versus checks for layout nodes."), .newlines(1)],
68+
conditions: ConditionElementList {
69+
ConditionElement(condition: .expression("childKind == .token"))
70+
}
71+
) {
72+
SwitchStmtSyntax(expression: ExprSyntax("(parentKind, indexInParent)")) {
73+
for childClassification in node_child_classifications where childClassification.isToken {
74+
SwitchCaseSyntax("case (.\(childClassification.parent.swiftSyntaxKind), \(childClassification.childIndex)):") {
75+
ReturnStmt("return (.\(childClassification.classification!.swiftName), \(childClassification.force))")
76+
}
77+
}
78+
79+
SwitchCaseSyntax("default: return nil")
80+
}
81+
} elseBody: {
82+
SwitchStmtSyntax(expression: ExprSyntax("(parentKind, indexInParent)")) {
83+
for childClassification in node_child_classifications where !childClassification.isToken {
84+
SwitchCaseSyntax("case (.\(childClassification.parent.swiftSyntaxKind), \(childClassification.childIndex)):") {
85+
ReturnStmt("return (.\(childClassification.classification!.swiftName), \(childClassification.force))")
86+
}
87+
}
88+
89+
SwitchCaseSyntax("default: return nil")
90+
}
91+
}
92+
}
93+
}
94+
95+
ExtensionDecl("extension RawTokenKind") {
96+
VariableDeclSyntax(
97+
modifiers: [DeclModifier(name: .internal)],
98+
name: IdentifierPattern("classification"),
99+
type: TypeAnnotation(type: TypeSyntax("SyntaxClassification"))) {
100+
SwitchStmt(expression: ExprSyntax("self")) {
101+
for token in SYNTAX_TOKENS {
102+
SwitchCaseSyntax("case .\(token.swiftKind):") {
103+
if let classification = token.classification {
104+
ReturnStmt("return .\(classification.swiftName)")
105+
} else {
106+
ReturnStmt("return .none)")
107+
}
108+
}
109+
}
110+
SwitchCaseSyntax("case .eof:") {
111+
ReturnStmt("return .none")
112+
}
113+
}
114+
}
115+
}
116+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
let copyrightHeader = """
14+
//// Automatically Generated by generate-ideutils
15+
//// Do Not Edit Directly!
16+
//===----------------------------------------------------------------------===//
17+
//
18+
// This source file is part of the Swift.org open source project
19+
//
20+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
21+
// Licensed under Apache License v2.0 with Runtime Library Exception
22+
//
23+
// See https://swift.org/LICENSE.txt for license information
24+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
25+
//
26+
//===----------------------------------------------------------------------===//
27+
28+
29+
"""

Package.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ let package = Package(
104104
dependencies: ["SwiftSyntax"],
105105
exclude: [
106106
"CMakeLists.txt",
107-
"SyntaxClassification.swift.gyb",
108107
]
109108
),
110109
.target(

Sources/IDEUtils/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
add_swift_host_library(IDEUtils
10-
gyb_generated/SyntaxClassification.swift
10+
generated/SyntaxClassification.swift
1111
Syntax+Classifications.swift
1212
SyntaxClassifier.swift
1313
)

0 commit comments

Comments
 (0)