Skip to content

Commit 23b2680

Browse files
authored
Clear a line with spaces to prevent a race between stderr and stdout (#18)
This PR resolves a race with `\u{001B}[2K`, but not with `\r`, which will be addressed in a separate PR by moving progress updates to `stdout`. Please test it in a small window in your terminal application on the `users/a_ramani/install-ux` branch using commands: ``` bin/container system stop make cleancontent bin/container system start ``` You should see the prompt: > Install the recommended default kernel from [https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz]? [Y/n]:
1 parent a756184 commit 23b2680

File tree

2 files changed

+22
-18
lines changed

2 files changed

+22
-18
lines changed

Sources/TerminalProgress/ProgressBar+Terminal.swift

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,32 @@ import Foundation
2020
enum EscapeSequence {
2121
static let hideCursor = "\u{001B}[?25l"
2222
static let showCursor = "\u{001B}[?25h"
23-
static let clearLine = "\u{001B}[2K"
2423
static let moveUp = "\u{001B}[1A"
2524
}
2625

2726
extension ProgressBar {
27+
static var terminalWidth: Int {
28+
guard
29+
let termimalHandle = ProgressBar.term,
30+
let terminal = try? Terminal(descriptor: termimalHandle.fileDescriptor)
31+
else {
32+
return 0
33+
}
34+
35+
let terminalWidth = (try? Int(terminal.size.width)) ?? 0
36+
return terminalWidth
37+
}
38+
2839
/// Clears the progress bar and resets the cursor.
29-
static public func clearAndResetCursor() {
30-
ProgressBar.clear()
40+
public func clearAndResetCursor() {
41+
clear()
3142
ProgressBar.resetCursor()
3243
}
3344

3445
/// Clears the progress bar.
35-
static public func clear() {
36-
ProgressBar.display(EscapeSequence.clearLine)
46+
public func clear() {
47+
// We can't use "\u{001B}[2K" for clearing the line because this may lead to a race with `stdout` when using `stderr` for progress updates.
48+
displayText("")
3749
}
3850

3951
/// Resets the cursor.
@@ -59,24 +71,16 @@ extension ProgressBar {
5971
}
6072

6173
func displayText(_ text: String, terminating: String = "\r") {
62-
guard
63-
let termimalHandle = ProgressBar.term,
64-
let terminal = try? Terminal(descriptor: termimalHandle.fileDescriptor)
65-
else {
66-
return
67-
}
68-
6974
var text = text
7075

7176
// Clears previously printed characters if the new string is shorter.
7277
text += String(repeating: " ", count: max(state.output.count - text.count, 0))
7378
state.output = text
7479

7580
// Clears previously printed lines.
76-
let terminalWidth = (try? Int(terminal.size.width)) ?? 0
7781
var lines = ""
78-
if terminalWidth > 0 {
79-
let lineCount = (text.count - 1) / terminalWidth
82+
if ProgressBar.terminalWidth > 0 {
83+
let lineCount = (text.count - 1) / ProgressBar.terminalWidth
8084
for _ in 0..<lineCount {
8185
lines += EscapeSequence.moveUp
8286
}

Sources/TerminalProgress/ProgressBar.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public final class ProgressBar: Sendable {
3838
}
3939

4040
deinit {
41-
ProgressBar.clear()
41+
clear()
4242
}
4343

4444
/// Allows resetting the progress state.
@@ -48,7 +48,7 @@ public final class ProgressBar: Sendable {
4848

4949
/// Allows resetting the progress state of the current task.
5050
public func resetCurrentTask() {
51-
ProgressBar.clear()
51+
clear()
5252
state = State(description: state.description, itemsName: state.itemsName, tasks: state.tasks, totalTasks: state.totalTasks, startTime: state.startTime)
5353
}
5454

@@ -125,7 +125,7 @@ public final class ProgressBar: Sendable {
125125
}
126126

127127
if config.clearOnFinish {
128-
ProgressBar.clearAndResetCursor()
128+
clearAndResetCursor()
129129
} else {
130130
ProgressBar.resetCursor()
131131
}

0 commit comments

Comments
 (0)