Skip to content

Commit 090945d

Browse files
committed
[Perf] Optimize TokenSequence
i.e. 'previousToken', 'nextToken', 'firstToken', and 'lastToken' Iterate over 'layoutBuffer' directly to avoid 'SyntaxChildren' which implies retaining 'SyntaxDataArena' for each element. This way, it only retains the found 'TokenSyntax', nothing else.
1 parent 37fa0c8 commit 090945d

File tree

3 files changed

+69
-47
lines changed

3 files changed

+69
-47
lines changed

Diff for: Sources/SwiftSyntax/Syntax.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable {
3636
self.dataRef = dataRef
3737
}
3838

39-
private var data: SyntaxData {
39+
var data: SyntaxData {
4040
@_transparent unsafeAddress { dataRef.pointer }
4141
}
4242

Diff for: Sources/SwiftSyntax/SyntaxProtocol.swift

+4-46
Original file line numberDiff line numberDiff line change
@@ -253,20 +253,7 @@ extension SyntaxProtocol {
253253
/// Recursively walks through the tree to find the token semantically before
254254
/// this node.
255255
public func previousToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
256-
guard let parent = self.parent else {
257-
return nil
258-
}
259-
let siblings = parent.children(viewMode: viewMode)
260-
// `self` could be a missing node at index 0 and `viewMode` be `.sourceAccurate`.
261-
// In that case `siblings` skips over the missing `self` node and has a `startIndex > 0`.
262-
if siblings.startIndex < self.indexInParent {
263-
for child in siblings[..<self.indexInParent].reversed() {
264-
if let token = child.lastToken(viewMode: viewMode) {
265-
return token
266-
}
267-
}
268-
}
269-
return parent.previousToken(viewMode: viewMode)
256+
return self._syntaxNode.previousToken(viewMode: viewMode)
270257
}
271258

272259
@available(*, deprecated, message: "Use nextToken(viewMode:) instead")
@@ -277,16 +264,7 @@ extension SyntaxProtocol {
277264
/// Recursively walks through the tree to find the next token semantically
278265
/// after this node.
279266
public func nextToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
280-
guard let parent = self.parent else {
281-
return nil
282-
}
283-
let siblings = parent.children(viewMode: viewMode)
284-
for child in siblings[siblings.index(after: self.indexInParent)...] {
285-
if let token = child.firstToken(viewMode: viewMode) {
286-
return token
287-
}
288-
}
289-
return parent.nextToken(viewMode: viewMode)
267+
return self._syntaxNode.nextToken(viewMode: viewMode)
290268
}
291269

292270
@available(*, deprecated, message: "Use firstToken(viewMode: .sourceAccurate) instead")
@@ -296,17 +274,7 @@ extension SyntaxProtocol {
296274

297275
/// Returns the first token node that is part of this syntax node.
298276
public func firstToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
299-
guard viewMode.shouldTraverse(node: raw) else { return nil }
300-
if let token = _syntaxNode.as(TokenSyntax.self) {
301-
return token
302-
}
303-
304-
for child in children(viewMode: viewMode) {
305-
if let token = child.firstToken(viewMode: viewMode) {
306-
return token
307-
}
308-
}
309-
return nil
277+
return self._syntaxNode.firstToken(viewMode: viewMode)
310278
}
311279

312280
@available(*, deprecated, message: "Use lastToken(viewMode: .sourceAccurate) instead")
@@ -316,17 +284,7 @@ extension SyntaxProtocol {
316284

317285
/// Returns the last token node that is part of this syntax node.
318286
public func lastToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
319-
guard viewMode.shouldTraverse(node: raw) else { return nil }
320-
if let token = _syntaxNode.as(TokenSyntax.self) {
321-
return token
322-
}
323-
324-
for child in children(viewMode: viewMode).reversed() {
325-
if let tok = child.lastToken(viewMode: viewMode) {
326-
return tok
327-
}
328-
}
329-
return nil
287+
return self._syntaxNode.lastToken(viewMode: viewMode)
330288
}
331289

332290
/// Sequence of tokens that are part of this Syntax node.

Diff for: Sources/SwiftSyntax/TokenSequence.swift

+64
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,70 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
extension Syntax {
14+
/// Implementation of 'SyntaxProtocol.previousToken(viewMode:)'
15+
func previousToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
16+
guard let parentDataRef = data.parent else {
17+
return nil
18+
}
19+
for case let childDataRef? in arena.layout(for: parentDataRef)[..<layoutIndexInParent].reversed() {
20+
if let token = Syntax(arena: arena, dataRef: childDataRef).lastToken(viewMode: viewMode) {
21+
return token
22+
}
23+
}
24+
return Syntax(arena: arena, dataRef: parentDataRef).previousToken(viewMode: viewMode)
25+
}
26+
27+
/// Implementation of 'SyntaxProtocol.nextToken(viewMode:)'
28+
func nextToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
29+
guard let parentDataRef = data.parent else {
30+
return nil
31+
}
32+
for case let childDataRef? in arena.layout(for: parentDataRef)[(layoutIndexInParent &+ 1)...] {
33+
if let token = Syntax(arena: arena, dataRef: childDataRef).firstToken(viewMode: viewMode) {
34+
return token
35+
}
36+
}
37+
return Syntax(arena: arena, dataRef: parentDataRef).nextToken(viewMode: viewMode)
38+
}
39+
40+
/// Implementation of 'SyntaxProtocol.firstToken(viewMode:)'
41+
///
42+
/// - Note: Can't use 'RawSyntax.firstToken(viewMode:)' because it loses absolute info.
43+
func firstToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
44+
guard viewMode.shouldTraverse(node: raw) else {
45+
return nil
46+
}
47+
if raw.isToken {
48+
return TokenSyntax(self)!
49+
}
50+
for case let childDataRef? in layoutBuffer {
51+
if let token = Syntax(arena: arena, dataRef: childDataRef).firstToken(viewMode: viewMode) {
52+
return token
53+
}
54+
}
55+
return nil
56+
}
57+
58+
/// Implementation of 'SyntaxProtocol.lastToken(viewMode:)'
59+
///
60+
/// - Note: Can't use 'RawSyntax.lastToken(viewMode:)' because it loses absolute info.
61+
func lastToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? {
62+
guard viewMode.shouldTraverse(node: raw) else {
63+
return nil
64+
}
65+
if raw.isToken {
66+
return TokenSyntax(self)!
67+
}
68+
for case let childDataRef? in layoutBuffer.reversed() {
69+
if let token = Syntax(arena: arena, dataRef: childDataRef).lastToken(viewMode: viewMode) {
70+
return token
71+
}
72+
}
73+
return nil
74+
}
75+
}
76+
1377
/// Sequence of tokens that are part of the provided Syntax node.
1478
public struct TokenSequence: Sequence, Sendable {
1579
/// Iterates over a ``TokenSequence``.

0 commit comments

Comments
 (0)