Skip to content

Commit

Permalink
add excluding by prefix option
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnReeze committed Oct 14, 2020
1 parent 5d6e25a commit 2129733
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@ extension Configuration {
/// - parameter path: The parent path in which to search for lintable files. Can be a directory or a file.
/// - parameter forceExclude: Whether or not excludes defined in this configuration should be applied even if `path`
/// is an exact match.
/// - parameter excludeByPrefix: Whether or not uses excluding by prefix algorightm
///
/// - returns: Files to lint.
public func lintableFiles(inPath path: String, forceExclude: Bool) -> [SwiftLintFile] {
return lintablePaths(inPath: path, forceExclude: forceExclude)
public func lintableFiles(inPath path: String, forceExclude: Bool,
excludeByPrefix: Bool = false) -> [SwiftLintFile] {
return lintablePaths(inPath: path, forceExclude: forceExclude, excludeByPrefix: excludeByPrefix)
.compactMap(SwiftLintFile.init(pathDeferringReading:))
}

/// Returns the paths for files that can be linted by SwiftLint in the specified parent path.
///
/// - parameter path: The parent path in which to search for lintable files. Can be a directory or a file.
/// - parameter forceExclude: Whether or not excludes defined in this configuration should be applied even if `path`
/// is an exact match.
/// - parameter fileManager: The lintable file manager to use to search for lintable files.
/// is an exact match.
/// - parameter excludeByPrefix: Whether or not uses excluding by prefix algorightm
/// - parameter fileManager: The lintable file manager to use to search for lintable files.
///
/// - returns: Paths for files to lint.
internal func lintablePaths(inPath path: String, forceExclude: Bool,
internal func lintablePaths(inPath path: String, forceExclude: Bool, excludeByPrefix: Bool = false,
fileManager: LintableFileManager = FileManager.default) -> [String] {
// If path is a file and we're not forcing excludes, skip filtering with excluded/included paths
if path.isFile && !forceExclude {
Expand All @@ -31,7 +34,11 @@ extension Configuration {
let includedPaths = included.parallelFlatMap {
fileManager.filesToLint(inPath: $0, rootDirectory: self.rootPath)
}
return filterExcludedPaths(fileManager: fileManager, in: pathsForPath, includedPaths)

let filesToLint = excludeByPrefix
? filterExcludedPathsByPrefix(in: pathsForPath, includedPaths)
: filterExcludedPaths(fileManager: fileManager, in: pathsForPath, includedPaths)
return filesToLint
}

/// Returns an array of file paths after removing the excluded paths as defined by this configuration.
Expand All @@ -55,6 +62,20 @@ extension Configuration {
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.parallelFlatMap { $0 }
let excludedPaths = excluded.parallelFlatMap(transform: Glob.resolveGlob)
.parallelMap { $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.
///
Expand Down
10 changes: 6 additions & 4 deletions Source/swiftlint/Commands/AnalyzeCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct AnalyzeOptions: OptionsProtocol {
let strict: Bool
let lenient: Bool
let forceExclude: Bool
let excludeByPrefix: Bool
let useScriptInputFiles: Bool
let benchmark: Bool
let reporter: String
Expand All @@ -51,17 +52,17 @@ struct AnalyzeOptions: OptionsProtocol {
let compileCommands: String

// swiftlint:disable line_length
static func create(_ path: String) -> (_ configurationFile: String) -> (_ strict: Bool) -> (_ lenient: Bool) -> (_ forceExclude: Bool) -> (_ useScriptInputFiles: Bool) -> (_ benchmark: Bool) -> (_ reporter: String) -> (_ quiet: Bool) -> (_ enableAllRules: Bool) -> (_ autocorrect: Bool) -> (_ compilerLogPath: String) -> (_ compileCommands: String) -> (_ paths: [String]) -> AnalyzeOptions {
return { configurationFile in { strict in { lenient in { forceExclude in { useScriptInputFiles in { benchmark in { reporter in { quiet in { enableAllRules in { autocorrect in { compilerLogPath in { compileCommands in { paths in
static func create(_ path: String) -> (_ configurationFile: String) -> (_ strict: Bool) -> (_ lenient: Bool) -> (_ forceExclude: Bool) -> (_ excludeByPrefix: Bool) -> (_ useScriptInputFiles: Bool) -> (_ benchmark: Bool) -> (_ reporter: String) -> (_ quiet: Bool) -> (_ enableAllRules: Bool) -> (_ autocorrect: Bool) -> (_ compilerLogPath: String) -> (_ compileCommands: String) -> (_ paths: [String]) -> AnalyzeOptions {
return { configurationFile in { strict in { lenient in { forceExclude in { excludeByPrefix in { useScriptInputFiles in { benchmark in { reporter in { quiet in { enableAllRules in { autocorrect in { compilerLogPath in { compileCommands in { paths in
let allPaths: [String]
if !path.isEmpty {
allPaths = [path]
} else {
allPaths = paths
}
return self.init(paths: allPaths, configurationFile: configurationFile, strict: strict, lenient: lenient, forceExclude: forceExclude, useScriptInputFiles: useScriptInputFiles, benchmark: benchmark, reporter: reporter, quiet: quiet, enableAllRules: enableAllRules, autocorrect: autocorrect, compilerLogPath: compilerLogPath, compileCommands: compileCommands)
return self.init(paths: allPaths, configurationFile: configurationFile, strict: strict, lenient: lenient, forceExclude: forceExclude, excludeByPrefix: excludeByPrefix, useScriptInputFiles: useScriptInputFiles, benchmark: benchmark, reporter: reporter, quiet: quiet, enableAllRules: enableAllRules, autocorrect: autocorrect, compilerLogPath: compilerLogPath, compileCommands: compileCommands)
// swiftlint:enable line_length
}}}}}}}}}}}}}
}}}}}}}}}}}}}}
}

static func evaluate(_ mode: CommandMode) -> Result<AnalyzeOptions, CommandantError<CommandantError<()>>> {
Expand All @@ -74,6 +75,7 @@ struct AnalyzeOptions: OptionsProtocol {
usage: "downgrades serious violations to warnings, warning threshold is disabled")
<*> mode <| Option(key: "force-exclude", defaultValue: false,
usage: "exclude files in config `excluded` even if their paths are explicitly specified")
<*> mode <| useAlternativeExcludingOption
<*> mode <| useScriptInputFilesOption
<*> mode <| Option(key: "benchmark", defaultValue: false,
usage: "save benchmarks to benchmark_files.txt " +
Expand Down
16 changes: 10 additions & 6 deletions Source/swiftlint/Commands/AutoCorrectCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,25 @@ struct AutoCorrectOptions: OptionsProtocol {
let useScriptInputFiles: Bool
let quiet: Bool
let forceExclude: Bool
let excludeByPrefix: Bool
let format: Bool
let cachePath: String
let ignoreCache: Bool

// swiftlint:disable line_length
static func create(_ path: String) -> (_ configurationFile: String) -> (_ useScriptInputFiles: Bool) -> (_ quiet: Bool) -> (_ forceExclude: Bool) -> (_ format: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ paths: [String]) -> AutoCorrectOptions {
return { configurationFile in { useScriptInputFiles in { quiet in { forceExclude in { format in { cachePath in { ignoreCache in { paths in
static func create(_ path: String) -> (_ configurationFile: String) -> (_ useScriptInputFiles: Bool) -> (_ quiet: Bool) -> (_ forceExclude: Bool) ->
(_ excludeByPrefix: Bool) -> (_ format: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ paths: [String]) -> AutoCorrectOptions {
return { configurationFile in { useScriptInputFiles in { quiet in { forceExclude in { excludeByPrefix in { format in { cachePath in { ignoreCache in { paths in
let allPaths: [String]
if !path.isEmpty {
allPaths = [path]
} else {
allPaths = paths
}
return self.init(paths: allPaths, configurationFile: configurationFile, useScriptInputFiles: useScriptInputFiles, quiet: quiet, forceExclude: forceExclude, format: format, cachePath: cachePath, ignoreCache: ignoreCache)
return self.init(paths: allPaths, configurationFile: configurationFile, useScriptInputFiles: useScriptInputFiles, quiet: quiet, forceExclude: forceExclude,
excludeByPrefix: excludeByPrefix, format: format, cachePath: cachePath, ignoreCache: ignoreCache)
// swiftlint:enable line_length
}}}}}}}}
}}}}}}}}}
}

static func evaluate(_ mode: CommandMode) -> Result<AutoCorrectOptions, CommandantError<CommandantError<()>>> {
Expand All @@ -53,6 +56,7 @@ struct AutoCorrectOptions: OptionsProtocol {
<*> mode <| quietOption(action: "correcting")
<*> mode <| Option(key: "force-exclude", defaultValue: false,
usage: "exclude files in config `excluded` even if their paths are explicitly specified")
<*> mode <| useAlternativeExcludingOption
<*> mode <| Option(key: "format", defaultValue: false,
usage: "should reformat the Swift files")
<*> mode <| Option(key: "cache-path", defaultValue: "",
Expand All @@ -66,8 +70,8 @@ struct AutoCorrectOptions: OptionsProtocol {
fileprivate func visitor(with configuration: Configuration, storage: RuleStorage) -> LintableFilesVisitor {
let cache = ignoreCache ? nil : LinterCache(configuration: configuration)
return LintableFilesVisitor(paths: paths, action: "Correcting", useSTDIN: false, quiet: quiet,
useScriptInputFiles: useScriptInputFiles, forceExclude: forceExclude, cache: cache,
parallel: true,
useScriptInputFiles: useScriptInputFiles, forceExclude: forceExclude,
useExcludingByPrefix: excludeByPrefix, cache: cache, parallel: true,
allowZeroLintableFiles: configuration.allowZeroLintableFiles) { linter in
if self.format {
switch configuration.indentation {
Expand Down
10 changes: 6 additions & 4 deletions Source/swiftlint/Commands/LintCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct LintOptions: OptionsProtocol {
let strict: Bool
let lenient: Bool
let forceExclude: Bool
let excludeByPrefix: Bool
let useScriptInputFiles: Bool
let benchmark: Bool
let reporter: String
Expand All @@ -25,17 +26,17 @@ struct LintOptions: OptionsProtocol {
let enableAllRules: Bool

// swiftlint:disable line_length
static func create(_ path: String) -> (_ useSTDIN: Bool) -> (_ configurationFile: String) -> (_ strict: Bool) -> (_ lenient: Bool) -> (_ forceExclude: Bool) -> (_ useScriptInputFiles: Bool) -> (_ benchmark: Bool) -> (_ reporter: String) -> (_ quiet: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ enableAllRules: Bool) -> (_ paths: [String]) -> LintOptions {
return { useSTDIN in { configurationFile in { strict in { lenient in { forceExclude in { useScriptInputFiles in { benchmark in { reporter in { quiet in { cachePath in { ignoreCache in { enableAllRules in { paths in
static func create(_ path: String) -> (_ useSTDIN: Bool) -> (_ configurationFile: String) -> (_ strict: Bool) -> (_ lenient: Bool) -> (_ forceExclude: Bool) -> (_ excludeByPrefix: Bool) -> (_ useScriptInputFiles: Bool) -> (_ benchmark: Bool) -> (_ reporter: String) -> (_ quiet: Bool) -> (_ cachePath: String) -> (_ ignoreCache: Bool) -> (_ enableAllRules: Bool) -> (_ paths: [String]) -> LintOptions {
return { useSTDIN in { configurationFile in { strict in { lenient in { forceExclude in { excludeByPrefix in { useScriptInputFiles in { benchmark in { reporter in { quiet in { cachePath in { ignoreCache in { enableAllRules in { paths in
let allPaths: [String]
if !path.isEmpty {
allPaths = [path]
} else {
allPaths = paths
}
return self.init(paths: allPaths, useSTDIN: useSTDIN, configurationFile: configurationFile, strict: strict, lenient: lenient, forceExclude: forceExclude, useScriptInputFiles: useScriptInputFiles, benchmark: benchmark, reporter: reporter, quiet: quiet, cachePath: cachePath, ignoreCache: ignoreCache, enableAllRules: enableAllRules)
return self.init(paths: allPaths, useSTDIN: useSTDIN, configurationFile: configurationFile, strict: strict, lenient: lenient, forceExclude: forceExclude, excludeByPrefix: excludeByPrefix, useScriptInputFiles: useScriptInputFiles, benchmark: benchmark, reporter: reporter, quiet: quiet, cachePath: cachePath, ignoreCache: ignoreCache, enableAllRules: enableAllRules)
// swiftlint:enable line_length
}}}}}}}}}}}}}
}}}}}}}}}}}}}}
}

static func evaluate(_ mode: CommandMode) -> Result<LintOptions, CommandantError<CommandantError<()>>> {
Expand All @@ -50,6 +51,7 @@ struct LintOptions: OptionsProtocol {
usage: "downgrades serious violations to warnings, warning threshold is disabled")
<*> mode <| Option(key: "force-exclude", defaultValue: false,
usage: "exclude files in config `excluded` even if their paths are explicitly specified")
<*> mode <| useAlternativeExcludingOption
<*> mode <| useScriptInputFilesOption
<*> mode <| Option(key: "benchmark", defaultValue: false,
usage: "save benchmarks to benchmark_files.txt " +
Expand Down
9 changes: 6 additions & 3 deletions Source/swiftlint/Extensions/Configuration+CommandLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,10 @@ extension Configuration {
}

let scriptInputPaths = files.compactMap { $0.path }
return filterExcludedPaths(in: scriptInputPaths)
.map(SwiftLintFile.init(pathDeferringReading:))
let filesToLint = visitor.useExcludingByPrefix
? filterExcludedPathsByPrefix(in: scriptInputPaths)
: filterExcludedPaths(in: scriptInputPaths)
return filesToLint.map(SwiftLintFile.init(pathDeferringReading:))
}
}
if !visitor.quiet {
Expand All @@ -218,7 +220,8 @@ extension Configuration {
queuedPrintError("\(visitor.action) Swift files \(filesInfo)")
}
return .success(visitor.paths.flatMap {
self.lintableFiles(inPath: $0, forceExclude: visitor.forceExclude)
self.lintableFiles(inPath: $0, forceExclude: visitor.forceExclude,
excludeByPrefix: visitor.useExcludingByPrefix)
})
}

Expand Down
5 changes: 5 additions & 0 deletions Source/swiftlint/Helpers/CommonOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ let useScriptInputFilesOption = Option(key: "use-script-input-files",
usage: "read SCRIPT_INPUT_FILE* environment variables " +
"as files")

let useAlternativeExcludingOption = Option(key: "use-alternative-excluding",
defaultValue: false,
usage: "alternative exclude paths algorithm for `excluded`." +
"may speed up excluding for some cases")

func quietOption(action: String) -> Option<Bool> {
return Option(key: "quiet",
defaultValue: false,
Expand Down
3 changes: 3 additions & 0 deletions Source/swiftlint/Helpers/LintOrAnalyzeCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ struct LintOrAnalyzeOptions {
let strict: Bool
let lenient: Bool
let forceExclude: Bool
let useExcludingByPrefix: Bool
let useScriptInputFiles: Bool
let benchmark: Bool
let reporter: String
Expand All @@ -149,6 +150,7 @@ struct LintOrAnalyzeOptions {
strict = options.strict
lenient = options.lenient
forceExclude = options.forceExclude
useExcludingByPrefix = options.excludeByPrefix
useScriptInputFiles = options.useScriptInputFiles
benchmark = options.benchmark
reporter = options.reporter
Expand All @@ -169,6 +171,7 @@ struct LintOrAnalyzeOptions {
strict = options.strict
lenient = options.lenient
forceExclude = options.forceExclude
useExcludingByPrefix = options.excludeByPrefix
useScriptInputFiles = options.useScriptInputFiles
benchmark = options.benchmark
reporter = options.reporter
Expand Down
12 changes: 9 additions & 3 deletions Source/swiftlint/Helpers/LintableFilesVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ struct LintableFilesVisitor {
let quiet: Bool
let useScriptInputFiles: Bool
let forceExclude: Bool
let useExcludingByPrefix: Bool
let cache: LinterCache?
let parallel: Bool
let allowZeroLintableFiles: Bool
let mode: LintOrAnalyzeModeWithCompilerArguments
let block: (CollectedLinter) -> Void

init(paths: [String], action: String, useSTDIN: Bool, quiet: Bool, useScriptInputFiles: Bool, forceExclude: Bool,
init(paths: [String], action: String, useSTDIN: Bool,
quiet: Bool, useScriptInputFiles: Bool, forceExclude: Bool, useExcludingByPrefix: Bool,
cache: LinterCache?, parallel: Bool,
allowZeroLintableFiles: Bool, block: @escaping (CollectedLinter) -> Void) {
self.paths = resolveParamsFiles(args: paths)
Expand All @@ -60,6 +62,7 @@ struct LintableFilesVisitor {
self.quiet = quiet
self.useScriptInputFiles = useScriptInputFiles
self.forceExclude = forceExclude
self.useExcludingByPrefix = useExcludingByPrefix
self.cache = cache
self.parallel = parallel
self.mode = .lint
Expand All @@ -68,7 +71,7 @@ struct LintableFilesVisitor {
}

private init(paths: [String], action: String, useSTDIN: Bool, quiet: Bool,
useScriptInputFiles: Bool, forceExclude: Bool,
useScriptInputFiles: Bool, forceExclude: Bool, useExcludingByPrefix: Bool,
cache: LinterCache?, compilerInvocations: CompilerInvocations?,
allowZeroLintableFiles: Bool, block: @escaping (CollectedLinter) -> Void) {
self.paths = resolveParamsFiles(args: paths)
Expand All @@ -77,6 +80,7 @@ struct LintableFilesVisitor {
self.quiet = quiet
self.useScriptInputFiles = useScriptInputFiles
self.forceExclude = forceExclude
self.useExcludingByPrefix = useExcludingByPrefix
self.cache = cache
self.parallel = true
if let compilerInvocations = compilerInvocations {
Expand Down Expand Up @@ -108,7 +112,9 @@ struct LintableFilesVisitor {
let visitor = LintableFilesVisitor(paths: options.paths, action: options.verb.bridge().capitalized,
useSTDIN: options.useSTDIN, quiet: options.quiet,
useScriptInputFiles: options.useScriptInputFiles,
forceExclude: options.forceExclude, cache: cache,
forceExclude: options.forceExclude,
useExcludingByPrefix: options.useExcludingByPrefix,
cache: cache,
compilerInvocations: compilerInvocations,
allowZeroLintableFiles: allowZeroLintableFiles, block: block)
return .success(visitor)
Expand Down
Loading

0 comments on commit 2129733

Please sign in to comment.