Skip to content

Commit 8df001d

Browse files
committed
Address fourth review
1 parent 312bbc9 commit 8df001d

File tree

2 files changed

+127
-134
lines changed

2 files changed

+127
-134
lines changed

Sources/SwiftSyntax/Trivia.swift

+101-65
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,23 @@ public struct Trivia: Sendable {
4343
}
4444

4545
/// The string contents of all the comment pieces with any comments tokens trimmed.
46-
public var commentValue: String {
46+
public var commentValue: String? {
4747
var comments = [Substring]()
48+
var hasBlockComment = false
49+
var hasLineComment = false
4850

4951
// Determine if all line comments have a single space
5052
lazy var allLineCommentsHaveSpace: Bool = {
51-
return pieces.allSatisfy { piece in
52-
switch piece {
53-
case .lineComment(let text):
54-
return text.hasPrefix("// ")
55-
case .docLineComment(let text):
56-
return text.hasPrefix("/// ")
57-
default:
58-
return true
53+
return pieces.allSatisfy { piece in
54+
switch piece {
55+
case .lineComment(let text):
56+
return text.hasPrefix("// ")
57+
case .docLineComment(let text):
58+
return text.hasPrefix("/// ")
59+
default:
60+
return true
61+
}
5962
}
60-
}
6163
}()
6264

6365
// Returns a substring with leading and trailing spaces removed.
@@ -71,71 +73,105 @@ public struct Trivia: Sendable {
7173

7274
// Strips /* */ markers and aligns content by removing common indentation.
7375
func processBlockComment(_ text: Substring) -> String {
74-
var lines = text.split(separator: "\n", omittingEmptySubsequences: false)
75-
76-
let minIndentation =
77-
lines
78-
.dropFirst()
79-
.filter { !$0.isEmpty }
80-
.map { $0.prefix { $0 == " " }.count }
81-
.min() ?? 0
82-
83-
var firstLineRemoved = false;
84-
var firstLine = lines[0]
85-
if trimWhitespace(firstLine) == "/*" || trimWhitespace(firstLine) == "/**" {
86-
lines.removeFirst()
87-
firstLineRemoved = true;
88-
} else {
89-
firstLine = firstLine.hasPrefix("/**") ? firstLine.dropFirst(3) : firstLine.dropFirst(2)
90-
while firstLine.first?.isWhitespace == true {
91-
firstLine = firstLine.dropFirst()
92-
}
93-
lines[0] = firstLine
94-
}
95-
96-
if let lastLine = lines.last {
97-
if trimWhitespace(lastLine) == "*/" {
98-
lines.removeLast()
99-
} else {
100-
var lastLine = lines[lines.count - 1]
101-
lastLine = lastLine.hasSuffix("*/") ? lastLine.dropLast(2) : lastLine
102-
while lastLine.last?.isWhitespace == true {
103-
lastLine = lastLine.dropLast()
104-
}
105-
lines[lines.count - 1] = lastLine
106-
}
107-
}
76+
var lines = text.split(separator: "\n", omittingEmptySubsequences: false)
77+
78+
let (minSpaceIndentation, minTabIndentation) =
79+
lines
80+
.dropFirst()
81+
.filter { !$0.isEmpty }
82+
.reduce((Int.max, Int.max)) { (currentMin, line) in
83+
var spaceCount = 0, tabCount = 0
84+
var inLeadingWhitespace = true
85+
86+
for char in line {
87+
guard inLeadingWhitespace else { break }
88+
89+
switch char {
90+
case " ":
91+
spaceCount += 1
92+
case "\t":
93+
tabCount += 1
94+
default:
95+
inLeadingWhitespace = false
96+
}
97+
}
98+
99+
return (Swift.min(currentMin.0, spaceCount), Swift.min(currentMin.1, tabCount))
100+
}
101+
102+
var minIndentation = minSpaceIndentation == Int.max ? 0 : minSpaceIndentation
103+
minIndentation += minTabIndentation == Int.max ? 0 : minTabIndentation
104+
105+
if let first = lines.first {
106+
let prefixToDrop = first.hasPrefix("/**") ? 3 : 2
107+
lines[0] = first.dropFirst(prefixToDrop)
108+
}
108109

109-
let unindentedLines = lines.enumerated().map { index, line -> Substring in
110-
if index == 0 && firstLineRemoved == false {
111-
return line
110+
var firstLineRemoved = false
111+
if trimWhitespace(lines[0]).isEmpty {
112+
lines.removeFirst()
113+
firstLineRemoved = true
114+
}
115+
116+
var unindentedLines = lines.enumerated().map { index, line -> Substring in
117+
if index == 0 && firstLineRemoved == false {
118+
return line
119+
}
120+
return line.count >= minIndentation ? line.dropFirst(minIndentation) : line
112121
}
113-
return line.count >= minIndentation ? line.dropFirst(minIndentation) : line
114-
}
115122

116-
return unindentedLines.joined(separator: "\n")
123+
if let last = unindentedLines.last, last.hasSuffix("*/") {
124+
unindentedLines[unindentedLines.count - 1] = last.dropLast(2)
125+
}
126+
127+
if trimWhitespace(unindentedLines[unindentedLines.count - 1]).isEmpty {
128+
unindentedLines.removeLast()
129+
}
130+
131+
return unindentedLines.joined(separator: "\n")
117132
}
118133

119134
for piece in pieces {
120-
switch piece {
121-
case .blockComment(let text), .docBlockComment(let text):
122-
let processedText = processBlockComment(text[...])
123-
comments.append(processedText[...])
135+
switch piece {
136+
case .blockComment(let text), .docBlockComment(let text):
137+
if hasBlockComment || hasLineComment {
138+
return nil
139+
}
140+
hasBlockComment = true
141+
let processedText = processBlockComment(text[...])
142+
comments.append(processedText[...])
124143

125-
case .lineComment(let text):
126-
let prefix = allLineCommentsHaveSpace ? "// " : "//"
127-
comments.append(text.dropFirst(prefix.count))
144+
case .lineComment(let text):
145+
if hasBlockComment {
146+
return nil
147+
}
148+
hasLineComment = true
149+
let prefix = allLineCommentsHaveSpace ? "// " : "//"
150+
comments.append(text.dropFirst(prefix.count))
128151

129-
case .docLineComment(let text):
130-
let prefix = allLineCommentsHaveSpace ? "/// " : "///"
131-
comments.append(text.dropFirst(prefix.count))
152+
case .docLineComment(let text):
153+
if hasBlockComment {
154+
return nil
155+
}
156+
hasLineComment = true
157+
let prefix = allLineCommentsHaveSpace ? "/// " : "///"
158+
comments.append(text.dropFirst(prefix.count))
132159

133-
default:
134-
break
135-
}
160+
default:
161+
break
162+
}
136163
}
137-
return comments.joined(separator: "\n")
138-
}
164+
165+
guard !comments.isEmpty else { return nil }
166+
167+
// If we have multiple line comments, they can be joined with newlines
168+
if hasLineComment {
169+
return comments.joined(separator: "\n")
170+
}
171+
172+
// In case of block comments, we should only have one
173+
return comments.first.map(String.init)
174+
}
139175

140176
/// The length of all the pieces in this ``Trivia``.
141177
public var sourceLength: SourceLength {

Tests/SwiftSyntaxTest/TriviaTests.swift

+26-69
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,14 @@ class TriviaTests: XCTestCase {
152152

153153
// MARK: Block comment
154154

155-
assertCommentValue("/* Some block comment */", commentValue: "Some block comment")
155+
assertCommentValue("/* Some block comment */", commentValue: " Some block comment ")
156156

157157
assertCommentValue(
158158
"""
159159
/* Some block comment
160160
* spread on many lines */
161161
""",
162-
commentValue: "Some block comment\n* spread on many lines"
162+
commentValue: " Some block comment\n* spread on many lines "
163163
)
164164

165165
assertCommentValue(
@@ -168,7 +168,7 @@ class TriviaTests: XCTestCase {
168168
* spread on many lines
169169
*/
170170
""",
171-
commentValue: "Some block comment\n* spread on many lines"
171+
commentValue: " Some block comment\n* spread on many lines"
172172
)
173173

174174
assertCommentValue(
@@ -177,26 +177,7 @@ class TriviaTests: XCTestCase {
177177
Some block comment
178178
spread on many lines */
179179
""",
180-
commentValue: "Some block comment\nspread on many lines"
181-
)
182-
183-
assertCommentValue(
184-
"""
185-
/* Some block comment
186-
* spread on many lines */
187-
/* Another block comment */
188-
""",
189-
commentValue: "Some block comment\n* spread on many lines\nAnother block comment"
190-
)
191-
192-
assertCommentValue(
193-
"""
194-
/* Some block comment
195-
* spread on many lines */
196-
197-
/* Another block comment */
198-
""",
199-
commentValue: "Some block comment\n* spread on many lines\nAnother block comment"
180+
commentValue: "Some block comment\nspread on many lines "
200181
)
201182

202183
assertCommentValue(
@@ -229,7 +210,7 @@ class TriviaTests: XCTestCase {
229210
commentValue: "/abc"
230211
)
231212

232-
assertCommentValue("/* ///// abc */", commentValue: "///// abc")
213+
assertCommentValue("/* ///// abc */", commentValue: " ///// abc ")
233214

234215
assertCommentValue(
235216
"""
@@ -266,51 +247,48 @@ class TriviaTests: XCTestCase {
266247
Comment
267248
Comment */
268249
""",
269-
commentValue: "Comment\n Comment")
270-
271-
// MARK: Doc block comment
250+
commentValue: "Comment\n Comment ")
272251

273252
assertCommentValue(
274253
"""
275-
/** Some doc block comment */
254+
/*
255+
\t\tComment
256+
\t\t\tComment */
276257
""",
277-
commentValue: "Some doc block comment"
278-
)
258+
commentValue: "Comment\n\tComment ")
279259

280260
assertCommentValue(
281261
"""
282-
/** Some doc block comment
283-
* spread on many lines */
262+
/*
263+
\t\tComment
264+
\t\t\tComment */
284265
""",
285-
commentValue: "Some doc block comment\n* spread on many lines"
286-
)
266+
commentValue: "\tComment\n\tComment ")
267+
268+
// MARK: Doc block comment
287269

288270
assertCommentValue(
289271
"""
290-
/** Some doc block comment
291-
* spread on many lines
292-
*/
272+
/** Some doc block comment */
293273
""",
294-
commentValue: "Some doc block comment\n* spread on many lines"
274+
commentValue: " Some doc block comment "
295275
)
296276

297277
assertCommentValue(
298278
"""
299279
/** Some doc block comment
300280
* spread on many lines */
301-
/** Another doc block comment */
302281
""",
303-
commentValue: "Some doc block comment\n* spread on many lines\nAnother doc block comment"
282+
commentValue: " Some doc block comment\n* spread on many lines "
304283
)
305284

306285
assertCommentValue(
307286
"""
308287
/** Some doc block comment
309-
* spread on many lines */
310-
311-
/** Another doc block comment */
288+
* spread on many lines
289+
*/
312290
""",
313-
commentValue: "Some doc block comment\n* spread on many lines\nAnother doc block comment"
291+
commentValue: " Some doc block comment\n* spread on many lines"
314292
)
315293

316294
assertCommentValue(
@@ -343,7 +321,7 @@ class TriviaTests: XCTestCase {
343321
commentValue: "* Some doc block comment\n* with a line comment"
344322
)
345323

346-
assertCommentValue("/** ///// abc */", commentValue: "///// abc")
324+
assertCommentValue("/** ///// abc */", commentValue: " ///// abc ")
347325

348326
assertCommentValue(
349327
"""
@@ -396,20 +374,8 @@ class TriviaTests: XCTestCase {
396374
397375
/// Some doc line comment
398376
// Some line comment
399-
400-
/* Some block comment
401-
* spread on many lines */
402-
/** Another doc block comment */
403377
""",
404-
commentValue: """
405-
Some doc block comment
406-
* spread on many lines
407-
Some doc line comment
408-
Some line comment
409-
Some block comment
410-
* spread on many lines
411-
Another doc block comment
412-
"""
378+
commentValue: nil
413379
)
414380

415381
assertCommentValue(
@@ -418,26 +384,17 @@ class TriviaTests: XCTestCase {
418384
* // A line comment in a block
419385
* spread on many lines */
420386
/** Some doc block comment
421-
* /// A doc line comment in a block
422387
* spread on
423388
* many lines */
424389
""",
425-
commentValue: """
426-
Some block comment
427-
* // A line comment in a block
428-
* spread on many lines
429-
Some doc block comment
430-
* /// A doc line comment in a block
431-
* spread on
432-
* many lines
433-
"""
390+
commentValue: nil
434391
)
435392
}
436393
}
437394

438395
func assertCommentValue(
439396
_ input: String,
440-
commentValue expected: String,
397+
commentValue expected: String?,
441398
file: StaticString = #filePath,
442399
line: UInt = #line
443400
) {

0 commit comments

Comments
 (0)