@@ -32,13 +32,17 @@ extension FixIt {
32
32
}
33
33
34
34
extension FixIt . Changes {
35
- /// Replaced a present token with a missing node
36
- static func makeMissing( _ token: TokenSyntax ) -> Self {
37
- return makeMissing ( [ token] )
35
+ /// Replaced a present token with a missing node.
36
+ /// If `transferTrivia` is `true`, the leading and trailing trivia of the
37
+ /// removed node will be transferred to the trailing trivia of the previous token.
38
+ static func makeMissing( _ token: TokenSyntax , transferTrivia: Bool = true ) -> Self {
39
+ return makeMissing ( [ token] , transferTrivia: transferTrivia)
38
40
}
39
41
40
- /// Replace present tokens with missing tokens
41
- static func makeMissing( _ tokens: [ TokenSyntax ] ) -> Self {
42
+ /// Replace present tokens with missing tokens.
43
+ /// If `transferTrivia` is `true`, the leading and trailing trivia of the
44
+ /// removed node will be transferred to the trailing trivia of the previous token.
45
+ static func makeMissing( _ tokens: [ TokenSyntax ] , transferTrivia: Bool = true ) -> Self {
42
46
assert ( !tokens. isEmpty)
43
47
assert ( tokens. allSatisfy ( { $0. presence == . present } ) )
44
48
var changes = tokens. map {
@@ -47,19 +51,20 @@ extension FixIt.Changes {
47
51
newNode: Syntax ( TokenSyntax ( $0. tokenKind, leadingTrivia: [ ] , trailingTrivia: [ ] , presence: . missing) )
48
52
)
49
53
}
50
- if let firstToken = tokens. first,
51
- firstToken. leadingTrivia. isEmpty == false ,
52
- let nextToken = tokens. last? . nextToken ( viewMode: . sourceAccurate) ,
53
- !nextToken. leadingTrivia. contains ( where: { $0. isNewline } ) {
54
- changes. append ( . replaceLeadingTrivia( token: nextToken, newTrivia: firstToken. leadingTrivia) )
54
+ if transferTrivia {
55
+ changes += FixIt . Changes. transferTriviaAtSides ( from: tokens) . changes
55
56
}
56
57
return FixIt . Changes ( changes: changes)
57
58
}
58
59
59
- static func makeMissing< SyntaxType: SyntaxProtocol > ( _ node: SyntaxType ) -> Self {
60
- return FixIt . Changes ( changes: [
61
- . replace( oldNode: Syntax ( node) , newNode: MissingMaker ( ) . visit ( Syntax ( node) ) )
62
- ] )
60
+ /// If `transferTrivia` is `true`, the leading and trailing trivia of the
61
+ /// removed node will be transferred to the trailing trivia of the previous token.
62
+ static func makeMissing< SyntaxType: SyntaxProtocol > ( _ node: SyntaxType , transferTrivia: Bool = true ) -> Self {
63
+ var changes = [ FixIt . Change. replace ( oldNode: Syntax ( node) , newNode: MissingMaker ( ) . visit ( Syntax ( node) ) ) ]
64
+ if transferTrivia {
65
+ changes += FixIt . Changes. transferTriviaAtSides ( from: [ node] ) . changes
66
+ }
67
+ return FixIt . Changes ( changes: changes)
63
68
}
64
69
65
70
/// Make a node present. If `leadingTrivia` or `trailingTrivia` is specified,
@@ -109,4 +114,74 @@ extension FixIt.Changes {
109
114
return . makePresent( token)
110
115
}
111
116
}
117
+
118
+ /// Transfers the leading and trivia trivia of `nodes` to the trailing trivia
119
+ /// of the previous token. While doing this, it tries to be smart, merging trivia
120
+ /// where it makes sense and refusing to add e.g. a space after punctuation,
121
+ /// where it usually doesn't make sense.
122
+ static func transferTriviaAtSides< SyntaxType: SyntaxProtocol > ( from nodes: [ SyntaxType ] ) -> Self {
123
+ let removedTriviaAtSides = Trivia . merged ( nodes. first? . leadingTrivia ?? [ ] , nodes. last? . trailingTrivia ?? [ ] )
124
+ if !removedTriviaAtSides. isEmpty, let previousToken = nodes. first? . previousToken ( viewMode: . sourceAccurate) {
125
+ let mergedTrivia = Trivia . merged ( previousToken. trailingTrivia, removedTriviaAtSides)
126
+ if previousToken. tokenKind. isPunctuation, mergedTrivia. allSatisfy ( { $0. isSpaceOrTab } ) {
127
+ // Punctuation is generally not followed by spaces in Swift.
128
+ // If this action would only add spaces to the punctuation, drop it.
129
+ // This generally yields better results.
130
+ return [ ]
131
+ }
132
+ return [ . replaceTrailingTrivia( token: previousToken, newTrivia: mergedTrivia) ]
133
+ } else {
134
+ return [ ]
135
+ }
136
+ }
137
+ }
138
+
139
+ extension Trivia {
140
+ /// Decomposes the trivia into pieces that all have count 1
141
+ var decomposed : Trivia {
142
+ let pieces = self . flatMap ( { ( piece: TriviaPiece ) -> [ TriviaPiece ] in
143
+ switch piece {
144
+ case . spaces( let count) :
145
+ return Array ( repeating: TriviaPiece . spaces ( 1 ) , count: count)
146
+ case . tabs( let count) :
147
+ return Array ( repeating: TriviaPiece . tabs ( 1 ) , count: count)
148
+ case . verticalTabs( let count) :
149
+ return Array ( repeating: TriviaPiece . verticalTabs ( 1 ) , count: count)
150
+ case . formfeeds( let count) :
151
+ return Array ( repeating: TriviaPiece . formfeeds ( 1 ) , count: count)
152
+ case . newlines( let count) :
153
+ return Array ( repeating: TriviaPiece . newlines ( 1 ) , count: count)
154
+ case . carriageReturns( let count) :
155
+ return Array ( repeating: TriviaPiece . carriageReturns ( 1 ) , count: count)
156
+ case . carriageReturnLineFeeds( let count) :
157
+ return Array ( repeating: TriviaPiece . carriageReturnLineFeeds ( 1 ) , count: count)
158
+ case . lineComment, . blockComment, . docLineComment, . docBlockComment, . unexpectedText, . shebang:
159
+ return [ piece]
160
+ }
161
+ } )
162
+ return Trivia ( pieces: pieces)
163
+ }
164
+
165
+ /// Concatenate `lhs` and `rhs`, merging an infix that is shared between both trivia pieces.
166
+ static func merged( _ lhs: Trivia , _ rhs: Trivia ) -> Self {
167
+ let lhs = lhs. decomposed
168
+ let rhs = rhs. decomposed
169
+ for infixLength in ( 0 ... Swift . min ( lhs. count, rhs. count) ) . reversed ( ) {
170
+ if lhs. suffix ( infixLength) == rhs. suffix ( infixLength) {
171
+ return lhs + Trivia( pieces: Array ( rhs. dropFirst ( infixLength) ) )
172
+ }
173
+ }
174
+ return lhs + rhs
175
+ }
176
+ }
177
+
178
+ extension TriviaPiece {
179
+ var isSpaceOrTab : Bool {
180
+ switch self {
181
+ case . spaces, . tabs:
182
+ return true
183
+ default :
184
+ return false
185
+ }
186
+ }
112
187
}
0 commit comments