diff --git a/Documentation/Configuration.md b/Documentation/Configuration.md index 42f044fb1..f84fb1c60 100644 --- a/Documentation/Configuration.md +++ b/Documentation/Configuration.md @@ -137,11 +137,40 @@ top-level keys and values: --- -## FIXME: fileScopedDeclarationPrivacy +## `fileScopedDeclarationPrivacy` +**type:** object + +**description:** Declarations at file scope with effective private access should be consistently declared as either `fileprivate` or `private`, determined by configuration. + +- `accessLevel` _(string)_: The formal access level to use when encountering a file-scoped declaration with effective private access. Allowed values are `private` and `fileprivate`. + +**default:** `{ "accessLevel" : "private" }` --- -### FIXME: indentSwitchCaseLabels +### `indentSwitchCaseLabels` +**type:** boolean + +**description:** Determines if `case` statements should be indented compared to the containing `switch` block. + +When `false`, the correct form is: +```swift +switch someValue { +case someCase: + someStatement +... +} +``` +When `true`, the correct form is: +```swift +switch someValue { + case someCase: + someStatement + ... +} +``` + +**default:** `false` --- @@ -154,7 +183,14 @@ top-level keys and values: --- -### FIXME: noAssignmentInExpressions +### `noAssignmentInExpressions` +**type:** object + +**description:** Assignment expressions must be their own statements. Assignment should not be used in an expression context that expects a `Void` value. For example, assigning a variable within a `return` statement existing a `Void` function is prohibited. + +- `allowedFunctions` _(strings array)_: A list of function names where assignments are allowed to be embedded in expressions that are passed as parameters to that function. + +**default:** `{ "allowedFunctions" : ["XCTAssertNoThrow"] }` --- @@ -167,7 +203,51 @@ top-level keys and values: --- -### FIXME: reflowMultilineStringLiterals +### `reflowMultilineStringLiterals` +**type:** `string` + +**description:** Determines how multiline string literals should reflow when formatted. + +- `never`: Never reflow multiline string literals. +- `onlyLinesOverLength`: Reflow lines in string literal that exceed the maximum line length. +For example with a line length of 10: +```swift +""" +an escape\ + line break +a hard line break +""" +``` +will be formatted as: +```swift +""" +an esacpe\ + line break +a hard \ +line break +""" +``` +- `always`: Always reflow multiline string literals, this will ignore existing escaped newlines in the literal and reflow each line. Hard linebreaks are still respected. +For example, with a line length of 10: +```swift +""" +one \ +word \ +a line. +this is too long. +""" +``` +will be formatted as: +```swift +""" +one word \ +a line. +this is \ +too long. +""" +``` + +**default:** `"never"` --- diff --git a/Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift b/Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift index 4b02d372e..9ea726206 100644 --- a/Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift +++ b/Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift @@ -119,18 +119,21 @@ struct PrettyPrintBuffer { consecutiveNewlineCount = 0 pendingSpaces = 0 - // In case of comments, we may get a multi-line string. - // To account for that case, we need to correct the lineNumber count. - // The new column is only the position within the last line. - let lines = text.split(separator: "\n") - lineNumber += lines.count - 1 - if lines.count > 1 { - // in case we have inserted new lines, we need to reset the column - column = lines.last?.count ?? 0 + // In case of comments, we may get a multi-line string. To account for that case, we need to correct the + // `lineNumber` count. The new `column` is the position within the last line. + + var lastNewlineIndex: String.Index? = nil + for i in text.utf8.indices { + if text.utf8[i] == UInt8(ascii: "\n") { + lastNewlineIndex = i + lineNumber += 1 + } + } + + if let lastNewlineIndex { + column = text.distance(from: text.utf8.index(after: lastNewlineIndex), to: text.endIndex) } else { - // in case it is an end of line comment or a single line comment, - // we just add to the current column - column += lines.last?.count ?? 0 + column += text.count } } diff --git a/Sources/SwiftFormat/PrettyPrint/WhitespaceLinter.swift b/Sources/SwiftFormat/PrettyPrint/WhitespaceLinter.swift index c5c5e2ae8..30f733952 100644 --- a/Sources/SwiftFormat/PrettyPrint/WhitespaceLinter.swift +++ b/Sources/SwiftFormat/PrettyPrint/WhitespaceLinter.swift @@ -339,9 +339,16 @@ public class WhitespaceLinter { startingAt offset: Int, in data: [UTF8.CodeUnit] ) -> ArraySlice { + func isWhitespace(_ char: UTF8.CodeUnit) -> Bool { + switch char { + case UInt8(ascii: " "), UInt8(ascii: "\n"), UInt8(ascii: "\t"), UInt8(ascii: "\r"), /*VT*/ 0x0B, /*FF*/ 0x0C: + return true + default: + return false + } + } guard - let whitespaceEnd = - data[offset...].firstIndex(where: { !UnicodeScalar($0).properties.isWhitespace }) + let whitespaceEnd = data[offset...].firstIndex(where: { !isWhitespace($0) }) else { return data[offset..