From 090945db4baad725ecf1638765f4e50029678dfb Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 10 Jan 2025 17:16:08 -0800 Subject: [PATCH] [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. --- Sources/SwiftSyntax/Syntax.swift | 2 +- Sources/SwiftSyntax/SyntaxProtocol.swift | 50 ++---------------- Sources/SwiftSyntax/TokenSequence.swift | 64 ++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 47 deletions(-) diff --git a/Sources/SwiftSyntax/Syntax.swift b/Sources/SwiftSyntax/Syntax.swift index 55f68315780..d0408e20ce5 100644 --- a/Sources/SwiftSyntax/Syntax.swift +++ b/Sources/SwiftSyntax/Syntax.swift @@ -36,7 +36,7 @@ public struct Syntax: SyntaxProtocol, SyntaxHashable { self.dataRef = dataRef } - private var data: SyntaxData { + var data: SyntaxData { @_transparent unsafeAddress { dataRef.pointer } } diff --git a/Sources/SwiftSyntax/SyntaxProtocol.swift b/Sources/SwiftSyntax/SyntaxProtocol.swift index da561ca0b3a..e6597c31ac8 100644 --- a/Sources/SwiftSyntax/SyntaxProtocol.swift +++ b/Sources/SwiftSyntax/SyntaxProtocol.swift @@ -253,20 +253,7 @@ extension SyntaxProtocol { /// Recursively walks through the tree to find the token semantically before /// this node. public func previousToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { - guard let parent = self.parent else { - return nil - } - let siblings = parent.children(viewMode: viewMode) - // `self` could be a missing node at index 0 and `viewMode` be `.sourceAccurate`. - // In that case `siblings` skips over the missing `self` node and has a `startIndex > 0`. - if siblings.startIndex < self.indexInParent { - for child in siblings[.. TokenSyntax? { - guard let parent = self.parent else { - return nil - } - let siblings = parent.children(viewMode: viewMode) - for child in siblings[siblings.index(after: self.indexInParent)...] { - if let token = child.firstToken(viewMode: viewMode) { - return token - } - } - return parent.nextToken(viewMode: viewMode) + return self._syntaxNode.nextToken(viewMode: viewMode) } @available(*, deprecated, message: "Use firstToken(viewMode: .sourceAccurate) instead") @@ -296,17 +274,7 @@ extension SyntaxProtocol { /// Returns the first token node that is part of this syntax node. public func firstToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { - guard viewMode.shouldTraverse(node: raw) else { return nil } - if let token = _syntaxNode.as(TokenSyntax.self) { - return token - } - - for child in children(viewMode: viewMode) { - if let token = child.firstToken(viewMode: viewMode) { - return token - } - } - return nil + return self._syntaxNode.firstToken(viewMode: viewMode) } @available(*, deprecated, message: "Use lastToken(viewMode: .sourceAccurate) instead") @@ -316,17 +284,7 @@ extension SyntaxProtocol { /// Returns the last token node that is part of this syntax node. public func lastToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { - guard viewMode.shouldTraverse(node: raw) else { return nil } - if let token = _syntaxNode.as(TokenSyntax.self) { - return token - } - - for child in children(viewMode: viewMode).reversed() { - if let tok = child.lastToken(viewMode: viewMode) { - return tok - } - } - return nil + return self._syntaxNode.lastToken(viewMode: viewMode) } /// Sequence of tokens that are part of this Syntax node. diff --git a/Sources/SwiftSyntax/TokenSequence.swift b/Sources/SwiftSyntax/TokenSequence.swift index ef31c2c129c..1567919c451 100644 --- a/Sources/SwiftSyntax/TokenSequence.swift +++ b/Sources/SwiftSyntax/TokenSequence.swift @@ -10,6 +10,70 @@ // //===----------------------------------------------------------------------===// +extension Syntax { + /// Implementation of 'SyntaxProtocol.previousToken(viewMode:)' + func previousToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { + guard let parentDataRef = data.parent else { + return nil + } + for case let childDataRef? in arena.layout(for: parentDataRef)[.. TokenSyntax? { + guard let parentDataRef = data.parent else { + return nil + } + for case let childDataRef? in arena.layout(for: parentDataRef)[(layoutIndexInParent &+ 1)...] { + if let token = Syntax(arena: arena, dataRef: childDataRef).firstToken(viewMode: viewMode) { + return token + } + } + return Syntax(arena: arena, dataRef: parentDataRef).nextToken(viewMode: viewMode) + } + + /// Implementation of 'SyntaxProtocol.firstToken(viewMode:)' + /// + /// - Note: Can't use 'RawSyntax.firstToken(viewMode:)' because it loses absolute info. + func firstToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { + guard viewMode.shouldTraverse(node: raw) else { + return nil + } + if raw.isToken { + return TokenSyntax(self)! + } + for case let childDataRef? in layoutBuffer { + if let token = Syntax(arena: arena, dataRef: childDataRef).firstToken(viewMode: viewMode) { + return token + } + } + return nil + } + + /// Implementation of 'SyntaxProtocol.lastToken(viewMode:)' + /// + /// - Note: Can't use 'RawSyntax.lastToken(viewMode:)' because it loses absolute info. + func lastToken(viewMode: SyntaxTreeViewMode) -> TokenSyntax? { + guard viewMode.shouldTraverse(node: raw) else { + return nil + } + if raw.isToken { + return TokenSyntax(self)! + } + for case let childDataRef? in layoutBuffer.reversed() { + if let token = Syntax(arena: arena, dataRef: childDataRef).lastToken(viewMode: viewMode) { + return token + } + } + return nil + } +} + /// Sequence of tokens that are part of the provided Syntax node. public struct TokenSequence: Sequence, Sendable { /// Iterates over a ``TokenSequence``.