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
8 changes: 6 additions & 2 deletions lib/compiler/build_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand Down
70 changes: 69 additions & 1 deletion lib/std/Progress.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)) |_|
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 ►
Expand Down Expand Up @@ -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;
Expand Down