// // Options.swift // SwiftFormat // // Created by Nick Lockwood on 21/10/2016. // Copyright 2016 Nick Lockwood // // Distributed under the permissive MIT license // Get the latest version from here: // // https://github.com/nicklockwood/SwiftFormat // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // import Foundation /// The indenting mode to use for #if/#endif statements public enum IndentMode: String, CaseIterable { case indent case noIndent = "no-indent" case preserve case outdent public init?(rawValue: String) { switch rawValue { case "indent": self = .indent case "no-indent", "noindent": self = .noIndent case "preserve": self = .preserve case "outdent": self = .outdent default: return nil } } } /// Wrap mode for arguments public enum WrapMode: String, CaseIterable { case beforeFirst = "before-first" case afterFirst = "after-first" case preserve case disabled case `default` public init?(rawValue: String) { switch rawValue { case "before-first", "beforefirst": self = .beforeFirst case "after-first", "afterfirst": self = .afterFirst case "preserve": self = .preserve case "disabled": self = .disabled case "default": self = .default default: return nil } } } /// Wrap enum cases public enum WrapEnumCases: String, CaseIterable { case always case withValues = "with-values" } /// Argument type for stripping public enum ArgumentStrippingMode: String, CaseIterable { case unnamedOnly = "unnamed-only" case closureOnly = "closure-only" case all = "always" } /// Wrap mode for @ attributes public enum AttributeMode: String, CaseIterable { case prevLine = "prev-line" case sameLine = "same-line" case preserve } /// Where to place the else or catch in an if/else or do/catch statement public enum ElsePosition: String, CaseIterable { case sameLine = "same-line" case nextLine = "next-line" } /// Where to place the else in a guard statement public enum GuardElsePosition: String, CaseIterable { case sameLine = "same-line" case nextLine = "next-line" case auto } /// Where to place the access control keyword of an extension public enum ExtensionACLPlacement: String, CaseIterable { case onExtension = "on-extension" case onDeclarations = "on-declarations" } /// Wrapping behavior for the return type of a function declaration public enum WrapReturnType: String, CaseIterable { case preserve /// `-> ReturnType` is wrapped to the line after the closing paren /// if the function signature spans multiple lines case ifMultiline = "if-multiline" /// `-> ReturnType` is never wrapped, and always include on the same line as the closing paren case never } /// Wrapping behavior for effects (`async`, `throws`) public enum WrapEffects: String, CaseIterable { case preserve /// `async` and `throws` are wrapped to the line after the closing paren /// if the function signature spans multiple lines case ifMultiline = "if-multiline" /// `async` and `throws` are never wrapped, and are always included on the same line as the closing paren case never } /// Argument type for whether explicit or inferred properties are preferred public enum PropertyTypes: String, CaseIterable { /// Preserves the type as a part of the property definition: /// `let foo: Foo = Foo()` becomes `let foo: Foo = .init()` case explicit /// Uses type inference to omit the type in the property definition: /// `let foo: Foo = Foo()` becomes `let foo = Foo()` case inferred /// Uses `.inferred` for properties within local scopes (method bodies, etc.), /// but `.explicit` for globals and properties within types. /// - This is because type checking for globals and type properties /// using inferred types can be more expensive. /// https://twitter.com/uint_min/status/1441448033988722691?s=21 case inferLocalsOnly = "infer-locals-only" } /// Argument type for empty brace spacing behavior public enum EmptyBracesSpacing: String, CaseIterable { case spaced case noSpace = "no-space" case linebreak } /// Wrapping behavior for multi-line ternary operators public enum TernaryOperatorWrapMode: String, CaseIterable { /// Wraps ternary operators using the default `wrap` behavior, /// which performs the minimum amount of wrapping necessary. case `default` /// Wraps long / multi-line ternary operators before each of the component operators case beforeOperators = "before-operators" } public enum StringInterpolationWrapMode: String, CaseIterable { /// Wraps string interpolation if necessary based on the max line length case `default` /// Preserve existing wrapping for string interpolations, /// and don't insert line breaks. case preserve } /// Whether or not to remove `-> Void` from closures public enum ClosureVoidReturn: String, CaseIterable { case remove case preserve } /// Format for Swift Testing test case / suite names public enum SwiftTestingNameFormat: String, CaseIterable { case preserve case rawIdentifiers = "raw-identifiers" case standardIdentifiers = "standard-identifiers" } public enum TrailingCommas: String, CaseIterable { case never case always case collectionsOnly = "collections-only" case multiElementLists = "multi-element-lists" } /// Whether to insert, remove, or preserve spaces around operators public enum OperatorSpacingMode: String, CaseIterable { case insert = "spaced" case remove = "no-space" case preserve } /// Version number wrapper public struct Version: RawRepresentable, Comparable, ExpressibleByStringLiteral, CustomStringConvertible { public let rawValue: String public static let undefined = Version(rawValue: "0")! public init(stringLiteral value: String) { self.init(rawValue: value)! } public init?(rawValue: String) { let rawValue = rawValue.trimmingCharacters(in: .whitespacesAndNewlines) guard CharacterSet.decimalDigits.contains(rawValue.unicodeScalars.first ?? " ") else { return nil } self.rawValue = rawValue } public static func < (lhs: Version, rhs: Version) -> Bool { lhs.rawValue.compare( rhs.rawValue, options: .numeric, locale: Locale(identifier: "en_US") ) == .orderedAscending } public var description: String { rawValue } } public enum ReplacementKey: String, CaseIterable { case fileName = "file" case currentYear = "year" case createdDate = "created" case createdYear = "created.year" case author case authorName = "author.name" case authorEmail = "author.email" var placeholder: String { "{\(rawValue)}" } } /// Argument type for stripping public enum FileHeaderMode: Equatable, RawRepresentable, ExpressibleByStringLiteral { case ignore case replace(String) public init(stringLiteral value: String) { self.init(rawValue: value)! } public init?(rawValue: String) { switch rawValue.lowercased() { case "ignore", "keep", "preserve": self = .ignore case "strip", "": self = .replace("") default: // Normalize the header let header = rawValue.trimmingCharacters(in: .whitespacesAndNewlines) let isMultiline = header.hasPrefix("/*") var lines = header.components(separatedBy: "\\n") lines = lines.map { var line = $0 if !isMultiline, !line.hasPrefix("//") { line = "//\(line.isEmpty ? "" : " ")\(line)" } return line } while lines.last?.isEmpty == true { lines.removeLast() } self = .replace(lines.joined(separator: "\n")) } } public var rawValue: String { switch self { case .ignore: return "ignore" case let .replace(string): return string.isEmpty ? "strip" : string.replacingOccurrences(of: "\n", with: "\\n") } } var needsGitInfo: Bool { guard case let .replace(str) = self else { return false } let keys: [ReplacementKey] = [.createdDate, .createdYear, .author, .authorName, .authorEmail] return keys.contains(where: { str.contains($0.placeholder) }) } } public struct ReplacementOptions: CustomStringConvertible { var dateFormat: DateFormat var timeZone: FormatTimeZone init(dateFormat: DateFormat, timeZone: FormatTimeZone) { self.dateFormat = dateFormat self.timeZone = timeZone } init(_ options: FormatOptions) { self.init(dateFormat: options.dateFormat, timeZone: options.timeZone) } public var description: String { "\(dateFormat)@\(timeZone)" } } public enum ReplacementType: Equatable, CustomStringConvertible { case constant(String) case dynamic((FileInfo, ReplacementOptions) -> String?) init?(_ value: String?) { guard let val = value else { return nil } self = .constant(val) } public static func == (lhs: ReplacementType, rhs: ReplacementType) -> Bool { switch (lhs, rhs) { case let (.constant(lhsVal), .constant(rhsVal)): return lhsVal == rhsVal case let (.dynamic(lhsClosure), .dynamic(rhsClosure)): return lhsClosure as AnyObject === rhsClosure as AnyObject default: return false } } public func resolve(_ info: FileInfo, _ options: ReplacementOptions) -> String? { switch self { case let .constant(value): return value case let .dynamic(fn): return fn(info, options) } } public var description: String { switch self { case let .constant(value): return value case .dynamic: return "dynamic" } } } /// File info, used for constructing header comments public struct FileInfo: Equatable, CustomStringConvertible { static var defaultReplacements: [ReplacementKey: ReplacementType] = [ .createdDate: .dynamic { info, options in info.creationDate?.format(with: options.dateFormat, timeZone: options.timeZone) }, .createdYear: .dynamic { info, _ in info.creationDate?.yearString }, .currentYear: .constant(Date.currentYear), ] let filePath: String? var creationDate: Date? var replacements: [ReplacementKey: ReplacementType] = Self.defaultReplacements var fileName: String? { filePath.map { URL(fileURLWithPath: $0).lastPathComponent } } public init( filePath: String? = nil, creationDate: Date? = nil, replacements: [ReplacementKey: ReplacementType] = [:] ) { self.filePath = filePath self.creationDate = creationDate self.replacements[.fileName] = fileName.map { .constant($0) } self.replacements.merge(replacements, uniquingKeysWith: { $1 }) } public var description: String { replacements .sorted(by: { $0.key.rawValue < $1.key.rawValue }) .map { "\($0)=\($1)" } .joined(separator: ";") } public func hasReplacement(for key: ReplacementKey, options: FormatOptions) -> Bool { replacements[key]?.resolve(self, ReplacementOptions(options)) != nil } } /// Grouping for numeric literals public enum Grouping: Equatable, RawRepresentable, CustomStringConvertible { case ignore case none case group(Int, Int) public init?(rawValue: String) { switch rawValue { case "ignore": self = .ignore case "none": self = .none default: let parts = rawValue.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } guard (1 ... 2).contains(parts.count), let group = parts.first.flatMap(Int.init), let threshold = parts.last.flatMap(Int.init) else { return nil } self = (group == 0) ? .none : .group(group, threshold) } } public var rawValue: String { switch self { case .ignore: return "ignore" case .none: return "none" case let .group(group, threshold): return "\(group),\(threshold)" } } public var description: String { rawValue } } /// Individual import sorting/grouping options, combined as a Set public enum ImportGrouping: String, CaseIterable, Hashable { case alpha case length case accessControl = "access-control" case testableFirst = "testable-first" case testableLast = "testable-last" public init?(rawValue: String) { switch rawValue { case "alphabetized", "alphabetical", "alpha": self = .alpha case "length": self = .length case "access-control": self = .accessControl case "testable-first", "testable-top": self = .testableFirst case "testable-last", "testable-bottom": self = .testableLast default: return nil } } } /// Self insertion mode public enum SelfMode: String, CaseIterable { case insert case remove case initOnly = "init-only" } /// Optionals mode public enum OptionalsMode: String, CaseIterable { case preserveStructInits = "preserve-struct-inits" case exceptPropertiesDeprecated = "except-properties" case always } /// Argument type for yoda conditions public enum YodaMode: String, CaseIterable { case literalsOnly = "literals-only" case always } /// Argument type for asset literals public enum AssetLiteralWidth: String, CaseIterable { case actualWidth = "actual-width" case visualWidth = "visual-width" } /// Whether or not to mark types / extensions public enum MarkMode: String, CaseIterable { case always case never case ifNotEmpty = "if-not-empty" } /// Whether to convert types to enum public enum EnumNamespacesMode: String, CaseIterable { case always case structsOnly = "structs-only" } /// Whether to add spacing around a delimiter public enum DelimiterSpacing: String, CaseIterable { case spaced case spaceAfter = "space-after" case noSpace = "no-space" } /// Declaration organization mode public enum DeclarationOrganizationMode: String, CaseIterable { /// Organize declarations by visibility case visibility /// Organize declarations by type case type } /// Treatment of MARK comments in type bodies public enum TypeBodyMarks: String, CaseIterable { /// Preserve all existing MARK comments in type bodies case preserve /// Remove MARK comments that don't match expected visibility/declaration kind marks case remove } /// Whether to insert or remove blank lines from the start / end of type bodies public enum TypeBlankLines: String, CaseIterable { case remove case insert case preserve } /// Treatment of semicolons public enum SemicolonsMode: String, CaseIterable { case inlineOnly = "inline-only" case never } /// Format to use when printing dates public enum DateFormat: Equatable, RawRepresentable, CustomStringConvertible { case dayMonthYear case iso case monthDayYear case system case custom(String) public init?(rawValue: String) { switch rawValue { case "dmy": self = .dayMonthYear case "iso": self = .iso case "mdy": self = .monthDayYear case "system": self = .system default: self = .custom(rawValue) } } public var rawValue: String { switch self { case .dayMonthYear: return "dmy" case .iso: return "iso" case .monthDayYear: return "mdy" case .system: return "system" case let .custom(str): return str } } public var description: String { rawValue } } /// Timezone to use when printing dates public enum FormatTimeZone: Equatable, RawRepresentable, CustomStringConvertible { case system case abbreviation(String) case identifier(String) static let utcNames = ["utc", "gmt"] public init?(rawValue: String) { if Self.utcNames.contains(rawValue.lowercased()) { self = .identifier("UTC") } else if TimeZone.knownTimeZoneIdentifiers.contains(rawValue) { self = .identifier(rawValue) } else if TimeZone.abbreviationDictionary.keys.contains(rawValue) { self = .abbreviation(rawValue) } else if rawValue == Self.system.rawValue { self = .system } else { return nil } } public var rawValue: String { switch self { case .system: return "system" case let .abbreviation(abbreviation): return abbreviation case let .identifier(identifier): return identifier } } public var timeZone: TimeZone? { switch self { case .system: return TimeZone.current case let .abbreviation(abbreviation): return TimeZone(abbreviation: abbreviation) case let .identifier(identifier): return TimeZone(identifier: identifier) } } public var description: String { rawValue } } /// When initializing an optional value type, /// is it necessary to explicitly declare a default value public enum NilInitType: String, CaseIterable { /// Remove redundant `nil` if it is added as default value case remove /// Add `nil` as default if not explicitly declared case insert } /// Placement for closing paren in a function call or definition public enum ClosingParenPosition: String, CaseIterable { case balanced case sameLine = "same-line" case `default` } public enum SwiftUIPropertiesSortMode: String, CaseIterable { /// No sorting case none /// Sort alphabetically case alphabetize /// Group all properties of the same type in order of the first time each property appears case firstAppearanceSort = "first-appearance-sort" } public enum EquatableMacro: Equatable, RawRepresentable, CustomStringConvertible { /// No equatable macro case none /// The name and the module for the macro, e.g. `@Equatable,EquatableMacroLib` case macro(String, module: String) public init?(rawValue: String) { let components = rawValue.components(separatedBy: ",") if components.count == 2 { self = .macro(components[0], module: components[1]) } else if rawValue == "none" { self = .none } else { return nil } } public var rawValue: String { switch self { case .none: return "none" case let .macro(name, module: module): return "\(name),\(module)" } } public var description: String { rawValue } } public enum BlankLineAfterSwitchCase: String, CaseIterable { /// Always add blank lines after switch cases case always /// Add blank lines after multiline switch cases only case multilineOnly = "multiline-only" } public enum URLMacro: Equatable, RawRepresentable, CustomStringConvertible { /// No URL macro case none /// The name and the module for the macro, e.g. `#URL,URLFoundation` case macro(String, module: String) public init?(rawValue: String) { let components = rawValue.components(separatedBy: ",") if components.count == 2 { self = .macro(components[0], module: components[1]) } else if rawValue == "none" { self = .none } else { return nil } } public var rawValue: String { switch self { case .none: return "none" case let .macro(name, module: module): return "\(name),\(module)" } } public var description: String { rawValue } } /// Mode for preferring synthesized memberwise init for internal structs public enum PreferSynthesizedInitMode: Equatable, CustomStringConvertible { /// Never prefer synthesized init (default) case never /// Always prefer synthesized init for internal structs case always /// Prefer synthesized init only for structs conforming to specific protocols case conformances([String]) public init?(rawValue: String) { switch rawValue.lowercased() { case "never", "false": self = .never case "always", "true": self = .always default: // Parse as comma-separated list of conformances let conformances = rawValue.split(separator: ",").map { String($0).trimmingCharacters(in: .whitespaces) } guard !conformances.isEmpty, conformances.allSatisfy({ !$0.isEmpty }) else { return nil } self = .conformances(conformances) } } public var rawValue: String { switch self { case .never: return "never" case .always: return "always" case let .conformances(list): return list.joined(separator: ",") } } public var description: String { rawValue } } /// Configuration options for formatting. These aren't actually used by the /// Formatter class itself, but it makes them available to the format rules. public struct FormatOptions: CustomStringConvertible { public var lineAfterMarks: Bool public var indent: String public var linebreak: String public var semicolons: SemicolonsMode public var spaceAroundRangeOperators: OperatorSpacingMode public var spaceAroundOperatorDeclarations: OperatorSpacingMode public var useVoid: Bool public var indentCase: Bool public var trailingCommas: TrailingCommas public var truncateBlankLines: Bool public var insertBlankLines: Bool public var removeBlankLines: Bool public var allmanBraces: Bool public var fileHeader: FileHeaderMode public var ifdefIndent: IndentMode public var wrapArguments: WrapMode public var wrapParameters: WrapMode public var wrapCollections: WrapMode public var wrapTypealiases: WrapMode public var wrapEnumCases: WrapEnumCases public var closingParenPosition: ClosingParenPosition public var callSiteClosingParenPosition: ClosingParenPosition public var wrapReturnType: WrapReturnType public var wrapConditions: WrapMode public var wrapTernaryOperators: TernaryOperatorWrapMode public var wrapStringInterpolation: StringInterpolationWrapMode public var uppercaseHex: Bool public var uppercaseExponent: Bool public var decimalGrouping: Grouping public var binaryGrouping: Grouping public var octalGrouping: Grouping public var hexGrouping: Grouping public var fractionGrouping: Bool public var exponentGrouping: Bool public var hoistPatternLet: Bool public var stripUnusedArguments: ArgumentStrippingMode public var elsePosition: ElsePosition public var guardElsePosition: GuardElsePosition public var explicitSelf: SelfMode public var selfRequired: Set public var throwCapturing: Set public var asyncCapturing: Set public var experimentalRules: Bool public var importGrouping: Set public var trailingClosures: Set public var neverTrailing: Set public var xcodeIndentation: Bool public var tabWidth: Int public var maxWidth: Int public var smartTabs: Bool public var assetLiteralWidth: AssetLiteralWidth public var noSpaceOperators: Set public var noWrapOperators: Set public var modifierOrder: [String] public var shortOptionals: OptionalsMode public var funcAttributes: AttributeMode public var typeAttributes: AttributeMode public var varAttributes: AttributeMode public var storedVarAttributes: AttributeMode public var computedVarAttributes: AttributeMode public var complexAttributes: AttributeMode public var complexAttributesExceptions: Set public var markTypes: MarkMode public var typeMarkComment: String public var markExtensions: MarkMode public var extensionMarkComment: String public var groupedExtensionMarkComment: String public var markCategories: Bool public var categoryMarkComment: String public var beforeMarks: Set public var lifecycleMethods: Set public var organizeTypes: Set public var organizeClassThreshold: Int public var organizeStructThreshold: Int public var organizeEnumThreshold: Int public var organizeExtensionThreshold: Int public var markStructThreshold: Int public var markClassThreshold: Int public var markEnumThreshold: Int public var markExtensionThreshold: Int public var organizationMode: DeclarationOrganizationMode public var typeBodyMarks: TypeBodyMarks public var visibilityOrder: [String]? public var typeOrder: [String]? public var customVisibilityMarks: Set public var customTypeMarks: Set public var blankLineAfterSubgroups: Bool public var alphabeticallySortedDeclarationPatterns: Set public var swiftUIPropertiesSortMode: SwiftUIPropertiesSortMode public var yodaSwap: YodaMode public var extensionACLPlacement: ExtensionACLPlacement public var propertyTypes: PropertyTypes public var preservedPropertyTypes: Set public var inferredTypesInConditionalExpressions: Bool public var emptyBracesSpacing: EmptyBracesSpacing public var acronyms: Set public var preserveAcronyms: Set public var indentStrings: Bool public var closureVoidReturn: ClosureVoidReturn public var enumNamespaces: EnumNamespacesMode public var typeBlankLines: TypeBlankLines public var genericTypes: String public var useSomeAny: Bool public var wrapEffects: WrapEffects public var preserveAnonymousForEach: Bool public var preserveSingleLineForEach: Bool public var preserveDocComments: Bool public var conditionalAssignmentOnlyAfterNewProperties: Bool public var typeDelimiterSpacing: DelimiterSpacing public var initCoderNil: Bool public var dateFormat: DateFormat public var timeZone: FormatTimeZone public var nilInit: NilInitType public var preservedPrivateDeclarations: Set public var additionalXCTestSymbols: Set public var defaultTestSuiteAttributes: [String] public var equatableMacro: EquatableMacro public var urlMacro: URLMacro public var preferFileMacro: Bool public var lineBetweenConsecutiveGuards: Bool public var blankLineAfterSwitchCase: BlankLineAfterSwitchCase public var redundantThrows: RedundantEffectMode public var redundantAsync: RedundantEffectMode public var allowPartialWrapping: Bool public var preferSynthesizedInitForInternalStructs: PreferSynthesizedInitMode public var testCaseNameFormat: SwiftTestingNameFormat public var suiteNameFormat: SwiftTestingNameFormat public var testCaseAccessControl: Visibility /// Deprecated public var indentComments: Bool /// Doesn't really belong here, but hard to put elsewhere public var fragment: Bool public var ignoreConflictMarkers: Bool public var swiftVersion: Version public var languageMode: Version public var fileInfo: FileInfo public var markdownFiles: MarkdownFormattingMode public var timeout: TimeInterval /// Enabled rules - this is a hack used to allow rules to vary their behavior /// based on other rules being enabled. Do not rely on it in other contexts var enabledRules: Set = [] public static let `default` = FormatOptions() public init(lineAfterMarks: Bool = true, indent: String = " ", linebreak: String = "\n", semicolons: SemicolonsMode = .inlineOnly, spaceAroundRangeOperators: OperatorSpacingMode = .insert, spaceAroundOperatorDeclarations: OperatorSpacingMode = .insert, useVoid: Bool = true, indentCase: Bool = false, trailingCommas: TrailingCommas = .always, indentComments: Bool = true, truncateBlankLines: Bool = true, insertBlankLines: Bool = true, removeBlankLines: Bool = true, allmanBraces: Bool = false, fileHeader: FileHeaderMode = .ignore, ifdefIndent: IndentMode = .indent, wrapArguments: WrapMode = .preserve, wrapParameters: WrapMode = .default, wrapCollections: WrapMode = .preserve, wrapTypealiases: WrapMode = .preserve, wrapEnumCases: WrapEnumCases = .always, closingParenPosition: ClosingParenPosition = .balanced, callSiteClosingParenPosition: ClosingParenPosition = .default, wrapReturnType: WrapReturnType = .preserve, wrapConditions: WrapMode = .preserve, wrapTernaryOperators: TernaryOperatorWrapMode = .default, wrapStringInterpolation: StringInterpolationWrapMode = .default, uppercaseHex: Bool = true, uppercaseExponent: Bool = false, decimalGrouping: Grouping = .group(3, 6), binaryGrouping: Grouping = .group(4, 8), octalGrouping: Grouping = .group(4, 8), hexGrouping: Grouping = .group(4, 8), fractionGrouping: Bool = false, exponentGrouping: Bool = false, hoistPatternLet: Bool = true, stripUnusedArguments: ArgumentStrippingMode = .all, elsePosition: ElsePosition = .sameLine, guardElsePosition: GuardElsePosition = .auto, explicitSelf: SelfMode = .remove, selfRequired: Set = [], throwCapturing: Set = [], asyncCapturing: Set = [], experimentalRules: Bool = false, importGrouping: Set = [.accessControl, .alpha], trailingClosures: Set = [], neverTrailing: Set = [], xcodeIndentation: Bool = false, tabWidth: Int = 0, maxWidth: Int = 0, smartTabs: Bool = true, assetLiteralWidth: AssetLiteralWidth = .visualWidth, noSpaceOperators: Set = [], noWrapOperators: Set = [], modifierOrder: [String] = [], shortOptionals: OptionalsMode = .preserveStructInits, funcAttributes: AttributeMode = .preserve, typeAttributes: AttributeMode = .preserve, varAttributes: AttributeMode = .preserve, storedVarAttributes: AttributeMode = .preserve, computedVarAttributes: AttributeMode = .preserve, complexAttributes: AttributeMode = .preserve, complexAttributesExceptions: Set = [], markTypes: MarkMode = .always, typeMarkComment: String = "MARK: - %t", markExtensions: MarkMode = .always, extensionMarkComment: String = "MARK: - %t + %c", groupedExtensionMarkComment: String = "MARK: %c", markCategories: Bool = true, categoryMarkComment: String = "MARK: %c", beforeMarks: Set = [], lifecycleMethods: Set = [], organizeTypes: Set = ["class", "actor", "struct", "enum"], organizeClassThreshold: Int = 0, organizeStructThreshold: Int = 0, organizeEnumThreshold: Int = 0, organizeExtensionThreshold: Int = 0, markStructThreshold: Int = 0, markClassThreshold: Int = 0, markEnumThreshold: Int = 0, markExtensionThreshold: Int = 0, organizationMode: DeclarationOrganizationMode = .visibility, typeBodyMarks: TypeBodyMarks = .preserve, visibilityOrder: [String]? = nil, typeOrder: [String]? = nil, customVisibilityMarks: Set = [], customTypeMarks: Set = [], blankLineAfterSubgroups: Bool = true, alphabeticallySortedDeclarationPatterns: Set = [], swiftUIPropertiesSortMode: SwiftUIPropertiesSortMode = .none, yodaSwap: YodaMode = .always, extensionACLPlacement: ExtensionACLPlacement = .onExtension, propertyTypes: PropertyTypes = .inferLocalsOnly, preservedPropertyTypes: Set = ["Package"], inferredTypesInConditionalExpressions: Bool = false, emptyBracesSpacing: EmptyBracesSpacing = .noSpace, acronyms: Set = ["ID", "URL", "UUID"], preserveAcronyms: Set = [], indentStrings: Bool = false, closureVoidReturn: ClosureVoidReturn = .remove, enumNamespaces: EnumNamespacesMode = .always, typeBlankLines: TypeBlankLines = .remove, genericTypes: String = "", useSomeAny: Bool = true, wrapEffects: WrapEffects = .preserve, preserveAnonymousForEach: Bool = false, preserveSingleLineForEach: Bool = true, preserveDocComments: Bool = false, conditionalAssignmentOnlyAfterNewProperties: Bool = true, typeDelimiterSpacing: DelimiterSpacing = .spaceAfter, initCoderNil: Bool = false, dateFormat: DateFormat = .system, timeZone: FormatTimeZone = .system, nilInit: NilInitType = .remove, preservedPrivateDeclarations: Set = [], additionalXCTestSymbols: Set = [], defaultTestSuiteAttributes: [String] = [], equatableMacro: EquatableMacro = .none, urlMacro: URLMacro = .none, preferFileMacro: Bool = true, lineBetweenConsecutiveGuards: Bool = false, blankLineAfterSwitchCase: BlankLineAfterSwitchCase = .multilineOnly, redundantThrows: RedundantEffectMode = .testsOnly, redundantAsync: RedundantEffectMode = .testsOnly, allowPartialWrapping: Bool = true, preferSynthesizedInitForInternalStructs: PreferSynthesizedInitMode = .never, testCaseNameFormat: SwiftTestingNameFormat = .rawIdentifiers, suiteNameFormat: SwiftTestingNameFormat = .preserve, testCaseAccessControl: Visibility = .internal, // Doesn't really belong here, but hard to put elsewhere fragment: Bool = false, ignoreConflictMarkers: Bool = false, swiftVersion: Version = .undefined, languageMode: Version? = nil, fileInfo: FileInfo = FileInfo(), markdownFiles: MarkdownFormattingMode = .ignore, timeout: TimeInterval = 1) { self.lineAfterMarks = lineAfterMarks self.indent = indent self.linebreak = linebreak self.semicolons = semicolons self.spaceAroundRangeOperators = spaceAroundRangeOperators self.spaceAroundOperatorDeclarations = spaceAroundOperatorDeclarations self.useVoid = useVoid self.indentCase = indentCase self.trailingCommas = trailingCommas self.truncateBlankLines = truncateBlankLines self.insertBlankLines = insertBlankLines self.removeBlankLines = removeBlankLines self.allmanBraces = allmanBraces self.fileHeader = fileHeader self.ifdefIndent = ifdefIndent self.wrapArguments = wrapArguments self.wrapParameters = wrapParameters self.wrapCollections = wrapCollections self.wrapTypealiases = wrapTypealiases self.wrapEnumCases = wrapEnumCases self.closingParenPosition = closingParenPosition self.callSiteClosingParenPosition = callSiteClosingParenPosition self.wrapReturnType = wrapReturnType self.wrapConditions = wrapConditions self.wrapTernaryOperators = wrapTernaryOperators self.wrapStringInterpolation = wrapStringInterpolation self.uppercaseHex = uppercaseHex self.uppercaseExponent = uppercaseExponent self.decimalGrouping = decimalGrouping self.fractionGrouping = fractionGrouping self.exponentGrouping = exponentGrouping self.binaryGrouping = binaryGrouping self.octalGrouping = octalGrouping self.hexGrouping = hexGrouping self.hoistPatternLet = hoistPatternLet self.stripUnusedArguments = stripUnusedArguments self.elsePosition = elsePosition self.guardElsePosition = guardElsePosition self.explicitSelf = explicitSelf self.selfRequired = selfRequired self.throwCapturing = throwCapturing self.asyncCapturing = asyncCapturing self.experimentalRules = experimentalRules self.importGrouping = importGrouping self.trailingClosures = trailingClosures self.neverTrailing = neverTrailing self.xcodeIndentation = xcodeIndentation self.tabWidth = tabWidth self.maxWidth = maxWidth self.smartTabs = smartTabs self.assetLiteralWidth = assetLiteralWidth self.noSpaceOperators = noSpaceOperators self.noWrapOperators = noWrapOperators self.modifierOrder = modifierOrder self.shortOptionals = shortOptionals self.funcAttributes = funcAttributes self.typeAttributes = typeAttributes self.varAttributes = varAttributes self.storedVarAttributes = storedVarAttributes self.computedVarAttributes = computedVarAttributes self.complexAttributes = complexAttributes self.complexAttributesExceptions = complexAttributesExceptions self.markTypes = markTypes self.typeMarkComment = typeMarkComment self.markExtensions = markExtensions self.extensionMarkComment = extensionMarkComment self.groupedExtensionMarkComment = groupedExtensionMarkComment self.markCategories = markCategories self.categoryMarkComment = categoryMarkComment self.beforeMarks = beforeMarks self.lifecycleMethods = lifecycleMethods self.organizeTypes = organizeTypes self.organizeClassThreshold = organizeClassThreshold self.organizeStructThreshold = organizeStructThreshold self.organizeEnumThreshold = organizeEnumThreshold self.organizeExtensionThreshold = organizeExtensionThreshold self.markStructThreshold = markStructThreshold self.markClassThreshold = markClassThreshold self.markEnumThreshold = markEnumThreshold self.markExtensionThreshold = markExtensionThreshold self.organizationMode = organizationMode self.typeBodyMarks = typeBodyMarks self.visibilityOrder = visibilityOrder self.typeOrder = typeOrder self.customVisibilityMarks = customVisibilityMarks self.customTypeMarks = customTypeMarks self.blankLineAfterSubgroups = blankLineAfterSubgroups self.alphabeticallySortedDeclarationPatterns = alphabeticallySortedDeclarationPatterns self.swiftUIPropertiesSortMode = swiftUIPropertiesSortMode self.yodaSwap = yodaSwap self.extensionACLPlacement = extensionACLPlacement self.propertyTypes = propertyTypes self.preservedPropertyTypes = preservedPropertyTypes self.inferredTypesInConditionalExpressions = inferredTypesInConditionalExpressions self.emptyBracesSpacing = emptyBracesSpacing self.acronyms = acronyms self.preserveAcronyms = preserveAcronyms self.indentStrings = indentStrings self.closureVoidReturn = closureVoidReturn self.enumNamespaces = enumNamespaces self.typeBlankLines = typeBlankLines self.genericTypes = genericTypes self.useSomeAny = useSomeAny self.wrapEffects = wrapEffects self.preserveAnonymousForEach = preserveAnonymousForEach self.preserveSingleLineForEach = preserveSingleLineForEach self.preserveDocComments = preserveDocComments self.conditionalAssignmentOnlyAfterNewProperties = conditionalAssignmentOnlyAfterNewProperties self.typeDelimiterSpacing = typeDelimiterSpacing self.initCoderNil = initCoderNil self.dateFormat = dateFormat self.timeZone = timeZone self.nilInit = nilInit self.preservedPrivateDeclarations = preservedPrivateDeclarations self.additionalXCTestSymbols = additionalXCTestSymbols self.defaultTestSuiteAttributes = defaultTestSuiteAttributes self.equatableMacro = equatableMacro self.urlMacro = urlMacro self.preferFileMacro = preferFileMacro self.lineBetweenConsecutiveGuards = lineBetweenConsecutiveGuards self.blankLineAfterSwitchCase = blankLineAfterSwitchCase self.redundantThrows = redundantThrows self.redundantAsync = redundantAsync self.allowPartialWrapping = allowPartialWrapping self.preferSynthesizedInitForInternalStructs = preferSynthesizedInitForInternalStructs self.testCaseNameFormat = testCaseNameFormat self.suiteNameFormat = suiteNameFormat self.testCaseAccessControl = testCaseAccessControl self.indentComments = indentComments self.fragment = fragment self.ignoreConflictMarkers = ignoreConflictMarkers self.swiftVersion = swiftVersion self.languageMode = languageMode ?? defaultLanguageMode(for: swiftVersion) self.fileInfo = fileInfo self.markdownFiles = markdownFiles self.timeout = timeout } public var useTabs: Bool { indent.first == "\t" } public var requiresFileInfo: Bool { let string = fileHeader.rawValue return string.contains("{created") || string.contains("{file") } public var allOptions: [String: Any] { let pairs = Mirror(reflecting: self).children.map { ($0!, $1) } var options = Dictionary(pairs, uniquingKeysWith: { $1 }) for key in ["fileInfo", "enabledRules", "timeout"] { // Special cases options[key] = nil } return options } public var description: String { let allowedCharacters = CharacterSet.newlines.inverted return Mirror(reflecting: self).children.compactMap { child in var value = child.value switch value { case let array as [String]: value = array.joined(separator: ",") case let set as Set: value = set.sorted().joined(separator: ",") case let set as Set: value = ImportGrouping.allCases.filter { set.contains($0) }.map(\.rawValue).joined(separator: ",") default: break } return "\(value);".addingPercentEncoding(withAllowedCharacters: allowedCharacters) }.joined() } } /// When to remove redundant `throws` / `async` effects public enum RedundantEffectMode: String, CaseIterable { /// Only remove redundant effects from test functions (default) case testsOnly = "tests-only" /// Remove redundant effects from all functions (can cause additional warnings / errors) case always } public enum MarkdownFormattingMode: String, CaseIterable { /// Swift code in markdown files is ignored (default) case ignore /// Errors in markdown code blocks are ignored case lenient /// Errors in markdown code blocks are reported case strict } /// File enumeration options public struct FileOptions { public var followSymlinks: Bool public var supportedFileExtensions: [String] public var excludedGlobs: [Glob] public var unexcludedGlobs: [Glob] public var minVersion: Version public static let `default` = FileOptions() public init(followSymlinks: Bool = false, supportedFileExtensions: [String] = ["swift", "md"], excludedGlobs: [Glob] = [], unexcludedGlobs: [Glob] = [], minVersion: Version = .undefined) { self.followSymlinks = followSymlinks self.supportedFileExtensions = supportedFileExtensions self.excludedGlobs = excludedGlobs self.unexcludedGlobs = unexcludedGlobs self.minVersion = minVersion } public func shouldSkipFile(_ inputURL: URL) -> Bool { let parts = inputURL.standardizedFileURL.path.components(separatedBy: "/") var path: String! var shouldSkip = false for part in parts { path = path.map { "\($0)/\(part)" } ?? part if !shouldSkip, excludedGlobs.contains(where: { $0.matches(path) }) { shouldSkip = true } if shouldSkip, unexcludedGlobs.contains(where: { $0.matches(path) }) { shouldSkip = false } } return shouldSkip } } /// All options public struct Options { public var fileOptions: FileOptions? public var formatOptions: FormatOptions? public var rules: Set? public var configURLs: [URL]? public var lint: Bool public var filterOptions: [Glob: [String: String]] public static let `default` = Options( fileOptions: .default, formatOptions: .default, rules: defaultRules, configURLs: nil, lint: false ) public init(fileOptions: FileOptions? = nil, formatOptions: FormatOptions? = nil, rules: Set? = nil, configURLs: [URL]? = nil, lint: Bool = false, filterOptions: [Glob: [String: String]] = [:]) { self.fileOptions = fileOptions self.formatOptions = formatOptions self.rules = rules self.configURLs = configURLs self.lint = lint self.filterOptions = filterOptions } public func shouldSkipFile(_ inputURL: URL) -> Bool { if inputURL.pathExtension == "md", (formatOptions ?? .default).markdownFiles == .ignore { return true } return fileOptions?.shouldSkipFile(inputURL) ?? false } }