Skip to content

Commit 285b337

Browse files
authored
Merge pull request #18424 from ahoppen/incremental-classification
[swiftSyntax] Incremental syntax classification
2 parents bbcd061 + bffa15b commit 285b337

File tree

3 files changed

+29
-6
lines changed

3 files changed

+29
-6
lines changed

tools/SwiftSyntax/RawSyntax.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extension CodingUserInfoKey {
2626

2727
/// A ID that uniquely identifies a syntax node and stays stable across multiple
2828
/// incremental parses
29-
struct SyntaxNodeId: Hashable, Codable {
29+
public struct SyntaxNodeId: Hashable, Codable {
3030
private let rawValue: UInt
3131

3232
// Start creating fresh node IDs for user generated nodes on in the upper
@@ -43,11 +43,11 @@ struct SyntaxNodeId: Hashable, Codable {
4343
self.rawValue = rawValue
4444
}
4545

46-
init(from decoder: Decoder) throws {
46+
public init(from decoder: Decoder) throws {
4747
self.rawValue = try decoder.singleValueContainer().decode(UInt.self)
4848
}
4949

50-
func encode(to encoder: Encoder) throws {
50+
public func encode(to encoder: Encoder) throws {
5151
var container = encoder.singleValueContainer()
5252
try container.encode(rawValue)
5353
}

tools/SwiftSyntax/SwiftSyntax.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ public final class SyntaxTreeDeserializer {
5353
/// they were omitted in an incremental syntax tree transfer
5454
private var nodeLookupTable: [SyntaxNodeId: RawSyntax] = [:]
5555

56+
/// The IDs of the nodes that were reused as part of incremental syntax
57+
/// parsing during the last deserialization
58+
public var reusedNodeIds: Set<SyntaxNodeId> = []
59+
5660
public init() {
5761
}
5862

@@ -61,6 +65,7 @@ public final class SyntaxTreeDeserializer {
6165
/// - Returns: A top-level Syntax node representing the contents of the tree,
6266
/// if the parse was successful.
6367
public func deserialize(_ data: Data) throws -> SourceFileSyntax {
68+
reusedNodeIds = []
6469
let decoder = JSONDecoder()
6570
decoder.userInfo[.rawSyntaxDecodedCallback] = self.addToLookupTable
6671
decoder.userInfo[.omittedNodeLookupFunction] = self.lookupNode
@@ -74,6 +79,7 @@ public final class SyntaxTreeDeserializer {
7479
// MARK: Incremental deserialization helper functions
7580

7681
private func lookupNode(id: SyntaxNodeId) -> RawSyntax? {
82+
reusedNodeIds.insert(id)
7783
return nodeLookupTable[id]
7884
}
7985

tools/SwiftSyntax/SyntaxClassifier.swift.gyb

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,18 @@ public enum SyntaxClassification {
3030

3131
class _SyntaxClassifier: SyntaxVisitor {
3232

33+
/// Don't classify nodes with these IDs or any of their children
34+
var skipNodeIds: Set<SyntaxNodeId> = []
35+
36+
/// The top of the `contextStack` determines the classification for all tokens
37+
/// encountered that do not have a native classification. If `force` is `true`
38+
/// the top of the stack also determines the classification of tokens with a
39+
/// native classification
3340
private var contextStack: [(classification: SyntaxClassification, force: Bool)] =
3441
[(classification: .none, force: false)]
3542

43+
/// The classifications that have determined so far are collected in this
44+
/// dictionary
3645
var classifications: [TokenSyntax: SyntaxClassification] = [:]
3746

3847
private func visit(
@@ -82,6 +91,9 @@ class _SyntaxClassifier: SyntaxVisitor {
8291
% for node in SYNTAX_NODES:
8392
% if is_visitable(node):
8493
override func visit(_ node: ${node.name}) {
94+
if skipNodeIds.contains(node.raw.id) {
95+
return
96+
}
8597
% if node.is_unknown() or node.is_syntax_collection():
8698
super.visit(node)
8799
% else:
@@ -114,10 +126,15 @@ class _SyntaxClassifier: SyntaxVisitor {
114126
}
115127

116128
public enum SyntaxClassifier {
117-
/// Classify all tokens in the given syntax tree for syntax highlighting
118-
public static func classifyTokensInTree(_ syntaxTree: SourceFileSyntax)
119-
-> [TokenSyntax: SyntaxClassification] {
129+
/// Classify all tokens in the given syntax tree for syntax highlighting.
130+
/// If a `IncrementalTreeTransferSession` is passed, only nodes that have
131+
/// changed since the last transfer will be classified.
132+
public static func classifyTokensInTree(
133+
_ syntaxTree: SourceFileSyntax,
134+
skipNodes: Set<SyntaxNodeId> = []
135+
) -> [TokenSyntax: SyntaxClassification] {
120136
let classifier = _SyntaxClassifier()
137+
classifier.skipNodeIds = skipNodes
121138
classifier.visit(syntaxTree)
122139
return classifier.classifications
123140
}

0 commit comments

Comments
 (0)