Add --smarttabs option (enabled by default)

This commit is contained in:
Nick Lockwood
2020-07-30 01:38:56 +01:00
parent 60796c2e89
commit 84968e643d
10 changed files with 84 additions and 32 deletions
+1
View File
@@ -472,6 +472,7 @@ Option | Description
--- | ---
`--indent` | Number of spaces to indent, or "tab" to use tabs
`--tabwidth` | The width of a tab character. Defaults to "unspecified"
`--smarttabs` | Align code independently of tab width. defaults to "enabled"
`--indentcase` | Indent cases inside a switch: "true" or "false" (default)
`--ifdef` | #if indenting: "indent" (default), "no-indent" or "outdent"
`--xcodeindentation` | Xcode indent guard/enum: "enabled" or "disabled" (default)
+2 -6
View File
@@ -816,8 +816,8 @@ func processInput(_ inputURLs: [URL],
if formatOptions.swiftVersion == .undefined {
print("warning: No Swift version was specified, so some formatting features were disabled. Specify the version of Swift you are using with the --swiftversion option, or by adding a \(swiftVersionFile) file to your project.", as: .warning)
}
if formatOptions.useTabs, formatOptions.tabWidth <= 0 {
print("warning: The --indent option is set to tabs, but no --tabwidth was specified.", as: .warning)
if formatOptions.useTabs, formatOptions.tabWidth <= 0, !formatOptions.smartTabs {
print("warning: The --smarttabs option is disabled, but no --tabwidth was specified.", as: .warning)
}
showedConfigurationWarnings = true
}
@@ -838,11 +838,7 @@ func processInput(_ inputURLs: [URL],
// Override options
var options = options
try options.addArguments(overrides, in: "") // No need for directory as overrides are formatOptions only
// Validate options
let formatOptions = options.formatOptions ?? .default
if formatOptions.useTabs, formatOptions.tabWidth <= 0 {
throw FormatError.options("Indenting with tabs requires --tabwidth to also be set")
}
// Check cache
let rules = options.rules ?? allRules.subtracting(FormatRules.disabledByDefault)
let configHash = computeHash("\(formatOptions)\(rules.sorted().joined(separator: ","))")
+3
View File
@@ -285,6 +285,7 @@ public struct FormatOptions: CustomStringConvertible {
public var xcodeIndentation: Bool
public var tabWidth: Int
public var maxWidth: Int
public var smartTabs: Bool
public var noSpaceOperators: Set<String>
public var noWrapOperators: Set<String>
public var modifierOrder: [String]
@@ -342,6 +343,7 @@ public struct FormatOptions: CustomStringConvertible {
xcodeIndentation: Bool = false,
tabWidth: Int = 0,
maxWidth: Int = 0,
smartTabs: Bool = true,
noSpaceOperators: Set<String> = [],
noWrapOperators: Set<String> = [],
modifierOrder: [String] = [],
@@ -391,6 +393,7 @@ public struct FormatOptions: CustomStringConvertible {
self.xcodeIndentation = xcodeIndentation
self.tabWidth = tabWidth
self.maxWidth = maxWidth
self.smartTabs = smartTabs
self.noSpaceOperators = noSpaceOperators
self.noWrapOperators = noWrapOperators
self.modifierOrder = modifierOrder
+10
View File
@@ -279,6 +279,7 @@ extension FormatOptions.Descriptor {
xcodeIndentation,
tabWidth,
maxWidth,
smartTabs,
modifierOrder,
noSpaceOperators,
noWrapOperators,
@@ -601,6 +602,15 @@ extension FormatOptions.Descriptor {
fromArgument: { $0.lowercased() == "none" ? 0 : Int($0).map { max(0, $0) } },
toArgument: { $0 > 0 ? String($0) : "none" }
)
static let smartTabs = FormatOptions.Descriptor(
argumentName: "smarttabs",
propertyName: "smartTabs",
displayName: "Smart Tabs",
help: "Align code independently of tab width. defaults to \"enabled\"",
keyPath: \.smartTabs,
trueValues: ["enabled", "true"],
falseValues: ["disabled", "false"]
)
static let noSpaceOperators = FormatOptions.Descriptor(
argumentName: "nospaceoperators",
propertyName: "noSpaceOperators",
+2 -2
View File
@@ -76,7 +76,7 @@ extension Formatter {
/// Returns white space made up of indent characters equvialent to the specified width
func spaceEquivalentToWidth(_ width: Int) -> String {
if options.useTabs, options.tabWidth > 0 {
if !options.smartTabs, options.useTabs, options.tabWidth > 0 {
let tabs = width / options.tabWidth
let remainder = width % options.tabWidth
return String(repeating: "\t", count: tabs) + String(repeating: " ", count: remainder)
@@ -86,7 +86,7 @@ extension Formatter {
/// Returns white space made up of indent characters equvialent to the specified token range
func spaceEquivalentToTokens(from start: Int, upTo end: Int) -> String {
if options.useTabs, options.tabWidth > 0 {
if !options.smartTabs, options.useTabs, options.tabWidth > 0 {
return spaceEquivalentToWidth(lineLength(from: start, upTo: end))
}
return tokens[start ..< end].reduce(into: "") { result, token in
+3 -3
View File
@@ -911,7 +911,7 @@ public struct _FormatRules {
public let indent = FormatRule(
help: "Indent code in accordance with the scope level.",
orderAfter: ["trailingSpace", "wrap", "wrapArguments", "wrapMultilineStatementBraces"],
options: ["indent", "tabwidth", "indentcase", "ifdef", "xcodeindentation"],
options: ["indent", "tabwidth", "smarttabs", "indentcase", "ifdef", "xcodeindentation"],
sharedOptions: ["trimwhitespace", "closingparen"]
) { formatter in
var scopeStack: [Token] = []
@@ -3142,7 +3142,7 @@ public struct _FormatRules {
help: "Wrap lines that exceed the specified maximum width.",
options: ["maxwidth", "nowrapoperators"],
sharedOptions: ["wraparguments", "wrapparameters", "wrapcollections", "closingparen", "indent",
"trimwhitespace", "linebreaks", "tabwidth", "maxwidth"]
"trimwhitespace", "linebreaks", "tabwidth", "maxwidth", "smarttabs"]
) { formatter in
let maxWidth = formatter.options.maxWidth
guard maxWidth > 0 else { return }
@@ -3195,7 +3195,7 @@ public struct _FormatRules {
help: "Align wrapped function arguments or collection elements.",
orderAfter: ["wrap"],
options: ["wraparguments", "wrapparameters", "wrapcollections", "closingparen"],
sharedOptions: ["indent", "trimwhitespace", "linebreaks", "tabwidth", "maxwidth"]
sharedOptions: ["indent", "trimwhitespace", "linebreaks", "tabwidth", "maxwidth", "smarttabs"]
) { formatter in
formatter.wrapCollectionsAndArguments(completePartialWrapping: true,
wrapSingleArguments: false)
+1 -1
View File
@@ -208,7 +208,7 @@ class ArgumentsTests: XCTestCase {
}
func testCommandLineArgumentsAreCorrect() {
let output = ["allman": "false", "wraparguments": "preserve", "wrapparameters": "preserve", "stripunusedargs": "always", "self": "remove", "header": "ignore", "importgrouping": "alphabetized", "fractiongrouping": "disabled", "binarygrouping": "4,8", "octalgrouping": "4,8", "indentcase": "false", "trimwhitespace": "always", "decimalgrouping": "3,6", "exponentgrouping": "disabled", "patternlet": "hoist", "commas": "always", "wrapcollections": "preserve", "semicolons": "inline", "indent": "4", "exponentcase": "lowercase", "operatorfunc": "spaced", "symlinks": "ignore", "elseposition": "same-line", "voidtype": "void", "hexliteralcase": "uppercase", "linebreaks": "lf", "hexgrouping": "4,8", "ifdef": "indent", "closingparen": "balanced", "selfrequired": "", "trailingclosures": "", "xcodeindentation": "disabled", "fragment": "false", "conflictmarkers": "reject", "tabwidth": "unspecified", "maxwidth": "none", "nospaceoperators": "", "nowrapoperators": "", "modifierorder": "", "minversion": "0", "shortoptionals": "always", "funcattributes": "preserve", "typeattributes": "preserve"]
let output = ["allman": "false", "wraparguments": "preserve", "wrapparameters": "preserve", "stripunusedargs": "always", "self": "remove", "header": "ignore", "importgrouping": "alphabetized", "fractiongrouping": "disabled", "binarygrouping": "4,8", "octalgrouping": "4,8", "indentcase": "false", "trimwhitespace": "always", "decimalgrouping": "3,6", "exponentgrouping": "disabled", "patternlet": "hoist", "commas": "always", "wrapcollections": "preserve", "semicolons": "inline", "indent": "4", "exponentcase": "lowercase", "operatorfunc": "spaced", "symlinks": "ignore", "elseposition": "same-line", "voidtype": "void", "hexliteralcase": "uppercase", "linebreaks": "lf", "hexgrouping": "4,8", "ifdef": "indent", "closingparen": "balanced", "selfrequired": "", "trailingclosures": "", "xcodeindentation": "disabled", "fragment": "false", "conflictmarkers": "reject", "tabwidth": "unspecified", "maxwidth": "none", "smarttabs": "enabled", "nospaceoperators": "", "nowrapoperators": "", "modifierorder": "", "minversion": "0", "shortoptionals": "always", "funcattributes": "preserve", "typeattributes": "preserve"]
XCTAssertEqual(argumentsFor(.default), output)
}
+7 -8
View File
@@ -121,12 +121,10 @@ class MetadataTests: XCTestCase {
continue
}
switch formatter.tokens[index] {
case let .identifier(fn) where [
"spaceEquivalentToWidth",
"spaceEquivalentToTokens",
"tokenLength",
"lineLength",
].contains(fn):
case .identifier("spaceEquivalentToWidth"),
.identifier("spaceEquivalentToTokens"):
referencedOptions += [.indentation, .tabWidth, .smartTabs]
case .identifier("tokenLength"), .identifier("lineLength"):
referencedOptions += [.indentation, .tabWidth]
case .identifier("isCommentedCode"):
referencedOptions.append(.indentation)
@@ -134,8 +132,9 @@ class MetadataTests: XCTestCase {
referencedOptions.append(.lineBreak)
case .identifier("wrapCollectionsAndArguments"):
referencedOptions += [
.wrapArguments, .wrapParameters, .wrapCollections, .closingParen,
.indentation, .truncateBlankLines, .lineBreak, .tabWidth, .maxWidth,
.wrapArguments, .wrapParameters, .wrapCollections,
.closingParen, .lineBreak, .truncateBlankLines,
.indentation, .tabWidth, .maxWidth, .smartTabs,
]
case .identifier("indexWhereLineShouldWrapInLine"):
referencedOptions.append(.noWrapOperators)
+48 -8
View File
@@ -3251,7 +3251,16 @@ class RulesTests: XCTestCase {
// indent with tabs
func testTabIndentWrappedTuple() {
func testTabIndentWrappedTupleWithSmartTabs() {
let input = """
let foo = (bar: Int,
baz: Int)
"""
let options = FormatOptions(indent: "\t", tabWidth: 2, smartTabs: true)
testFormatting(for: input, rule: FormatRules.indent, options: options)
}
func testTabIndentWrappedTupleWithoutSmartTabs() {
let input = """
let foo = (bar: Int,
baz: Int)
@@ -3260,11 +3269,30 @@ class RulesTests: XCTestCase {
let foo = (bar: Int,
\t\t\t\t\t baz: Int)
"""
let options = FormatOptions(indent: "\t", tabWidth: 2)
let options = FormatOptions(indent: "\t", tabWidth: 2, smartTabs: false)
testFormatting(for: input, output, rule: FormatRules.indent, options: options)
}
func testTabIndentCase() {
func testTabIndentCaseWithSmartTabs() {
let input = """
switch x {
case .foo,
.bar:
break
}
"""
let output = """
switch x {
case .foo,
.bar:
\tbreak
}
"""
let options = FormatOptions(indent: "\t", tabWidth: 2, smartTabs: true)
testFormatting(for: input, output, rule: FormatRules.indent, options: options)
}
func testTabIndentCaseWithoutSmartTabs() {
let input = """
switch x {
case .foo,
@@ -3279,11 +3307,11 @@ class RulesTests: XCTestCase {
\tbreak
}
"""
let options = FormatOptions(indent: "\t", tabWidth: 2)
let options = FormatOptions(indent: "\t", tabWidth: 2, smartTabs: false)
testFormatting(for: input, output, rule: FormatRules.indent, options: options)
}
func testTabIndentCase2() {
func testTabIndentCaseWithoutSmartTabs2() {
let input = """
switch x {
case .foo,
@@ -3298,7 +3326,8 @@ class RulesTests: XCTestCase {
\t\tbreak
}
"""
let options = FormatOptions(indent: "\t", indentCase: true, tabWidth: 2)
let options = FormatOptions(indent: "\t", indentCase: true,
tabWidth: 2, smartTabs: false)
testFormatting(for: input, output, rule: FormatRules.indent, options: options)
}
@@ -9153,7 +9182,17 @@ class RulesTests: XCTestCase {
// MARK: indent with tabs
func testTabIndentWrappedFunction() {
func testTabIndentWrappedFunctionWithSmartTabs() {
let input = """
func foo(bar: Int,
baz: Int) {}
"""
let options = FormatOptions(indent: "\t", wrapParameters: .afterFirst, tabWidth: 2)
testFormatting(for: input, rule: FormatRules.wrapArguments, options: options,
exclude: ["unusedArguments"])
}
func testTabIndentWrappedFunctionWithoutSmartTabs() {
let input = """
func foo(bar: Int,
baz: Int) {}
@@ -9162,7 +9201,8 @@ class RulesTests: XCTestCase {
func foo(bar: Int,
\t\t\t\t baz: Int) {}
"""
let options = FormatOptions(indent: "\t", wrapParameters: .afterFirst, tabWidth: 2)
let options = FormatOptions(indent: "\t", wrapParameters: .afterFirst,
tabWidth: 2, smartTabs: false)
testFormatting(for: input, output, rule: FormatRules.wrapArguments, options: options,
exclude: ["unusedArguments"])
}
+7 -4
View File
@@ -1680,10 +1680,13 @@ extension RulesTests {
("testSwitchWrappedEnumCaseIndentingVariant2", testSwitchWrappedEnumCaseIndentingVariant2),
("testSwitchWrappedEnumCaseIsIndenting", testSwitchWrappedEnumCaseIsIndenting),
("testSwitchWrappedEnumCaseWithIndentCaseTrue", testSwitchWrappedEnumCaseWithIndentCaseTrue),
("testTabIndentCase", testTabIndentCase),
("testTabIndentCase2", testTabIndentCase2),
("testTabIndentWrappedFunction", testTabIndentWrappedFunction),
("testTabIndentWrappedTuple", testTabIndentWrappedTuple),
("testTabIndentCaseWithoutSmartTabs", testTabIndentCaseWithoutSmartTabs),
("testTabIndentCaseWithoutSmartTabs2", testTabIndentCaseWithoutSmartTabs2),
("testTabIndentCaseWithSmartTabs", testTabIndentCaseWithSmartTabs),
("testTabIndentWrappedFunctionWithoutSmartTabs", testTabIndentWrappedFunctionWithoutSmartTabs),
("testTabIndentWrappedFunctionWithSmartTabs", testTabIndentWrappedFunctionWithSmartTabs),
("testTabIndentWrappedTupleWithoutSmartTabs", testTabIndentWrappedTupleWithoutSmartTabs),
("testTabIndentWrappedTupleWithSmartTabs", testTabIndentWrappedTupleWithSmartTabs),
("testTernaryCountEqualsZero", testTernaryCountEqualsZero),
("testTernaryCountNotEqualToZero", testTernaryCountNotEqualToZero),
("testTestableImportIsNotWrapped", testTestableImportIsNotWrapped),