diff --git a/Release Notes/510.md b/Release Notes/510.md index a3c1a6f9b55..2da30866ae9 100644 --- a/Release Notes/510.md +++ b/Release Notes/510.md @@ -8,6 +8,11 @@ - `SyntaxCollection.index(at:)` - Description: Returns the index of the n-th element in a `SyntaxCollection`. This computation is in O(n) and `SyntaxCollection` is not subscriptable by an integer. - Pull Request: https://github.com/apple/swift-syntax/pull/2014 +- `SyntaxProtocol.tracked` / `SyntaxProtocol.originalNode(in:)` + - Description: `tracked` enables node tracking of a tree. For every tree derived from a tracked tree, `originalNode(in:)` returns the original node in the tracked tree. This allows clients to e.g. get the original location of a node in a source file after a tree has been modified. + - Issue: rdar://112679655 + - Pull Request: https://github.com/apple/swift-syntax/pull/2118 + ## API Behavior Changes diff --git a/Sources/SwiftSyntax/SyntaxTracking.swift b/Sources/SwiftSyntax/SyntaxTracking.swift index f20390d01ff..58421612ded 100644 --- a/Sources/SwiftSyntax/SyntaxTracking.swift +++ b/Sources/SwiftSyntax/SyntaxTracking.swift @@ -226,7 +226,7 @@ extension SyntaxData { fileprivate var tracked: SyntaxData { if let parent { let parentTracked = parent.tracked - return parentTracked.child(at: self.indexInParent, parent: Syntax(parentTracked))! + return parentTracked.child(at: self.indexInParent)! } else { let result = SyntaxData.forRoot(self.raw, rawNodeArena: self.raw.arena) let syntaxTracking = SyntaxTracking( @@ -267,18 +267,39 @@ class IndexInTreeFinder: SyntaxAnyVisitor { } extension SyntaxProtocol { - /// The same `SyntaxData` but with tracking enabled in the entire tree. + /// Start tracking this syntax tree as the original tree for all trees derived + /// from it. /// - /// All syntax nodes derived from this will be able to find their - /// corresponding location in this original tree. + /// All syntax nodes derived from the returned, tracked, tree will be able to + /// find the corresponding node in the original tree by calling + /// ``SyntaxProtocol/originalNode(in:)``. + /// + /// Derived nodes are + /// - Nodes that contain a node from this tree as a subtree. + /// - Nodes that resulted from modification of a node in this tree (e.g. + /// modification of a child node or insertion of an element into a + /// collection). + /// - Detached subtrees of this tree (see ``SyntaxProtocol/detached``). + /// + /// Node tracking is not enabled by default because maintaining the mapping + /// back to the tracked tree has a performance cost that. + /// + /// A tree can only track a single original tree. I.e. it is not possible to + /// create a node that has one child in tracked tree A and another child in + /// tracked tree B. In practice, this should seldom pose an issue because the + /// most common use case is to mark the tree obtained from a file on disk as + /// the tracked tree and trees from separate source files will rarely be + /// merged. /// + /// - SeeAlso: ``SyntaxProtocol/originalNode(in:)`` /// - Complexity: O(number of ancestors) public var tracked: Self { return Syntax(self.data.tracked).cast(Self.self) } /// If this syntax node is tracking `originalTree` and this node originated - /// in that tree, return the corresponding corresponding node in `originalTree`. + /// from that tree, return the corresponding corresponding node in + /// `originalTree`. /// /// The original node will have the same structure as this node but the parent /// might be different since it's anchored in `originalTree`.