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,
6363/// while it was still being accessed by the `refresh` function.
6464update_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
6670/// Represents one unit of progress. Each node can have children nodes, or
6771/// one can use integers with `update`.
6872pub const Node = struct {
......@@ -160,6 +164,7 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
160164 .unprotected_estimated_total_items = estimated_total_items,
161165 .unprotected_completed_items = 0,
162166 };
167 self.columns_written = 0;
163168 self.prev_refresh_timestamp = 0;
164169 self.timer = try std.time.Timer.start();
165170 self.done = false;
......@@ -187,15 +192,6 @@ pub fn refresh(self: *Progress) void {
187192 return self.refreshWithHeldLock();
188193}
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
199195fn refreshWithHeldLock(self: *Progress) void {
200196 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
201197 if (is_dumb and self.dont_print_on_dumb) return;
......@@ -203,54 +199,59 @@ fn refreshWithHeldLock(self: *Progress) void {
203199 const file = self.terminal orelse return;
204200
205201 var end: usize = 0;
206 // Save the cursor position and clear the part of the screen below.
207 // Clearing only the line is not enough as the terminal may wrap the line
208 // when it becomes too long.
209 var saved_cursor_pos: windows.COORD = undefined;
210 if (self.supports_ansi_escape_codes) {
211 const seq_before = DECSC ++ ED;
212 std.mem.copy(u8, self.output_buffer[end..], seq_before);
213 end += seq_before.len;
214 } else if (std.builtin.os.tag == .windows) winapi: {
215 std.debug.assert(self.is_windows_terminal);
216
217 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
218 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
219 unreachable;
220
221 saved_cursor_pos = info.dwCursorPosition;
222
223 const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top + 1);
224 const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left + 1);
225 // Number of terminal cells to clear, starting from the cursor position
226 // and ending at the window bottom right corner.
227 const fill_chars = if (window_width == 0 or window_height == 0) 0 else chars: {
228 break :chars window_width * (window_height -
229 @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) -
230 @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left);
231 };
232
233 var written: windows.DWORD = undefined;
234 if (windows.kernel32.FillConsoleOutputAttribute(
235 file.handle,
236 info.wAttributes,
237 fill_chars,
238 saved_cursor_pos,
239 &written,
240 ) != windows.TRUE) {
241 // Stop trying to write to this file.
242 self.terminal = null;
243 break :winapi;
244 }
245 if (windows.kernel32.FillConsoleOutputCharacterW(
246 file.handle,
247 ' ',
248 fill_chars,
249 saved_cursor_pos,
250 &written,
251 ) != windows.TRUE) {
252 unreachable;
202 if (self.columns_written > 0) {
203 // restore the cursor position by moving the cursor
204 // `columns_written` cells to the left, then clear the rest of the
205 // line
206 if (self.supports_ansi_escape_codes) {
207 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
208 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
209 } else if (std.builtin.os.tag == .windows) winapi: {
210 std.debug.assert(self.is_windows_terminal);
211
212 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
213 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
214 unreachable;
215
216 var cursor_pos = windows.COORD{
217 .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
218 .Y = info.dwCursorPosition.Y,
219 };
220
221 if (cursor_pos.X < 0)
222 cursor_pos.X = 0;
223
224 const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
225
226 var written: windows.DWORD = undefined;
227 if (windows.kernel32.FillConsoleOutputAttribute(
228 file.handle,
229 info.wAttributes,
230 fill_chars,
231 cursor_pos,
232 &written,
233 ) != windows.TRUE) {
234 // Stop trying to write to this file.
235 self.terminal = null;
236 break :winapi;
237 }
238 if (windows.kernel32.FillConsoleOutputCharacterW(
239 file.handle,
240 ' ',
241 fill_chars,
242 cursor_pos,
243 &written,
244 ) != windows.TRUE) unreachable;
245
246 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
247 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;
253252 }
253
254 self.columns_written = 0;
254255 }
255256
256257 if (!self.done) {
......@@ -285,28 +286,10 @@ fn refreshWithHeldLock(self: *Progress) void {
285286 }
286287 }
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
298289 _ = file.write(self.output_buffer[0..end]) catch {
299290 // Stop trying to write to this file once it errors.
300291 self.terminal = null;
301292 };
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
310293 self.prev_refresh_timestamp = self.timer.read();
311294}
312295
......@@ -317,14 +300,17 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
317300 self.terminal = null;
318301 return;
319302 };
303 self.columns_written = 0;
320304}
321305
322306fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
323307 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
324308 const amt = written.len;
325309 end.* += amt;
310 self.columns_written += amt;
326311 } else |err| switch (err) {
327312 error.NoSpaceLeft => {
313 self.columns_written += self.output_buffer.len - end.*;
328314 end.* = self.output_buffer.len;
329315 },
330316 }
......@@ -332,6 +318,7 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any
332318 const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
333319 if (end.* > max_end) {
334320 const suffix = "... ";
321 self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
335322 std.mem.copy(u8, self.output_buffer[max_end..], suffix);
336323 end.* = max_end + suffix.len;
337324 }