mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
Add --smarttabs option (enabled by default)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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: ","))")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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"])
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user