Skip to content

Commit e7bcc57

Browse files
authored
Merge pull request swiftlang#91 from hartbit/syntax-trivia
Helper methods to modify leading and trailing trivia on nodes
2 parents 87977eb + c0266e1 commit e7bcc57

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
@@ -1099,6 +1099,44 @@ final class RawSyntax: ManagedBuffer<RawSyntaxBase, RawSyntaxDataElement> {
10991099
}
11001100
}
11011101

1102+
func withLeadingTrivia(_ leadingTrivia: Trivia) -> RawSyntax {
1103+
if isToken {
1104+
return RawSyntax.createAndCalcLength(
1105+
kind: formTokenKind()!,
1106+
leadingTrivia: leadingTrivia,
1107+
trailingTrivia: formTrailingTrivia()!,
1108+
presence: presence)
1109+
} else {
1110+
var layout = formLayoutArray()
1111+
for (index, raw) in layout.enumerated() {
1112+
if let raw = raw {
1113+
layout[index] = raw.withLeadingTrivia(leadingTrivia)
1114+
return replacingLayout(layout)
1115+
}
1116+
}
1117+
return self
1118+
}
1119+
}
1120+
1121+
func withTrailingTrivia(_ trailingTrivia: Trivia) -> RawSyntax {
1122+
if isToken {
1123+
return RawSyntax.createAndCalcLength(
1124+
kind: formTokenKind()!,
1125+
leadingTrivia: formLeadingTrivia()!,
1126+
trailingTrivia: trailingTrivia,
1127+
presence: presence)
1128+
} else {
1129+
var layout = formLayoutArray()
1130+
for (index, raw) in layout.enumerated().reversed() {
1131+
if let raw = raw {
1132+
layout[index] = raw.withTrailingTrivia(trailingTrivia)
1133+
return replacingLayout(layout)
1134+
}
1135+
}
1136+
return self
1137+
}
1138+
}
1139+
11021140
/// Creates a RawSyntax node that's marked missing in the source with the
11031141
/// provided kind and layout.
11041142
/// - Parameters:

Sources/SwiftSyntax/Syntax.swift

+2-11
Original file line numberDiff line numberDiff line change
@@ -630,11 +630,7 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
630630
guard raw.kind == .token else {
631631
fatalError("TokenSyntax must have token as its raw")
632632
}
633-
let newRaw = RawSyntax.createAndCalcLength(kind: raw.formTokenKind()!,
634-
leadingTrivia: leadingTrivia, trailingTrivia: raw.formTrailingTrivia()!,
635-
presence: raw.presence)
636-
let newData = data.replacingSelf(newRaw)
637-
return TokenSyntax(newData)
633+
return TokenSyntax(data.withLeadingTrivia(leadingTrivia))
638634
}
639635

640636
/// Returns a new TokenSyntax with its trailing trivia replaced
@@ -643,12 +639,7 @@ public struct TokenSyntax: _SyntaxBase, Hashable {
643639
guard raw.kind == .token else {
644640
fatalError("TokenSyntax must have token as its raw")
645641
}
646-
let newRaw = RawSyntax.createAndCalcLength(kind: raw.formTokenKind()!,
647-
leadingTrivia: raw.formLeadingTrivia()!,
648-
trailingTrivia: trailingTrivia,
649-
presence: raw.presence)
650-
let newData = data.replacingSelf(newRaw)
651-
return TokenSyntax(newData)
642+
return TokenSyntax(data.withTrailingTrivia(trailingTrivia))
652643
}
653644

654645
/// 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
@@ -291,4 +291,12 @@ struct SyntaxData {
291291
where CursorType.RawValue == Int {
292292
return replacingChild(child, at: cursor.rawValue)
293293
}
294+
295+
func withLeadingTrivia(_ leadingTrivia: Trivia) -> SyntaxData {
296+
return replacingSelf(raw.withLeadingTrivia(leadingTrivia))
297+
}
298+
299+
func withTrailingTrivia(_ trailingTrivia: Trivia) -> SyntaxData {
300+
return replacingSelf(raw.withTrailingTrivia(trailingTrivia))
301+
}
294302
}

Sources/SwiftSyntax/SyntaxNodes.swift.gyb

+47
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,53 @@ public struct ${node.name}: ${base_type}, _SyntaxBase, Hashable {
165165
}
166166
% end
167167

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