Skip to content

Commit 7355492

Browse files
authored
Merge pull request swiftlang#18503 from ahoppen/comment-classification
[swiftSyntax] Add syntax classification infrastructure for comments
2 parents 6a2c8bd + e4eb76f commit 7355492

File tree

3 files changed

+65
-43
lines changed

3 files changed

+65
-43
lines changed

tools/SwiftSyntax/SyntaxClassifier.swift.gyb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public enum SyntaxClassification {
2828
% end
2929
}
3030

31-
class _SyntaxClassifier: SyntaxVisitor {
31+
fileprivate class _SyntaxClassifier: SyntaxVisitor {
3232

3333
/// Don't classify nodes with these IDs or any of their children
3434
var skipNodeIds: Set<SyntaxNodeId> = []
@@ -71,6 +71,9 @@ class _SyntaxClassifier: SyntaxVisitor {
7171
}
7272

7373
override func visit(_ token: TokenSyntax) {
74+
// FIXME: We need to come up with some way in which the SyntaxClassifier can
75+
// classify trivia (i.e. comments). In particular we need to be able to
76+
// look into the comments to find things like URLs or keywords like MARK.
7477
var classification = contextStack.last!.classification
7578
if !contextStack.last!.force {
7679
if let contextFreeClassification =
@@ -84,7 +87,8 @@ class _SyntaxClassifier: SyntaxVisitor {
8487
classification = .editorPlaceholder
8588
}
8689
}
87-
assert(classifications[token] == nil)
90+
assert(classifications[token] == nil,
91+
"\(token) has already been classified")
8892
classifications[token] = classification
8993
}
9094

tools/swift-swiftsyntax-test/ClassifiedSyntaxTreePrinter.swift

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,34 @@
11
import SwiftSyntax
22
import Foundation
33

4+
extension SyntaxClassification {
5+
var tag: String {
6+
switch self {
7+
case .none: return ""
8+
case .keyword: return "kw"
9+
case .identifier: return ""
10+
case .typeIdentifier: return "type"
11+
case .dollarIdentifier: return "dollar"
12+
case .integerLiteral: return "int"
13+
case .floatingLiteral: return "float"
14+
case .stringLiteral: return "str"
15+
case .stringInterpolationAnchor: return "anchor"
16+
case .poundDirectiveKeyword: return "#kw"
17+
case .buildConfigId: return "#id"
18+
case .attribute: return "attr-builtin"
19+
case .objectLiteral: return "object-literal"
20+
case .editorPlaceholder: return "placeholder"
21+
case .lineComment: return "comment-line"
22+
case .blockComment: return "comment-block"
23+
case .docLineComment: return "doc-comment-line"
24+
case .docBlockComment: return "doc-comment-block"
25+
}
26+
}
27+
}
28+
429
class ClassifiedSyntaxTreePrinter: SyntaxVisitor {
530
private let classifications: [TokenSyntax: SyntaxClassification]
6-
private var currentTag = ""
31+
private var currentClassification = SyntaxClassification.none
732
private var skipNextNewline = false
833
private var result = ""
934

@@ -17,56 +42,58 @@ class ClassifiedSyntaxTreePrinter: SyntaxVisitor {
1742
result = ""
1843
visit(tree)
1944
// Emit the last closing tag
20-
recordCurrentTag("")
45+
recordCurrentClassification(.none)
2146
return result
2247
}
2348

2449
// MARK: Implementation
2550

2651
/// Closes the current tag if it is different from the previous one and opens
2752
/// a tag with the specified ID.
28-
private func recordCurrentTag(_ tag: String) {
29-
if currentTag != tag {
30-
if !currentTag.isEmpty {
31-
result += "</" + currentTag + ">"
53+
private func recordCurrentClassification(
54+
_ classification: SyntaxClassification
55+
) {
56+
if currentClassification != classification {
57+
if currentClassification != .none {
58+
result += "</" + currentClassification.tag + ">"
3259
}
33-
if !tag.isEmpty {
34-
result += "<" + tag + ">"
60+
if classification != .none {
61+
result += "<" + classification.tag + ">"
3562
}
3663
}
37-
currentTag = tag
64+
currentClassification = classification
3865
}
3966

4067
private func visit(_ piece: TriviaPiece) {
41-
let tag: String
68+
let classification: SyntaxClassification
4269
switch piece {
4370
case .spaces, .tabs, .verticalTabs, .formfeeds:
44-
tag = ""
71+
classification = .none
4572
case .newlines, .carriageReturns, .carriageReturnLineFeeds:
4673
if skipNextNewline {
4774
skipNextNewline = false
4875
return
4976
}
50-
tag = ""
77+
classification = .none
5178
case .backticks:
52-
tag = ""
79+
classification = .none
5380
case .lineComment(let text):
5481
// Don't print CHECK lines
5582
if text.hasPrefix("// CHECK") {
5683
skipNextNewline = true
5784
return
5885
}
59-
tag = "comment-line"
86+
classification = .lineComment
6087
case .blockComment:
61-
tag = "comment-block"
88+
classification = .blockComment
6289
case .docLineComment:
63-
tag = "doc-comment-line"
90+
classification = .docLineComment
6491
case .docBlockComment:
65-
tag = "doc-comment-block"
92+
classification = .docBlockComment
6693
case .garbageText:
67-
tag = ""
94+
classification = .none
6895
}
69-
recordCurrentTag(tag)
96+
recordCurrentClassification(classification)
7097
piece.write(to: &result)
7198
}
7299

@@ -76,31 +103,10 @@ class ClassifiedSyntaxTreePrinter: SyntaxVisitor {
76103
}
77104
}
78105

79-
private func getTagForSyntaxClassification(
80-
_ classification: SyntaxClassification
81-
) -> String {
82-
switch (classification) {
83-
case .none: return ""
84-
case .keyword: return "kw"
85-
case .identifier: return ""
86-
case .typeIdentifier: return "type"
87-
case .dollarIdentifier: return "dollar"
88-
case .integerLiteral: return "int"
89-
case .floatingLiteral: return "float"
90-
case .stringLiteral: return "str"
91-
case .stringInterpolationAnchor: return "anchor"
92-
case .poundDirectiveKeyword: return "#kw"
93-
case .buildConfigId: return "#id"
94-
case .attribute: return "attr-builtin"
95-
case .objectLiteral: return "object-literal"
96-
case .editorPlaceholder: return "placeholder"
97-
}
98-
}
99-
100106
override func visit(_ node: TokenSyntax) {
101107
visit(node.leadingTrivia)
102108
let classification = classifications[node] ?? SyntaxClassification.none
103-
recordCurrentTag(getTagForSyntaxClassification(classification))
109+
recordCurrentClassification(classification)
104110
result += node.text
105111
visit(node.trailingTrivia)
106112
}

utils/gyb_syntax_support/Classification.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ def __init__(self, name, description):
5656
SyntaxClassification('EditorPlaceholder', description='''
5757
An editor placeholder of the form `<#content#>`
5858
'''),
59+
SyntaxClassification('LineComment', description='''
60+
A line comment starting with `//`.
61+
'''),
62+
SyntaxClassification('DocLineComment', description='''
63+
A doc line comment starting with `///`.
64+
'''),
65+
SyntaxClassification('BlockComment', description='''
66+
A block comment starting with `/**` and ending with `*/.
67+
'''),
68+
SyntaxClassification('DocBlockComment', description='''
69+
A doc block comment starting with `/**` and ending with `*/.
70+
'''),
5971
]
6072

6173

0 commit comments

Comments
 (0)