Skip to content

Commit 8157755

Browse files
committed
Add an option to allow space around range formation operators.
Fixes swiftlang#457
1 parent 3dd9b51 commit 8157755

File tree

4 files changed

+57
-7
lines changed

4 files changed

+57
-7
lines changed

Documentation/Configuration.md

+3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ top-level keys and values:
7979
true, a line break is forced before the "." of the component and after the component's
8080
closing delimiter (i.e. right paren, right bracket, right brace, etc.).
8181

82+
* `spacesAroundRangeFormationOperators` _(boolean)_: Determines whether whitespace should be forced
83+
before and after the range formation operators `...` and `..<`.
84+
8285
> TODO: Add support for enabling/disabling specific syntax transformations in
8386
> the pipeline.
8487

Sources/SwiftFormatConfiguration/Configuration.swift

+10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public struct Configuration: Codable, Equatable {
3535
case fileScopedDeclarationPrivacy
3636
case indentSwitchCaseLabels
3737
case rules
38+
case spacesAroundRangeFormationOperators
3839
}
3940

4041
/// The version of this configuration.
@@ -142,6 +143,10 @@ public struct Configuration: Codable, Equatable {
142143
///```
143144
public var indentSwitchCaseLabels = false
144145

146+
/// Determines whether whitespace should be forced before and after the range formation operators
147+
/// `...` and `..<`.
148+
public var spacesAroundRangeFormationOperators = false
149+
145150
/// Constructs a Configuration with all default values.
146151
public init() {
147152
self.version = highestSupportedConfigurationVersion
@@ -194,6 +199,9 @@ public struct Configuration: Codable, Equatable {
194199
self.lineBreakAroundMultilineExpressionChainComponents =
195200
try container.decodeIfPresent(
196201
Bool.self, forKey: .lineBreakAroundMultilineExpressionChainComponents) ?? false
202+
self.spacesAroundRangeFormationOperators =
203+
try container.decodeIfPresent(
204+
Bool.self, forKey: .spacesAroundRangeFormationOperators) ?? false
197205
self.fileScopedDeclarationPrivacy =
198206
try container.decodeIfPresent(
199207
FileScopedDeclarationPrivacyConfiguration.self, forKey: .fileScopedDeclarationPrivacy)
@@ -226,6 +234,8 @@ public struct Configuration: Codable, Equatable {
226234
try container.encode(
227235
lineBreakAroundMultilineExpressionChainComponents,
228236
forKey: .lineBreakAroundMultilineExpressionChainComponents)
237+
try container.encode(
238+
spacesAroundRangeFormationOperators, forKey: .spacesAroundRangeFormationOperators)
229239
try container.encode(fileScopedDeclarationPrivacy, forKey: .fileScopedDeclarationPrivacy)
230240
try container.encode(indentSwitchCaseLabels, forKey: .indentSwitchCaseLabels)
231241
try container.encode(rules, forKey: .rules)

Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift

+9-5
Original file line numberDiff line numberDiff line change
@@ -1763,7 +1763,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
17631763

17641764
let wrapsBeforeOperator = !isAssigningOperator(binOp)
17651765

1766-
if shouldRequireWhitespace(around: binOp) {
1766+
if shouldRequireWhitespace(around: binOp, config: config) {
17671767
if isAssigningOperator(binOp) {
17681768
var beforeTokens: [Token]
17691769

@@ -3317,20 +3317,24 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
33173317
return nil
33183318
}
33193319

3320-
/// Returns a value indicating whether whitespace should be required around the given operator.
3320+
/// Returns a value indicating whether whitespace should be required around the given operator,
3321+
/// for the given configuration.
33213322
///
33223323
/// If spaces are not required (for example, range operators), then the formatter will also forbid
33233324
/// breaks around the operator. This is to prevent situations where a break could occur before an
33243325
/// unspaced operator (e.g., turning `0...10` into `0<newline>...10`), which would be a breaking
33253326
/// change because it would treat it as a prefix operator `...10` instead of an infix operator.
3326-
private func shouldRequireWhitespace(around operatorExpr: ExprSyntax) -> Bool {
3327+
private func shouldRequireWhitespace(
3328+
around operatorExpr: ExprSyntax, config: Configuration) -> Bool
3329+
{
33273330
// Note that we look at the operator itself to make this determination, not the token kind.
33283331
// The token kind (spaced or unspaced operator) represents how the *user* wrote it, and we want
33293332
// to ignore that and apply our own rules.
33303333
if let binaryOperator = operatorExpr.as(BinaryOperatorExprSyntax.self) {
33313334
let token = binaryOperator.operatorToken
3332-
if let precedence = operatorContext.infixOperator(named: token.text)?.precedenceGroup,
3333-
precedence === operatorContext.precedenceGroup(named: .rangeFormation)
3335+
if !config.spacesAroundRangeFormationOperators,
3336+
let precedence = operatorContext.infixOperator(named: token.text)?.precedenceGroup,
3337+
precedence === operatorContext.precedenceGroup(named: .rangeFormation)
33343338
{
33353339
// We want to omit whitespace around range formation operators if possible. We can't do this
33363340
// if the token is either preceded by a postfix operator, followed by a prefix operator, or

Tests/SwiftFormatPrettyPrintTests/BinaryOperatorExprTests.swift

+35-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import SwiftFormatConfiguration
2+
13
final class BinaryOperatorExprTests: PrettyPrintTestCase {
24
func testNonRangeFormationOperatorsAreSurroundedByBreaks() {
35
let input =
@@ -26,7 +28,7 @@ final class BinaryOperatorExprTests: PrettyPrintTestCase {
2628
assertPrettyPrintEqual(input: input, expected: expected10, linelength: 10)
2729
}
2830

29-
func testRangeFormationOperatorsAreCompactedWhenPossible() {
31+
func testRangeFormationOperatorCompaction_noSpacesAroundRangeFormation() {
3032
let input =
3133
"""
3234
x = 1...100
@@ -48,7 +50,38 @@ final class BinaryOperatorExprTests: PrettyPrintTestCase {
4850
4951
"""
5052

51-
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
53+
var configuration = Configuration()
54+
configuration.spacesAroundRangeFormationOperators = false
55+
assertPrettyPrintEqual(
56+
input: input, expected: expected, linelength: 80, configuration: configuration)
57+
}
58+
59+
func testRangeFormationOperatorCompaction_spacesAroundRangeFormation() {
60+
let input =
61+
"""
62+
x = 1...100
63+
x = 1..<100
64+
x = (1++)...(-100)
65+
x = 1 ... 100
66+
x = 1 ..< 100
67+
x = (1++) ... (-100)
68+
"""
69+
70+
let expected =
71+
"""
72+
x = 1 ... 100
73+
x = 1 ..< 100
74+
x = (1++) ... (-100)
75+
x = 1 ... 100
76+
x = 1 ..< 100
77+
x = (1++) ... (-100)
78+
79+
"""
80+
81+
var configuration = Configuration()
82+
configuration.spacesAroundRangeFormationOperators = true
83+
assertPrettyPrintEqual(
84+
input: input, expected: expected, linelength: 80, configuration: configuration)
5285
}
5386

5487
func testRangeFormationOperatorsAreNotCompactedWhenFollowingAPostfixOperator() {

0 commit comments

Comments
 (0)