Skip to content

Add an option to allow spaces around range formation operators. #459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 13, 2022
3 changes: 3 additions & 0 deletions Documentation/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ top-level keys and values:
true, a line break is forced before the "." of the component and after the component's
closing delimiter (i.e. right paren, right bracket, right brace, etc.).

* `spacesAroundRangeFormationOperators` _(boolean)_: Determines whether whitespace should be forced
before and after the range formation operators `...` and `..<`.

> TODO: Add support for enabling/disabling specific syntax transformations in
> the pipeline.

Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
),
.package(
url: "https://github.com/apple/swift-tools-support-core.git",
branch: "main"
.upToNextMinor(from: "0.2.7")
),
]
} else {
Expand Down
10 changes: 10 additions & 0 deletions Sources/SwiftFormatConfiguration/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public struct Configuration: Codable, Equatable {
case fileScopedDeclarationPrivacy
case indentSwitchCaseLabels
case rules
case spacesAroundRangeFormationOperators
}

/// The version of this configuration.
Expand Down Expand Up @@ -142,6 +143,10 @@ public struct Configuration: Codable, Equatable {
///```
public var indentSwitchCaseLabels = false

/// Determines whether whitespace should be forced before and after the range formation operators
/// `...` and `..<`.
public var spacesAroundRangeFormationOperators = false

/// Constructs a Configuration with all default values.
public init() {
self.version = highestSupportedConfigurationVersion
Expand Down Expand Up @@ -194,6 +199,9 @@ public struct Configuration: Codable, Equatable {
self.lineBreakAroundMultilineExpressionChainComponents =
try container.decodeIfPresent(
Bool.self, forKey: .lineBreakAroundMultilineExpressionChainComponents) ?? false
self.spacesAroundRangeFormationOperators =
try container.decodeIfPresent(
Bool.self, forKey: .spacesAroundRangeFormationOperators) ?? false
self.fileScopedDeclarationPrivacy =
try container.decodeIfPresent(
FileScopedDeclarationPrivacyConfiguration.self, forKey: .fileScopedDeclarationPrivacy)
Expand Down Expand Up @@ -226,6 +234,8 @@ public struct Configuration: Codable, Equatable {
try container.encode(
lineBreakAroundMultilineExpressionChainComponents,
forKey: .lineBreakAroundMultilineExpressionChainComponents)
try container.encode(
spacesAroundRangeFormationOperators, forKey: .spacesAroundRangeFormationOperators)
try container.encode(fileScopedDeclarationPrivacy, forKey: .fileScopedDeclarationPrivacy)
try container.encode(indentSwitchCaseLabels, forKey: .indentSwitchCaseLabels)
try container.encode(rules, forKey: .rules)
Expand Down
8 changes: 5 additions & 3 deletions Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3296,7 +3296,8 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
return nil
}

/// Returns a value indicating whether whitespace should be required around the given operator.
/// Returns a value indicating whether whitespace should be required around the given operator,
/// for the given configuration.
///
/// If spaces are not required (for example, range operators), then the formatter will also forbid
/// breaks around the operator. This is to prevent situations where a break could occur before an
Expand All @@ -3308,8 +3309,9 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
// to ignore that and apply our own rules.
if let binaryOperator = operatorExpr.as(BinaryOperatorExprSyntax.self) {
let token = binaryOperator.operatorToken
if let binOp = operatorTable.infixOperator(named: token.text),
let precedenceGroup = binOp.precedenceGroup, precedenceGroup == "RangeFormationPrecedence"
if !config.spacesAroundRangeFormationOperators,
let binOp = operatorTable.infixOperator(named: token.text),
let precedenceGroup = binOp.precedenceGroup, precedenceGroup == "RangeFormationPrecedence"
{
// We want to omit whitespace around range formation operators if possible. We can't do this
// if the token is either preceded by a postfix operator, followed by a prefix operator, or
Expand Down
37 changes: 35 additions & 2 deletions Tests/SwiftFormatPrettyPrintTests/BinaryOperatorExprTests.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import SwiftFormatConfiguration

final class BinaryOperatorExprTests: PrettyPrintTestCase {
func testNonRangeFormationOperatorsAreSurroundedByBreaks() {
let input =
Expand Down Expand Up @@ -26,7 +28,7 @@ final class BinaryOperatorExprTests: PrettyPrintTestCase {
assertPrettyPrintEqual(input: input, expected: expected10, linelength: 10)
}

func testRangeFormationOperatorsAreCompactedWhenPossible() {
func testRangeFormationOperatorCompaction_noSpacesAroundRangeFormation() {
let input =
"""
x = 1...100
Expand All @@ -48,7 +50,38 @@ final class BinaryOperatorExprTests: PrettyPrintTestCase {

"""

assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
var configuration = Configuration()
configuration.spacesAroundRangeFormationOperators = false
assertPrettyPrintEqual(
input: input, expected: expected, linelength: 80, configuration: configuration)
}

func testRangeFormationOperatorCompaction_spacesAroundRangeFormation() {
let input =
"""
x = 1...100
x = 1..<100
x = (1++)...(-100)
x = 1 ... 100
x = 1 ..< 100
x = (1++) ... (-100)
"""

let expected =
"""
x = 1 ... 100
x = 1 ..< 100
x = (1++) ... (-100)
x = 1 ... 100
x = 1 ..< 100
x = (1++) ... (-100)

"""

var configuration = Configuration()
configuration.spacesAroundRangeFormationOperators = true
assertPrettyPrintEqual(
input: input, expected: expected, linelength: 80, configuration: configuration)
}

func testRangeFormationOperatorsAreNotCompactedWhenFollowingAPostfixOperator() {
Expand Down