authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-27 09:48:13-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-27 20:56:48-07:00
log52ffdec74b5854bc842107f40f9fa31b40cf5432
tree8fc13b63abe57cfe30e4e4286deb503b9ab0654b
parent849693f07c882fad369e557940583b8ac9d1c648

std.Progress: keep cursor on newline

Don't truncate trailing newline. This better handles stray writes to stderr that are not std.Progress-aware, such as from non-zig child processes. This commit also makes `Node.start` and `Node.end` bail out early with a comptime branch when it is known the target will not be spawning an update thread.

1 files changed, 21 insertions(+), 6 deletions(-)

lib/std/Progress.zig+21-6
...@@ -155,6 +155,10 @@ pub const Node = struct {...@@ -155,6 +155,10 @@ pub const Node = struct {
155 ///155 ///
156 /// Passing 0 for `estimated_total_items` means unknown.156 /// Passing 0 for `estimated_total_items` means unknown.
157 pub fn start(node: Node, name: []const u8, estimated_total_items: usize) Node {157 pub fn start(node: Node, name: []const u8, estimated_total_items: usize) Node {
158 if (noop_impl) {
159 assert(node.index == .none);
160 return .{ .index = .none };
161 }
158 const node_index = node.index.unwrap() orelse return .{ .index = .none };162 const node_index = node.index.unwrap() orelse return .{ .index = .none };
159 const parent = node_index.toParent();163 const parent = node_index.toParent();
160164
...@@ -208,6 +212,10 @@ pub const Node = struct {...@@ -208,6 +212,10 @@ pub const Node = struct {
208212
209 /// Finish a started `Node`. Thread-safe.213 /// Finish a started `Node`. Thread-safe.
210 pub fn end(n: Node) void {214 pub fn end(n: Node) void {
215 if (noop_impl) {
216 assert(n.index == .none);
217 return;
218 }
211 const index = n.index.unwrap() orelse return;219 const index = n.index.unwrap() orelse return;
212 const parent_ptr = parentByIndex(index);220 const parent_ptr = parentByIndex(index);
213 if (parent_ptr.unwrap()) |parent_index| {221 if (parent_ptr.unwrap()) |parent_index| {
...@@ -296,6 +304,11 @@ var default_draw_buffer: [4096]u8 = undefined;...@@ -296,6 +304,11 @@ var default_draw_buffer: [4096]u8 = undefined;
296304
297var debug_start_trace = std.debug.Trace.init;305var debug_start_trace = std.debug.Trace.init;
298306
307const noop_impl = builtin.single_threaded or switch (builtin.os.tag) {
308 .wasi, .freestanding => true,
309 else => false,
310};
311
299/// Initializes a global Progress instance.312/// Initializes a global Progress instance.
300///313///
301/// Asserts there is only one global Progress instance.314/// Asserts there is only one global Progress instance.
...@@ -319,6 +332,9 @@ pub fn start(options: Options) Node {...@@ -319,6 +332,9 @@ pub fn start(options: Options) Node {
319 global_progress.refresh_rate_ns = options.refresh_rate_ns;332 global_progress.refresh_rate_ns = options.refresh_rate_ns;
320 global_progress.initial_delay_ns = options.initial_delay_ns;333 global_progress.initial_delay_ns = options.initial_delay_ns;
321334
335 if (noop_impl)
336 return .{ .index = .none };
337
322 if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| {338 if (std.process.parseEnvVarInt("ZIG_PROGRESS", u31, 10)) |ipc_fd| {
323 global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{339 global_progress.update_thread = std.Thread.spawn(.{}, ipcThreadRun, .{
324 @as(posix.fd_t, switch (@typeInfo(posix.fd_t)) {340 @as(posix.fd_t, switch (@typeInfo(posix.fd_t)) {
...@@ -507,7 +523,7 @@ fn computeClear(buf: []u8, start_i: usize) usize {...@@ -507,7 +523,7 @@ fn computeClear(buf: []u8, start_i: usize) usize {
507 global_progress.newline_count = 0;523 global_progress.newline_count = 0;
508 buf[i] = '\r';524 buf[i] = '\r';
509 i += 1;525 i += 1;
510 for (1..prev_nl_n) |_| {526 for (0..prev_nl_n) |_| {
511 buf[i..][0..up_one_line.len].* = up_one_line.*;527 buf[i..][0..up_one_line.len].* = up_one_line.*;
512 i += up_one_line.len;528 i += up_one_line.len;
513 }529 }
...@@ -841,9 +857,6 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 {...@@ -841,9 +857,6 @@ fn computeRedraw(serialized_buffer: *Serialized.Buffer) []u8 {
841 const root_node_index: Node.Index = @enumFromInt(0);857 const root_node_index: Node.Index = @enumFromInt(0);
842 i = computeNode(buf, i, serialized, children, root_node_index);858 i = computeNode(buf, i, serialized, children, root_node_index);
843859
844 // Truncate trailing newline.
845 if (buf[i - 1] == '\n') i -= 1;
846
847 buf[i..][0..finish_sync.len].* = finish_sync.*;860 buf[i..][0..finish_sync.len].* = finish_sync.*;
848 i += finish_sync.len;861 i += finish_sync.len;
849862
...@@ -932,8 +945,10 @@ fn computeNode(...@@ -932,8 +945,10 @@ fn computeNode(
932}945}
933946
934fn withinRowLimit(p: *Progress) bool {947fn withinRowLimit(p: *Progress) bool {
935 // The +1 here is so that the PS1 is not scrolled off the top of the terminal.948 // The +2 here is so that the PS1 is not scrolled off the top of the terminal.
936 return p.newline_count + 1 < p.rows;949 // one because we keep the cursor on the next line
950 // one more to account for the PS1
951 return p.newline_count + 2 < p.rows;
937}952}
938953
939fn write(buf: []const u8) void {954fn write(buf: []const u8) void {