1010//
1111//===----------------------------------------------------------------------===//
1212
13- public protocol SyntaxCollection : SyntaxProtocol , BidirectionalCollection , MutableCollection where Element: SyntaxProtocol {
13+ public protocol SyntaxCollection : SyntaxProtocol , BidirectionalCollection where Element: SyntaxProtocol , Index == SyntaxChildrenIndex {
1414 associatedtype Iterator = SyntaxCollectionIterator < Element >
1515
1616 /// The ``SyntaxKind`` of the syntax node that conforms to ``SyntaxCollection``.
@@ -33,6 +33,17 @@ extension SyntaxCollection {
3333 self . init ( Syntax ( data) ) !
3434 }
3535
36+ /// Initialize the collection from a collection of the same type.
37+ ///
38+ /// - Note: This initializer is needed to improve source compatibility after
39+ /// the `+` operators have been added. Previously, they created an array
40+ /// that was converted to a ``SyntaxCollection`` and now they create a
41+ /// ``SyntaxCollection``, which makes the initializer a no-op.
42+ @available ( * , deprecated, message: " Call to initializer is not necessary. " )
43+ public init ( _ collection: Self ) {
44+ self = collection
45+ }
46+
3647 public init < Children: Sequence > ( _ children: Children ) where Children. Element == Element {
3748 let arena = SyntaxArena ( )
3849 // Extend the lifetime of children so their arenas don't get destroyed
@@ -208,6 +219,170 @@ public struct SyntaxCollectionIterator<E: SyntaxProtocol>: IteratorProtocol {
208219 }
209220}
210221
222+ // MARK: Functions analogous to RangeReplaceableCollection
223+
224+ /// Defines functions that are similar to those in `RangeReplaceableCollection`.
225+ ///
226+ /// SyntaxCollection doesn’t conform to `RangeReplaceableCollection` because of
227+ /// two reasons:
228+ /// - `RangeReplaceableCollection` assumes that creating a new colleciton and
229+ /// adding all elements from another collection to it results in the same
230+ /// collection. This is not true for `SyntaxCollection` because the new
231+ /// collection wouldn’t have a parent. This causes eg. the default
232+ /// implementation of `filter` to misbehave.
233+ /// - It can’t make the complexity guarantees that `RangeReplaceableCollection`
234+ /// requires, e.g. `append` is `O(tree depth)` instead of `O(1)`.
235+ extension SyntaxCollection {
236+ /// Replace the nodes in `subrange` by `newElements`.
237+ public mutating func replaceSubrange( _ subrange: Range < Self . Index > , with newElements: some Collection < Element > ) {
238+ // We only access the raw nodes of `newElements` below.
239+ // Keep `newElements` alive so their arena doesn't get deallocated.
240+ withExtendedLifetime ( newElements) {
241+ var newLayout = layoutView. formLayoutArray ( )
242+ let layoutRangeLowerBound = ( subrange. lowerBound. data? . indexInParent) . map ( Int . init) ?? newLayout. endIndex
243+ let layoutRangeUpperBound = ( subrange. upperBound. data? . indexInParent) . map ( Int . init) ?? newLayout. endIndex
244+ newLayout. replaceSubrange ( layoutRangeLowerBound..< layoutRangeUpperBound, with: newElements. map { $0. raw } )
245+ self = replacingLayout ( newLayout)
246+ }
247+ }
248+
249+ /// Adds an element to the end of the collection.
250+ ///
251+ /// - Parameter newElement: The element to append to the collection.
252+ public mutating func append( _ newElement: Element ) {
253+ insert ( newElement, at: endIndex)
254+ }
255+
256+ /// Adds the elements of a sequence to the end of this collection.
257+ ///
258+ /// - Parameter newElements: The elements to append to the collection.
259+ public mutating func append( contentsOf newElements: some Sequence < Element > ) {
260+ insert ( contentsOf: Array ( newElements) , at: endIndex)
261+ }
262+
263+ /// Inserts a new element into the collection at the specified position.
264+ ///
265+ /// - Parameter newElement: The new element to insert into the collection.
266+ /// - Parameter i: The position at which to insert the new element.
267+ /// `index` must be a valid index into the collection.
268+ public mutating func insert( _ newElement: Element , at i: Index ) {
269+ replaceSubrange ( i..< i, with: CollectionOfOne ( newElement) )
270+ }
271+
272+ /// Inserts the elements of a sequence into the collection at the specified
273+ /// position.
274+ ///
275+ /// - Parameter newElements: The new elements to insert into the collection.
276+ /// - Parameter i: The position at which to insert the new elements. `index`
277+ /// must be a valid index of the collection.
278+ public mutating func insert( contentsOf newElements: some Collection < Element > , at i: Index ) {
279+ replaceSubrange ( i..< i, with: newElements)
280+ }
281+
282+ /// Removes and returns the element at the specified position.
283+ ///
284+ /// - Parameter position: The position of the element to remove. `position`
285+ /// must be a valid index of the collection that is not equal to the
286+ /// collection's end index.
287+ /// - Returns: The removed element.
288+ @discardableResult
289+ public mutating func remove( at position: Index ) -> Element {
290+ precondition ( !isEmpty, " Can't remove from an empty collection " )
291+ let result : Element = self [ position]
292+ replaceSubrange ( position..< index ( after: position) , with: EmptyCollection ( ) )
293+ return result
294+ }
295+
296+ /// Removes the elements in the specified subrange from the collection.
297+ ///
298+ /// - Parameter bounds: The range of the collection to be removed. The
299+ /// bounds of the range must be valid indices of the collection.
300+ public mutating func removeSubrange( _ bounds: Range < Index > ) {
301+ replaceSubrange ( bounds, with: EmptyCollection ( ) )
302+ }
303+
304+ /// Creates a new collection by concatenating the elements of a collection and
305+ /// a sequence.
306+ ///
307+ /// - Parameters:
308+ /// - lhs: A ``SyntaxCollection``
309+ /// - rhs: A collection or finite sequence.
310+ public static func + ( lhs: Self , rhs: some Sequence < Element > ) -> Self {
311+ var result = lhs
312+ result. append ( contentsOf: rhs)
313+ return result
314+ }
315+
316+ /// Creates a new collection by concatenating the elements of a collection and
317+ /// a sequence.
318+ ///
319+ /// - Parameters:
320+ /// - lhs: A ``SyntaxCollection``
321+ /// - rhs: A collection or finite sequence.
322+ ///
323+ /// - Note: This overload exists to resolve when adding an array to a ``SyntaxCollection``.
324+ public static func + ( lhs: Self , rhs: some RangeReplaceableCollection < Element > ) -> Self {
325+ var result = lhs
326+ result. append ( contentsOf: rhs)
327+ return result
328+ }
329+
330+ /// Creates a new collection by concatenating the elements of a sequence and a
331+ /// collection.
332+ ///
333+ /// - Parameters:
334+ /// - lhs: A collection or finite sequence.
335+ /// - rhs: A range-replaceable collection.
336+ public static func + ( lhs: some Sequence < Element > , rhs: Self ) -> Self {
337+ var result = rhs
338+ result. insert ( contentsOf: Array ( lhs) , at: result. startIndex)
339+ return result
340+ }
341+
342+ /// Creates a new collection by concatenating the elements of a sequence and a
343+ /// collection.
344+ ///
345+ /// - Parameters:
346+ /// - lhs: A collection or finite sequence.
347+ /// - rhs: A range-replaceable collection.
348+ ///
349+ /// - Note: This overload exists to resolve when adding an array to a ``SyntaxCollection``.
350+ public static func + ( lhs: some RangeReplaceableCollection < Element > , rhs: Self ) -> Self {
351+ var result = rhs
352+ result. insert ( contentsOf: Array ( lhs) , at: result. startIndex)
353+ return result
354+ }
355+
356+ /// Appends the elements of a sequence to a range-replaceable collection.
357+ ///
358+ /// - Parameters:
359+ /// - lhs: The ``SyntaxCollection`` to append to.
360+ /// - rhs: A collection or finite sequence.
361+ ///
362+ /// - Note: This will result in an allocation of a copy of this node.
363+ public static func += ( lhs: inout Self , rhs: some Sequence < Element > ) {
364+ lhs. append ( contentsOf: rhs)
365+ }
366+
367+ /// Returns a new ``SyntaxCollection`` that just contains the elements
368+ /// satisfying the given predicate.
369+ ///
370+ /// - Parameter isIncluded: A closure that takes an element of the
371+ /// collection as its argument and returns a Boolean value indicating
372+ /// whether the element should be included in the returned collection.
373+ /// - Returns: A ``SyntaxCollection`` of the elements that `isIncluded` allowed.
374+ ///
375+ /// - Note: This creates a new ``SyntaxCollection`` node. If the resulting node
376+ /// is not needed (e.g. because it’s only being iterated), convert the
377+ /// ``SyntaxCollection`` to an array first or use the `where` clause in
378+ /// a `for` statement.
379+ public func filter( _ isIncluded: ( Element ) throws -> Bool ) rethrows -> Self {
380+ var result = self
381+ result. replaceSubrange ( self . startIndex..< self . endIndex, with: try Array ( self ) . filter ( isIncluded) )
382+ return result
383+ }
384+ }
385+
211386/// Conformance to `BidirectionalCollection`.
212387extension SyntaxCollection {
213388 public func makeIterator( ) -> SyntaxCollectionIterator < Element > {
0 commit comments