Skip to content

Commit 4b7b315

Browse files
authored
Merge pull request #501 from stackotter/fix-import-formatting
Fix formatting of import with multiple attributes (fixes #445) and ensure that imports never wrap
2 parents e0d014c + 1ca6845 commit 4b7b315

File tree

4 files changed

+35
-9
lines changed

4 files changed

+35
-9
lines changed

Sources/SwiftFormatPrettyPrint/PrettyPrint.swift

+16-5
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,16 @@ public class PrettyPrinter {
129129
private var activeBreakSuppressionCount = 0
130130

131131
/// Whether breaks are supressed from firing. When true, no breaks should fire and the only way to
132-
/// move to a new line is an explicit new line token.
133-
private var isBreakingSupressed: Bool {
132+
/// move to a new line is an explicit new line token. Discretionary breaks aren't suppressed
133+
/// if ``allowSuppressedDiscretionaryBreaks`` is true.
134+
private var isBreakingSuppressed: Bool {
134135
return activeBreakSuppressionCount > 0
135136
}
136137

138+
/// Indicates whether discretionary breaks should still be included even if break suppression is
139+
/// enabled (see ``isBreakingSuppressed``).
140+
private var allowSuppressedDiscretionaryBreaks = false
141+
137142
/// The computed indentation level, as a number of spaces, based on the state of any unclosed
138143
/// delimiters and whether or not the current line is a continuation line.
139144
private var currentIndentation: [Indent] {
@@ -469,15 +474,15 @@ public class PrettyPrinter {
469474
case .soft(_, let discretionary):
470475
// A discretionary newline (i.e. from the source) should create a line break even if the
471476
// rules for breaking are disabled.
472-
overrideBreakingSuppressed = discretionary
477+
overrideBreakingSuppressed = discretionary && allowSuppressedDiscretionaryBreaks
473478
mustBreak = true
474479
case .hard:
475480
// A hard newline must always create a line break, regardless of the context.
476481
overrideBreakingSuppressed = true
477482
mustBreak = true
478483
}
479484

480-
let suppressBreaking = isBreakingSupressed && !overrideBreakingSuppressed
485+
let suppressBreaking = isBreakingSuppressed && !overrideBreakingSuppressed
481486
if !suppressBreaking && ((!isAtStartOfLine && length > spaceRemaining) || mustBreak) {
482487
currentLineIsContinuation = isContinuationIfBreakFires
483488
writeNewlines(newline)
@@ -527,8 +532,14 @@ public class PrettyPrinter {
527532

528533
case .printerControl(let kind):
529534
switch kind {
530-
case .disableBreaking:
535+
case .disableBreaking(let allowDiscretionary):
531536
activeBreakSuppressionCount += 1
537+
// Override the supression of discretionary breaks if we're at the top level or
538+
// discretionary breaks are currently allowed (false should override true, but not the other
539+
// way around).
540+
if activeBreakSuppressionCount == 1 || allowSuppressedDiscretionaryBreaks {
541+
allowSuppressedDiscretionaryBreaks = allowDiscretionary
542+
}
532543
case .enableBreaking:
533544
activeBreakSuppressionCount -= 1
534545
}

Sources/SwiftFormatPrettyPrint/Token.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,10 @@ enum PrinterControlKind {
162162
/// control token is encountered.
163163
///
164164
/// It's valid to nest `disableBreaking` and `enableBreaking` tokens. Breaks will be suppressed
165-
/// long as there is at least 1 unmatched disable token.
166-
case disableBreaking
165+
/// long as there is at least 1 unmatched disable token. If `allowDiscretionary` is `true`, then
166+
/// discretionary breaks aren't effected. An `allowDiscretionary` value of true never overrides a
167+
/// value of false. Hard breaks are always inserted no matter what.
168+
case disableBreaking(allowDiscretionary: Bool)
167169

168170
/// A signal that break tokens should be allowed to fire following this token, as long as there
169171
/// are no other unmatched disable tokens.

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

+7-2
Original file line numberDiff line numberDiff line change
@@ -1394,7 +1394,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
13941394
]
13951395
)
13961396
} else if let condition = node.condition {
1397-
before(condition.firstToken, tokens: .printerControl(kind: .disableBreaking))
1397+
before(condition.firstToken, tokens: .printerControl(kind: .disableBreaking(allowDiscretionary: true)))
13981398
after(
13991399
condition.lastToken,
14001400
tokens: .printerControl(kind: .enableBreaking), .break(.reset, size: 0))
@@ -1723,9 +1723,14 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
17231723
}
17241724

17251725
override func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind {
1726-
after(node.attributes?.lastToken, tokens: .space)
1726+
// Import declarations should never be wrapped.
1727+
before(node.firstToken, tokens: .printerControl(kind: .disableBreaking(allowDiscretionary: false)))
1728+
1729+
arrangeAttributeList(node.attributes)
17271730
after(node.importTok, tokens: .space)
17281731
after(node.importKind, tokens: .space)
1732+
1733+
after(node.lastToken, tokens: .printerControl(kind: .enableBreaking))
17291734
return .visitChildren
17301735
}
17311736

Tests/SwiftFormatPrettyPrintTests/ImportTests.swift

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ final class ImportTests: PrettyPrintTestCase {
77
import class MyModule.MyClass
88
import struct MyModule.MyStruct
99
@testable import testModule
10+
11+
@_spi(
12+
STP
13+
)
14+
@testable
15+
import testModule
1016
"""
1117

1218
let expected =
@@ -17,6 +23,8 @@ final class ImportTests: PrettyPrintTestCase {
1723
import struct MyModule.MyStruct
1824
@testable import testModule
1925
26+
@_spi(STP) @testable import testModule
27+
2028
"""
2129

2230
// Imports should not wrap

0 commit comments

Comments
 (0)