mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-05-17 10:30:35 +00:00
b94d3d80bf
Co-authored-by: calda <1811727+calda@users.noreply.github.com>
1344 lines
48 KiB
Swift
1344 lines
48 KiB
Swift
//
|
|
// 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
|
|
case consistent
|
|
}
|
|
|
|
/// 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<String>
|
|
public var throwCapturing: Set<String>
|
|
public var asyncCapturing: Set<String>
|
|
public var experimentalRules: Bool
|
|
public var importGrouping: Set<ImportGrouping>
|
|
public var trailingClosures: Set<String>
|
|
public var neverTrailing: Set<String>
|
|
public var xcodeIndentation: Bool
|
|
public var tabWidth: Int
|
|
public var maxWidth: Int
|
|
public var smartTabs: Bool
|
|
public var assetLiteralWidth: AssetLiteralWidth
|
|
public var noSpaceOperators: Set<String>
|
|
public var noWrapOperators: Set<String>
|
|
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<String>
|
|
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<String>
|
|
public var lifecycleMethods: Set<String>
|
|
public var organizeTypes: Set<String>
|
|
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<String>
|
|
public var customTypeMarks: Set<String>
|
|
public var blankLineAfterSubgroups: Bool
|
|
public var alphabeticallySortedDeclarationPatterns: Set<String>
|
|
public var swiftUIPropertiesSortMode: SwiftUIPropertiesSortMode
|
|
public var yodaSwap: YodaMode
|
|
public var extensionACLPlacement: ExtensionACLPlacement
|
|
public var propertyTypes: PropertyTypes
|
|
public var preservedPropertyTypes: Set<String>
|
|
public var inferredTypesInConditionalExpressions: Bool
|
|
public var emptyBracesSpacing: EmptyBracesSpacing
|
|
public var acronyms: Set<String>
|
|
public var preserveAcronyms: Set<String>
|
|
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<String>
|
|
public var additionalXCTestSymbols: Set<String>
|
|
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<String> = []
|
|
|
|
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<String> = [],
|
|
throwCapturing: Set<String> = [],
|
|
asyncCapturing: Set<String> = [],
|
|
experimentalRules: Bool = false,
|
|
importGrouping: Set<ImportGrouping> = [.accessControl, .alpha],
|
|
trailingClosures: Set<String> = [],
|
|
neverTrailing: Set<String> = [],
|
|
xcodeIndentation: Bool = false,
|
|
tabWidth: Int = 0,
|
|
maxWidth: Int = 0,
|
|
smartTabs: Bool = true,
|
|
assetLiteralWidth: AssetLiteralWidth = .visualWidth,
|
|
noSpaceOperators: Set<String> = [],
|
|
noWrapOperators: Set<String> = [],
|
|
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<String> = [],
|
|
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<String> = [],
|
|
lifecycleMethods: Set<String> = [],
|
|
organizeTypes: Set<String> = ["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<String> = [],
|
|
customTypeMarks: Set<String> = [],
|
|
blankLineAfterSubgroups: Bool = true,
|
|
alphabeticallySortedDeclarationPatterns: Set<String> = [],
|
|
swiftUIPropertiesSortMode: SwiftUIPropertiesSortMode = .none,
|
|
yodaSwap: YodaMode = .always,
|
|
extensionACLPlacement: ExtensionACLPlacement = .onExtension,
|
|
propertyTypes: PropertyTypes = .inferLocalsOnly,
|
|
preservedPropertyTypes: Set<String> = ["Package"],
|
|
inferredTypesInConditionalExpressions: Bool = false,
|
|
emptyBracesSpacing: EmptyBracesSpacing = .noSpace,
|
|
acronyms: Set<String> = ["ID", "URL", "UUID"],
|
|
preserveAcronyms: Set<String> = [],
|
|
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<String> = [],
|
|
additionalXCTestSymbols: Set<String> = [],
|
|
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<String>:
|
|
value = set.sorted().joined(separator: ",")
|
|
case let set as Set<ImportGrouping>:
|
|
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<String>?
|
|
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<String>? = 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
|
|
}
|
|
}
|