Skip to content

Commit

Permalink
Document modified configurations used for examples
Browse files Browse the repository at this point in the history
  • Loading branch information
SimplyDanny committed Jan 4, 2024
1 parent 8040b9f commit 16b7bf2
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 43 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
[tonell-m](https://github.com/tonell-m)
[#5373](https://github.com/realm/SwiftLint/issues/5373)

* Add modified configurations to examples in rule documentation.
[SimplyDanny](https://github.com/SimplyDanny)

* Add new `one_declaration_per_file` rule that allows only a
single class/struct/enum/protocol declaration per file.
Extensions are an exception; more than one is allowed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@ private extension SwiftLintFile {
}

struct MissingDocsRule: OptInRule {
init() {
configuration = MissingDocsConfiguration()
}

typealias ConfigurationType = MissingDocsConfiguration

var configuration: MissingDocsConfiguration
var configuration = MissingDocsConfiguration()

static let description = RuleDescription(
identifier: "missing_docs",
Expand Down
36 changes: 23 additions & 13 deletions Source/SwiftLintCore/Documentation/RuleDocumentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ struct RuleDocumentation {
}
return content.joined(separator: "\n\n")
}

private func formattedCode(_ example: Example) -> String {
if let config = example.configuration, let configuredRule = try? ruleType.init(configuration: config) {
let configDescription = configuredRule.createConfigurationDescription(exclusiveOptions: Set(config.keys))
return """
```swift
\(example.code)
//
// \(configDescription.yaml().linesPrefixed(with: "// "))
//
```
"""
}
return """
```swift
\(example.code)
```
"""
}
}

private func h1(_ text: String) -> String { "# \(text)" }
Expand All @@ -67,23 +87,13 @@ private func detailsSummary(_ rule: some Rule) -> String {
* **Analyzer rule:** \(rule is any AnalyzerRule ? "Yes" : "No")
* **Minimum Swift compiler version:** \(type(of: rule).description.minSwiftVersion.rawValue)
"""
if rule.configurationDescription.hasContent {
let configurationTable = rule.configurationDescription.markdown()
.split(separator: "\n")
.joined(separator: "\n ")
let description = rule.createConfigurationDescription()
if description.hasContent {
return ruleDescription + """
* **Default configuration:**
\(configurationTable)
\(description.markdown().linesPrefixed(with: " "))
"""
}
return ruleDescription
}

private func formattedCode(_ example: Example) -> String {
return """
```swift
\(example.code)
```
"""
}
4 changes: 4 additions & 0 deletions Source/SwiftLintCore/Extensions/String+SwiftLint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,8 @@ public extension String {
.map { String(repeating: " ", count: spaces) + $0 }
.joined(separator: "\n")
}

func linesPrefixed(with prefix: Self) -> Self {
split(separator: "\n").joined(separator: "\n\(prefix)")
}
}
15 changes: 9 additions & 6 deletions Source/SwiftLintCore/Models/RuleConfigurationDescription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public protocol Documentable {
public struct RuleConfigurationDescription: Equatable {
fileprivate let options: [RuleConfigurationOption]

fileprivate init(options: [RuleConfigurationOption]) {
fileprivate init(options: [RuleConfigurationOption], exclusiveOptions: Set<String> = []) {
if options.contains(.noOptions) {
if options.count > 1 {
queuedFatalError(
Expand All @@ -39,15 +39,18 @@ public struct RuleConfigurationDescription: Equatable {
)
}
self.options = []
} else {
self.options = options.filter { $0.value != .empty }
return
}
let nonEmptyOptions = options.filter { $0.value != .empty }
self.options = exclusiveOptions.isEmpty
? nonEmptyOptions
: nonEmptyOptions.filter { exclusiveOptions.contains($0.key) }
}

static func from(configuration: some RuleConfiguration) -> Self {
static func from(configuration: some RuleConfiguration, exclusiveOptions: Set<String> = []) -> Self {
// Prefer custom descriptions.
if let customDescription = configuration.parameterDescription {
return customDescription
return Self(options: customDescription.options, exclusiveOptions: exclusiveOptions)
}
let options: [RuleConfigurationOption] = Mirror(reflecting: configuration).children
.compactMap { child -> RuleConfigurationDescription? in
Expand All @@ -69,7 +72,7 @@ public struct RuleConfigurationDescription: Equatable {
"""
)
}
return Self(options: options)
return Self(options: options, exclusiveOptions: exclusiveOptions)
}

func allowedKeys() -> [String] {
Expand Down
16 changes: 6 additions & 10 deletions Source/SwiftLintCore/Protocols/Rule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,12 @@ import SourceKittenFramework

/// An executable value that can identify issues (violations) in Swift source code.
public protocol Rule {
/// The rule's description type.
associatedtype Description: Documentable

/// The type of the configuration used to configure this rule.
associatedtype ConfigurationType: RuleConfiguration

/// A verbose description of many of this rule's properties.
static var description: RuleDescription { get }

/// A description of how this rule has been configured to run. It can be built using the annotated result builder.
@RuleConfigurationDescriptionBuilder
var configurationDescription: Description { get }

/// This rule's configuration.
var configuration: ConfigurationType { get set }

Expand All @@ -29,6 +22,9 @@ public protocol Rule {
/// - throws: Throws if the configuration didn't match the expected format.
init(configuration: Any) throws

/// Create a description of how this rule has been configured to run.
func createConfigurationDescription(exclusiveOptions: Set<String>) -> RuleConfigurationDescription

/// Executes the rule on a file and returns any violations to the rule's expectations.
///
/// - parameter file: The file for which to execute the rule.
Expand Down Expand Up @@ -102,11 +98,11 @@ public extension Rule {
/// The cache description which will be used to determine if a previous
/// cached value is still valid given the new cache value.
var cacheDescription: String {
(self as? any CacheDescriptionProvider)?.cacheDescription ?? configurationDescription.oneLiner()
(self as? any CacheDescriptionProvider)?.cacheDescription ?? createConfigurationDescription().oneLiner()
}

var configurationDescription: some Documentable {
RuleConfigurationDescription.from(configuration: configuration)
func createConfigurationDescription(exclusiveOptions: Set<String> = []) -> RuleConfigurationDescription {
RuleConfigurationDescription.from(configuration: configuration, exclusiveOptions: exclusiveOptions)
}
}

Expand Down
12 changes: 7 additions & 5 deletions Source/swiftlint/Commands/Rules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ extension SwiftLint {
}

print("\(description.consoleDescription)")
if rule.configurationDescription.hasContent {
let configDescription = rule.createConfigurationDescription()
if configDescription.hasContent {
print("\nConfiguration (YAML):\n")
print(" \(description.identifier):")
print(rule.configurationDescription.yaml().indent(by: 4))
print(configDescription.yaml().indent(by: 4))
}

guard description.triggeringExamples.isNotEmpty else { return }
Expand All @@ -85,9 +86,10 @@ extension SwiftLint {
}

private func printConfig(for rule: any Rule) {
if rule.configurationDescription.hasContent {
let configDescription = rule.createConfigurationDescription()
if configDescription.hasContent {
print("\(type(of: rule).identifier):")
print(rule.configurationDescription.yaml().indent(by: 2))
print(configDescription.yaml().indent(by: 2))
}
}

Expand Down Expand Up @@ -141,7 +143,7 @@ private extension TextTable {
ruleType.description.kind.rawValue,
(rule is any AnalyzerRule) ? "yes" : "no",
(rule is any SourceKitFreeRule) ? "no" : "yes",
truncate((defaultConfig ? rule : configuredRule ?? rule).configurationDescription.oneLiner())
truncate((defaultConfig ? rule : configuredRule ?? rule).createConfigurationDescription().oneLiner())
])
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,12 @@ extension ConfigurationTests {
)

XCTAssertEqual(
Set(configuration1.rulesWrapper.allRulesWrapped.map { $0.rule.configurationDescription.oneLiner() }),
Set(configuration2.rulesWrapper.allRulesWrapped.map { $0.rule.configurationDescription.oneLiner() })
Set(configuration1.rulesWrapper.allRulesWrapped.map {
$0.rule.createConfigurationDescription().oneLiner()
}),
Set(configuration2.rulesWrapper.allRulesWrapped.map {
$0.rule.createConfigurationDescription().oneLiner()
})
)

XCTAssertEqual(Set(configuration1.includedPaths), Set(configuration2.includedPaths))
Expand Down

0 comments on commit 16b7bf2

Please sign in to comment.