Implemented Allman braces

This commit is contained in:
Nick Lockwood committed 2016-10-27 19:00:27 +01:00
1 parent 19a4a969e8
commit cc180313d6
4 files changed
+160 -69

No files matched your search

+14
View File
@@ -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":
+4 -1
View File
@@ -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
View File
@@ -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,
+67 -17
View File
@@ -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() {