@@ -26,29 +26,40 @@ public protocol IncrementalParseReusedNodeDelegate {
2626 /// - range: The source region of the currently parsed source.
2727 /// - previousNode: The node from the previous tree that is associated with
2828 /// the skipped source region.
29- func parserReusedNode( range : ByteSourceRange , previousNode: Syntax )
29+ func parserReusedNode( previousNode: Syntax )
3030}
3131
3232/// An implementation of `IncrementalParseReusedNodeDelegate` that just collects
3333/// the range and re-used node into an array.
3434public final class IncrementalParseReusedNodeCollector :
3535 IncrementalParseReusedNodeDelegate
3636{
37- public var rangeAndNodes : [ ( ByteSourceRange , Syntax ) ] = [ ]
37+ public var nodes : [ Syntax ] = [ ]
3838
3939 public init ( ) { }
4040
41- public func parserReusedNode( range: ByteSourceRange , previousNode: Syntax ) {
42- rangeAndNodes. append ( ( range, previousNode) )
41+ public func parserReusedNode( previousNode: Syntax ) {
42+ nodes. append ( previousNode)
43+ }
44+ }
45+
46+ public final class IncrementalParseNodeAffectRangeCollector {
47+ fileprivate var nodeAffectRange : [ RawSyntax . ID : Int ] = [ : ]
48+
49+ public init ( ) { }
50+
51+ @_spi ( RawSyntax)
52+ public func registerNodeForIncrementalParse( node: RawSyntax , length: Int ) {
53+ self . nodeAffectRange [ node. id] = length
4354 }
4455}
4556
4657/// Keeps track of a previously parsed syntax tree and the source edits that
4758/// occurred since it was created.
4859public final class IncrementalParseTransition {
49- fileprivate let previousTree : SourceFileSyntax
50- fileprivate let edits : ConcurrentEdits
51- fileprivate let reusedDelegate : IncrementalParseReusedNodeDelegate ?
60+ fileprivate var previousTree : SourceFileSyntax
61+ fileprivate var edits : ConcurrentEdits
62+ fileprivate var reusedDelegate : IncrementalParseReusedNodeDelegate ?
5263
5364 /// - Parameters:
5465 /// - previousTree: The previous tree to do lookups on.
@@ -59,7 +70,7 @@ public final class IncrementalParseTransition {
5970 public init (
6071 previousTree: SourceFileSyntax ,
6172 edits: ConcurrentEdits ,
62- reusedNodeDelegate: IncrementalParseReusedNodeDelegate ? = nil
73+ reusedNodeDelegate: IncrementalParseReusedNodeDelegate ?
6374 ) {
6475 self . previousTree = previousTree
6576 self . edits = edits
@@ -71,23 +82,31 @@ public final class IncrementalParseTransition {
7182/// updated source that was already parsed during a previous parse invocation.
7283public struct IncrementalParseLookup {
7384 fileprivate let transition : IncrementalParseTransition
85+
7486 fileprivate var cursor : SyntaxCursor
7587
88+ fileprivate let nodeAffectRangeCollector : IncrementalParseNodeAffectRangeCollector
89+
7690 /// Create a new ``IncrementalParseLookup`` that can look nodes up based on the
7791 /// given ``IncrementalParseTransition``.
78- public init ( transition: IncrementalParseTransition ) {
92+ public init ( transition: IncrementalParseTransition , nodeAffectRangeCollector : IncrementalParseNodeAffectRangeCollector ) {
7993 self . transition = transition
8094 self . cursor = . init( root: Syntax ( transition. previousTree) )
95+ self . nodeAffectRangeCollector = nodeAffectRangeCollector
8196 }
8297
83- fileprivate var edits : ConcurrentEdits {
98+ fileprivate var edits : ConcurrentEdits ? {
8499 return transition. edits
85100 }
86101
87102 fileprivate var reusedDelegate : IncrementalParseReusedNodeDelegate ? {
88103 return transition. reusedDelegate
89104 }
90105
106+ fileprivate var nodeAffectRange : [ RawSyntax . ID : Int ] {
107+ return nodeAffectRangeCollector. nodeAffectRange
108+ }
109+
91110 /// Does a lookup to see if the current source `offset` should be associated
92111 /// with a known ``Syntax`` node and its region skipped during parsing.
93112 ///
@@ -109,7 +128,6 @@ public struct IncrementalParseLookup {
109128 let node = cursorLookup ( prevPosition: prevPosition, kind: kind)
110129 if let delegate = reusedDelegate, let node {
111130 delegate. parserReusedNode (
112- range: ByteSourceRange ( offset: newOffset, length: node. byteSizeAfterTrimmingTrivia) ,
113131 previousNode: node
114132 )
115133 }
@@ -134,6 +152,8 @@ public struct IncrementalParseLookup {
134152 prevPosition: AbsolutePosition ,
135153 kind: SyntaxKind
136154 ) -> Bool {
155+ guard let edits = edits else { return false }
156+
137157 let node = cursor. node
138158 if node. position != prevPosition {
139159 return false
@@ -148,24 +168,13 @@ public struct IncrementalParseLookup {
148168 return true
149169 }
150170
151- // Node can also not be reused if an edit has been made in the next token's
152- // text, e.g. because `private struct Foo {}` parses as a CodeBlockItem with
153- // a StructDecl inside and `private struc Foo {}` parses as two
154- // CodeBlockItems one for `private` and one for `struc Foo {}`
155- var nextLeafNodeLength : SourceLength = . zero
156- if let nextSibling = cursor. nextSibling {
157- // Fast path check: if next sibling is before all the edits then we can
158- // re-use the node.
159- if !edits. edits. isEmpty && edits. edits. first!. range. offset > nextSibling. endPosition. utf8Offset {
160- return true
161- }
162- if let nextToken = nextSibling. firstToken ( viewMode: . sourceAccurate) {
163- nextLeafNodeLength = nextToken. leadingTriviaLength + nextToken. contentLength
164- }
171+ guard let nodeAffectRangeLength = self . nodeAffectRange [ node. raw. id] else {
172+ return false
165173 }
174+
166175 let nodeAffectRange = ByteSourceRange (
167176 offset: node. position. utf8Offset,
168- length: ( node . totalLength + nextLeafNodeLength ) . utf8Length
177+ length: nodeAffectRangeLength
169178 )
170179
171180 for edit in edits. edits {
@@ -184,6 +193,8 @@ public struct IncrementalParseLookup {
184193 }
185194
186195 fileprivate func translateToPreEditOffset( _ postEditOffset: Int ) -> Int ? {
196+ guard let edits = edits else { return nil }
197+
187198 var offset = postEditOffset
188199 for edit in edits. edits {
189200 if edit. range. offset > offset {
0 commit comments