Skip to content

Commit 021a5ab

Browse files
authored
Merge pull request #791 from shawnhyam/support-block-comment-reindent
Add support for re-indenting block comments.
2 parents 5936e56 + 29c39eb commit 021a5ab

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)