Skip to content
Merged
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
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

##### Breaking

- None.
- The `--no-color` option has been replaced with `--color=never`.

##### Enhancements

- None.
- The `--color` option now accepts one of `auto`, `always` and `never`. In `auto` mode, color is disabled for dumb terminals and non-TTYs.

##### Bug Fixes

Expand Down
1 change: 1 addition & 0 deletions Sources/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ swift_library(
swift_library(
name = "Configuration",
srcs = [
"Configuration/ColorOption.swift",
"Configuration/Configuration.swift",
"Configuration/OutputFormat.swift",
],
Expand Down
17 changes: 17 additions & 0 deletions Sources/Configuration/ColorOption.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
public enum ColorOption: String, CaseIterable, Equatable {
case auto
case always
case never

public static let `default` = ColorOption.auto

init?(anyValue: Any) {
if let option = anyValue as? ColorOption {
self = option
return
}
guard let stringValue = anyValue as? String else { return nil }

self.init(rawValue: stringValue)
}
}
12 changes: 9 additions & 3 deletions Sources/Configuration/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ public final class Configuration {
@Setting(key: "quiet", defaultValue: false)
public var quiet: Bool

@Setting(key: "color", defaultValue: true)
public var color: Bool
@Setting(key: "color", defaultValue: .default, setter: { ColorOption(anyValue: $0) })
public var color: ColorOption

@Setting(key: "disable_update_check", defaultValue: false)
public var disableUpdateCheck: Bool
Expand Down Expand Up @@ -174,7 +174,7 @@ public final class Configuration {

let encodedYAML = try String(contentsOf: path.url, encoding: .utf8)
let yaml = try Yams.load(yaml: encodedYAML) as? [String: Any] ?? [:]
let logger = Logger(quiet: false, verbose: false, coloredOutputEnabled: false)
let logger = Logger(quiet: false, verbose: false, colorMode: .never)

for (key, value) in yaml {
if let setting = settings.first(where: { key == $0.key }) {
Expand Down Expand Up @@ -338,3 +338,9 @@ extension FilePath: Yams.ScalarRepresentable {
string.represented()
}
}

extension ColorOption: Yams.ScalarRepresentable {
public func represented() -> Node.Scalar {
rawValue.represented()
}
}
14 changes: 9 additions & 5 deletions Sources/Frontend/Commands/ScanCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct ScanCommand: ParsableCommand {
@Option(parsing: .upToNextOption, help: "Schemes to build. All targets built by these schemes will be scanned")
var schemes: [String] = defaultConfiguration.$schemes.defaultValue

@Option(help: "Output format (allowed: \(OutputFormat.allValueStrings.joined(separator: ", ")))")
@Option(help: "Output format")
var format: OutputFormat = defaultConfiguration.$outputFormat.defaultValue

@Flag(help: "Exclude test targets from indexing")
Expand Down Expand Up @@ -126,8 +126,11 @@ struct ScanCommand: ParsableCommand {
@Flag(help: "Only output results")
var quiet: Bool = defaultConfiguration.$quiet.defaultValue

@Flag(inversion: .prefixedNo, help: "Colored output")
var color: Bool = defaultConfiguration.$color.defaultValue
@Option(help: "Colored output mode")
var color: ColorOption = defaultConfiguration.$color.defaultValue

@Flag(name: .customLong("no-color"), help: .hidden)
var noColor: Bool = false

@Option(help: "JSON package manifest path (obtained using `swift package describe --type json` or manually)")
var jsonPackageManifestPath: FilePath?
Expand Down Expand Up @@ -191,7 +194,7 @@ struct ScanCommand: ParsableCommand {
configuration.apply(\.$externalTestCaseClasses, externalTestCaseClasses)
configuration.apply(\.$verbose, verbose)
configuration.apply(\.$quiet, quiet)
configuration.apply(\.$color, color)
configuration.apply(\.$color, noColor ? .never : color)
configuration.apply(\.$disableUpdateCheck, disableUpdateCheck)
configuration.apply(\.$strict, strict)
configuration.apply(\.$indexStorePath, indexStorePath)
Expand Down Expand Up @@ -268,7 +271,7 @@ struct ScanCommand: ParsableCommand {

let outputFormat = configuration.outputFormat
let formatter = outputFormat.formatter.init(configuration: configuration, logger: logger)
let colored = outputFormat.supportsColoredOutput && configuration.color
let colored = outputFormat.supportsColoredOutput && logger.isColoredOutputEnabled

if let output = try formatter.format(filteredResults, colored: colored) {
if outputFormat.supportsAuxiliaryOutput {
Expand Down Expand Up @@ -309,6 +312,7 @@ struct ScanCommand: ParsableCommand {
}

extension OutputFormat: ExpressibleByArgument {}
extension ColorOption: ExpressibleByArgument {}

extension FilePath: ArgumentParser.ExpressibleByArgument {
public init?(argument: String) {
Expand Down
7 changes: 6 additions & 1 deletion Sources/Frontend/Logger+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ import Logger
public extension Logger {
@inlinable
init(configuration: Configuration) {
let colorMode: LoggerColorMode = switch configuration.color {
case .auto: .auto
case .always: .always
case .never: .never
}
self.init(
quiet: configuration.quiet,
verbose: configuration.verbose,
coloredOutputEnabled: configuration.color
colorMode: colorMode
)
}
}
43 changes: 30 additions & 13 deletions Sources/Logger/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,46 @@ public enum ANSIColor: String {
case gray = "\u{001B}[0;1;30m"
}

public enum LoggerColorMode: Sendable {
case auto
case always
case never
}

public struct Logger: Sendable {
let outputQueue: DispatchQueue
let quiet: Bool
let verbose: Bool
let coloredOutputEnabled: Bool
let colorMode: LoggerColorMode

#if canImport(os)
let signposter = OSSignposter()
#endif

private var isColorOutputCapable: Bool = {
guard let term = ProcessInfo.processInfo.environment["TERM"],
term.lowercased() != "dumb",
isatty(fileno(stdout)) != 0
else {
return false
public var isColoredOutputEnabled: Bool {
switch colorMode {
case .auto:
isColorOutputCapable
case .always:
true
case .never:
false
}

return true
}()
}

public init(
quiet: Bool,
verbose: Bool,
coloredOutputEnabled: Bool
colorMode: LoggerColorMode
) {
self.quiet = quiet
self.verbose = verbose
self.coloredOutputEnabled = coloredOutputEnabled
self.colorMode = colorMode
outputQueue = DispatchQueue(label: "Logger.outputQueue")
}

public func colorize(_ text: String, _ color: ANSIColor) -> String {
guard isColorOutputCapable, coloredOutputEnabled else { return text }
guard isColoredOutputEnabled else { return text }

return "\(color.rawValue)\(text)\u{001B}[0;0m"
}
Expand Down Expand Up @@ -112,6 +118,17 @@ public struct Logger: Sendable {
func log(_ line: String, output: UnsafeMutablePointer<FILE>) {
_ = outputQueue.sync { fputs(line + "\n", output) }
}

private var isColorOutputCapable: Bool = {
guard let term = ProcessInfo.processInfo.environment["TERM"],
term.lowercased() != "dumb",
isatty(fileno(stdout)) != 0
else {
return false
}

return true
}()
}

public struct ContextualLogger: Sendable {
Expand Down
2 changes: 1 addition & 1 deletion Tests/PeripheryTests/Syntax/FunctionVisitTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class FunctionVisitTest: XCTestCase {

override func setUpWithError() throws {
try super.setUpWithError()
let shell = ShellImpl(logger: Logger(quiet: true, verbose: false, coloredOutputEnabled: false))
let shell = ShellImpl(logger: Logger(quiet: true, verbose: false, colorMode: .never))
let swiftVersion = SwiftVersion(shell: shell)
let multiplexingVisitor = try MultiplexingSyntaxVisitor(file: fixturePath, swiftVersion: swiftVersion)
let visitor = multiplexingVisitor.add(DeclarationSyntaxVisitor.self)
Expand Down
2 changes: 1 addition & 1 deletion Tests/PeripheryTests/Syntax/ImportVisitTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class ImportVisitTest: XCTestCase {

override func setUpWithError() throws {
try super.setUpWithError()
let shell = ShellImpl(logger: Logger(quiet: true, verbose: false, coloredOutputEnabled: false))
let shell = ShellImpl(logger: Logger(quiet: true, verbose: false, colorMode: .never))
let swiftVersion = SwiftVersion(shell: shell)
let multiplexingVisitor = try MultiplexingSyntaxVisitor(file: fixturePath, swiftVersion: swiftVersion)
let visitor = multiplexingVisitor.add(ImportSyntaxVisitor.self)
Expand Down
2 changes: 1 addition & 1 deletion Tests/PeripheryTests/Syntax/PropertyVisitTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class PropertyVisitTest: XCTestCase {

override func setUpWithError() throws {
try super.setUpWithError()
let shell = ShellImpl(logger: Logger(quiet: true, verbose: false, coloredOutputEnabled: false))
let shell = ShellImpl(logger: Logger(quiet: true, verbose: false, colorMode: .never))
let swiftVersion = SwiftVersion(shell: shell)
let multiplexingVisitor = try MultiplexingSyntaxVisitor(file: fixturePath, swiftVersion: swiftVersion)
let visitor = multiplexingVisitor.add(DeclarationSyntaxVisitor.self)
Expand Down
2 changes: 1 addition & 1 deletion Tests/Shared/SourceGraphTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ open class SourceGraphTestCase: XCTestCase {

override open class func setUp() {
super.setUp()
logger = Logger(quiet: true, verbose: false, coloredOutputEnabled: false)
logger = Logger(quiet: true, verbose: false, colorMode: .never)
shell = ShellImpl(logger: logger)
swiftVersion = SwiftVersion(shell: shell)
let configuration = Configuration()
Expand Down
2 changes: 1 addition & 1 deletion Tests/XcodeTests/XcodeTargetTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class XcodeTargetTest: XCTestCase {

override func setUp() {
super.setUp()
let logger = Logger(quiet: true, verbose: false, coloredOutputEnabled: false)
let logger = Logger(quiet: true, verbose: false, colorMode: .never)
let shell = ShellImpl(logger: logger)
let xcodebuild = Xcodebuild(shell: shell, logger: logger)
var loadedProjectPaths: Set<FilePath> = []
Expand Down
2 changes: 1 addition & 1 deletion Tests/XcodeTests/XcodebuildBuildProjectTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ final class XcodebuildBuildProjectTest: XCTestCase {
override func setUp() {
super.setUp()

let logger = Logger(quiet: true, verbose: false, coloredOutputEnabled: false)
let logger = Logger(quiet: true, verbose: false, colorMode: .never)
let shell = ShellImpl(logger: logger)
var loadedProjectPaths: Set<FilePath> = []
xcodebuild = Xcodebuild(shell: shell, logger: logger)
Expand Down
2 changes: 1 addition & 1 deletion Tests/XcodeTests/XcodebuildSchemesTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ final class XcodebuildSchemesTest: XCTestCase {
func testParseSchemes() {
for output in XcodebuildListOutputs {
let shell = ShellMock(output: output)
let logger = Logger(quiet: true, verbose: false, coloredOutputEnabled: false)
let logger = Logger(quiet: true, verbose: false, colorMode: .never)
var loadedProjectPaths: Set<FilePath> = []
let xcodebuild = Xcodebuild(shell: shell, logger: logger)
let project = try! XcodeProject(path: UIKitProjectPath, loadedProjectPaths: &loadedProjectPaths, xcodebuild: xcodebuild, shell: shell, logger: logger)
Expand Down