From c13cd2613094290b1bd0cbb8d8c9b52e99553247 Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Sun, 6 Apr 2025 04:41:00 +0800 Subject: [PATCH 1/9] Add `String` raw type to `MultilineStringReflowBehavior` --- Sources/SwiftFormat/API/Configuration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftFormat/API/Configuration.swift b/Sources/SwiftFormat/API/Configuration.swift index ac1c742d8..d4480f873 100644 --- a/Sources/SwiftFormat/API/Configuration.swift +++ b/Sources/SwiftFormat/API/Configuration.swift @@ -197,7 +197,7 @@ public struct Configuration: Codable, Equatable { public var multiElementCollectionTrailingCommas: Bool /// Determines how multiline string literals should reflow when formatted. - public enum MultilineStringReflowBehavior: Codable { + public enum MultilineStringReflowBehavior: String, Codable { /// Never reflow multiline string literals. case never /// Reflow lines in string literal that exceed the maximum line length. For example with a line length of 10: From fbac9f55b4299bec8c1b88455547386b69914c6c Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Mon, 7 Apr 2025 23:14:37 +0800 Subject: [PATCH 2/9] Create a `LegacyMultilineStringReflowBehavior` to avoid version bump --- Sources/SwiftFormat/API/Configuration.swift | 75 ++++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftFormat/API/Configuration.swift b/Sources/SwiftFormat/API/Configuration.swift index d4480f873..48017a1e7 100644 --- a/Sources/SwiftFormat/API/Configuration.swift +++ b/Sources/SwiftFormat/API/Configuration.swift @@ -259,6 +259,53 @@ public struct Configuration: Codable, Equatable { } } + /// A private enum created to maintain backward compatibility with swift-format version 601.0.0, + /// which had a `MultilineStringReflowBehavior` enum without a String raw type. + /// + /// In version 601.0.0, the `reflowMultilineStringLiterals` configuration was encoded as an object + /// with a single key (e.g., `{ "never": {} }`) rather than as a string (e.g., `"never"`). This + /// enum allows decoding from both formats: + /// - First, we attempt to decode as a String using `MultilineStringReflowBehavior` + /// - If that fails, we fall back to this legacy format + /// - If both attempts fail, an error will be thrown + /// + /// This approach preserves compatibility without requiring a configuration version bump. + private enum LegacyMultilineStringReflowBehavior: Codable { + case never + case onlyLinesOverLength + case always + + var isNever: Bool { + switch self { + case .never: + return true + default: + return false + } + } + + var isAlways: Bool { + switch self { + case .always: + return true + default: + return false + } + } + + /// Converts this legacy enum to the corresponding `MultilineStringReflowBehavior` value. + func toMultilineStringReflowBehavior() -> MultilineStringReflowBehavior { + switch self { + case .never: + return .never + case .always: + return .always + case .onlyLinesOverLength: + return .onlyLinesOverLength + } + } + } + public var reflowMultilineStringLiterals: MultilineStringReflowBehavior /// Determines whether to add indentation whitespace to blank lines or remove it entirely. @@ -371,9 +418,31 @@ public struct Configuration: Codable, Equatable { ) ?? defaults.multiElementCollectionTrailingCommas - self.reflowMultilineStringLiterals = - try container.decodeIfPresent(MultilineStringReflowBehavior.self, forKey: .reflowMultilineStringLiterals) - ?? defaults.reflowMultilineStringLiterals + self.reflowMultilineStringLiterals = try { + // Try to decode `reflowMultilineStringLiterals` as a string + // This handles configurations using the String raw value format (e.g. "never"). + // If an error occurs, we'll silently bypass it and fall back to the legacy behavior. + if let behavior = try? container.decodeIfPresent( + MultilineStringReflowBehavior.self, + forKey: .reflowMultilineStringLiterals + ) { + return behavior + } + + // If the modern format fails, try to decode as an object with a single key. + // This handles configurations from swift-format v601.0.0 (e.g. { "never": {} }). + // If an error occurs in this step, we'll propagate it to the caller. + if let legacyBehavior = try container.decodeIfPresent( + LegacyMultilineStringReflowBehavior.self, + forKey: .reflowMultilineStringLiterals + ) { + return legacyBehavior.toMultilineStringReflowBehavior() + } + + // If the key is not present in the configuration at all, use the default value. + return defaults.reflowMultilineStringLiterals + }() + self.indentBlankLines = try container.decodeIfPresent( Bool.self, From 52ca71629010540b80dde1285e4263082eaa36c1 Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Mon, 7 Apr 2025 23:36:23 +0800 Subject: [PATCH 3/9] Add document and notes for legacy object type in `reflowMultilineStringLiterals` --- Documentation/Configuration.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Documentation/Configuration.md b/Documentation/Configuration.md index 8decd52e6..347115f7a 100644 --- a/Documentation/Configuration.md +++ b/Documentation/Configuration.md @@ -204,7 +204,30 @@ switch someValue { --- ### `reflowMultilineStringLiterals` -**type:** `string` + +> [!NOTE] +> This setting should be specified as a string value (e.g. "never") +> For backward compatibility with swift-format version 601.0.0, the configuration also accepts the legacy object format where the setting is specified as an object with a single key (e.g., ⁠{ "never": {} }). + +**type:** `string` or `object` (legacy) + +**example:** + +For all versions above 601.0.0, the configuration should be specified as a string, for example: +```json +{ + "reflowMultilineStringLiterals": "never" +} +``` + +For version 601.0.0, the configuration should be specified as an object, for example: +```json +{ + "reflowMultilineStringLiterals": { + "never": {} + } +} +``` **description:** Determines how multiline string literals should reflow when formatted. From 7de00b407c2f026bc46b6066060d650cf171ed00 Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Mon, 7 Apr 2025 23:50:33 +0800 Subject: [PATCH 4/9] refactor: make code in `MultilineStringReflowBehavior` concise --- Sources/SwiftFormat/API/Configuration.swift | 37 ++++----------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/Sources/SwiftFormat/API/Configuration.swift b/Sources/SwiftFormat/API/Configuration.swift index 48017a1e7..d45e19aaf 100644 --- a/Sources/SwiftFormat/API/Configuration.swift +++ b/Sources/SwiftFormat/API/Configuration.swift @@ -241,21 +241,11 @@ public struct Configuration: Codable, Equatable { case always var isNever: Bool { - switch self { - case .never: - return true - default: - return false - } + self == .never } var isAlways: Bool { - switch self { - case .always: - return true - default: - return false - } + self == .always } } @@ -276,32 +266,19 @@ public struct Configuration: Codable, Equatable { case always var isNever: Bool { - switch self { - case .never: - return true - default: - return false - } + self == .never } var isAlways: Bool { - switch self { - case .always: - return true - default: - return false - } + self == .always } /// Converts this legacy enum to the corresponding `MultilineStringReflowBehavior` value. func toMultilineStringReflowBehavior() -> MultilineStringReflowBehavior { switch self { - case .never: - return .never - case .always: - return .always - case .onlyLinesOverLength: - return .onlyLinesOverLength + case .never: .never + case .always: .always + case .onlyLinesOverLength: .onlyLinesOverLength } } } From 1a1f33634ce74debbc2d07ee50b4893a4b7b5e41 Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Tue, 8 Apr 2025 00:25:18 +0800 Subject: [PATCH 5/9] Remove unused var getter from `LegacyMultilineStringReflowBehavior` --- Sources/SwiftFormat/API/Configuration.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Sources/SwiftFormat/API/Configuration.swift b/Sources/SwiftFormat/API/Configuration.swift index d45e19aaf..cb5dfb025 100644 --- a/Sources/SwiftFormat/API/Configuration.swift +++ b/Sources/SwiftFormat/API/Configuration.swift @@ -265,14 +265,6 @@ public struct Configuration: Codable, Equatable { case onlyLinesOverLength case always - var isNever: Bool { - self == .never - } - - var isAlways: Bool { - self == .always - } - /// Converts this legacy enum to the corresponding `MultilineStringReflowBehavior` value. func toMultilineStringReflowBehavior() -> MultilineStringReflowBehavior { switch self { From adbbbe253ade48b02b83df7baec5b29b807f1faf Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Tue, 8 Apr 2025 00:26:54 +0800 Subject: [PATCH 6/9] Add backquote to value in documentation --- Documentation/Configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Configuration.md b/Documentation/Configuration.md index 347115f7a..dbdb171a3 100644 --- a/Documentation/Configuration.md +++ b/Documentation/Configuration.md @@ -206,8 +206,8 @@ switch someValue { ### `reflowMultilineStringLiterals` > [!NOTE] -> This setting should be specified as a string value (e.g. "never") -> For backward compatibility with swift-format version 601.0.0, the configuration also accepts the legacy object format where the setting is specified as an object with a single key (e.g., ⁠{ "never": {} }). +> This setting should be specified as a string value (e.g. `"never"`) +> For backward compatibility with swift-format version 601.0.0, the configuration also accepts the legacy object format where the setting is specified as an object with a single key (e.g., ⁠`{ "never": {} }`). **type:** `string` or `object` (legacy) From 28a70a29eaacc7b77563668fa4a293dec6514098 Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Tue, 8 Apr 2025 01:26:09 +0800 Subject: [PATCH 7/9] Add test for decoding `reflowMultilineStringLiterals` --- .../API/ConfigurationTests.swift | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Tests/SwiftFormatTests/API/ConfigurationTests.swift b/Tests/SwiftFormatTests/API/ConfigurationTests.swift index 8fd982f5d..42a42db53 100644 --- a/Tests/SwiftFormatTests/API/ConfigurationTests.swift +++ b/Tests/SwiftFormatTests/API/ConfigurationTests.swift @@ -64,4 +64,47 @@ final class ConfigurationTests: XCTestCase { let path = #"\\mount\test.swift"# XCTAssertNil(Configuration.url(forConfigurationFileApplyingTo: URL(fileURLWithPath: path))) } + + func testDecodingReflowMultilineStringLiteralsAsString() throws { + typealias MultilineStringReflowBehavior = Configuration.MultilineStringReflowBehavior + + let testCases = [ + "never": MultilineStringReflowBehavior.never, + "always": MultilineStringReflowBehavior.always, + "onlyLinesOverLength": MultilineStringReflowBehavior.onlyLinesOverLength, + ] + + for (jsonString, expectedBehavior) in testCases { + let jsonData = """ + { + "reflowMultilineStringLiterals": "\(jsonString)" + } + """.data(using: .utf8)! + + let config = try JSONDecoder().decode(Configuration.self, from: jsonData) + XCTAssertEqual(config.reflowMultilineStringLiterals, expectedBehavior) + } + } + + func testDecodingReflowMultilineStringLiteralsAsObject() throws { + typealias MultilineStringReflowBehavior = Configuration.MultilineStringReflowBehavior + + let testCases = [ + "{ \"never\": {} }": MultilineStringReflowBehavior.never, + "{ \"always\": {} }": MultilineStringReflowBehavior.always, + "{ \"onlyLinesOverLength\": {} }": MultilineStringReflowBehavior.onlyLinesOverLength, + ] + + for (jsonString, expectedBehavior) in testCases { + let jsonData = """ + { + "reflowMultilineStringLiterals": \(jsonString) + } + """.data(using: .utf8)! + + let config = try JSONDecoder().decode(Configuration.self, from: jsonData) + XCTAssertEqual(config.reflowMultilineStringLiterals, expectedBehavior) + } + } + } From 0927ba638e523d5bdc8d7893dae4b04304edbaf7 Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Tue, 8 Apr 2025 02:54:44 +0800 Subject: [PATCH 8/9] Replace dict value with enum shorthand in tests --- .../API/ConfigurationTests.swift | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Tests/SwiftFormatTests/API/ConfigurationTests.swift b/Tests/SwiftFormatTests/API/ConfigurationTests.swift index 42a42db53..9c6977db8 100644 --- a/Tests/SwiftFormatTests/API/ConfigurationTests.swift +++ b/Tests/SwiftFormatTests/API/ConfigurationTests.swift @@ -66,12 +66,10 @@ final class ConfigurationTests: XCTestCase { } func testDecodingReflowMultilineStringLiteralsAsString() throws { - typealias MultilineStringReflowBehavior = Configuration.MultilineStringReflowBehavior - - let testCases = [ - "never": MultilineStringReflowBehavior.never, - "always": MultilineStringReflowBehavior.always, - "onlyLinesOverLength": MultilineStringReflowBehavior.onlyLinesOverLength, + let testCases: [String: Configuration.MultilineStringReflowBehavior] = [ + "never": .never, + "always": .always, + "onlyLinesOverLength": .onlyLinesOverLength, ] for (jsonString, expectedBehavior) in testCases { @@ -87,12 +85,11 @@ final class ConfigurationTests: XCTestCase { } func testDecodingReflowMultilineStringLiteralsAsObject() throws { - typealias MultilineStringReflowBehavior = Configuration.MultilineStringReflowBehavior - let testCases = [ - "{ \"never\": {} }": MultilineStringReflowBehavior.never, - "{ \"always\": {} }": MultilineStringReflowBehavior.always, - "{ \"onlyLinesOverLength\": {} }": MultilineStringReflowBehavior.onlyLinesOverLength, + let testCases: [String: Configuration.MultilineStringReflowBehavior] = [ + "{ \"never\": {} }": .never, + "{ \"always\": {} }": .always, + "{ \"onlyLinesOverLength\": {} }": .onlyLinesOverLength, ] for (jsonString, expectedBehavior) in testCases { From 547dcfefcb2245eff3a0a3e8dfffd1f7dd385129 Mon Sep 17 00:00:00 2001 From: dong <93483694+dongdong867@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:29:11 +0800 Subject: [PATCH 9/9] Add detected breaking changes to `api-breakages.txt` --- api-breakages.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api-breakages.txt b/api-breakages.txt index 0b04b73c8..519c9a091 100644 --- a/api-breakages.txt +++ b/api-breakages.txt @@ -13,4 +13,8 @@ API breakage: enum Finding.Severity has been removed API breakage: var Finding.severity has been removed API breakage: var FindingCategorizing.defaultSeverity has been removed API breakage: var FindingCategorizing.defaultSeverity has been removed -API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:) \ No newline at end of file +API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:) +API breakage: func Configuration.MultilineStringReflowBehavior.hash(into:) has been removed +API breakage: func Configuration.MultilineStringReflowBehavior.encode(to:) has been removed +API breakage: var Configuration.MultilineStringReflowBehavior.hashValue has been removed +API breakage: constructor Configuration.MultilineStringReflowBehavior.init(from:) has been removed