Skip to content

Commit

Permalink
Add LibraryContentProvider to file_types_order rule (realm#4209)
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlborn authored Sep 19, 2022
1 parent 345a904 commit f3d367f
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 30 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
* Include the configured `bind_identifier` in `self_binding` violation
messages.
[JP Simard](https://github.com/jpims)

* Add `library_content_provider` file type to `file_types_order` rule
to allow `LibraryContentProvider` to be ordered independent from `main_type`.
[dahlborn](https://github.com/dahlborn)

#### Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ enum FileType: String {
case mainType = "main_type"
case `extension` = "extension"
case previewProvider = "preview_provider"
case libraryContentProvider = "library_content_provider"
}

public struct FileTypesOrderConfiguration: RuleConfiguration, Equatable {
Expand All @@ -11,7 +12,8 @@ public struct FileTypesOrderConfiguration: RuleConfiguration, Equatable {
[.supportingType],
[.mainType],
[.extension],
[.previewProvider]
[.previewProvider],
[.libraryContentProvider]
]

public var consoleDescription: String {
Expand Down
75 changes: 49 additions & 26 deletions Source/SwiftLintFramework/Rules/Style/FileTypesOrderRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
// swiftlint:disable:next function_body_length
public func validate(file: SwiftLintFile) -> [StyleViolation] {
guard let mainTypeSubstructure = mainTypeSubstructure(in: file),
let mainTypeSubstuctureOffset = mainTypeSubstructure.offset else { return [] }
let mainTypeSubstuctureOffset = mainTypeSubstructure.offset else { return [] }

let extensionsSubstructures = self.extensionsSubstructures(
in: file,
Expand All @@ -32,25 +32,28 @@ public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
mainTypeSubstructure: mainTypeSubstructure
)

let previewProviderSubstructures = self.previewProviderSubstructures(in: file)
let previewProviderSubstructures = self.substructures(
in: file,
withInheritedType: "PreviewProvider"
)

let mainTypeOffset: [FileTypeOffset] = [(.mainType, mainTypeSubstuctureOffset)]
let extensionOffsets: [FileTypeOffset] = extensionsSubstructures.compactMap { substructure in
guard let offset = substructure.offset else { return nil }
return (.extension, offset)
}
let libraryContentSubstructures = self.substructures(
in: file,
withInheritedType: "LibraryContentProvider"
)

let supportingTypeOffsets: [FileTypeOffset] = supportingTypesSubstructures.compactMap { substructure in
guard let offset = substructure.offset else { return nil }
return (.supportingType, offset)
}
let mainTypeOffset: [FileTypeOffset] = [(.mainType, mainTypeSubstuctureOffset)]
let extensionOffsets: [FileTypeOffset] = extensionsSubstructures.offsets(for: .extension)
let supportingTypeOffsets: [FileTypeOffset] = supportingTypesSubstructures.offsets(for: .supportingType)
let previewProviderOffsets: [FileTypeOffset] = previewProviderSubstructures.offsets(for: .previewProvider)
let libraryContentOffsets: [FileTypeOffset] = libraryContentSubstructures.offsets(for: .libraryContentProvider)

let previewProviderOffsets: [FileTypeOffset] = previewProviderSubstructures.compactMap { substructure in
guard let offset = substructure.offset else { return nil }
return (.previewProvider, offset)
}
let allOffsets = mainTypeOffset
+ extensionOffsets
+ supportingTypeOffsets
+ previewProviderOffsets
+ libraryContentOffsets

let allOffsets = mainTypeOffset + extensionOffsets + supportingTypeOffsets + previewProviderOffsets
let orderedFileTypeOffsets = allOffsets.sorted { lhs, rhs in lhs.offset < rhs.offset }

var violations = [StyleViolation]()
Expand Down Expand Up @@ -97,8 +100,8 @@ public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
let dict = file.structureDictionary
return dict.substructure.filter { substructure in
guard let kind = substructure.kind else { return false }
return substructure.offset != mainTypeSubstructure.offset &&
kind.contains(SwiftDeclarationKind.extension.rawValue)
return substructure.offset != mainTypeSubstructure.offset
&& kind.contains(SwiftDeclarationKind.extension.rawValue)
}
}

Expand All @@ -112,16 +115,19 @@ public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {
let dict = file.structureDictionary
return dict.substructure.filter { substructure in
guard let declarationKind = substructure.declarationKind else { return false }
guard !substructure.inheritedTypes.contains("PreviewProvider") else { return false }
guard !substructure.hasExcludedInheritedType else { return false }

return substructure.offset != mainTypeSubstructure.offset &&
supportingTypeKinds.contains(declarationKind)
return substructure.offset != mainTypeSubstructure.offset
&& supportingTypeKinds.contains(declarationKind)
}
}

private func previewProviderSubstructures(in file: SwiftLintFile) -> [SourceKittenDictionary] {
return file.structureDictionary.substructure.filter { substructure in
return substructure.inheritedTypes.contains("PreviewProvider")
private func substructures(
in file: SwiftLintFile,
withInheritedType inheritedType: String
) -> [SourceKittenDictionary] {
file.structureDictionary.substructure.filter { substructure in
substructure.inheritedTypes.contains(inheritedType)
}
}

Expand All @@ -147,16 +153,33 @@ public struct FileTypesOrderRule: ConfigurationProviderRule, OptInRule {

let priorityKindSubstructures = dict.substructure.filter { substructure in
guard let kind = substructure.declarationKind else { return false }
guard !substructure.inheritedTypes.contains("PreviewProvider") else { return false }
guard !substructure.hasExcludedInheritedType else { return false }

return priorityKinds.contains(kind)
}

let substructuresSortedByBodyLength = priorityKindSubstructures.sorted { lhs, rhs in
return (lhs.bodyLength ?? 0) > (rhs.bodyLength ?? 0)
(lhs.bodyLength ?? 0) > (rhs.bodyLength ?? 0)
}

// specify class, enum or struct with longest body as main type
return substructuresSortedByBodyLength.first
}
}

private extension SourceKittenDictionary {
var hasExcludedInheritedType: Bool {
self.inheritedTypes.contains { inheritedType in
inheritedType == "PreviewProvider" || inheritedType == "LibraryContentProvider"
}
}
}

private extension Array where Element == SourceKittenDictionary {
func offsets(for fileType: FileType) -> [(fileType: FileType, offset: ByteCount)] {
self.compactMap { substructure in
guard let offset = substructure.offset else { return nil }
return (fileType, offset)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// swiftlint:disable:next type_body_length
internal struct FileTypesOrderRuleExamples {
static let defaultOrderParts = [
"""
Expand Down Expand Up @@ -120,15 +121,24 @@ internal struct FileTypesOrderRuleExamples {
}
"""),
Example("""
// Main Type
struct ContentView: View {
var body: some View {
Text("Hello, World!")
}
}
// Preview Provider
struct ContentView_Previews: PreviewProvider {
static var previews: some View { ContentView() }
}
// Library Content Provider
struct ContentView_LibraryContent: LibraryContentProvider {
var views: [LibraryItem] {
LibraryItem(ContentView())
}
}
""")
]

Expand Down Expand Up @@ -188,10 +198,31 @@ internal struct FileTypesOrderRuleExamples {
"""),
Example("""
// Preview Provider
↓struct ContentView_Previews: PreviewProvider {}
↓struct ContentView_Previews: PreviewProvider {
static var previews: some View { ContentView() }
}
// Main Type
struct ContentView: View {}
struct ContentView: View {
var body: some View {
Text("Hello, World!")
}
}
"""),
Example("""
// Library Content Provider
↓struct ContentView_LibraryContent: LibraryContentProvider {
var views: [LibraryItem] {
LibraryItem(ContentView())
}
}
// Main Type
struct ContentView: View {
var body: some View {
Text("Hello, World!")
}
}
""")
]
}
15 changes: 14 additions & 1 deletion Tests/SwiftLintFrameworkTests/FileTypesOrderRuleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ class FileTypesOrderRuleTests: XCTestCase {
struct ContentView_Previews: PreviewProvider {
static var previews: some View { ContentView() }
}
"""),
Example("""
↓struct ContentView: View {
var body: some View {
Text("Hello, World!")
}
}
struct ContentView_LibraryContent: LibraryContentProvider {
var views: [LibraryItem] {
LibraryItem(ContentView())
}
}
""")
]

Expand All @@ -65,7 +78,7 @@ class FileTypesOrderRuleTests: XCTestCase {
verifyRule(
reversedOrderDescription,
ruleConfiguration: [
"order": ["preview_provider", "extension", "main_type", "supporting_type"]
"order": ["library_content_provider", "preview_provider", "extension", "main_type", "supporting_type"]
]
)
}
Expand Down

0 comments on commit f3d367f

Please sign in to comment.