From 78291e1c1cffa334c720b7ce93319f89ce0d9231 Mon Sep 17 00:00:00 2001 From: max rabiciuc Date: Wed, 2 Jun 2021 12:17:03 -0700 Subject: [PATCH] Add include and exclude flags to doc command --- Source/SourceKittenFramework/Module.swift | 64 +++++++++++++++++++---- Source/sourcekitten/Doc.swift | 8 ++- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/Source/SourceKittenFramework/Module.swift b/Source/SourceKittenFramework/Module.swift index fc8951e3b..bfee4a76a 100755 --- a/Source/SourceKittenFramework/Module.swift +++ b/Source/SourceKittenFramework/Module.swift @@ -35,8 +35,10 @@ public struct Module { package if `nil`. - parameter path: Path of the directory containing the SPM `.build` directory. Uses the current directory by default. + - parameter include: Source file pathnames to be included in documentation. Supports wildcards. Empty array will include all files. + - parameter exclude: Source file pathnames to be excluded from documentation. Supports wildcards. */ - public init?(spmName: String? = nil, inPath path: String = FileManager.default.currentDirectoryPath) { + public init?(spmName: String? = nil, inPath path: String = FileManager.default.currentDirectoryPath, include: [String] = [], exclude: [String] = []) { let yamlPath = URL(fileURLWithPath: path).appendingPathComponent(".build/debug.yaml").path guard let yaml = try? Yams.compose(yaml: String(contentsOfFile: yamlPath, encoding: .utf8)), let commands = (yaml as Node?)?["commands"]?.mapping?.values else { @@ -76,7 +78,7 @@ public struct Module { arguments.append(contentsOf: imports) return arguments }() - sourceFiles = sources + sourceFiles = sources.filteringElements(include: include, exclude: exclude) } /** @@ -89,8 +91,10 @@ public struct Module { package if `nil`. - parameter path: Path of the directory containing the `Package.swift` file. Uses the current directory by default. + - parameter include: Source file pathnames to be included in documentation. Supports wildcards. Empty array will include all files. + - parameter exclude: Source file pathnames to be excluded from documentation. Supports wildcards. */ - public init?(spmArguments: [String], spmName: String? = nil, inPath path: String = FileManager.default.currentDirectoryPath) { + public init?(spmArguments: [String], spmName: String? = nil, inPath path: String = FileManager.default.currentDirectoryPath, include: [String] = [], exclude: [String] = []) { fputs("Running swift build\n", stderr) let buildResults = Exec.run("/usr/bin/env", ["swift", "build"] + spmArguments, currentDirectory: path, stderr: .merge) guard buildResults.terminationStatus == 0 else { @@ -100,7 +104,7 @@ public struct Module { return nil } - self.init(spmName: spmName, inPath: path) + self.init(spmName: spmName, inPath: path, include: include, exclude: exclude) } /** @@ -110,8 +114,10 @@ public struct Module { - parameter xcodeBuildArguments: The arguments necessary pass in to `xcodebuild` to build this Module. - parameter name: Module name. Will be parsed from `xcodebuild` output if nil. - parameter path: Path to run `xcodebuild` from. Uses current path by default. + - parameter include: Source file pathnames to be included in documentation. Supports wildcards. Empty array will include all files. + - parameter exclude: Source file pathnames to be excluded from documentation. Supports wildcards. */ - public init?(xcodeBuildArguments: [String], name: String? = nil, inPath path: String = FileManager.default.currentDirectoryPath) { + public init?(xcodeBuildArguments: [String], name: String? = nil, inPath path: String = FileManager.default.currentDirectoryPath, include: [String] = [], exclude: [String] = []) { let buildSettings = XcodeBuild.showBuildSettings(arguments: xcodeBuildArguments, inPath: path) let name = name @@ -132,7 +138,7 @@ public struct Module { if let output = results.string, let arguments = parseCompilerArguments(xcodebuildOutput: output, language: .swift, moduleName: name), let moduleName = moduleName(fromArguments: arguments) { - self.init(name: moduleName, compilerArguments: arguments) + self.init(name: moduleName, compilerArguments: arguments, include: include, exclude: exclude) return } // Check New Build System is used @@ -140,7 +146,7 @@ public struct Module { if let projectTempRoot = buildSettings?.firstBuildSettingValue(for: { $0.PROJECT_TEMP_ROOT }), let arguments = checkNewBuildSystem(in: projectTempRoot, moduleName: name), let moduleName = moduleName(fromArguments: arguments) { - self.init(name: moduleName, compilerArguments: arguments) + self.init(name: moduleName, compilerArguments: arguments, include: include, exclude: exclude) return } // Executing `clean build` is a fallback. @@ -157,7 +163,7 @@ public struct Module { fputs("Could not parse module name from compiler arguments.\n", stderr) return nil } - self.init(name: moduleName, compilerArguments: arguments) + self.init(name: moduleName, compilerArguments: arguments, include: include, exclude: exclude) } /** @@ -165,15 +171,17 @@ public struct Module { - parameter name: Module name. - parameter compilerArguments: Compiler arguments required by SourceKit to process the source files in this Module. + - parameter include: Source file pathnames to be included in documentation. Supports wildcards. Empty array will include all files. + - parameter exclude: Source file pathnames to be excluded from documentation. Supports wildcards. */ - public init(name: String, compilerArguments: [String]) { + public init(name: String, compilerArguments: [String], include: [String] = [], exclude: [String] = []) { self.name = name self.compilerArguments = compilerArguments.expandingResponseFiles sourceFiles = self.compilerArguments.filter({ $0.bridge().isSwiftFile() && $0.isFile }).map { return URL(fileURLWithPath: $0).resolvingSymlinksInPath().path - } + }.filteringElements(include: include, exclude: exclude) } } @@ -202,3 +210,39 @@ private extension Collection where Element == XcodeBuildSetting { return lazy.compactMap(getterClosure).first } } + + +private extension Array where Element == String { + + func filteringElements(include: [String], exclude: [String]) -> [String] { + var result = self + if !include.isEmpty { + result = result.filter { file in + for pattern in include { + if file.matches(pattern: pattern) { + return true + } + } + return false + } + } + + result = result.filter { file in + for pattern in exclude { + if file.matches(pattern: pattern) { + return false + } + } + return true + } + return result + } +} + +private extension String { + + func matches(pattern: String) -> Bool { + let pred = NSPredicate(format: "self LIKE %@", pattern) + return !NSArray(object: self).filtered(using: pred).isEmpty + } +} diff --git a/Source/sourcekitten/Doc.swift b/Source/sourcekitten/Doc.swift index d7af17cd5..4e2fe7b55 100644 --- a/Source/sourcekitten/Doc.swift +++ b/Source/sourcekitten/Doc.swift @@ -10,6 +10,10 @@ extension SourceKitten { var singleFile: Bool = false @Option(help: "Name of Swift module to document (can't be used with `--single-file`)") var moduleName: String = "" + @Option(help: "Source file pathnames to be included in documentation. Supports wildcards. (can't be used with `--single-file`)") + var include: [String] = [] + @Option(help: "Source file pathnames to be excluded from documentation. Supports wildcards. (can't be used with `--single-file`)") + var exclude: [String] = [] @Flag(help: "Document a Swift Package Manager module") var spm: Bool = false @Flag(help: "Document Objective-C headers instead of Swift code") @@ -21,7 +25,7 @@ extension SourceKitten { let moduleName = self.moduleName.isEmpty ? nil : self.moduleName if spm { - if let docs = Module(spmArguments: arguments, spmName: moduleName)?.docs { + if let docs = Module(spmArguments: arguments, spmName: moduleName, include: include, exclude: exclude)?.docs { print(docs) return } @@ -55,7 +59,7 @@ extension SourceKitten { throw SourceKittenError.readFailed(path: arguments[0]) } - let module = Module(xcodeBuildArguments: arguments, name: moduleName) + let module = Module(xcodeBuildArguments: arguments, name: moduleName, include: include, exclude: exclude) if let docs = module?.docs { print(docs) return