diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb50f50bd..a8bcca5df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +## Master + +##### Breaking + +* None. + +##### Enhancements + +* Print to stderr for all informational logs. Only reporter outputs is logged to + stdout. + [JP Simard](https://github.com/jpsim) + +* JSON and CSV reporters now only print at the very end of the linting + process. + [JP Simard](https://github.com/jpsim) + +##### Bug Fixes + +* Custom reporters are now supported even when not running with `--use-stdin`. + [JP Simard](https://github.com/jpsim) + [#151](https://github.com/realm/SwiftLint/issues/151) + + ## 0.2.0: Tumble Dry ##### Breaking diff --git a/Source/SwiftLintFramework/Configuration.swift b/Source/SwiftLintFramework/Configuration.swift index b601ae51af..1211da1bec 100644 --- a/Source/SwiftLintFramework/Configuration.swift +++ b/Source/SwiftLintFramework/Configuration.swift @@ -6,6 +6,7 @@ // Copyright (c) 2015 Realm. All rights reserved. // +import Foundation import Yaml extension Yaml { @@ -112,7 +113,7 @@ public struct Configuration { let yamlContents = try NSString(contentsOfFile: fullPath, encoding: NSUTF8StringEncoding) as String if let _ = Configuration(yaml: yamlContents) { - print("Loading configuration from '\(path)'") + fputs("Loading configuration from '\(path)'\n", stderr) self.init(yaml: yamlContents)! } else { self.init()! diff --git a/Source/SwiftLintFramework/Linter.swift b/Source/SwiftLintFramework/Linter.swift index 8a47a2303c..f8cfa34094 100644 --- a/Source/SwiftLintFramework/Linter.swift +++ b/Source/SwiftLintFramework/Linter.swift @@ -13,7 +13,7 @@ import SourceKittenFramework public struct Linter { private let file: File private let rules: [Rule] - private let reporter: Reporter.Type + public let reporter: Reporter.Type public var styleViolations: [StyleViolation] { let regions = file.regions() @@ -29,10 +29,6 @@ public struct Linter { } } - public func generateReport() -> String { - return reporter.generateReport(styleViolations) - } - public var ruleExamples: [RuleExample] { return rules.flatMap { $0.example } } diff --git a/Source/SwiftLintFramework/Reporter.swift b/Source/SwiftLintFramework/Reporter.swift index 9b360007a8..65e92f7285 100644 --- a/Source/SwiftLintFramework/Reporter.swift +++ b/Source/SwiftLintFramework/Reporter.swift @@ -9,4 +9,5 @@ public protocol Reporter: CustomStringConvertible { static var identifier: String { get } static func generateReport(violations: [StyleViolation]) -> String + static var isRealtime: Bool { get } } diff --git a/Source/SwiftLintFramework/Reporters/CSVReporter.swift b/Source/SwiftLintFramework/Reporters/CSVReporter.swift index 0ab4f87b56..7ffb3f6765 100644 --- a/Source/SwiftLintFramework/Reporters/CSVReporter.swift +++ b/Source/SwiftLintFramework/Reporters/CSVReporter.swift @@ -10,6 +10,7 @@ import Foundation public struct CSVReporter: Reporter { public static let identifier = "csv" + public static let isRealtime = false public var description: String { return "Reports violations as a newline-separated string of comma-separated values (CSV)." diff --git a/Source/SwiftLintFramework/Reporters/JSONReporter.swift b/Source/SwiftLintFramework/Reporters/JSONReporter.swift index 04132e1c5b..cc184d25ae 100644 --- a/Source/SwiftLintFramework/Reporters/JSONReporter.swift +++ b/Source/SwiftLintFramework/Reporters/JSONReporter.swift @@ -11,6 +11,7 @@ import SourceKittenFramework public struct JSONReporter: Reporter { public static let identifier = "json" + public static let isRealtime = false public var description: String { return "Reports violations as a JSON array." diff --git a/Source/SwiftLintFramework/Reporters/XcodeReporter.swift b/Source/SwiftLintFramework/Reporters/XcodeReporter.swift index 542b5275fe..6b65726222 100644 --- a/Source/SwiftLintFramework/Reporters/XcodeReporter.swift +++ b/Source/SwiftLintFramework/Reporters/XcodeReporter.swift @@ -8,6 +8,7 @@ public struct XcodeReporter: Reporter { public static let identifier = "xcode" + public static let isRealtime = true public var description: String { return "Reports violations in the format Xcode uses to display in the IDE. (default)" diff --git a/Source/swiftlint/LintCommand.swift b/Source/swiftlint/LintCommand.swift index 9ad9849ec1..d350aab6a7 100644 --- a/Source/swiftlint/LintCommand.swift +++ b/Source/swiftlint/LintCommand.swift @@ -28,10 +28,9 @@ struct LintCommand: CommandType { let stdinData = standardInput.readDataToEndOfFile() let stdinNSString = NSString(data: stdinData, encoding: NSUTF8StringEncoding) if let stdinString = stdinNSString as? String { - let file = File(contents: stdinString) - let linter = Linter(file: file, configuration: configuration) - print(linter.generateReport()) - return .Success() + return lint([File(contents: stdinString)], + configuration: configuration, + strict: options.strict) } return .Failure(CommandantError<()>.CommandError()) } @@ -41,47 +40,63 @@ struct LintCommand: CommandType { } } - private func lint(path: String, - configuration: Configuration, strict: Bool) -> Result<(), CommandantError<()>> { + private func lint(path: String, configuration: Configuration, strict: Bool) -> + Result<(), CommandantError<()>> { let filesToLint = (configuration.included.count == 0 ? filesToLintAtPath(path) : []) .filter({ !configuration.excluded.flatMap(filesToLintAtPath).contains($0) }) + configuration.included.flatMap(filesToLintAtPath) - if filesToLint.count > 0 { - if path.isEmpty { - print("Linting Swift files in current working directory") - } else { - print("Linting Swift files at path \(path)") - } - var numberOfViolations = 0, numberOfSeriousViolations = 0 - for (index, path) in filesToLint.enumerate() { + if path.isEmpty { + fputs("Linting Swift files in current working directory\n", stderr) + } else { + fputs("Linting Swift files at path \(path)\n", stderr) + } + let files = filesToLint.flatMap(File.init) + if files.count > 0 { + return lint(files, configuration: configuration, strict: strict) + } + return .Failure(CommandantError<()>.UsageError(description: "No lintable files found at" + + " path \(path)")) + } + + private func lint(files: [File], configuration: Configuration, strict: Bool) -> + Result<(), CommandantError<()>> { + var violations = [StyleViolation]() + var reporter: Reporter.Type = XcodeReporter.self + for (index, file) in files.enumerate() { + if let path = file.path { let filename = (path as NSString).lastPathComponent - print("Linting '\(filename)' (\(index + 1)/\(filesToLint.count))") - let file = File(path: path)! - for violation in Linter(file: file, configuration: configuration).styleViolations { - fputs("\(violation)\n", stderr) - numberOfViolations++ - if violation.severity == .Error { - numberOfSeriousViolations++ - } - } + fputs("Linting '\(filename)' (\(index + 1)/\(files.count))\n", stderr) } - let violationSuffix = (numberOfViolations != 1 ? "s" : "") - let filesSuffix = (filesToLint.count != 1 ? "s." : ".") - print( - "Done linting!" + - " Found \(numberOfViolations) violation\(violationSuffix)," + - " \(numberOfSeriousViolations) serious" + - " in \(filesToLint.count) file\(filesSuffix)" - ) - if strict && numberOfViolations > 0 { - return .Failure(CommandantError<()>.CommandError()) - } else if numberOfSeriousViolations <= 0 { - return .Success() + let linter = Linter(file: file, configuration: configuration) + let currentViolations = linter.styleViolations + violations += currentViolations + reporter = linter.reporter + if reporter.isRealtime { + let report = reporter.generateReport(currentViolations) + if !report.isEmpty { + print(report) + } } + } + if !reporter.isRealtime { + print(reporter.generateReport(violations)) + } + let numberOfSeriousViolations = violations.filter({ $0.severity == .Error }).count + let violationSuffix = (violations.count != 1 ? "s" : "") + let filesSuffix = (files.count != 1 ? "s." : ".") + fputs( + "Done linting!" + + " Found \(violations.count) violation\(violationSuffix)," + + " \(numberOfSeriousViolations) serious" + + " in \(files.count) file\(filesSuffix)\n", + stderr + ) + if strict && violations.count > 0 { return .Failure(CommandantError<()>.CommandError()) + } else if numberOfSeriousViolations <= 0 { + return .Success() } - return .Failure(CommandantError<()>.UsageError(description: "No lintable files found at" + - " path \(path)")) + return .Failure(CommandantError<()>.CommandError()) } private func filesToLintAtPath(path: String) -> [String] {