Skip to content

Commit 4844ec7

Browse files
committed
Parse comment blocks out to commentValues
Blocks of comments are now concatenated with their related lines within the same block comment and added to the commentValues property as one stripped, concatenated string If there are n code blocks, there will be n stripped, concatenated elements in the commentValues (plus x line comments)
1 parent d1d4020 commit 4844ec7

File tree

3 files changed

+220
-19
lines changed

3 files changed

+220
-19
lines changed

Sources/SwiftSyntax/Trivia.swift

+49-19
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,10 @@ public struct Trivia: Sendable {
4646
///
4747
/// Each element in the array is the trimmed contents of a line comment, or, in the case of a multi-line comment a trimmed, concatenated single string.
4848
public var commentValues: [String] {
49-
pieces.compactMap {
50-
switch $0 {
51-
case .lineComment(let text), .docLineComment(let text):
52-
sanitizingLineComment(text)
53-
54-
default:
55-
nil
56-
}
57-
}
49+
[
50+
sanitizedLineCommentValues,
51+
sanitizedBlockCommentValues,
52+
].flatMap { $0 }
5853
}
5954

6055
/// The length of all the pieces in this ``Trivia``.
@@ -231,18 +226,53 @@ extension RawTriviaPiece: CustomDebugStringConvertible {
231226
}
232227
}
233228

234-
private func sanitizingLineComment(_ text: String) -> String {
235-
// TODO: adammcarter - can we import Foundation instead and use trimmingCharacters(in:)
236-
237-
var charactersToDrop = 0
229+
private extension Trivia {
230+
var sanitizedLineCommentValues: [String] {
231+
compactMap {
232+
switch $0 {
233+
case .lineComment(let text), .docLineComment(let text):
234+
String(sanitizingLineComment(text))
238235

239-
for character in text {
240-
if (character == "/" || character == " ") {
241-
charactersToDrop += 1
242-
} else {
243-
break
236+
default:
237+
nil
238+
}
244239
}
245240
}
246241

247-
return String(text.dropFirst(charactersToDrop))
242+
func sanitizingLineComment(_ text: String) -> Substring {
243+
text.trimmingPrefix("/ ")
244+
}
245+
246+
var sanitizedBlockCommentValues: [String] {
247+
var lines = [String]()
248+
var substrings = [Substring]()
249+
var foundTerminator = false
250+
251+
for piece in self {
252+
switch piece {
253+
case .blockComment(let text), .docBlockComment(let text):
254+
let sanitized = text.trimmingCharacters(in: "/* ")
255+
256+
if substrings.isEmpty || sanitized.isEmpty == false {
257+
substrings.append(sanitized)
258+
}
259+
260+
foundTerminator = text.hasSuffix("*/")
261+
262+
default:
263+
break
264+
}
265+
266+
if foundTerminator, substrings.isEmpty == false {
267+
lines.append(substrings.joined(separator: " "))
268+
substrings.removeAll()
269+
}
270+
}
271+
272+
if substrings.isEmpty == false {
273+
lines.append(substrings.joined(separator: " "))
274+
}
275+
276+
return lines
277+
}
248278
}

Sources/SwiftSyntax/Utils.swift

+42
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,45 @@ extension RawUnexpectedNodesSyntax {
120120
self.init(raw: raw)
121121
}
122122
}
123+
124+
extension String {
125+
func trimmingCharacters(in charactersToTrim: any BidirectionalCollection<Character>) -> Substring {
126+
// TODO: adammcarter - this feels a bit dirty
127+
self[startIndex...].trimmingPrefix(charactersToTrim).trimmingSuffix(charactersToTrim)
128+
}
129+
130+
func trimmingPrefix(_ charactersToTrim: any BidirectionalCollection<Character>) -> Substring {
131+
self[startIndex...].trimmingPrefix(charactersToTrim)
132+
}
133+
134+
func trimmingSuffix(_ charactersToTrim: any BidirectionalCollection<Character>) -> Substring {
135+
self[startIndex...].trimmingSuffix(charactersToTrim)
136+
}
137+
}
138+
139+
extension Substring {
140+
func trimmingPrefix(_ charactersToTrim: any BidirectionalCollection<Character>) -> Self {
141+
dropFirst(countOfSequentialCharacters(charactersToTrim, in: self))
142+
}
143+
144+
func trimmingSuffix(_ charactersToTrim: any BidirectionalCollection<Character>) -> Self {
145+
dropLast(countOfSequentialCharacters(charactersToTrim, in: reversed()))
146+
}
147+
}
148+
149+
private func countOfSequentialCharacters(
150+
_ charactersToCount: any BidirectionalCollection<Character>,
151+
in characters: any BidirectionalCollection<Character>
152+
) -> Int {
153+
var count = 0
154+
155+
for character in characters {
156+
if charactersToCount.contains(character) {
157+
count += 1
158+
} else {
159+
break
160+
}
161+
}
162+
163+
return count
164+
}

Tests/SwiftSyntaxTest/TriviaTests.swift

+129
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,134 @@ class TriviaTests: XCTestCase {
150150
"Other",
151151
]
152152
)
153+
154+
// MARK: block comment
155+
156+
XCTAssertEqual(
157+
Trivia(pieces: [.blockComment("")]).commentValues,
158+
[""]
159+
)
160+
161+
XCTAssertEqual(
162+
Trivia(pieces: [.blockComment("Some block comment")]).commentValues,
163+
["Some block comment"]
164+
)
165+
166+
XCTAssertEqual(
167+
Trivia(pieces: [.blockComment("/** Some block comment */")]).commentValues,
168+
["Some block comment"]
169+
)
170+
171+
XCTAssertEqual(
172+
Trivia(pieces: [
173+
.blockComment("/** Some block comment"),
174+
.blockComment("* spread on many lines */"),
175+
]).commentValues,
176+
[
177+
"Some block comment spread on many lines"
178+
]
179+
)
180+
181+
XCTAssertEqual(
182+
Trivia(pieces: [
183+
.blockComment("/** Some block comment"),
184+
.blockComment("* spread on many lines"),
185+
.blockComment("*/"),
186+
]).commentValues,
187+
[
188+
"Some block comment spread on many lines"
189+
]
190+
)
191+
192+
XCTAssertEqual(
193+
Trivia(pieces: [
194+
.blockComment("/** Some block comment"),
195+
.blockComment("* spread on many lines */"),
196+
.blockComment("/** Another block comment */"),
197+
]).commentValues,
198+
[
199+
"Some block comment spread on many lines",
200+
"Another block comment",
201+
]
202+
)
203+
204+
XCTAssertEqual(
205+
Trivia(pieces: [
206+
.blockComment("/** Some block comment"),
207+
.blockComment("* spread on many lines */"),
208+
.newlines(2),
209+
.blockComment("/** Another block comment */"),
210+
]).commentValues,
211+
[
212+
"Some block comment spread on many lines",
213+
"Another block comment",
214+
]
215+
)
216+
217+
// MARK: doc block comment
218+
219+
XCTAssertEqual(
220+
Trivia(pieces: [.docBlockComment("")]).commentValues,
221+
[""]
222+
)
223+
224+
XCTAssertEqual(
225+
Trivia(pieces: [.docBlockComment("Some doc block comment")]).commentValues,
226+
["Some doc block comment"]
227+
)
228+
229+
XCTAssertEqual(
230+
Trivia(pieces: [.docBlockComment("/** Some doc block comment */")]).commentValues,
231+
["Some doc block comment"]
232+
)
233+
234+
XCTAssertEqual(
235+
Trivia(pieces: [
236+
.docBlockComment("/** Some doc block comment"),
237+
.docBlockComment("* spread on many lines */"),
238+
]).commentValues,
239+
[
240+
"Some doc block comment spread on many lines"
241+
]
242+
)
243+
244+
XCTAssertEqual(
245+
Trivia(pieces: [
246+
.docBlockComment("/** Some doc block comment"),
247+
.docBlockComment("* spread on many lines"),
248+
.docBlockComment("*/"),
249+
]).commentValues,
250+
[
251+
"Some doc block comment spread on many lines"
252+
]
253+
)
254+
255+
XCTAssertEqual(
256+
Trivia(pieces: [
257+
.docBlockComment("/** Some doc block comment"),
258+
.docBlockComment("* spread on many lines */"),
259+
.docBlockComment("/** Another doc block comment */"),
260+
]).commentValues,
261+
[
262+
"Some doc block comment spread on many lines",
263+
"Another doc block comment",
264+
]
265+
)
266+
267+
XCTAssertEqual(
268+
Trivia(pieces: [
269+
.docBlockComment("/** Some doc block comment"),
270+
.docBlockComment("* spread on many lines */"),
271+
.newlines(2),
272+
.docBlockComment("/** Another doc block comment */"),
273+
]).commentValues,
274+
[
275+
"Some doc block comment spread on many lines",
276+
"Another doc block comment",
277+
]
278+
)
279+
280+
// TODO: adammcarter - mixing trivia of lines and blocks
281+
// TODO: adammcarter - newline chars in the prefix/suffix of code block too
153282
}
154283
}

0 commit comments

Comments
 (0)