// // GenericExtensions.swift // SwiftFormat // // Created by Cal Stephens on 7/18/22. // Copyright © 2024 Nick Lockwood. All rights reserved. // import Foundation public extension FormatRule { static let genericExtensions = FormatRule( help: """ Use angle brackets (`extension Array`) for generic type extensions instead of type constraints (`extension Array where Element == Foo`). """, options: ["generic-types"] ) { formatter in formatter.forEach(.keyword("extension")) { extensionIndex, _ in guard // Angle brackets syntax in extensions is only supported in Swift 5.7+ formatter.options.swiftVersion >= "5.7", let typeNameIndex = formatter.index(of: .nonSpaceOrCommentOrLinebreak, after: extensionIndex), let extendedType = formatter.token(at: typeNameIndex)?.string, // If there's already an open angle bracket after the generic type name // then the extension is already using the target syntax, so there's // no work to do formatter.next(.nonSpaceOrCommentOrLinebreak, after: typeNameIndex) != .startOfScope("<"), let openBraceIndex = formatter.index(of: .startOfScope("{"), after: typeNameIndex), let whereIndex = formatter.index(of: .keyword("where"), after: typeNameIndex), whereIndex < openBraceIndex else { return } // Prepopulate a `Self` generic type, which is implicitly present in extension definitions let selfType = Formatter.GenericType( name: "Self", definitionSourceRange: typeNameIndex ... typeNameIndex, conformances: [ Formatter.GenericType.GenericConformance( name: extendedType, typeName: "Self", type: .concreteType, sourceRange: typeNameIndex ... typeNameIndex ), ] ) var genericTypes = [selfType] // Parse the generic constraints in the where clause formatter.parseGenericTypes( from: whereIndex, into: &genericTypes, qualifyGenericTypeName: { genericTypeName in // In an extension all types implicitly refer to `Self`. // For example, `Element == Foo` is actually fully-qualified as // `Self.Element == Foo`. Using the fully-qualified `Self.Element` name // here makes it so the generic constraint is populated as a child // of `selfType`. if !genericTypeName.hasPrefix("Self.") { return "Self." + genericTypeName } else { return genericTypeName } } ) var knownGenericTypes: [(name: String, genericTypes: [String])] = [ (name: "Collection", genericTypes: ["Element"]), (name: "Sequence", genericTypes: ["Element"]), (name: "Array", genericTypes: ["Element"]), (name: "Set", genericTypes: ["Element"]), (name: "Dictionary", genericTypes: ["Key", "Value"]), (name: "Optional", genericTypes: ["Wrapped"]), ] // Users can provide additional generic types via the `generictypes` option for userProvidedType in formatter.options.genericTypes.components(separatedBy: ";") { guard let openAngleBracket = userProvidedType.firstIndex(of: "<"), let closeAngleBracket = userProvidedType.firstIndex(of: ">") else { continue } let typeName = String(userProvidedType[.. {} + extension Optional {} + extension Dictionary {} + extension Collection {} // With `typeSugar` also enabled: - extension Array where Element == Foo {} - extension Optional where Wrapped == Foo {} - extension Dictionary where Key == Foo, Value == Bar {} + extension [Foo] {} + extension Foo? {} + extension [Key: Value] {} // Also supports user-defined types! - extension LinkedList where Element == Foo {} - extension Reducer where - State == FooState, - Action == FooAction, - Environment == FooEnvironment {} + extension LinkedList {} + extension Reducer {} ``` """ } }