mirror of
https://github.com/realm/SwiftLint.git
synced 2026-06-06 20:18:40 +00:00
Add support for nested configuration
Original commit here: https://github.com/realm/SwiftLint/commit/9eef125245e43a8ff333d45abd13f9f0177d7258 Significant modifications made by @jpsim
This commit is contained in:
committed by
JP Simard
parent
32fde1e7b4
commit
97c1dcd0fa
@@ -43,10 +43,66 @@ extension Configuration {
|
||||
return self
|
||||
}
|
||||
|
||||
// Currently merge simply overrides the current configuration with the new configuration.
|
||||
// This requires that all configuration files be fully specified. In the future this should be
|
||||
// improved to do a more intelligent merge allowing for partial nested configurations.
|
||||
private struct HashableRule: Hashable {
|
||||
let rule: Rule
|
||||
|
||||
static func == (lhs: HashableRule, rhs: HashableRule) -> Bool {
|
||||
// Don't use `isEqualTo` in case its internal implementation changes from
|
||||
// using the identifier to something else, which could mess up with the `Set`
|
||||
return type(of: lhs.rule).description.identifier == type(of: rhs.rule).description.identifier
|
||||
}
|
||||
|
||||
var hashValue: Int {
|
||||
return type(of: rule).description.identifier.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
private func mergingRules(with configuration: Configuration) -> [Rule] {
|
||||
guard configuration.whitelistRules.isEmpty else {
|
||||
// Use an intermediate set to filter out duplicate rules when merging configurations
|
||||
// (always use the nested rule first if it exists)
|
||||
return Set(configuration.rules.map(HashableRule.init))
|
||||
.union(rules.map(HashableRule.init))
|
||||
.map { $0.rule }
|
||||
.filter { rule in
|
||||
return configuration.whitelistRules.contains(type(of: rule).description.identifier)
|
||||
}
|
||||
}
|
||||
|
||||
// Same here
|
||||
return Set(
|
||||
configuration.rules
|
||||
// Enable rules that are opt-in by the nested configuration
|
||||
.filter { rule in
|
||||
return configuration.optInRules.contains(type(of: rule).description.identifier)
|
||||
}
|
||||
.map(HashableRule.init)
|
||||
)
|
||||
// And disable rules that are disabled by the nested configuration
|
||||
.union(
|
||||
rules.filter { rule in
|
||||
return !configuration.disabledRules.contains(type(of: rule).description.identifier)
|
||||
}.map(HashableRule.init)
|
||||
)
|
||||
.map { $0.rule }
|
||||
}
|
||||
|
||||
internal func merge(with configuration: Configuration) -> Configuration {
|
||||
return configuration
|
||||
return Configuration(
|
||||
disabledRules: [],
|
||||
optInRules: [],
|
||||
whitelistRules: [],
|
||||
included: configuration.included, // Always use the nested included directories
|
||||
excluded: configuration.excluded, // Always use the nested excluded directories
|
||||
// The minimum warning threshold if both exist, otherwise the nested,
|
||||
// and if it doesn't exist try to use the parent one
|
||||
warningThreshold: warningThreshold.map { warningThreshold in
|
||||
return min(configuration.warningThreshold ?? .max, warningThreshold)
|
||||
} ?? configuration.warningThreshold,
|
||||
reporter: reporter, // Always use the parent reporter
|
||||
rules: mergingRules(with: configuration),
|
||||
cachePath: cachePath, // Always use the parent cache path
|
||||
rootPath: configuration.rootPath
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,15 @@ public struct Configuration: Equatable {
|
||||
public var configurationPath: String? // if successfully loaded from a path
|
||||
public let cachePath: String?
|
||||
|
||||
// MARK: Rules Properties
|
||||
|
||||
// All rules enabled in this configuration, derived from disabled, opt-in and whitelist rules
|
||||
public let rules: [Rule]
|
||||
|
||||
internal let disabledRules: [String]
|
||||
internal let optInRules: [String]
|
||||
internal let whitelistRules: [String]
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
public init?(disabledRules: [String] = [],
|
||||
@@ -46,11 +52,6 @@ public struct Configuration: Equatable {
|
||||
"configuration specified version \(pinnedVersion).")
|
||||
}
|
||||
|
||||
self.included = included
|
||||
self.excluded = excluded
|
||||
self.reporter = reporter
|
||||
self.cachePath = cachePath
|
||||
|
||||
let configuredRules = configuredRules
|
||||
?? (try? ruleList.configuredRules(with: [:]))
|
||||
?? []
|
||||
@@ -73,10 +74,8 @@ public struct Configuration: Equatable {
|
||||
return nil
|
||||
}
|
||||
|
||||
// set the config threshold to the threshold provided in the config file
|
||||
self.warningThreshold = warningThreshold
|
||||
|
||||
// Precedence is enableAllRules > whitelistRules > everything else
|
||||
let rules: [Rule]
|
||||
if enableAllRules {
|
||||
rules = configuredRules
|
||||
} else if !whitelistRules.isEmpty {
|
||||
@@ -97,6 +96,40 @@ public struct Configuration: Equatable {
|
||||
return optInRules.contains(id) || !(rule is OptInRule)
|
||||
}
|
||||
}
|
||||
self.init(disabledRules: disabledRules,
|
||||
optInRules: optInRules,
|
||||
whitelistRules: whitelistRules,
|
||||
included: included,
|
||||
excluded: excluded,
|
||||
warningThreshold: warningThreshold,
|
||||
reporter: reporter,
|
||||
rules: rules,
|
||||
cachePath: cachePath)
|
||||
}
|
||||
|
||||
internal init(disabledRules: [String],
|
||||
optInRules: [String],
|
||||
whitelistRules: [String],
|
||||
included: [String],
|
||||
excluded: [String],
|
||||
warningThreshold: Int?,
|
||||
reporter: String,
|
||||
rules: [Rule],
|
||||
cachePath: String?,
|
||||
rootPath: String? = nil) {
|
||||
|
||||
self.disabledRules = disabledRules
|
||||
self.optInRules = optInRules
|
||||
self.whitelistRules = whitelistRules
|
||||
self.included = included
|
||||
self.excluded = excluded
|
||||
self.reporter = reporter
|
||||
self.cachePath = cachePath
|
||||
self.rules = rules
|
||||
self.rootPath = rootPath
|
||||
|
||||
// set the config threshold to the threshold provided in the config file
|
||||
self.warningThreshold = warningThreshold
|
||||
}
|
||||
|
||||
private init(_ configuration: Configuration) {
|
||||
|
||||
Reference in New Issue
Block a user