From f7ee3b4ca5339791b08d20568270908d5e71e7ea Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 20 Jul 2021 19:03:21 -0700 Subject: std.Progress: revert to the older strategy This reverts the most recent big changes to `std.Progress` changing the strategy for printing. Before the changes, it would leave the cursor after the progress line, having better behavior when a stray print happened, and supporting sub-process progress without any coordination. After the changes, the cursor was left at the beginning of the line, making any prints print garbage and often interfering with stack traces or other debug information. This commit reverts to before the changes. Revert "std: Use more common escape sequences in Progress" This reverts commit 8ebb18d9da0bfbe6a974636fd36e3391d1de253b. Revert "Handle some weird edge cases of Win32 API" This reverts commit b0724a350f07c5e2e8fab572951ffaaa92860b2c. Revert "Fix many thinkos" This reverts commit b5a50a26ebac6a08dacf79f5d1db9bdd94ba33a5. Revert "Fix Progress printing on Windows systems" This reverts commit 3010bfb08af0b47d801d492e4f2e21a988e8399a. Revert "std: Better handling of line-wrapping in Progress" This reverts commit 4fc2e92876d8aafd087a5f0bdb6ea7a54f195704. --- lib/std/Progress.zig | 135 +++++++++++++++++++++++---------------------------- 1 file changed, 61 insertions(+), 74 deletions(-) (limited to 'lib/std') diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 9afb93348a..ba60f91233 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -63,6 +63,10 @@ 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 { @@ -160,6 +164,7 @@ 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,15 +192,6 @@ pub fn refresh(self: *Progress) void { return self.refreshWithHeldLock(); } -// ED -- Clear screen -const ED = "\x1b[J"; -// DECSC -- Save cursor position -const DECSC = "\x1b7"; -// DECRC -- Restore cursor position -const DECRC = "\x1b8"; -// Note that ESC7/ESC8 are used instead of CSI s/CSI u as the latter are not -// supported by some terminals (eg. Terminal.app). - fn refreshWithHeldLock(self: *Progress) void { const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal; if (is_dumb and self.dont_print_on_dumb) return; @@ -203,54 +199,59 @@ fn refreshWithHeldLock(self: *Progress) void { const file = self.terminal orelse return; var end: usize = 0; - // 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: { - std.debug.assert(self.is_windows_terminal); - - 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); - }; - - 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.FillConsoleOutputCharacterW( - file.handle, - ' ', - fill_chars, - saved_cursor_pos, - &written, - ) != windows.TRUE) { - unreachable; + 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: { + std.debug.assert(self.is_windows_terminal); + + 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.FillConsoleOutputCharacterW( + 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; } + + self.columns_written = 0; } if (!self.done) { @@ -285,28 +286,10 @@ 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 (!self.is_windows_terminal) { - self.output_buffer[end] = '\n'; - end += 1; - } - _ = file.write(self.output_buffer[0..end]) catch { // Stop trying to write to this file once it errors. self.terminal = null; }; - - if (std.builtin.os.tag == .windows) { - if (self.is_windows_terminal) { - const res = windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos); - std.debug.assert(res == windows.TRUE); - } - } - self.prev_refresh_timestamp = self.timer.read(); } @@ -317,14 +300,17 @@ 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; }, } @@ -332,6 +318,7 @@ 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; } -- cgit v1.2.3