Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions Source/SwiftLintFramework/Configuration+CommandLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,9 @@ extension Configuration {

let scriptInputPaths = files.compactMap(\.path)

if options.useExcludingByPrefix {
return filterExcludedPathsByPrefix(in: scriptInputPaths)
.map(SwiftLintFile.init(pathDeferringReading:))
}
return filterExcludedPaths(excludedPaths(), in: scriptInputPaths)
let excludeByType = ExcludeByStrategyType.createExcludeByStrategy(options: options, configuration: self)
let excludeBy = excludeByType.strategy
return excludeBy.filterExcludedPaths(in: scriptInputPaths)
.map(SwiftLintFile.init(pathDeferringReading:))
}
if !options.quiet {
Expand All @@ -272,14 +270,14 @@ extension Configuration {

queuedPrintError("\(options.capitalizedVerb) Swift files \(filesInfo)")
}
let excludeLintableFilesBy = options.useExcludingByPrefix
? Configuration.ExcludeBy.prefix
: .paths(excludedPaths: excludedPaths())

let excludeBy = ExcludeByStrategyType.createExcludeByStrategy(options: options, configuration: self).strategy

return options.paths.flatMap {
self.lintableFiles(
inPath: $0,
forceExclude: options.forceExclude,
excludeBy: excludeLintableFilesBy)
excludeBy: excludeBy)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import Foundation

extension Configuration {
public enum ExcludeBy {
case prefix
case paths(excludedPaths: [String])
}

// MARK: Lintable Paths
/// Returns the files that can be linted by SwiftLint in the specified parent path.
///
Expand All @@ -18,7 +13,7 @@ extension Configuration {
/// - returns: Files to lint.
public func lintableFiles(inPath path: String,
forceExclude: Bool,
excludeBy: ExcludeBy) -> [SwiftLintFile] {
excludeBy: some ExcludeByStrategy) -> [SwiftLintFile] {
lintablePaths(inPath: path, forceExclude: forceExclude, excludeBy: excludeBy)
.parallelCompactMap {
SwiftLintFile(pathDeferringReading: $0)
Expand All @@ -38,17 +33,12 @@ extension Configuration {
internal func lintablePaths(
inPath path: String,
forceExclude: Bool,
excludeBy: ExcludeBy,
excludeBy: any ExcludeByStrategy,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can also be some, can't it?

fileManager: some LintableFileManager = FileManager.default
) -> [String] {
if fileManager.isFile(atPath: path) {
if forceExclude {
switch excludeBy {
case .prefix:
return filterExcludedPathsByPrefix(in: [path.absolutePathStandardized()])
case .paths(let excludedPaths):
return filterExcludedPaths(excludedPaths, in: [path.absolutePathStandardized()])
}
return excludeBy.filterExcludedPaths(in: [path.absolutePathStandardized()])
}
// If path is a file and we're not forcing excludes, skip filtering with excluded/included paths
return [path]
Expand All @@ -59,62 +49,6 @@ extension Configuration {
.flatMap(Glob.resolveGlob)
.parallelFlatMap { fileManager.filesToLint(inPath: $0, rootDirectory: rootDirectory) }

switch excludeBy {
case .prefix:
return filterExcludedPathsByPrefix(in: pathsForPath, includedPaths)
case .paths(let excludedPaths):
return filterExcludedPaths(excludedPaths, in: pathsForPath, includedPaths)
}
}

/// Returns an array of file paths after removing the excluded paths as defined by this configuration.
///
/// - parameter fileManager: The lintable file manager to use to expand the excluded paths into all matching paths.
/// - parameter paths: The input paths to filter.
///
/// - returns: The input paths after removing the excluded paths.
public func filterExcludedPaths(
_ excludedPaths: [String],
in paths: [String]...
) -> [String] {
let allPaths = paths.flatMap { $0 }
#if os(Linux)
let result = NSMutableOrderedSet(capacity: allPaths.count)
result.addObjects(from: allPaths)
#else
let result = NSMutableOrderedSet(array: allPaths)
#endif

result.minusSet(Set(excludedPaths))
// swiftlint:disable:next force_cast
return result.map { $0 as! String }
}

/// Returns the file paths that are excluded by this configuration using filtering by absolute path prefix.
///
/// For cases when excluded directories contain many lintable files (e. g. Pods) it works faster than default
/// algorithm `filterExcludedPaths`.
///
/// - returns: The input paths after removing the excluded paths.
public func filterExcludedPathsByPrefix(in paths: [String]...) -> [String] {
let allPaths = paths.flatMap { $0 }
let excludedPaths = self.excludedPaths
.parallelFlatMap { @Sendable in Glob.resolveGlob($0) }
.map { $0.absolutePathStandardized() }
return allPaths.filter { path in
!excludedPaths.contains { path.hasPrefix($0) }
}
}

/// Returns the file paths that are excluded by this configuration after expanding them using the specified file
/// manager.
///
/// - parameter fileManager: The file manager to get child paths in a given parent location.
///
/// - returns: The expanded excluded file paths.
public func excludedPaths(fileManager: some LintableFileManager = FileManager.default) -> [String] {
excludedPaths
.flatMap(Glob.resolveGlob)
.parallelFlatMap { fileManager.filesToLint(inPath: $0, rootDirectory: rootDirectory) }
return excludeBy.filterExcludedPaths(in: pathsForPath, includedPaths)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Foundation

/// Returns an array of file paths after removing the excluded paths as defined by this configuration.
///
/// - parameter fileManager: The lintable file manager to use to expand the excluded paths into all matching paths.
/// - parameter paths: The input paths to filter.
///
/// - returns: The input paths after removing the excluded paths.
struct ExcludeByPathsByExpandingSubPaths: ExcludeByStrategy {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
struct ExcludeByPathsByExpandingSubPaths: ExcludeByStrategy {
struct ExcludeByExpandingSubPathsStrategy: ExcludeByStrategy {

let excludedPaths: [String]

init(configuration: Configuration, fileManager: some LintableFileManager = FileManager.default) {
self.excludedPaths = configuration.excludedPaths
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can call the other init.

.flatMap(Glob.resolveGlob)
.parallelFlatMap { fileManager.filesToLint(inPath: $0, rootDirectory: configuration.rootDirectory) }
}

init(_ excludedPaths: [String]) {
self.excludedPaths = excludedPaths
}

func filterExcludedPaths(in paths: [String]...) -> [String] {
let allPaths = paths.flatMap { $0 }
#if os(Linux)
let result = NSMutableOrderedSet(capacity: allPaths.count)
result.addObjects(from: allPaths)
#else
let result = NSMutableOrderedSet(array: allPaths)
#endif

result.minusSet(Set(excludedPaths))
// swiftlint:disable:next force_cast
return result.map { $0 as! String }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// Returns the file paths that are excluded by this configuration using filtering by absolute path prefix.
///
/// For cases when excluded directories contain many lintable files (e. g. Pods) it works faster than default
/// algorithm `filterExcludedPaths`.
Comment on lines +3 to +4
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// For cases when excluded directories contain many lintable files (e. g. Pods) it works faster than default
/// algorithm `filterExcludedPaths`.
/// For cases when excluded directories contain many lintable files (e. g. Pods) it works faster than the
/// default `filterExcludedPaths` strategy.

///
/// - returns: The input paths after removing the excluded paths.
struct ExcludeByPrefixStrategy: ExcludeByStrategy {
let excludedPaths: [String]

func filterExcludedPaths(in paths: [String]...) -> [String] {
let allPaths = paths.flatMap { $0 }
let excludedPaths = self.excludedPaths
.parallelFlatMap { @Sendable in Glob.resolveGlob($0) }
.map { $0.absolutePathStandardized() }
return allPaths.filter { path in
!excludedPaths.contains { path.hasPrefix($0) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol ExcludeByStrategy {
func filterExcludedPaths(in paths: [String]...) -> [String]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Foundation

enum ExcludeByStrategyType {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enum was only to introduce a namespace containing the factory method. We don't really need that, especially not the cases, which are sufficiently described by the two strategies conforming to the protocol.

As written in the other comment, the static factory method can be part of the protocol itself via an extension.

case excludeByPrefix(ExcludeByPrefixStrategy)
case excludeByPathsByExpandingSubPaths(ExcludeByPathsByExpandingSubPaths)

static func createExcludeByStrategy(options: LintOrAnalyzeOptions,
configuration: Configuration,
fileManager: some LintableFileManager = FileManager.default)
-> Self {
if options.useExcludingByPrefix {
let strategy = ExcludeByPrefixStrategy(excludedPaths: configuration.excludedPaths)
return .excludeByPrefix(strategy)
}

let strategy = ExcludeByPathsByExpandingSubPaths(configuration: configuration, fileManager: fileManager)
return .excludeByPathsByExpandingSubPaths(strategy)
}

var strategy: any ExcludeByStrategy {
switch self {
case .excludeByPrefix(let strategy):
return strategy
case .excludeByPathsByExpandingSubPaths(let strategy):
return strategy
}
}
}
Loading