Skip to content

Commit 497ab7b

Browse files
committed
Insert fileHeader comment after hashbang
1 parent db55127 commit 497ab7b

3 files changed

Lines changed: 106 additions & 6 deletions

File tree

Sources/Rules.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3984,8 +3984,17 @@ public struct _FormatRules {
39843984
}
39853985
header = string
39863986
}
3987+
var start = 0
39873988
var lastHeaderTokenIndex = -1
39883989
if var startIndex = formatter.index(of: .nonSpaceOrLinebreak, after: -1) {
3990+
if formatter.tokens[startIndex] == .startOfScope("#!") {
3991+
guard let endIndex = formatter.index(of: .linebreak, after: startIndex) else {
3992+
return
3993+
}
3994+
startIndex = formatter.index(of: .nonSpaceOrLinebreak, after: endIndex) ?? endIndex
3995+
start = startIndex
3996+
lastHeaderTokenIndex = startIndex - 1
3997+
}
39893998
switch formatter.tokens[startIndex] {
39903999
case .startOfScope("//"):
39914000
if case let .commentBody(body)? = formatter.next(.nonSpace, after: startIndex) {
@@ -4048,7 +4057,7 @@ public struct _FormatRules {
40484057
}
40494058
}
40504059
if header.isEmpty {
4051-
formatter.removeTokens(in: 0 ..< lastHeaderTokenIndex + 1)
4060+
formatter.removeTokens(in: start ..< lastHeaderTokenIndex + 1)
40524061
return
40534062
}
40544063
var headerTokens = tokenize(header)
@@ -4070,7 +4079,7 @@ public struct _FormatRules {
40704079
}) {
40714080
lastHeaderTokenIndex = index
40724081
}
4073-
formatter.replaceTokens(in: 0 ..< lastHeaderTokenIndex + 1, with: headerTokens)
4082+
formatter.replaceTokens(in: start ..< lastHeaderTokenIndex + 1, with: headerTokens)
40744083
}
40754084

40764085
/// Strip redundant `.init` from type instantiations

Tests/RulesTests.swift

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,12 @@ class RulesTests: XCTestCase {
6868
if input != output {
6969
XCTAssertEqual(try format(output, rules: rules, options: options),
7070
output, file: file, line: line)
71-
for rule in rules {
72-
let disabled = "// swiftformat:disable \(rule.name)\n\(input)"
73-
XCTAssertEqual(try format(disabled, rules: [rule], options: options),
74-
disabled, "Failed to disable \(rule.name) rule", file: file, line: line)
71+
if !input.hasPrefix("#!") {
72+
for rule in rules {
73+
let disabled = "// swiftformat:disable \(rule.name)\n\(input)"
74+
XCTAssertEqual(try format(disabled, rules: [rule], options: options),
75+
disabled, "Failed to disable \(rule.name) rule", file: file, line: line)
76+
}
7577
}
7678
}
7779
if input != output2, output != output2 {
@@ -529,6 +531,89 @@ class RulesTests: XCTestCase {
529531
testFormatting(for: input, output, rule: FormatRules.fileHeader, options: options)
530532
}
531533

534+
func testFileHeaderRemovedAfterHashbang() {
535+
let input = """
536+
#!/usr/bin/swift
537+
538+
// Header line1
539+
// Header line2
540+
541+
let foo = 5
542+
"""
543+
let output = """
544+
#!/usr/bin/swift
545+
546+
let foo = 5
547+
"""
548+
let options = FormatOptions(fileHeader: "")
549+
testFormatting(for: input, output, rule: FormatRules.fileHeader, options: options)
550+
}
551+
552+
func testFileHeaderPlacedAfterHashbang() {
553+
let input = """
554+
#!/usr/bin/swift
555+
556+
let foo = 5
557+
"""
558+
let output = """
559+
#!/usr/bin/swift
560+
561+
// Header line1
562+
// Header line2
563+
564+
let foo = 5
565+
"""
566+
let options = FormatOptions(fileHeader: "// Header line1\n// Header line2")
567+
testFormatting(for: input, output, rule: FormatRules.fileHeader, options: options)
568+
}
569+
570+
func testBlankLineAfterHashbangNotRemovedByFileHeader() {
571+
let input = """
572+
#!/usr/bin/swift
573+
574+
let foo = 5
575+
"""
576+
let options = FormatOptions(fileHeader: "")
577+
testFormatting(for: input, rule: FormatRules.fileHeader, options: options)
578+
}
579+
580+
func testLineAfterHashbangNotAffectedByFileHeaderRemoval() {
581+
let input = """
582+
#!/usr/bin/swift
583+
let foo = 5
584+
"""
585+
let options = FormatOptions(fileHeader: "")
586+
testFormatting(for: input, rule: FormatRules.fileHeader, options: options)
587+
}
588+
589+
func testDisableFileHeaderCommentRespectedAfterHashbang() {
590+
let input = """
591+
#!/usr/bin/swift
592+
// swiftformat:disable fileHeader
593+
594+
// Header line1
595+
// Header line2
596+
597+
let foo = 5
598+
"""
599+
let options = FormatOptions(fileHeader: "")
600+
testFormatting(for: input, rule: FormatRules.fileHeader, options: options)
601+
}
602+
603+
func testDisableFileHeaderCommentRespectedAfterHashbang2() {
604+
let input = """
605+
#!/usr/bin/swift
606+
607+
// swiftformat:disable fileHeader
608+
// Header line1
609+
// Header line2
610+
611+
let foo = 5
612+
"""
613+
let options = FormatOptions(fileHeader: "")
614+
testFormatting(for: input, rule: FormatRules.fileHeader, options: options)
615+
}
616+
532617
// MARK: - strongOutlets
533618

534619
func testRemoveWeakFromOutlet() {

Tests/XCTestManifests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ extension RulesTests {
542542
("testBelowCustomStructOrganizationThreshold", testBelowCustomStructOrganizationThreshold),
543543
("testBinaryGroupingCustom", testBinaryGroupingCustom),
544544
("testBlankCodeCommentBlockLinesNotIndented", testBlankCodeCommentBlockLinesNotIndented),
545+
("testBlankLineAfterHashbangNotRemovedByFileHeader", testBlankLineAfterHashbangNotRemovedByFileHeader),
545546
("testBlankLineAfterProtocolBeforeProperty", testBlankLineAfterProtocolBeforeProperty),
546547
("testBlankLineBeforeAtAvailabilityOnLineBeforeClass", testBlankLineBeforeAtAvailabilityOnLineBeforeClass),
547548
("testBlankLineBeforeAtObjcOnLineBeforeProtocol", testBlankLineBeforeAtObjcOnLineBeforeProtocol),
@@ -696,6 +697,8 @@ extension RulesTests {
696697
("testDictionaryLiteralNotEqualYodaCondition", testDictionaryLiteralNotEqualYodaCondition),
697698
("testDictionaryLiteralsRuinEverything", testDictionaryLiteralsRuinEverything),
698699
("testDictionaryTypeConvertedToSugar", testDictionaryTypeConvertedToSugar),
700+
("testDisableFileHeaderCommentRespectedAfterHashbang", testDisableFileHeaderCommentRespectedAfterHashbang),
701+
("testDisableFileHeaderCommentRespectedAfterHashbang2", testDisableFileHeaderCommentRespectedAfterHashbang2),
699702
("testDisableNextRemoveSelf", testDisableNextRemoveSelf),
700703
("testDisableRemoveSelf", testDisableRemoveSelf),
701704
("testDispatchAsyncAfterClosureArgumentMadeTrailing", testDispatchAsyncAfterClosureArgumentMadeTrailing),
@@ -808,6 +811,8 @@ extension RulesTests {
808811
("testFileHeaderCreationDateReplacement", testFileHeaderCreationDateReplacement),
809812
("testFileHeaderCreationYearReplacement", testFileHeaderCreationYearReplacement),
810813
("testFileHeaderFileReplacement", testFileHeaderFileReplacement),
814+
("testFileHeaderPlacedAfterHashbang", testFileHeaderPlacedAfterHashbang),
815+
("testFileHeaderRemovedAfterHashbang", testFileHeaderRemovedAfterHashbang),
811816
("testFileHeaderRuleThrowsIfCreationDateUnavailable", testFileHeaderRuleThrowsIfCreationDateUnavailable),
812817
("testFileHeaderRuleThrowsIfFileNameUnavailable", testFileHeaderRuleThrowsIfFileNameUnavailable),
813818
("testFileHeaderYearReplacement", testFileHeaderYearReplacement),
@@ -1033,6 +1038,7 @@ extension RulesTests {
10331038
("testLetRedundantGenericTypeRemovalExplicitType", testLetRedundantGenericTypeRemovalExplicitType),
10341039
("testLetRedundantTypeRemoval", testLetRedundantTypeRemoval),
10351040
("testLetRedundantTypeRemovalExplicitType", testLetRedundantTypeRemovalExplicitType),
1041+
("testLineAfterHashbangNotAffectedByFileHeaderRemoval", testLineAfterHashbangNotAffectedByFileHeaderRemoval),
10361042
("testLinebreakAtEndOfFile", testLinebreakAtEndOfFile),
10371043
("testLinebreakInsertedAtEndOfWrappedFunction", testLinebreakInsertedAtEndOfWrappedFunction),
10381044
("testLinebreaksRemovedInsideBraces", testLinebreaksRemovedInsideBraces),

0 commit comments

Comments
 (0)