aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/Progress.zig128
1 files changed, 68 insertions, 60 deletions
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
index 4afd191b93..3f462f5c08 100644
--- a/lib/std/Progress.zig
+++ b/lib/std/Progress.zig
@@ -59,10 +59,6 @@ done: bool = true,
/// while it was still being accessed by the `refresh` function.
update_lock: std.Thread.Mutex = .{},
-/// Keeps track of how many columns in the terminal have been output, so that
-/// we can move the cursor back later.
-columns_written: usize = undefined,
-
/// Represents one unit of progress. Each node can have children nodes, or
/// one can use integers with `update`.
pub const Node = struct {
@@ -159,7 +155,6 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
.unprotected_estimated_total_items = estimated_total_items,
.unprotected_completed_items = 0,
};
- self.columns_written = 0;
self.prev_refresh_timestamp = 0;
self.timer = try std.time.Timer.start();
self.done = false;
@@ -187,64 +182,65 @@ pub fn refresh(self: *Progress) void {
return self.refreshWithHeldLock();
}
+// ED -- Clear screen
+const ED = "\x1b[J";
+// DECSC -- Save cursor position
+const DECSC = "\x1b[s";
+// DECRC -- Restore cursor position
+const DECRC = "\x1b[u";
+
fn refreshWithHeldLock(self: *Progress) void {
const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows);
if (is_dumb and self.dont_print_on_dumb) return;
const file = self.terminal orelse return;
- const prev_columns_written = self.columns_written;
var end: usize = 0;
- if (self.columns_written > 0) {
- // restore the cursor position by moving the cursor
- // `columns_written` cells to the left, then clear the rest of the
- // line
- if (self.supports_ansi_escape_codes) {
- end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
- end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
- } else if (std.builtin.os.tag == .windows) winapi: {
- var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
- if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
- unreachable;
-
- var cursor_pos = windows.COORD{
- .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
- .Y = info.dwCursorPosition.Y,
- };
-
- if (cursor_pos.X < 0)
- cursor_pos.X = 0;
-
- const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
-
- var written: windows.DWORD = undefined;
- if (windows.kernel32.FillConsoleOutputAttribute(
- file.handle,
- info.wAttributes,
- fill_chars,
- cursor_pos,
- &written,
- ) != windows.TRUE) {
- // Stop trying to write to this file.
- self.terminal = null;
- break :winapi;
- }
- if (windows.kernel32.FillConsoleOutputCharacterA(
- file.handle,
- ' ',
- fill_chars,
- cursor_pos,
- &written,
- ) != windows.TRUE) unreachable;
-
- if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
- unreachable;
- } else {
- // we are in a "dumb" terminal like in acme or writing to a file
- self.output_buffer[end] = '\n';
- end += 1;
- }
+ // Save the cursor position and clear the part of the screen below.
+ // Clearing only the line is not enough as the terminal may wrap the line
+ // when it becomes too long.
+ var saved_cursor_pos: windows.COORD = undefined;
+ if (self.supports_ansi_escape_codes) {
+ const seq_before = DECSC ++ ED;
+ std.mem.copy(u8, self.output_buffer[end..], seq_before);
+ end += seq_before.len;
+ } else if (std.builtin.os.tag == .windows) winapi: {
+ var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
+ if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
+ unreachable;
+
+ saved_cursor_pos = info.dwCursorPosition;
+
+ const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top + 1);
+ const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left + 1);
+ // Number of terminal cells to clear, starting from the cursor position
+ // and ending at the window bottom right corner.
+ const fill_chars = if (window_width == 0 or window_height == 0) 0 else chars: {
+ break :chars window_width * (window_height -
+ @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) -
+ @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left);
+ };
- self.columns_written = 0;
+ var written: windows.DWORD = undefined;
+ if (windows.kernel32.FillConsoleOutputAttribute(
+ file.handle,
+ info.wAttributes,
+ fill_chars,
+ saved_cursor_pos,
+ &written,
+ ) != windows.TRUE) {
+ // Stop trying to write to this file.
+ self.terminal = null;
+ break :winapi;
+ }
+ if (windows.kernel32.FillConsoleOutputCharacterA(
+ file.handle,
+ ' ',
+ fill_chars,
+ saved_cursor_pos,
+ &written,
+ ) != windows.TRUE) {
+ unreachable;
+ }
}
if (!self.done) {
@@ -279,10 +275,26 @@ fn refreshWithHeldLock(self: *Progress) void {
}
}
+ // We're done printing the updated message, restore the cursor position.
+ if (self.supports_ansi_escape_codes) {
+ const seq_after = DECRC;
+ std.mem.copy(u8, self.output_buffer[end..], seq_after);
+ end += seq_after.len;
+ } else if (std.builtin.os.tag != .windows) {
+ self.output_buffer[end] = '\n';
+ end += 1;
+ }
+
_ = file.write(self.output_buffer[0..end]) catch |e| {
// Stop trying to write to this file once it errors.
self.terminal = null;
};
+
+ if (std.builtin.os.tag == .windows) {
+ if (windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos) != windows.TRUE)
+ unreachable;
+ }
+
self.prev_refresh_timestamp = self.timer.read();
}
@@ -293,17 +305,14 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
self.terminal = null;
return;
};
- self.columns_written = 0;
}
fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
const amt = written.len;
end.* += amt;
- self.columns_written += amt;
} else |err| switch (err) {
error.NoSpaceLeft => {
- self.columns_written += self.output_buffer.len - end.*;
end.* = self.output_buffer.len;
},
}
@@ -311,7 +320,6 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any
const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
if (end.* > max_end) {
const suffix = "... ";
- self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
std.mem.copy(u8, self.output_buffer[max_end..], suffix);
end.* = max_end + suffix.len;
}