Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't animate ActivityIndicator when outputting to a file #202

Merged
merged 3 commits into from
Aug 20, 2024
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
2 changes: 1 addition & 1 deletion Sources/ConsoleKitTerminal/Activity/ActivityBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extension ActivityBar {
public func outputActivityIndicator(to console: any Console, state: ActivityIndicatorState) {
let bar: ConsoleText
switch state {
case .ready: bar = "[]"
case .ready: bar = "[...]"
case .active(let tick): bar = renderActiveBar(tick: tick, width: console.activityBarWidth)
case .success: bar = "[Done]".consoleText(.success)
case .failure: bar = "[Failed]".consoleText(.error)
Expand Down
19 changes: 12 additions & 7 deletions Sources/ConsoleKitTerminal/Activity/ActivityIndicator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ public final class ActivityIndicator<A>: Sendable where A: ActivityIndicatorType

/// The `Console` this `ActivityIndicator` is running on.
private let console: any Console

/// Current state.
private let state: NIOLockedValueBox<ActivityIndicatorState>

/// The queue on which to handle timer events
private let queue: DispatchQueue
Expand All @@ -74,10 +71,14 @@ public final class ActivityIndicator<A>: Sendable where A: ActivityIndicatorType
/// Creates a new `ActivityIndicator`. Use `ActivityIndicatorType.newActivity(for:)`.
init(activity: A, console: any Console, targetQueue: DispatchQueue? = nil) {
self.console = console
self.state = NIOLockedValueBox(.ready)
self._activity = NIOLockedValueBox(activity)
self.queue = DispatchQueue(label: "codes.vapor.consolekit.activityindicator", target: targetQueue)
self._timer = NIOLockedValueBox(DispatchSource.makeTimerSource(flags: [], queue: self.queue) as! DispatchSource)

let timer = DispatchSource.makeTimerSource(flags: [], queue: self.queue) as! DispatchSource
// Activate the timer in case the activity indicator is never started
timer.activate()
self._timer = NIOLockedValueBox(timer)

self.stopGroup = DispatchGroup()
}

Expand All @@ -90,6 +91,12 @@ public final class ActivityIndicator<A>: Sendable where A: ActivityIndicatorType
/// - refreshRate: The time interval (specified in milliseconds) to use
/// when updating the activity.
public func start(refreshRate: Int = 40) {
guard console.supportsANSICommands else {
// Skip animations if the console does not support ANSI commands
self.activity.outputActivityIndicator(to: self.console, state: .ready)
return
}

self.timer.schedule(
deadline: DispatchTime.now(),
repeating: .milliseconds(refreshRate),
Expand All @@ -113,8 +120,6 @@ public final class ActivityIndicator<A>: Sendable where A: ActivityIndicatorType
}
self.stopGroup.leave()
}

self.timer.resume()
}

/// Stops the `ActivityIndicator`, yielding a failed / error appearance.
Expand Down
17 changes: 17 additions & 0 deletions Sources/ConsoleKitTerminal/Terminal/Console.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Foundation

/// Protocol for powering styled Console I/O.
///
/// # Output
Expand Down Expand Up @@ -76,4 +78,19 @@ public protocol Console: AnyObject, Sendable {
func report(error: String, newLine: Bool)

var userInfo: [AnySendableHashable: any Sendable] { get set }

/// If the `Console` supports ANSI commands such as color and cursor movement.
var supportsANSICommands: Bool { get }
}

extension Console {
public var supportsANSICommands: Bool {
#if Xcode
// Xcode output does not support ANSI commands
return false
#else
// If STDOUT is not an interactive terminal then omit ANSI commands
return isatty(STDOUT_FILENO) > 0
#endif
}
}
6 changes: 1 addition & 5 deletions Sources/ConsoleKitTerminal/Terminal/Terminal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ public final class Terminal: Console, Sendable {
if let stylizeOverride = self.stylizedOutputOverride {
return stylizeOverride
}
#if Xcode
return false
#else
return isatty(STDOUT_FILENO) > 0
#endif
return supportsANSICommands
}

/// Create a new Terminal.
Expand Down