Skip to content

Commit c0266e1

Browse files
committed
Helper methods to modify leading and trailing trivia on nodes
1 parent 4440e57 commit c0266e1

File tree

6 files changed

+190
-23
lines changed

6 files changed

+190
-23
lines changed

Sources/SwiftSyntax/RawSyntax.swift

+38
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,44 @@ final class RawSyntax: ManagedBuffer<RawSyntaxBase, RawSyntaxDataElement> {
915915
}
916916
}
917917

918+
func withLeadingTrivia(_ leadingTrivia: Trivia) -> RawSyntax {
919+
if isToken {
920+
return RawSyntax.createAndCalcLength(
921+
kind: formTokenKind()!,
922+
leadingTrivia: leadingTrivia,
923+
trailingTrivia: formTrailingTrivia()!,
924+
presence: presence)
925+
} else {
926+
var layout = formLayoutArray()
927+
for (index, raw) in layout.enumerated() {
928+
if let raw = raw {
929+
layout[index] = raw.withLeadingTrivia(leadingTrivia)
930+
return replacingLayout(layout)
931+
}
932+
}
933+
return self
934+
}
935+
}
936+
937+
func withTrailingTrivia(_ trailingTrivia: Trivia) -> RawSyntax {
938+
if isToken {
939+
return RawSyntax.createAndCalcLength(
940+
kind: formTokenKind()!,
941+
leadingTrivia: formLeadingTrivia()!,
942+
trailingTrivia: trailingTrivia,
943+
presence: presence)
944+
} else {
945+
var layout = formLayoutArray()
946+
for (index, raw) in layout.enumerated().reversed() {
947+
if let raw = raw {
948+
layout[index] = raw.withTrailingTrivia(trailingTrivia)
949+
return replacingLayout(layout)
950+
}
951+
}
952+
return self
953+
}
954+
}
955+
918956
/// Creates a RawSyntax node that's marked missing in the source with the
919957
/// provided kind and layout.
920958
/// - Parameters:

Sources/SwiftSyntax/Syntax.swift

+2-11
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,7 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
527527
guard raw.kind == .token else {
528528
fatalError("TokenSyntax must have token as its raw")
529529
}
530-
let newRaw = RawSyntax.createAndCalcLength(kind: raw.formTokenKind()!,
531-
leadingTrivia: leadingTrivia, trailingTrivia: raw.formTrailingTrivia()!,
532-
presence: raw.presence)
533-
let newData = data.replacingSelf(newRaw)
534-
return TokenSyntax(newData)
530+
return TokenSyntax(data.withLeadingTrivia(leadingTrivia))
535531
}
536532

537533
/// Returns a new TokenSyntax with its trailing trivia replaced
@@ -540,12 +536,7 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
540536
guard raw.kind == .token else {
541537
fatalError("TokenSyntax must have token as its raw")
542538
}
543-
let newRaw = RawSyntax.createAndCalcLength(kind: raw.formTokenKind()!,
544-
leadingTrivia: raw.formLeadingTrivia()!,
545-
trailingTrivia: trailingTrivia,
546-
presence: raw.presence)
547-
let newData = data.replacingSelf(newRaw)
548-
return TokenSyntax(newData)
539+
return TokenSyntax(data.withTrailingTrivia(trailingTrivia))
549540
}
550541

551542
/// Returns a new TokenSyntax with its leading trivia removed.

Sources/SwiftSyntax/SyntaxCollections.swift.gyb

+47
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,53 @@ public struct ${node.name}: _SyntaxBase, Hashable, SyntaxCollection {
144144
return replacingLayout(newLayout)
145145
}
146146

147+
/// Returns a new `${node.name}` with its leading trivia replaced
148+
/// by the provided trivia.
149+
public func withLeadingTrivia(_ leadingTrivia: Trivia) -> ${node.name} {
150+
return ${node.name}(data.withLeadingTrivia(leadingTrivia))
151+
}
152+
153+
/// Returns a new `${node.name}` with its trailing trivia replaced
154+
/// by the provided trivia.
155+
public func withTrailingTrivia(_ trailingTrivia: Trivia) -> ${node.name} {
156+
return ${node.name}(data.withTrailingTrivia(trailingTrivia))
157+
}
158+
159+
/// Returns a new `${node.name}` with its leading trivia removed.
160+
public func withoutLeadingTrivia() -> ${node.name} {
161+
return withLeadingTrivia([])
162+
}
163+
164+
/// Returns a new `${node.name}` with its trailing trivia removed.
165+
public func withoutTrailingTrivia() -> ${node.name} {
166+
return withTrailingTrivia([])
167+
}
168+
169+
/// Returns a new `${node.name}` with all trivia removed.
170+
public func withoutTrivia() -> ${node.name} {
171+
return withoutLeadingTrivia().withoutTrailingTrivia()
172+
}
173+
174+
/// The leading trivia (spaces, newlines, etc.) associated with this `${node.name}`.
175+
public var leadingTrivia: Trivia? {
176+
get {
177+
return raw.formLeadingTrivia()
178+
}
179+
set {
180+
self = withLeadingTrivia(newValue ?? [])
181+
}
182+
}
183+
184+
/// The trailing trivia (spaces, newlines, etc.) associated with this `${node.name}`.
185+
public var trailingTrivia: Trivia? {
186+
get {
187+
return raw.formTrailingTrivia()
188+
}
189+
set {
190+
self = withTrailingTrivia(newValue ?? [])
191+
}
192+
}
193+
147194
/// Determines if two `${node.name}` nodes are equal to each other.
148195
public static func ==(lhs: ${node.name}, rhs: ${node.name}) -> Bool {
149196
return lhs.data.nodeId == rhs.data.nodeId

Sources/SwiftSyntax/SyntaxData.swift

+8
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,12 @@ struct SyntaxData {
255255
where CursorType.RawValue == Int {
256256
return replacingChild(child, at: cursor.rawValue)
257257
}
258+
259+
func withLeadingTrivia(_ leadingTrivia: Trivia) -> SyntaxData {
260+
return replacingSelf(raw.withLeadingTrivia(leadingTrivia))
261+
}
262+
263+
func withTrailingTrivia(_ trailingTrivia: Trivia) -> SyntaxData {
264+
return replacingSelf(raw.withTrailingTrivia(trailingTrivia))
265+
}
258266
}

Sources/SwiftSyntax/SyntaxNodes.swift.gyb

+47
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,53 @@ public struct ${node.name}: ${base_type}, _SyntaxBase, Hashable {
159159
}
160160
% end
161161

162+
/// Returns a new `${node.name}` with its leading trivia replaced
163+
/// by the provided trivia.
164+
public func withLeadingTrivia(_ leadingTrivia: Trivia) -> ${node.name} {
165+
return ${node.name}(data.withLeadingTrivia(leadingTrivia))
166+
}
167+
168+
/// Returns a new `${node.name}` with its trailing trivia replaced
169+
/// by the provided trivia.
170+
public func withTrailingTrivia(_ trailingTrivia: Trivia) -> ${node.name} {
171+
return ${node.name}(data.withTrailingTrivia(trailingTrivia))
172+
}
173+
174+
/// Returns a new `${node.name}` with its leading trivia removed.
175+
public func withoutLeadingTrivia() -> ${node.name} {
176+
return withLeadingTrivia([])
177+
}
178+
179+
/// Returns a new `${node.name}` with its trailing trivia removed.
180+
public func withoutTrailingTrivia() -> ${node.name} {
181+
return withTrailingTrivia([])
182+
}
183+
184+
/// Returns a new `${node.name}` with all trivia removed.
185+
public func withoutTrivia() -> ${node.name} {
186+
return withoutLeadingTrivia().withoutTrailingTrivia()
187+
}
188+
189+
/// The leading trivia (spaces, newlines, etc.) associated with this `${node.name}`.
190+
public var leadingTrivia: Trivia? {
191+
get {
192+
return raw.formLeadingTrivia()
193+
}
194+
set {
195+
self = withLeadingTrivia(newValue ?? [])
196+
}
197+
}
198+
199+
/// The trailing trivia (spaces, newlines, etc.) associated with this `${node.name}`.
200+
public var trailingTrivia: Trivia? {
201+
get {
202+
return raw.formTrailingTrivia()
203+
}
204+
set {
205+
self = withTrailingTrivia(newValue ?? [])
206+
}
207+
}
208+
162209
/// Determines if two `${node.name}` nodes are equal to each other.
163210
public static func ==(lhs: ${node.name}, rhs: ${node.name}) -> Bool {
164211
return lhs.data.nodeId == rhs.data.nodeId

Tests/SwiftSyntaxTest/AbsolutePosition.swift

+48-12
Original file line numberDiff line numberDiff line change
@@ -88,24 +88,26 @@ public class AbsolutePositionTestCase: XCTestCase {
8888
_ = root.statements[idx].positionAfterSkippingLeadingTrivia
8989
}
9090

91+
static let leadingTrivia = Trivia(pieces: [
92+
.newlines(1),
93+
.backticks(1),
94+
.docLineComment("/// some comment"),
95+
.carriageReturns(1),
96+
])
97+
98+
static let trailingTrivia = Trivia(pieces: [
99+
.blockComment("/* This is comment \r\r\n */"),
100+
.carriageReturnLineFeeds(1),
101+
])
102+
91103
func createSourceFile(_ count: Int) -> SourceFileSyntax {
92-
let leading = Trivia(pieces: [
93-
.newlines(1),
94-
.backticks(1),
95-
.docLineComment("/// some comment"),
96-
.carriageReturns(1),
97-
])
98-
let trailing = Trivia(pieces: [
99-
.blockComment("/* This is comment \r\r\n */"),
100-
.carriageReturnLineFeeds(1),
101-
])
102104
let items : [CodeBlockItemSyntax] =
103105
[CodeBlockItemSyntax](repeating: CodeBlockItemSyntax {
104106
$0.useItem(ReturnStmtSyntax {
105107
$0.useReturnKeyword(
106108
SyntaxFactory.makeReturnKeyword(
107-
leadingTrivia: leading,
108-
trailingTrivia: trailing))
109+
leadingTrivia: AbsolutePositionTestCase.leadingTrivia,
110+
trailingTrivia: AbsolutePositionTestCase.trailingTrivia))
109111
})}, count: count)
110112
return SyntaxFactory.makeSourceFile(
111113
statements: SyntaxFactory.makeCodeBlockItemList(items),
@@ -124,6 +126,40 @@ public class AbsolutePositionTestCase: XCTestCase {
124126
state.leadingTrivia!.byteSize + state.trailingTrivia!.byteSize
125127
+ state.byteSizeAfterTrimmingTrivia)
126128
XCTAssertFalse(root.statements.isImplicit)
129+
130+
// Test Node trivia setters and getters
131+
132+
XCTAssertEqual(AbsolutePositionTestCase.leadingTrivia, root.leadingTrivia)
133+
XCTAssertEqual([], root.trailingTrivia)
134+
135+
var modifiedRoot1 = root.withLeadingTrivia([.spaces(6), .tabs(1)])
136+
XCTAssertEqual([.spaces(6), .tabs(1)], modifiedRoot1.leadingTrivia)
137+
XCTAssertEqual(AbsolutePositionTestCase.leadingTrivia, root.leadingTrivia)
138+
modifiedRoot1.leadingTrivia = [.blockComment("/* this is a comment */")]
139+
XCTAssertEqual([.blockComment("/* this is a comment */")], modifiedRoot1.leadingTrivia)
140+
141+
var modifiedRoot2 = root.withTrailingTrivia([.backticks(2)])
142+
XCTAssertEqual([.backticks(2)], modifiedRoot2.trailingTrivia)
143+
XCTAssertEqual([], root.trailingTrivia)
144+
modifiedRoot2.trailingTrivia = [.carriageReturns(1), .newlines(2)]
145+
XCTAssertEqual([.carriageReturns(1), .newlines(2)], modifiedRoot2.trailingTrivia)
146+
147+
// Test Collection trivia setters and getters
148+
149+
XCTAssertEqual(AbsolutePositionTestCase.leadingTrivia, root.statements.leadingTrivia)
150+
XCTAssertEqual(AbsolutePositionTestCase.trailingTrivia, root.statements.trailingTrivia)
151+
152+
var modifiedStatements1 = root.withLeadingTrivia([.carriageReturnLineFeeds(3)])
153+
XCTAssertEqual([.carriageReturnLineFeeds(3)], modifiedStatements1.leadingTrivia)
154+
XCTAssertEqual(AbsolutePositionTestCase.leadingTrivia, root.statements.leadingTrivia)
155+
modifiedStatements1.leadingTrivia = [.garbageText("GARBAGE")]
156+
XCTAssertEqual([.garbageText("GARBAGE")], modifiedStatements1.leadingTrivia)
157+
158+
var modifiedStatements2 = root.withTrailingTrivia([.formfeeds(1), .carriageReturns(3)])
159+
XCTAssertEqual([.formfeeds(1), .carriageReturns(3)], modifiedStatements2.trailingTrivia)
160+
XCTAssertEqual(AbsolutePositionTestCase.trailingTrivia, root.statements.trailingTrivia)
161+
modifiedStatements2.trailingTrivia = [.verticalTabs(4)]
162+
XCTAssertEqual([.verticalTabs(4)], modifiedStatements2.trailingTrivia)
127163
}
128164

129165
public func testImplicit() {

0 commit comments

Comments
 (0)