Skip to content

Commit f83fe74

Browse files
committed
Add a function to merge trivia from a node into an existing trivia
Generally when a node is removed, we need to add its trivia to an existing node. Add a small helper function to aid in this case.
1 parent cb0f24e commit f83fe74

File tree

4 files changed

+74
-63
lines changed

4 files changed

+74
-63
lines changed

Sources/SwiftParserDiagnostics/DiagnosticExtensions.swift

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ extension FixIt.Changes {
142142
/// where it makes sense and refusing to add e.g. a space after punctuation,
143143
/// where it usually doesn't make sense.
144144
static func transferTriviaAtSides<SyntaxType: SyntaxProtocol>(from nodes: [SyntaxType]) -> Self {
145-
let removedTriviaAtSides = Trivia.merged(nodes.first?.leadingTrivia ?? [], nodes.last?.trailingTrivia ?? [])
145+
let removedTriviaAtSides = (nodes.first?.leadingTrivia ?? []).merging(nodes.last?.trailingTrivia ?? [])
146146
if !removedTriviaAtSides.isEmpty, let previousToken = nodes.first?.previousToken(viewMode: .sourceAccurate) {
147-
let mergedTrivia = Trivia.merged(previousToken.trailingTrivia, removedTriviaAtSides)
147+
let mergedTrivia = previousToken.trailingTrivia.merging(removedTriviaAtSides)
148148
if previousToken.tokenKind.isPunctuation, mergedTrivia.allSatisfy({ $0.isSpaceOrTab }) {
149149
// Punctuation is generally not followed by spaces in Swift.
150150
// If this action would only add spaces to the punctuation, drop it.
@@ -158,49 +158,6 @@ extension FixIt.Changes {
158158
}
159159
}
160160

161-
extension Trivia {
162-
/// Decomposes the trivia into pieces that all have count 1
163-
var decomposed: Trivia {
164-
let pieces = self.flatMap({ (piece: TriviaPiece) -> [TriviaPiece] in
165-
switch piece {
166-
case .spaces(let count):
167-
return Array(repeating: TriviaPiece.spaces(1), count: count)
168-
case .tabs(let count):
169-
return Array(repeating: TriviaPiece.tabs(1), count: count)
170-
case .verticalTabs(let count):
171-
return Array(repeating: TriviaPiece.verticalTabs(1), count: count)
172-
case .formfeeds(let count):
173-
return Array(repeating: TriviaPiece.formfeeds(1), count: count)
174-
case .newlines(let count):
175-
return Array(repeating: TriviaPiece.newlines(1), count: count)
176-
case .backslashes(let count):
177-
return Array(repeating: TriviaPiece.backslashes(1), count: count)
178-
case .pounds(let count):
179-
return Array(repeating: TriviaPiece.pounds(1), count: count)
180-
case .carriageReturns(let count):
181-
return Array(repeating: TriviaPiece.carriageReturns(1), count: count)
182-
case .carriageReturnLineFeeds(let count):
183-
return Array(repeating: TriviaPiece.carriageReturnLineFeeds(1), count: count)
184-
case .lineComment, .blockComment, .docLineComment, .docBlockComment, .unexpectedText, .shebang:
185-
return [piece]
186-
}
187-
})
188-
return Trivia(pieces: pieces)
189-
}
190-
191-
/// Concatenate `lhs` and `rhs`, merging an infix that is shared between both trivia pieces.
192-
static func merged(_ lhs: Trivia, _ rhs: Trivia) -> Self {
193-
let lhs = lhs.decomposed
194-
let rhs = rhs.decomposed
195-
for infixLength in (0...Swift.min(lhs.count, rhs.count)).reversed() {
196-
if lhs.suffix(infixLength) == rhs.suffix(infixLength) {
197-
return lhs + Trivia(pieces: Array(rhs.dropFirst(infixLength)))
198-
}
199-
}
200-
return lhs + rhs
201-
}
202-
}
203-
204161
extension TriviaPiece {
205162
var isSpaceOrTab: Bool {
206163
switch self {

Sources/SwiftParserDiagnostics/MultiLineStringLiteralDiagnosticsGenerator.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import SwiftDiagnostics
14+
@_spi(RawSyntax)
1415
import SwiftSyntax
1516

1617
/// A diagnostic that `MultiLineStringLiteralIndentatinDiagnosticsGenerator` is building.

Sources/SwiftSyntax/Trivia.swift

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,60 @@ public struct Trivia {
2525
self.pieces = Array(pieces)
2626
}
2727

28-
/// Creates Trivia with no pieces.
29-
public static var zero: Trivia {
30-
return Trivia(pieces: [])
31-
}
32-
3328
/// Whether the Trivia contains no pieces.
3429
public var isEmpty: Bool {
3530
pieces.isEmpty
3631
}
3732

33+
public var sourceLength: SourceLength {
34+
return pieces.map({ $0.sourceLength }).reduce(.zero, +)
35+
}
36+
37+
/// Get the byteSize of this trivia
38+
public var byteSize: Int {
39+
return sourceLength.utf8Length
40+
}
41+
3842
/// Creates a new `Trivia` by appending the provided `TriviaPiece` to the end.
3943
public func appending(_ piece: TriviaPiece) -> Trivia {
4044
var copy = pieces
4145
copy.append(piece)
4246
return Trivia(pieces: copy)
4347
}
4448

45-
public var sourceLength: SourceLength {
46-
return pieces.map({ $0.sourceLength }).reduce(.zero, +)
49+
/// Creates a new `Trivia` by appending the given trivia to the end.
50+
public func appending(_ trivia: Trivia) -> Trivia {
51+
var copy = pieces
52+
copy.append(contentsOf: trivia.pieces)
53+
return Trivia(pieces: copy)
4754
}
4855

49-
/// Get the byteSize of this trivia
50-
public var byteSize: Int {
51-
return sourceLength.utf8Length
56+
/// Creates a new `Trivia` by appending the given trivia to the end.
57+
public func merging(_ trivia: Trivia) -> Trivia {
58+
let lhs = self.decomposed
59+
let rhs = trivia.decomposed
60+
for infixLength in (0...Swift.min(lhs.count, rhs.count)).reversed() {
61+
if lhs.suffix(infixLength) == rhs.suffix(infixLength) {
62+
return lhs.appending(Trivia(pieces: Array(rhs.dropFirst(infixLength))))
63+
}
64+
}
65+
return lhs.appending(rhs)
66+
}
67+
68+
/// Creates a new `Trivia` by merging the leading and trailing `Trivia`
69+
/// of `triviaOf` to the end, .
70+
public func merging<T: SyntaxProtocol>(triviaOf node: T) -> Trivia {
71+
return merging(node.leadingTrivia).merging(node.trailingTrivia)
5272
}
5373

5474
/// Concatenates two collections of `Trivia` into one collection.
55-
public static func +(lhs: Trivia, rhs: Trivia) -> Trivia {
56-
return Trivia(pieces: lhs.pieces + rhs.pieces)
75+
public static func + (lhs: Trivia, rhs: Trivia) -> Trivia {
76+
return lhs.appending(rhs)
5777
}
5878

5979
/// Concatenates two collections of `Trivia` into the left-hand side.
60-
public static func +=(lhs: inout Trivia, rhs: Trivia) {
61-
lhs = lhs + rhs
80+
public static func += (lhs: inout Trivia, rhs: Trivia) {
81+
lhs = lhs.appending(rhs)
6282
}
6383
}
6484

@@ -118,13 +138,45 @@ extension Trivia: CustomDebugStringConvertible {
118138
}
119139
}
120140

141+
extension Trivia {
142+
/// Decomposes the trivia into pieces that all have count 1
143+
@_spi(RawSyntax)
144+
public var decomposed: Trivia {
145+
let pieces = self.flatMap({ (piece: TriviaPiece) -> [TriviaPiece] in
146+
switch piece {
147+
case .spaces(let count):
148+
return Array(repeating: TriviaPiece.spaces(1), count: count)
149+
case .tabs(let count):
150+
return Array(repeating: TriviaPiece.tabs(1), count: count)
151+
case .verticalTabs(let count):
152+
return Array(repeating: TriviaPiece.verticalTabs(1), count: count)
153+
case .formfeeds(let count):
154+
return Array(repeating: TriviaPiece.formfeeds(1), count: count)
155+
case .newlines(let count):
156+
return Array(repeating: TriviaPiece.newlines(1), count: count)
157+
case .backslashes(let count):
158+
return Array(repeating: TriviaPiece.backslashes(1), count: count)
159+
case .pounds(let count):
160+
return Array(repeating: TriviaPiece.pounds(1), count: count)
161+
case .carriageReturns(let count):
162+
return Array(repeating: TriviaPiece.carriageReturns(1), count: count)
163+
case .carriageReturnLineFeeds(let count):
164+
return Array(repeating: TriviaPiece.carriageReturnLineFeeds(1), count: count)
165+
case .lineComment, .blockComment, .docLineComment, .docBlockComment, .unexpectedText, .shebang:
166+
return [piece]
167+
}
168+
})
169+
return Trivia(pieces: pieces)
170+
}
171+
}
172+
121173
extension TriviaPiece {
122174
/// Returns true if the trivia is `.newlines`, `.carriageReturns` or `.carriageReturnLineFeeds`
123175
public var isNewline: Bool {
124176
switch self {
125177
case .newlines,
126-
.carriageReturns,
127-
.carriageReturnLineFeeds:
178+
.carriageReturns,
179+
.carriageReturnLineFeeds:
128180
return true
129181
default:
130182
return false
@@ -137,8 +189,8 @@ extension RawTriviaPiece {
137189
public var isNewline: Bool {
138190
switch self {
139191
case .newlines,
140-
.carriageReturns,
141-
.carriageReturnLineFeeds:
192+
.carriageReturns,
193+
.carriageReturnLineFeeds:
142194
return true
143195
default:
144196
return false

utils/group.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"TokenKind.swift",
1919
"SyntaxTreeViewMode.swift",
2020
"Trivia.swift",
21+
"TriviaPieces.swift",
2122
"Tokens.swift",
2223
],
2324
"Syntax nodes": [

0 commit comments

Comments
 (0)