@@ -27,15 +27,54 @@ public protocol FixItMessage: Sendable {
27
27
var fixItID : MessageID { get }
28
28
}
29
29
30
+ /// Types conforming to this protocol provide the data required for replacing a child node of a parent node.
31
+ ///
32
+ /// Conforming types should ensure the child of ``parent`` to be replaced at ``replacementRange`` is type-compatible
33
+ /// with ``newChild``. Conforming types are stored as type-erased existentials (i.e. `any ReplacingChildData`) in
34
+ /// ``FixIt/Change/replaceChild(data:)`` to keep ``FixIt`` type-erased.
35
+ public protocol ReplacingChildData : Sendable {
36
+ associatedtype Parent : SyntaxProtocol
37
+ associatedtype Child : SyntaxProtocol
38
+
39
+ /// The node whose child node at ``replacementRange`` to be replaced by ``newChild``.
40
+ var parent : Parent { get }
41
+
42
+ /// The node to replace the child node of ``parent`` at ``replacementRange``.
43
+ var newChild : Child { get }
44
+
45
+ /// The absolute position range of the child node to be replaced.
46
+ ///
47
+ /// If a nil child node is to be replaced, conforming types should provide a zero-length range with both bounds
48
+ /// denoting the start position of ``newChild`` in ``parent`` after replacement.
49
+ var replacementRange : Range < AbsolutePosition > { get }
50
+ }
51
+
30
52
/// A Fix-It that can be applied to resolve a diagnostic.
31
53
public struct FixIt : Sendable {
32
54
public enum Change : Sendable {
55
+ struct ReplacingOptionalChildData < Parent: SyntaxProtocol , Child: SyntaxProtocol > : ReplacingChildData {
56
+ let parent : Parent
57
+ let newChild : Child
58
+ let keyPath : WritableKeyPath < Parent , Child ? > & Sendable
59
+
60
+ var replacementRange : Range < AbsolutePosition > {
61
+ if let oldChild: Child = parent [ keyPath: keyPath] {
62
+ return oldChild. range
63
+ } else {
64
+ let newChild : Child = parent. with ( keyPath, newChild) [ keyPath: keyPath] !
65
+ return newChild. position..< newChild. position
66
+ }
67
+ }
68
+ }
69
+
33
70
/// Replace `oldNode` by `newNode`.
34
71
case replace( oldNode: Syntax , newNode: Syntax )
35
72
/// Replace the leading trivia on the given token
36
73
case replaceLeadingTrivia( token: TokenSyntax , newTrivia: Trivia )
37
74
/// Replace the trailing trivia on the given token
38
75
case replaceTrailingTrivia( token: TokenSyntax , newTrivia: Trivia )
76
+ /// Replace the child node of the given parent node at the given replacement range with the given new child node
77
+ case replaceChild( data: any ReplacingChildData )
39
78
}
40
79
41
80
/// A description of what this Fix-It performs.
@@ -89,6 +128,12 @@ private extension FixIt.Change {
89
128
range: token. endPositionBeforeTrailingTrivia..< token. endPosition,
90
129
replacement: newTrivia. description
91
130
)
131
+
132
+ case . replaceChild( let replacingChildData) :
133
+ return SourceEdit (
134
+ range: replacingChildData. replacementRange,
135
+ replacement: replacingChildData. newChild. description
136
+ )
92
137
}
93
138
}
94
139
}
0 commit comments