authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 19:03:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 19:11:47-07:00
logf7ee3b4ca5339791b08d20568270908d5e71e7ea
treedd67e75fe2675dd1aabc23160cd71c164495ac32
parent885477e2dfe1176a31dc0a2e8140a37ffb1237cd

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.

1 files changed, 61 insertions(+), 74 deletions(-)

lib/std/Progress.zig+61-74
...@@ -63,6 +63,10 @@ done: bool = true,...@@ -63,6 +63,10 @@ done: bool = true,
63/// while it was still being accessed by the `refresh` function.63/// while it was still being accessed by the `refresh` function.
64update_lock: std.Thread.Mutex = .{},64update_lock: std.Thread.Mutex = .{},
6565
66/// Keeps track of how many columns in the terminal have been output, so that
67/// we can move the cursor back later.
68columns_written: usize = undefined,
69
66/// Represents one unit of progress. Each node can have children nodes, or70/// Represents one unit of progress. Each node can have children nodes, or
67/// one can use integers with `update`.71/// one can use integers with `update`.
68pub const Node = struct {72pub const Node = struct {
...@@ -160,6 +164,7 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*...@@ -160,6 +164,7 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
160 .unprotected_estimated_total_items = estimated_total_items,164 .unprotected_estimated_total_items = estimated_total_items,
161 .unprotected_completed_items = 0,165 .unprotected_completed_items = 0,
162 };166 };
167 self.columns_written = 0;
163 self.prev_refresh_timestamp = 0;168 self.prev_refresh_timestamp = 0;
164 self.timer = try std.time.Timer.start();169 self.timer = try std.time.Timer.start();
165 self.done = false;170 self.done = false;
...@@ -187,15 +192,6 @@ pub fn refresh(self: *Progress) void {...@@ -187,15 +192,6 @@ pub fn refresh(self: *Progress) void {
187 return self.refreshWithHeldLock();192 return self.refreshWithHeldLock();
188}193}
189194
190// ED -- Clear screen
191const ED = "\x1b[J";
192// DECSC -- Save cursor position
193const DECSC = "\x1b7";
194// DECRC -- Restore cursor position
195const DECRC = "\x1b8";
196// Note that ESC7/ESC8 are used instead of CSI s/CSI u as the latter are not
197// supported by some terminals (eg. Terminal.app).
198
199fn refreshWithHeldLock(self: *Progress) void {195fn refreshWithHeldLock(self: *Progress) void {
200 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;196 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
201 if (is_dumb and self.dont_print_on_dumb) return;197 if (is_dumb and self.dont_print_on_dumb) return;
...@@ -203,54 +199,59 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -203,54 +199,59 @@ fn refreshWithHeldLock(self: *Progress) void {
203 const file = self.terminal orelse return;199 const file = self.terminal orelse return;
204200
205 var end: usize = 0;201 var end: usize = 0;
206 // Save the cursor position and clear the part of the screen below.202 if (self.columns_written > 0) {
207 // Clearing only the line is not enough as the terminal may wrap the line203 // restore the cursor position by moving the cursor
208 // when it becomes too long.204 // `columns_written` cells to the left, then clear the rest of the
209 var saved_cursor_pos: windows.COORD = undefined;205 // line
210 if (self.supports_ansi_escape_codes) {206 if (self.supports_ansi_escape_codes) {
211 const seq_before = DECSC ++ ED;207 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
212 std.mem.copy(u8, self.output_buffer[end..], seq_before);208 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
213 end += seq_before.len;209 } else if (std.builtin.os.tag == .windows) winapi: {
214 } else if (std.builtin.os.tag == .windows) winapi: {210 std.debug.assert(self.is_windows_terminal);
215 std.debug.assert(self.is_windows_terminal);211
216212 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
217 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;213 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
218 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)214 unreachable;
219 unreachable;215
220216 var cursor_pos = windows.COORD{
221 saved_cursor_pos = info.dwCursorPosition;217 .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
222218 .Y = info.dwCursorPosition.Y,
223 const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top + 1);219 };
224 const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left + 1);220
225 // Number of terminal cells to clear, starting from the cursor position221 if (cursor_pos.X < 0)
226 // and ending at the window bottom right corner.222 cursor_pos.X = 0;
227 const fill_chars = if (window_width == 0 or window_height == 0) 0 else chars: {223
228 break :chars window_width * (window_height -224 const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
229 @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) -225
230 @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left);226 var written: windows.DWORD = undefined;
231 };227 if (windows.kernel32.FillConsoleOutputAttribute(
232228 file.handle,
233 var written: windows.DWORD = undefined;229 info.wAttributes,
234 if (windows.kernel32.FillConsoleOutputAttribute(230 fill_chars,
235 file.handle,231 cursor_pos,
236 info.wAttributes,232 &written,
237 fill_chars,233 ) != windows.TRUE) {
238 saved_cursor_pos,234 // Stop trying to write to this file.
239 &written,235 self.terminal = null;
240 ) != windows.TRUE) {236 break :winapi;
241 // Stop trying to write to this file.237 }
242 self.terminal = null;238 if (windows.kernel32.FillConsoleOutputCharacterW(
243 break :winapi;239 file.handle,
244 }240 ' ',
245 if (windows.kernel32.FillConsoleOutputCharacterW(241 fill_chars,
246 file.handle,242 cursor_pos,
247 ' ',243 &written,
248 fill_chars,244 ) != windows.TRUE) unreachable;
249 saved_cursor_pos,245
250 &written,246 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
251 ) != windows.TRUE) {247 unreachable;
252 unreachable;248 } else {
249 // we are in a "dumb" terminal like in acme or writing to a file
250 self.output_buffer[end] = '\n';
251 end += 1;
253 }252 }
253
254 self.columns_written = 0;
254 }255 }
255256
256 if (!self.done) {257 if (!self.done) {
...@@ -285,28 +286,10 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -285,28 +286,10 @@ fn refreshWithHeldLock(self: *Progress) void {
285 }286 }
286 }287 }
287288
288 // We're done printing the updated message, restore the cursor position.
289 if (self.supports_ansi_escape_codes) {
290 const seq_after = DECRC;
291 std.mem.copy(u8, self.output_buffer[end..], seq_after);
292 end += seq_after.len;
293 } else if (!self.is_windows_terminal) {
294 self.output_buffer[end] = '\n';
295 end += 1;
296 }
297
298 _ = file.write(self.output_buffer[0..end]) catch {289 _ = file.write(self.output_buffer[0..end]) catch {
299 // Stop trying to write to this file once it errors.290 // Stop trying to write to this file once it errors.
300 self.terminal = null;291 self.terminal = null;
301 };292 };
302
303 if (std.builtin.os.tag == .windows) {
304 if (self.is_windows_terminal) {
305 const res = windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos);
306 std.debug.assert(res == windows.TRUE);
307 }
308 }
309
310 self.prev_refresh_timestamp = self.timer.read();293 self.prev_refresh_timestamp = self.timer.read();
311}294}
312295
...@@ -317,14 +300,17 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {...@@ -317,14 +300,17 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
317 self.terminal = null;300 self.terminal = null;
318 return;301 return;
319 };302 };
303 self.columns_written = 0;
320}304}
321305
322fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {306fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
323 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {307 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
324 const amt = written.len;308 const amt = written.len;
325 end.* += amt;309 end.* += amt;
310 self.columns_written += amt;
326 } else |err| switch (err) {311 } else |err| switch (err) {
327 error.NoSpaceLeft => {312 error.NoSpaceLeft => {
313 self.columns_written += self.output_buffer.len - end.*;
328 end.* = self.output_buffer.len;314 end.* = self.output_buffer.len;
329 },315 },
330 }316 }
...@@ -332,6 +318,7 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any...@@ -332,6 +318,7 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any
332 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;318 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
333 if (end.* > max_end) {319 if (end.* > max_end) {
334 const suffix = "... ";320 const suffix = "... ";
321 self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
335 std.mem.copy(u8, self.output_buffer[max_end..], suffix);322 std.mem.copy(u8, self.output_buffer[max_end..], suffix);
336 end.* = max_end + suffix.len;323 end.* = max_end + suffix.len;
337 }324 }