mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
Implemented Allman braces
This commit is contained in:
1 parent
19a4a969e8
commit
cc180313d6
4 files changed
+160
-69
No files matched your search
@@ -38,6 +38,7 @@ let version = "0.14"
|
||||
let arguments = [
|
||||
"output",
|
||||
"indent",
|
||||
"allman",
|
||||
"linebreaks",
|
||||
"semicolons",
|
||||
"commas",
|
||||
@@ -45,6 +46,8 @@ let arguments = [
|
||||
"ranges",
|
||||
"empty",
|
||||
"trimwhitespace",
|
||||
"insertlines",
|
||||
"removelines",
|
||||
"experimental",
|
||||
"fragment",
|
||||
"help",
|
||||
@@ -60,6 +63,7 @@ func showHelp() {
|
||||
print(" <file> input file or directory path")
|
||||
print(" --output output path (defaults to input path)")
|
||||
print(" --indent number of spaces to indent, or \"tab\" to use tabs")
|
||||
print(" --allman use allman indentation style \"true\" or \"false\" (default)")
|
||||
print(" --linebreaks linebreak character to use. \"cr\", \"crlf\" or \"lf\" (default)")
|
||||
print(" --semicolons allow semicolons. \"never\" or \"inline\" (default)")
|
||||
print(" --commas commas in collection literals. \"always\" (default) or \"inline\"")
|
||||
@@ -114,6 +118,16 @@ func optionsForArguments(_ args: [String: String]) throws -> FormatOptions {
|
||||
throw NSError()
|
||||
}
|
||||
}
|
||||
try processOption("allman") {
|
||||
switch $0 {
|
||||
case "true", "enabled":
|
||||
options.allmanBraces = true
|
||||
case "false", "disabled":
|
||||
options.allmanBraces = false
|
||||
default:
|
||||
throw NSError()
|
||||
}
|
||||
}
|
||||
try processOption("semicolons") {
|
||||
switch $0 {
|
||||
case "inline":
|
||||
|
||||
@@ -19,6 +19,7 @@ public struct FormatOptions {
|
||||
public var truncateBlankLines: Bool
|
||||
public var insertBlankLines: Bool
|
||||
public var removeBlankLines: Bool
|
||||
public var allmanBraces: Bool
|
||||
public var experimentalRules: Bool
|
||||
public var fragment: Bool
|
||||
|
||||
@@ -32,6 +33,7 @@ public struct FormatOptions {
|
||||
truncateBlankLines: Bool = true,
|
||||
insertBlankLines: Bool = true,
|
||||
removeBlankLines: Bool = true,
|
||||
allmanBraces: Bool = false,
|
||||
experimentalRules: Bool = false,
|
||||
fragment: Bool = false) {
|
||||
|
||||
@@ -43,9 +45,10 @@ public struct FormatOptions {
|
||||
self.trailingCommas = trailingCommas
|
||||
self.indentComments = indentComments
|
||||
self.truncateBlankLines = truncateBlankLines
|
||||
self.experimentalRules = experimentalRules
|
||||
self.insertBlankLines = insertBlankLines
|
||||
self.removeBlankLines = removeBlankLines
|
||||
self.allmanBraces = allmanBraces
|
||||
self.experimentalRules = experimentalRules
|
||||
self.fragment = fragment
|
||||
}
|
||||
}
|
||||
|
||||
+75
-51
@@ -669,7 +669,7 @@ public func blankLinesBetweenScopes(_ formatter: Formatter) {
|
||||
}
|
||||
if let nextTokenIndex = formatter.indexOfNextToken(fromIndex: i, matching: {
|
||||
!$0.isWhitespaceOrLinebreak }), let nextToken = formatter.tokenAtIndex(nextTokenIndex),
|
||||
nextToken.type != .endOfScope && nextToken.type != .error && nextToken.string != "." {
|
||||
![.endOfScope, .error].contains(nextToken.type) && ![".", ",", ":", "else", "catch"].contains(nextToken.string) {
|
||||
if let firstLinebreakIndex = formatter.indexOfNextToken(fromIndex: i, matching: { $0.type == .linebreak }),
|
||||
firstLinebreakIndex < nextTokenIndex {
|
||||
if let secondLinebreakIndex = formatter.indexOfNextToken(
|
||||
@@ -756,9 +756,7 @@ public func indent(_ formatter: Formatter) {
|
||||
"switch",
|
||||
"where",
|
||||
"while",
|
||||
"do",
|
||||
"as",
|
||||
"catch",
|
||||
"is",
|
||||
"super",
|
||||
"throw",
|
||||
@@ -871,7 +869,7 @@ public func indent(_ formatter: Formatter) {
|
||||
while let token = formatter.tokenAtIndex(i) {
|
||||
switch token.type {
|
||||
case .identifier:
|
||||
if ["if", "for", "while", "catch", "switch", "guard" /* TODO: get/set/didSet */ ].contains(token.string) {
|
||||
if ["if", "else", "for", "while", "do", "catch", "switch", "guard" /* TODO: get/set/didSet */ ].contains(token.string) {
|
||||
// Check that it's actually a keyword and not a member property or enum value
|
||||
return formatter.previousNonWhitespaceOrCommentOrLinebreakToken(fromIndex: i)?.string == "."
|
||||
}
|
||||
@@ -1077,62 +1075,88 @@ public func indent(_ formatter: Formatter) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement K&R-style braces, where opening brace appears on the same line as
|
||||
/// the related function or keyword, and the closing brace is on its own line,
|
||||
/// except for inline closures where opening and closing brace are on same line.
|
||||
public func knrBraces(_ formatter: Formatter) {
|
||||
// Implement brace-wrapping rules
|
||||
public func braces(_ formatter: Formatter) {
|
||||
formatter.forEachToken("{") { i, token in
|
||||
var index = i - 1
|
||||
var linebreakIndex: Int?
|
||||
while let token = formatter.tokenAtIndex(index) {
|
||||
switch token.type {
|
||||
case .linebreak:
|
||||
linebreakIndex = index
|
||||
case .whitespace, .commentBody:
|
||||
break
|
||||
case .startOfScope:
|
||||
if token.string != "/*" && token.string != "//" {
|
||||
fallthrough
|
||||
if formatter.options.allmanBraces {
|
||||
// Check this isn't an inline block
|
||||
guard let nextLinebreakIndex = formatter.indexOfNextToken(fromIndex: i, matching: {
|
||||
return $0.type == .linebreak
|
||||
}), let closingBraceIndex = formatter.indexOfNextToken(fromIndex: i, matching: {
|
||||
return $0.type == .endOfScope && $0.string == "}"
|
||||
}), nextLinebreakIndex < closingBraceIndex else { return }
|
||||
// Implement Allman-style braces, where opening brace appears on the next line
|
||||
if let previousTokenIndex = formatter.indexOfPreviousToken(fromIndex: i, matching: {
|
||||
$0.type != .whitespace }), let previousToken = formatter.tokenAtIndex(previousTokenIndex) {
|
||||
switch previousToken.type {
|
||||
case .identifier, .endOfScope:
|
||||
formatter.insertToken(Token(.linebreak, formatter.options.linebreak), atIndex: i)
|
||||
if let indentToken = formatter.indentTokenForLineAtIndex(i) {
|
||||
formatter.insertToken(indentToken, atIndex: i + 1)
|
||||
}
|
||||
if formatter.tokens[i - 1].type == .whitespace {
|
||||
formatter.removeTokenAtIndex(i - 1)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
case .endOfScope:
|
||||
if token.string != "*/" {
|
||||
fallthrough
|
||||
}
|
||||
default:
|
||||
if let linebreakIndex = linebreakIndex {
|
||||
formatter.removeTokensInRange(Range(linebreakIndex ... i))
|
||||
formatter.insertToken(Token(.whitespace, " "), atIndex: index + 1)
|
||||
formatter.insertToken(Token(.startOfScope, "{"), atIndex: index + 2)
|
||||
}
|
||||
return
|
||||
}
|
||||
index -= 1
|
||||
} else {
|
||||
// Implement K&R-style braces, where opening brace appears on the same line
|
||||
var index = i - 1
|
||||
var linebreakIndex: Int?
|
||||
while let token = formatter.tokenAtIndex(index) {
|
||||
switch token.type {
|
||||
case .linebreak:
|
||||
linebreakIndex = index
|
||||
case .whitespace, .commentBody:
|
||||
break
|
||||
case .startOfScope:
|
||||
if token.string != "/*" && token.string != "//" {
|
||||
fallthrough
|
||||
}
|
||||
case .endOfScope:
|
||||
if token.string != "*/" {
|
||||
fallthrough
|
||||
}
|
||||
default:
|
||||
if let linebreakIndex = linebreakIndex {
|
||||
formatter.removeTokensInRange(Range(linebreakIndex ... i))
|
||||
formatter.insertToken(Token(.whitespace, " "), atIndex: index + 1)
|
||||
formatter.insertToken(Token(.startOfScope, "{"), atIndex: index + 2)
|
||||
}
|
||||
return
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that an `else` statement following `if { ... }` appears on the same line
|
||||
/// as the closing brace. This has no effect on the `else` part of a `guard` statement
|
||||
public func elseOnSameLine(_ formatter: Formatter) {
|
||||
formatter.forEachToken("else") { i, token in
|
||||
var index = i - 1
|
||||
var containsLinebreak = false
|
||||
while let token = formatter.tokenAtIndex(index) {
|
||||
switch token.type {
|
||||
case .linebreak:
|
||||
containsLinebreak = true
|
||||
case .whitespace:
|
||||
break
|
||||
case .endOfScope:
|
||||
if token.string == "}" && containsLinebreak &&
|
||||
formatter.previousNonWhitespaceToken(fromIndex: index)?.type == .linebreak {
|
||||
formatter.replaceTokensInRange(index + 1 ..< i, with: [Token(.whitespace, " ")])
|
||||
public func elseOrCatchOnSameLine(_ formatter: Formatter) {
|
||||
formatter.forEachToken(ofType: .identifier) { i, token in
|
||||
if token.string == "else" || token.string == "catch" {
|
||||
if let prevTokenIndex = formatter.indexOfPreviousToken(fromIndex: i, matching: {
|
||||
!$0.isWhitespaceOrLinebreak }), let prevToken = formatter.tokenAtIndex(prevTokenIndex),
|
||||
prevToken.type == .endOfScope, prevToken.string == "}" {
|
||||
// Only applies to dangling braces
|
||||
if formatter.previousNonWhitespaceToken(fromIndex: prevTokenIndex)?.type == .linebreak {
|
||||
if let prevLinebreakIndex = formatter.indexOfPreviousToken(fromIndex: i, matching: {
|
||||
$0.type == .linebreak }), prevTokenIndex < prevLinebreakIndex {
|
||||
if !formatter.options.allmanBraces {
|
||||
formatter.replaceTokensInRange(prevTokenIndex + 1 ..< i, with: [Token(.whitespace, " ")])
|
||||
}
|
||||
} else if formatter.options.allmanBraces {
|
||||
formatter.replaceTokensInRange(prevTokenIndex + 1 ..< i,
|
||||
with: [Token(.linebreak, formatter.options.linebreak)])
|
||||
if let indentToken = formatter.indentTokenForLineAtIndex(i) {
|
||||
formatter.insertToken(indentToken, atIndex: prevTokenIndex + 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
default:
|
||||
return
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1339,8 +1363,8 @@ public let defaultRules: [FormatRule] = [
|
||||
semicolons,
|
||||
specifiers,
|
||||
void,
|
||||
knrBraces,
|
||||
elseOnSameLine,
|
||||
braces,
|
||||
elseOrCatchOnSameLine,
|
||||
indent,
|
||||
spaceAroundParens,
|
||||
spaceInsideParens,
|
||||
|
||||
@@ -1688,66 +1688,116 @@ class RulesTests: XCTestCase {
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules, options: options), output + "\n")
|
||||
}
|
||||
|
||||
// MARK: knrBraces
|
||||
// MARK: knr braces
|
||||
|
||||
func testAllmanBracesAreConverted() {
|
||||
let input = "func foo()\n{\n statement\n}"
|
||||
let output = "func foo() {\n statement\n}"
|
||||
XCTAssertEqual(try! format(input, rules: [knrBraces]), output)
|
||||
XCTAssertEqual(try! format(input, rules: [braces]), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules), output + "\n")
|
||||
}
|
||||
|
||||
func testBracesAfterComment() {
|
||||
func testKnRBracesAfterComment() {
|
||||
let input = "func foo() // comment\n{\n statement\n}"
|
||||
let output = "func foo() { // comment\n statement\n}"
|
||||
XCTAssertEqual(try! format(input, rules: [knrBraces]), output)
|
||||
XCTAssertEqual(try! format(input, rules: [braces]), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules), output + "\n")
|
||||
}
|
||||
|
||||
func testBracesAfterMultilineComment() {
|
||||
func testKnRBracesAfterMultilineComment() {
|
||||
let input = "func foo() /* comment/ncomment */\n{\n statement\n}"
|
||||
let output = "func foo() { /* comment/ncomment */\n statement\n}"
|
||||
XCTAssertEqual(try! format(input, rules: [knrBraces]), output)
|
||||
XCTAssertEqual(try! format(input, rules: [braces]), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules), output + "\n")
|
||||
}
|
||||
|
||||
func testExtraSpaceNotAddedBeforeBrace() {
|
||||
func testKnRExtraSpaceNotAddedBeforeBrace() {
|
||||
let input = "foo({ bar })"
|
||||
let output = "foo({ bar })"
|
||||
XCTAssertEqual(try! format(input, rules: [knrBraces]), output)
|
||||
XCTAssertEqual(try! format(input, rules: [braces]), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules), output + "\n")
|
||||
}
|
||||
|
||||
// MARK: elseOnSameLine
|
||||
// MARK: allman braces
|
||||
|
||||
func testElseOnSameLine() {
|
||||
func testKnRBracesAreConverted() {
|
||||
let input = "func foo() {\n statement\n}"
|
||||
let output = "func foo()\n{\n statement\n}"
|
||||
let options = FormatOptions(allmanBraces: true)
|
||||
XCTAssertEqual(try! format(input, rules: [braces], options: options), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules, options: options), output + "\n")
|
||||
}
|
||||
|
||||
func testAllmanBraceInsideParensNotConverted() {
|
||||
let input = "foo({\n bar\n})"
|
||||
let output = "foo({\n bar\n})"
|
||||
let options = FormatOptions(allmanBraces: true)
|
||||
XCTAssertEqual(try! format(input, rules: [braces], options: options), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules, options: options), output + "\n")
|
||||
}
|
||||
|
||||
func testAllmanBraceDoClauseIndent() {
|
||||
let input = "do {\n foo\n}"
|
||||
let output = "do\n{\n foo\n}"
|
||||
let options = FormatOptions(allmanBraces: true)
|
||||
XCTAssertEqual(try! format(input, rules: [braces], options: options), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules, options: options), output + "\n")
|
||||
}
|
||||
|
||||
func testAllmanBraceCatchClauseIndent() {
|
||||
let input = "do {\n try foo\n}\ncatch {\n}"
|
||||
let output = "do\n{\n try foo\n}\ncatch\n{\n}"
|
||||
let options = FormatOptions(allmanBraces: true)
|
||||
XCTAssertEqual(try! format(input, rules: [braces], options: options), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules, options: options), output + "\n")
|
||||
}
|
||||
|
||||
// MARK: elseOrCatchOnSameLine
|
||||
|
||||
func testelseOrCatchOnSameLine() {
|
||||
let input = "if true {\n 1\n}\nelse { 2 }"
|
||||
let output = "if true {\n 1\n} else { 2 }"
|
||||
XCTAssertEqual(try! format(input, rules: [elseOnSameLine]), output)
|
||||
XCTAssertEqual(try! format(input, rules: [elseOrCatchOnSameLine]), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules), output + "\n")
|
||||
}
|
||||
|
||||
func testElseOnSameLineOnlyAppliedToDanglingBrace() {
|
||||
func testelseOrCatchOnSameLineOnlyAppliedToDanglingBrace() {
|
||||
let input = "if true { 1 }\nelse { 2 }"
|
||||
let output = "if true { 1 }\nelse { 2 }"
|
||||
XCTAssertEqual(try! format(input, rules: [elseOnSameLine]), output)
|
||||
XCTAssertEqual(try! format(input, rules: [elseOrCatchOnSameLine]), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules), output + "\n")
|
||||
}
|
||||
|
||||
func testGuardNotAffectedByElseOnSameLine() {
|
||||
func testGuardNotAffectedByelseOrCatchOnSameLine() {
|
||||
let input = "guard true\nelse { return }"
|
||||
let output = "guard true\nelse { return }"
|
||||
XCTAssertEqual(try! format(input, rules: [elseOnSameLine]), output)
|
||||
XCTAssertEqual(try! format(input, rules: [elseOrCatchOnSameLine]), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules), output + "\n")
|
||||
}
|
||||
|
||||
func testElseOnSameLineDoesntEatPreviousStatement() {
|
||||
func testelseOrCatchOnSameLineDoesntEatPreviousStatement() {
|
||||
let input = "if true {}\nguard true else { return }"
|
||||
let output = "if true {}\nguard true else { return }"
|
||||
XCTAssertEqual(try! format(input, rules: [elseOnSameLine]), output)
|
||||
XCTAssertEqual(try! format(input, rules: [elseOrCatchOnSameLine]), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules), output + "\n")
|
||||
}
|
||||
|
||||
func testElseNotOnSameLineForAllman() {
|
||||
let input = "if true\n{\n 1\n} else { 2 }"
|
||||
let output = "if true\n{\n 1\n}\nelse { 2 }"
|
||||
let options = FormatOptions(allmanBraces: true)
|
||||
XCTAssertEqual(try! format(input, rules: [elseOrCatchOnSameLine], options: options), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules, options: options), output + "\n")
|
||||
}
|
||||
|
||||
func testGuardNotAffectedByelseOrCatchOnSameLineForAllman() {
|
||||
let input = "guard true else { return }"
|
||||
let output = "guard true else { return }"
|
||||
let options = FormatOptions(allmanBraces: true)
|
||||
XCTAssertEqual(try! format(input, rules: [elseOrCatchOnSameLine], options: options), output)
|
||||
XCTAssertEqual(try! format(input + "\n", rules: defaultRules, options: options), output + "\n")
|
||||
}
|
||||
|
||||
// MARK: trailingCommas
|
||||
|
||||
func testCommaAddedToSingleItem() {
|
||||
|
||||
Reference in new issue
Block a user