Skip to content
88 changes: 84 additions & 4 deletions Documentation/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

---

Expand All @@ -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"] }`

---

Expand All @@ -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"`

---

Expand Down
25 changes: 14 additions & 11 deletions Sources/SwiftFormat/PrettyPrint/PrettyPrintBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
11 changes: 9 additions & 2 deletions Sources/SwiftFormat/PrettyPrint/WhitespaceLinter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,16 @@ public class WhitespaceLinter {
startingAt offset: Int,
in data: [UTF8.CodeUnit]
) -> ArraySlice<UTF8.CodeUnit> {
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..<data.endIndex]
}
Expand Down
18 changes: 17 additions & 1 deletion Sources/swift-format/Frontend/Frontend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,23 @@ class Frontend {

/// Runs the linter or formatter over the inputs.
final func run() {
if lintFormatOptions.paths.isEmpty {
if lintFormatOptions.paths == ["-"] {
processStandardInput()
} else if lintFormatOptions.paths.isEmpty {
diagnosticsEngine.emitWarning(
"""
Running swift-format without input paths is deprecated and will be removed in the future.

Please update your invocation to do either of the following:

- Pass `-` to read from stdin (e.g., `cat MyFile.swift | swift-format -`).
- Pass one or more paths to Swift source files or directories containing
Swift source files. When passing directories, make sure to include the
`--recursive` flag.

For more information, use the `--help` option.
"""
)
processStandardInput()
} else {
processURLs(
Expand Down
2 changes: 1 addition & 1 deletion Sources/swift-format/Subcommands/LintFormatOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ struct LintFormatOptions: ParsableArguments {
var experimentalFeatures: [String] = []

/// The list of paths to Swift source files that should be formatted or linted.
@Argument(help: "Zero or more input filenames.")
@Argument(help: "Zero or more input filenames. Use `-` for stdin.")
var paths: [String] = []

@Flag(help: .hidden) var debugDisablePrettyPrint: Bool = false
Expand Down
34 changes: 34 additions & 0 deletions Tests/SwiftFormatTests/PrettyPrint/LineNumbersTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,38 @@ final class LineNumbersTests: PrettyPrintTestCase {
]
)
}

func testCharacterVsCodepoint() {
let input =
"""
let fo = 1 // 🤥

"""

assertPrettyPrintEqual(
input: input,
expected: input,
linelength: 16,
whitespaceOnly: true,
findings: []
)
}

func testCharacterVsCodepointMultiline() {
let input =
#"""
/// This is a multiline
/// comment that is in 🤥
/// fact perfectly sized

"""#

assertPrettyPrintEqual(
input: input,
expected: input,
linelength: 25,
whitespaceOnly: true,
findings: []
)
}
}