10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
- public protocol SyntaxCollection : SyntaxProtocol , BidirectionalCollection , MutableCollection where Element: SyntaxProtocol {
13
+ public protocol SyntaxCollection : SyntaxProtocol , BidirectionalCollection , MutableCollection , RangeReplaceableCollection where Element: SyntaxProtocol {
14
14
associatedtype Iterator = SyntaxCollectionIterator < Element >
15
15
16
16
/// The ``SyntaxKind`` of the syntax node that conforms to ``SyntaxCollection``.
@@ -33,6 +33,11 @@ extension SyntaxCollection {
33
33
self . init ( Syntax ( data) ) !
34
34
}
35
35
36
+ /// Create an empty ``SyntaxCollection`` with no children.
37
+ public init ( ) {
38
+ self . init ( [ ] )
39
+ }
40
+
36
41
public init < Children: Sequence > ( _ children: Children ) where Children. Element == Element {
37
42
let arena = SyntaxArena ( )
38
43
// Extend the lifetime of children so their arenas don't get destroyed
@@ -89,6 +94,19 @@ extension SyntaxCollection {
89
94
return node. indexInParent
90
95
}
91
96
97
+ /// Replace the nodes in `subrange` by `newElements`.
98
+ public mutating func replaceSubrange( _ subrange: Range < SyntaxChildrenIndex > , with newElements: some Collection < Element > ) {
99
+ // We only access the raw nodes of `newElements` below.
100
+ // Keep `newElements` alive so their arena doesn't get deallocated.
101
+ withExtendedLifetime ( newElements) {
102
+ var newLayout = layoutView. formLayoutArray ( )
103
+ let layoutRangeLowerBound = ( subrange. lowerBound. data? . indexInParent) . map ( Int . init) ?? newLayout. endIndex
104
+ let layoutRangeUpperBound = ( subrange. upperBound. data? . indexInParent) . map ( Int . init) ?? newLayout. endIndex
105
+ newLayout. replaceSubrange ( layoutRangeLowerBound..< layoutRangeUpperBound, with: newElements. map { $0. raw } )
106
+ self = replacingLayout ( newLayout)
107
+ }
108
+ }
109
+
92
110
/// Creates a new collection by appending the provided syntax element
93
111
/// to the children.
94
112
///
@@ -208,6 +226,55 @@ public struct SyntaxCollectionIterator<E: SyntaxProtocol>: IteratorProtocol {
208
226
}
209
227
}
210
228
229
+ // MARK: Specializations of functions from RangeReplaceableCollection
230
+
231
+ extension SyntaxCollection {
232
+ /// Returns a new ``SyntaxCollection`` that just contains the elements
233
+ /// satisfying the given predicate.
234
+ ///
235
+ /// - Parameter isIncluded: A closure that takes an element of the
236
+ /// collection as its argument and returns a Boolean value indicating
237
+ /// whether the element should be included in the returned collection.
238
+ /// - Returns: A ``SyntaxCollection`` of the elements that `isIncluded` allowed.
239
+ public func filter( _ isIncluded: ( Element ) throws -> Bool ) rethrows -> Self {
240
+ // This implementation is mor performant than `RangeReplacableCollection.filter`
241
+ // because `RangeReplacableCollection.filter` calls `replaceSubrange` for
242
+ // every included element, which is an expensive operation on
243
+ // `SyntaxCollection` since all the parents need to be rewritten. It’s more
244
+ // efficient to filter all the elements first and then create a final result.
245
+ // Also, `RangeReplacableCollection.filter` creates an empty collection
246
+ // first and appends to that collection, which means that the filtered
247
+ // collection doesn’t have a parent. This implementation makes sure that
248
+ // the filtered sequence has the same parent
249
+ var result = self
250
+ result. replaceSubrange ( self . startIndex..< self . endIndex, with: try Array ( self ) . filter ( isIncluded) )
251
+ return result
252
+ }
253
+
254
+ /// Removes all elements from the collection.
255
+ ///
256
+ /// - Parameter keepCapacity: Ignored for `SyntaxCollection`
257
+ public mutating func removeAll( keepingCapacity keepCapacity: Bool = false ) {
258
+ // The default implementation of this in `RangeReplacableCollection` creates
259
+ // a node without a parent, which is not what we want.
260
+ replaceSubrange ( startIndex..< endIndex, with: EmptyCollection ( ) )
261
+ }
262
+
263
+ /// Creates a new collection by concatenating the elements of a sequence and a
264
+ /// collection. The parent of the new collection will be the parent of `rhs`
265
+ ///
266
+ /// - Parameters:
267
+ /// - lhs: A collection or finite sequence.
268
+ /// - rhs: A range-replaceable collection.
269
+ public static func + < Other: Sequence > ( lhs: Other , rhs: Self ) -> Self where Element == Other . Element {
270
+ // The standard implementation in `RangeReplacableCollection` does not
271
+ // maintain the parent of `rhs`. This implementation does.
272
+ var result = rhs
273
+ result. insert ( contentsOf: Array ( lhs) , at: result. startIndex)
274
+ return result
275
+ }
276
+ }
277
+
211
278
/// Conformance to `BidirectionalCollection`.
212
279
extension SyntaxCollection {
213
280
public func makeIterator( ) -> SyntaxCollectionIterator < Element > {
0 commit comments