mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
Merge pull request #24564 from ziglang/terminal-progress-bar
std.Progress: support progress bar escape codes
This commit is contained in:
commit
fc4b7c968a
2 changed files with 75 additions and 3 deletions
|
|
@ -696,8 +696,11 @@ fn runStepNames(
|
|||
.failures, .none => true,
|
||||
else => false,
|
||||
};
|
||||
if (failure_count == 0 and failures_only) {
|
||||
return run.cleanExit();
|
||||
if (failure_count == 0) {
|
||||
std.Progress.setStatus(.success);
|
||||
if (failures_only) return run.cleanExit();
|
||||
} else {
|
||||
std.Progress.setStatus(.failure);
|
||||
}
|
||||
|
||||
const ttyconf = run.ttyconf;
|
||||
|
|
@ -1149,6 +1152,7 @@ fn workerMakeOneStep(
|
|||
} else |err| switch (err) {
|
||||
error.MakeFailed => {
|
||||
@atomicStore(Step.State, &s.state, .failure, .seq_cst);
|
||||
std.Progress.setStatus(.failure_working);
|
||||
break :handle_result;
|
||||
},
|
||||
error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .seq_cst),
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ redraw_event: std.Thread.ResetEvent,
|
|||
/// Accessed atomically.
|
||||
done: bool,
|
||||
need_clear: bool,
|
||||
status: Status,
|
||||
|
||||
refresh_rate_ns: u64,
|
||||
initial_delay_ns: u64,
|
||||
|
|
@ -47,6 +48,22 @@ node_freelist: Freelist,
|
|||
/// value may at times temporarily exceed the node count.
|
||||
node_end_index: u32,
|
||||
|
||||
pub const Status = enum {
|
||||
/// Indicates the application is progressing towards completion of a task.
|
||||
/// Unless the application is interactive, this is the only status the
|
||||
/// program will ever have!
|
||||
working,
|
||||
/// The application has completed an operation, and is now waiting for user
|
||||
/// input rather than calling exit(0).
|
||||
success,
|
||||
/// The application encountered an error, and is now waiting for user input
|
||||
/// rather than calling exit(1).
|
||||
failure,
|
||||
/// The application encountered at least one error, but is still working on
|
||||
/// more tasks.
|
||||
failure_working,
|
||||
};
|
||||
|
||||
const Freelist = packed struct(u32) {
|
||||
head: Node.OptionalIndex,
|
||||
/// Whenever `node_freelist` is added to, this generation is incremented
|
||||
|
|
@ -383,6 +400,7 @@ var global_progress: Progress = .{
|
|||
.draw_buffer = undefined,
|
||||
.done = false,
|
||||
.need_clear = false,
|
||||
.status = .working,
|
||||
|
||||
.node_parents = &node_parents_buffer,
|
||||
.node_storage = &node_storage_buffer,
|
||||
|
|
@ -498,6 +516,11 @@ pub fn start(options: Options) Node {
|
|||
return root_node;
|
||||
}
|
||||
|
||||
pub fn setStatus(new_status: Status) void {
|
||||
if (noop_impl) return;
|
||||
@atomicStore(Status, &global_progress.status, new_status, .monotonic);
|
||||
}
|
||||
|
||||
/// Returns whether a resize is needed to learn the terminal size.
|
||||
fn wait(timeout_ns: u64) bool {
|
||||
const resize_flag = if (global_progress.redraw_event.timedWait(timeout_ns)) |_|
|
||||
|
|
@ -678,6 +701,14 @@ const save = "\x1b7";
|
|||
const restore = "\x1b8";
|
||||
const finish_sync = "\x1b[?2026l";
|
||||
|
||||
const progress_remove = "\x1b]9;4;0\x07";
|
||||
const @"progress_normal {d}" = "\x1b]9;4;1;{d}\x07";
|
||||
const @"progress_error {d}" = "\x1b]9;4;2;{d}\x07";
|
||||
const progress_pulsing = "\x1b]9;4;3\x07";
|
||||
const progress_pulsing_error = "\x1b]9;4;2\x07";
|
||||
const progress_normal_100 = "\x1b]9;4;1;100\x07";
|
||||
const progress_error_100 = "\x1b]9;4;2;100\x07";
|
||||
|
||||
const TreeSymbol = enum {
|
||||
/// ├─
|
||||
tee,
|
||||
|
|
@ -760,7 +791,7 @@ fn clearWrittenWithEscapeCodes() anyerror!void {
|
|||
if (noop_impl or !global_progress.need_clear) return;
|
||||
|
||||
global_progress.need_clear = false;
|
||||
try write(clear);
|
||||
try write(clear ++ progress_remove);
|
||||
}
|
||||
|
||||
/// U+25BA or ►
|
||||
|
|
@ -1203,6 +1234,43 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) struct { []u8, usize } {
|
|||
i, const nl_n = computeNode(buf, i, 0, serialized, children, root_node_index);
|
||||
|
||||
if (global_progress.terminal_mode == .ansi_escape_codes) {
|
||||
{
|
||||
// Set progress state https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
|
||||
const root_storage = &serialized.storage[0];
|
||||
const storage = if (root_storage.name[0] != 0 or children[0].child == .none) root_storage else &serialized.storage[@intFromEnum(children[0].child)];
|
||||
const estimated_total = storage.estimated_total_count;
|
||||
const completed_items = storage.completed_count;
|
||||
const status = @atomicLoad(Status, &global_progress.status, .monotonic);
|
||||
switch (status) {
|
||||
.working => {
|
||||
if (estimated_total == 0) {
|
||||
buf[i..][0..progress_pulsing.len].* = progress_pulsing.*;
|
||||
i += progress_pulsing.len;
|
||||
} else {
|
||||
const percent = completed_items * 100 / estimated_total;
|
||||
i += (std.fmt.bufPrint(buf[i..], @"progress_normal {d}", .{percent}) catch &.{}).len;
|
||||
}
|
||||
},
|
||||
.success => {
|
||||
buf[i..][0..progress_remove.len].* = progress_remove.*;
|
||||
i += progress_remove.len;
|
||||
},
|
||||
.failure => {
|
||||
buf[i..][0..progress_error_100.len].* = progress_error_100.*;
|
||||
i += progress_error_100.len;
|
||||
},
|
||||
.failure_working => {
|
||||
if (estimated_total == 0) {
|
||||
buf[i..][0..progress_pulsing_error.len].* = progress_pulsing_error.*;
|
||||
i += progress_pulsing_error.len;
|
||||
} else {
|
||||
const percent = completed_items * 100 / estimated_total;
|
||||
i += (std.fmt.bufPrint(buf[i..], @"progress_error {d}", .{percent}) catch &.{}).len;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (nl_n > 0) {
|
||||
buf[i] = '\r';
|
||||
i += 1;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue