Skip to content

Commit 29c39eb

Browse files
committed
Add support for re-indenting block comments.
Re-indent all lines of a block comment with the current indentation level. The comment must not have anything other than whitespace preceding it on the line, and all lines must have at least that level of whitespace (or be empty).
1 parent 5936e56 commit 29c39eb

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

Sources/SwiftFormat/PrettyPrint/Comment.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,12 @@ struct Comment {
5858
let kind: Kind
5959
var text: [String]
6060
var length: Int
61+
// what was the leading indentation, if any, that preceded this comment?
62+
var leadingIndent: Indent?
6163

62-
init(kind: Kind, text: String) {
64+
init(kind: Kind, leadingIndent: Indent?, text: String) {
6365
self.kind = kind
66+
self.leadingIndent = leadingIndent
6467

6568
switch kind {
6669
case .line, .docLine:
@@ -93,6 +96,25 @@ struct Comment {
9396
return kind.prefix + trimmedLines.joined(separator: separator)
9497
case .block, .docBlock:
9598
let separator = "\n"
99+
100+
// if all the lines after the first matching leadingIndent, replace that prefix with the
101+
// current indentation level
102+
if let leadingIndent {
103+
let rest = self.text.dropFirst()
104+
105+
let hasLeading = rest.allSatisfy {
106+
let result = $0.hasPrefix(leadingIndent.text) || $0.isEmpty
107+
return result
108+
}
109+
if hasLeading, let first = self.text.first, !rest.isEmpty {
110+
let restStr = rest.map {
111+
let stripped = $0.dropFirst(leadingIndent.text.count)
112+
return indent.indentation() + stripped
113+
}.joined(separator: separator)
114+
return kind.prefix + first + separator + restStr + "*/"
115+
}
116+
}
117+
96118
return kind.prefix + self.text.joined(separator: separator) + "*/"
97119
}
98120
}

Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3194,7 +3194,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
31943194
true,
31953195
[
31963196
.space(size: config.spacesBeforeEndOfLineComments, flexible: true),
3197-
.comment(Comment(kind: .line, text: text), wasEndOfLine: true),
3197+
.comment(Comment(kind: .line, leadingIndent: nil, text: text), wasEndOfLine: true),
31983198
// There must be a break with a soft newline after the comment, but it's impossible to
31993199
// know which kind of break must be used. Adding this newline is deferred until the
32003200
// comment is added to the token stream.
@@ -3205,7 +3205,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
32053205
false,
32063206
[
32073207
.space(size: 1, flexible: true),
3208-
.comment(Comment(kind: .block, text: text), wasEndOfLine: false),
3208+
.comment(Comment(kind: .block, leadingIndent: nil, text: text), wasEndOfLine: false),
32093209
// We place a size-0 break after the comment to allow a discretionary newline after
32103210
// the comment if the user places one here but the comment is otherwise adjacent to a
32113211
// text token.
@@ -3294,24 +3294,29 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
32943294
// example, even if discretionary newlines are discarded). This is the case when the preceding
32953295
// trivia was a line comment or garbage text.
32963296
var requiresNextNewline = false
3297+
// Tracking whether or not the last piece was leading indentation. A newline is considered
3298+
// a 0-space indentation; used for nesting/un-nesting block comments during formatting.
3299+
var leadingIndent: Indent? = nil
32973300

32983301
for (index, piece) in trivia.enumerated() {
32993302
if let cutoff = cutoffIndex, index == cutoff { break }
3303+
33003304
switch piece {
33013305
case .lineComment(let text):
33023306
if index > 0 || isStartOfFile {
33033307
generateEnableFormattingIfNecessary(position ..< position + piece.sourceLength)
3304-
appendToken(.comment(Comment(kind: .line, text: text), wasEndOfLine: false))
3308+
appendToken(.comment(Comment(kind: .line, leadingIndent: leadingIndent, text: text), wasEndOfLine: false))
33053309
generateDisableFormattingIfNecessary(position + piece.sourceLength)
33063310
appendNewlines(.soft)
33073311
isStartOfFile = false
33083312
}
33093313
requiresNextNewline = true
3314+
leadingIndent = nil
33103315

33113316
case .blockComment(let text):
33123317
if index > 0 || isStartOfFile {
33133318
generateEnableFormattingIfNecessary(position ..< position + piece.sourceLength)
3314-
appendToken(.comment(Comment(kind: .block, text: text), wasEndOfLine: false))
3319+
appendToken(.comment(Comment(kind: .block, leadingIndent: leadingIndent, text: text), wasEndOfLine: false))
33153320
generateDisableFormattingIfNecessary(position + piece.sourceLength)
33163321
// There is always a break after the comment to allow a discretionary newline after it.
33173322
var breakSize = 0
@@ -3325,24 +3330,28 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
33253330
isStartOfFile = false
33263331
}
33273332
requiresNextNewline = false
3333+
leadingIndent = nil
33283334

33293335
case .docLineComment(let text):
33303336
generateEnableFormattingIfNecessary(position ..< position + piece.sourceLength)
3331-
appendToken(.comment(Comment(kind: .docLine, text: text), wasEndOfLine: false))
3337+
appendToken(.comment(Comment(kind: .docLine, leadingIndent: leadingIndent, text: text), wasEndOfLine: false))
33323338
generateDisableFormattingIfNecessary(position + piece.sourceLength)
33333339
appendNewlines(.soft)
33343340
isStartOfFile = false
33353341
requiresNextNewline = true
3342+
leadingIndent = nil
33363343

33373344
case .docBlockComment(let text):
33383345
generateEnableFormattingIfNecessary(position ..< position + piece.sourceLength)
3339-
appendToken(.comment(Comment(kind: .docBlock, text: text), wasEndOfLine: false))
3346+
appendToken(.comment(Comment(kind: .docBlock, leadingIndent: leadingIndent, text: text), wasEndOfLine: false))
33403347
generateDisableFormattingIfNecessary(position + piece.sourceLength)
33413348
appendNewlines(.soft)
33423349
isStartOfFile = false
33433350
requiresNextNewline = false
3351+
leadingIndent = nil
33443352

33453353
case .newlines(let count), .carriageReturns(let count), .carriageReturnLineFeeds(let count):
3354+
leadingIndent = .spaces(0)
33463355
guard !isStartOfFile else { break }
33473356

33483357
if requiresNextNewline ||
@@ -3372,9 +3381,17 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
33723381
let isBOM = text == "\u{feff}"
33733382
requiresNextNewline = !isBOM
33743383
isStartOfFile = isStartOfFile && isBOM
3384+
leadingIndent = nil
33753385

3376-
default:
3377-
break
3386+
case .backslashes, .formfeeds, .pounds, .verticalTabs:
3387+
leadingIndent = nil
3388+
3389+
case .spaces(let n):
3390+
guard leadingIndent == .spaces(0) else { break }
3391+
leadingIndent = .spaces(n)
3392+
case .tabs(let n):
3393+
guard leadingIndent == .spaces(0) else { break }
3394+
leadingIndent = .tabs(n)
33783395
}
33793396
position += piece.sourceLength
33803397
}

Tests/SwiftFormatTests/PrettyPrint/CommentTests.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -583,14 +583,14 @@ final class CommentTests: PrettyPrintTestCase {
583583
func testBlockComments() {
584584
let input =
585585
"""
586-
/* Line Comment1 */
586+
/* Line Comment1 */
587587
/* Line Comment2 */
588588
let a = 123
589589
let b = "456" /* End of line comment */
590590
let c = "More content"
591591

592-
/* Comment 3
593-
Comment 4 */
592+
/* Comment 3
593+
Comment 4 */
594594
595595
let reallyLongVariableName = 123 /* This comment should wrap */
596596
@@ -603,7 +603,9 @@ final class CommentTests: PrettyPrintTestCase {
603603
}
604604
605605
let d = 123
606-
/* Trailing Comment */
606+
/* Trailing Comment */
607+
/* Trailing
608+
Block Comment */
607609
"""
608610

609611
let expected =
@@ -633,6 +635,8 @@ final class CommentTests: PrettyPrintTestCase {
633635
634636
let d = 123
635637
/* Trailing Comment */
638+
/* Trailing
639+
Block Comment */
636640
637641
"""
638642

0 commit comments

Comments
 (0)