@@ -46,9 +46,14 @@ public final class IncrementalParseReusedNodeCollector:
46
46
/// Keeps track of a previously parsed syntax tree and the source edits that
47
47
/// occurred since it was created.
48
48
public final class IncrementalParseTransition {
49
- fileprivate let previousTree : SourceFileSyntax
50
- fileprivate let edits : ConcurrentEdits
51
- fileprivate let reusedDelegate : IncrementalParseReusedNodeDelegate ?
49
+ fileprivate var previousTree : SourceFileSyntax ?
50
+ fileprivate var edits : ConcurrentEdits ?
51
+ fileprivate var reusedDelegate : IncrementalParseReusedNodeDelegate ?
52
+
53
+ fileprivate var previousLookaheadRange : [ Int : Int ] = [ : ]
54
+ /// Keep track of how far we would look when calling ``Lookahead``
55
+ /// Key is offset to buffer start and value is the length of we lookahead
56
+ fileprivate var cursorLookaheadRange : [ Int : Int ] = [ : ]
52
57
53
58
/// - Parameters:
54
59
/// - previousTree: The previous tree to do lookups on.
@@ -57,30 +62,44 @@ public final class IncrementalParseTransition {
57
62
/// - reusedNodeDelegate: Optional delegate to accept information about the
58
63
/// reused regions and nodes.
59
64
public init (
60
- previousTree: SourceFileSyntax ,
61
- edits: ConcurrentEdits ,
65
+ previousTree: SourceFileSyntax ? = nil ,
66
+ edits: ConcurrentEdits ? = nil ,
62
67
reusedNodeDelegate: IncrementalParseReusedNodeDelegate ? = nil
63
68
) {
64
69
self . previousTree = previousTree
65
70
self . edits = edits
66
71
self . reusedDelegate = reusedNodeDelegate
67
72
}
73
+
74
+ public func setupTransition( tree: SourceFileSyntax , edits: ConcurrentEdits , delegate: IncrementalParseReusedNodeDelegate ? = nil ) {
75
+ self . previousTree = tree
76
+ self . edits = edits
77
+ self . reusedDelegate = delegate
78
+ self . previousLookaheadRange = cursorLookaheadRange
79
+ self . cursorLookaheadRange = [ : ]
80
+ }
81
+
82
+ public func registerAffectRange( at offset: Int , length: Int ) {
83
+ self . cursorLookaheadRange [ offset] = length
84
+ }
85
+
86
+ public func isValidTransition( ) -> Bool {
87
+ return previousTree != nil
88
+ }
68
89
}
69
90
70
91
/// Provides a mechanism for the parser to skip regions of an incrementally
71
92
/// updated source that was already parsed during a previous parse invocation.
72
93
public struct IncrementalParseLookup {
73
94
fileprivate let transition : IncrementalParseTransition
74
- fileprivate var cursor : SyntaxCursor
75
95
76
96
/// Create a new ``IncrementalParseLookup`` that can look nodes up based on the
77
97
/// given ``IncrementalParseTransition``.
78
98
public init ( transition: IncrementalParseTransition ) {
79
99
self . transition = transition
80
- self . cursor = . init( root: Syntax ( transition. previousTree) )
81
100
}
82
101
83
- fileprivate var edits : ConcurrentEdits {
102
+ fileprivate var edits : ConcurrentEdits ? {
84
103
return transition. edits
85
104
}
86
105
@@ -109,7 +128,7 @@ public struct IncrementalParseLookup {
109
128
let node = cursorLookup ( prevPosition: prevPosition, kind: kind)
110
129
if let delegate = reusedDelegate, let node {
111
130
delegate. parserReusedNode (
112
- range: ByteSourceRange ( offset: newOffset , length: node. byteSizeAfterTrimmingTrivia) ,
131
+ range: ByteSourceRange ( offset: node . positionAfterSkippingLeadingTrivia . utf8Offset , length: node. byteSizeAfterTrimmingTrivia) ,
113
132
previousNode: node
114
133
)
115
134
}
@@ -120,20 +139,28 @@ public struct IncrementalParseLookup {
120
139
prevPosition: AbsolutePosition ,
121
140
kind: SyntaxKind
122
141
) -> Syntax ? {
142
+ guard let previousTree = transition. previousTree else {
143
+ return nil
144
+ }
145
+
146
+ var cursor = SyntaxCursor ( root: Syntax ( previousTree) )
123
147
guard !cursor. finished else { return nil }
124
148
125
149
while true {
126
- if nodeAtCursorCanBeReused ( prevPosition: prevPosition, kind: kind) {
150
+ if nodeAtCursorCanBeReused ( cursor , prevPosition: prevPosition, kind: kind) {
127
151
return cursor. asSyntaxNode
128
152
}
129
153
guard cursor. advanceToNextNode ( at: prevPosition) else { return nil }
130
154
}
131
155
}
132
156
133
157
fileprivate func nodeAtCursorCanBeReused(
158
+ _ cursor: SyntaxCursor ,
134
159
prevPosition: AbsolutePosition ,
135
160
kind: SyntaxKind
136
161
) -> Bool {
162
+ guard let edits = edits else { return false }
163
+
137
164
let node = cursor. node
138
165
if node. position != prevPosition {
139
166
return false
@@ -165,7 +192,7 @@ public struct IncrementalParseLookup {
165
192
}
166
193
let nodeAffectRange = ByteSourceRange (
167
194
offset: node. position. utf8Offset,
168
- length: ( node. totalLength + nextLeafNodeLength) . utf8Length
195
+ length: max ( mergeLookaheadRange ( at : node. position . utf8Offset , length : node . totalLength. utf8Length ) , ( node . totalLength + nextLeafNodeLength) . utf8Length)
169
196
)
170
197
171
198
for edit in edits. edits {
@@ -184,6 +211,8 @@ public struct IncrementalParseLookup {
184
211
}
185
212
186
213
fileprivate func translateToPreEditOffset( _ postEditOffset: Int ) -> Int ? {
214
+ guard let edits = edits else { return nil }
215
+
187
216
var offset = postEditOffset
188
217
for edit in edits. edits {
189
218
if edit. range. offset > offset {
@@ -199,6 +228,21 @@ public struct IncrementalParseLookup {
199
228
}
200
229
return offset
201
230
}
231
+
232
+ fileprivate func mergeLookaheadRange( at start: Int , length: Int ) -> Int {
233
+ var totalLength = start + length
234
+
235
+ let targetRanges = transition. previousLookaheadRange. filter { $0. key >= totalLength } . sorted ( by: { $0. key < $1. key } )
236
+
237
+ for targetRange in targetRanges {
238
+ if targetRange. key != totalLength {
239
+ break
240
+ }
241
+ totalLength += targetRange. value
242
+ }
243
+
244
+ return totalLength
245
+ }
202
246
}
203
247
204
248
/// Functions as an iterator that walks the tree looking for nodes with a
0 commit comments