authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-16 15:03:05-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-15 10:48:12-07:00
log7ebaa05bb138ea397859edf8ec96a9209482ae8e
tree405d0f5ca9f6775d276cccfb09eada21a94947e8
parent8d384722937edcfdcb3c461d316ed97405e6e2ee

std.Progress: add lock_stderr and unlock_stderr

API users can take advantage of these to freely write to the terminal which has an ongoing progress display, similar to what Ninja does when compiling C/C++ objects and a warning or error message is printed.

1 files changed, 46 insertions(+), 19 deletions(-)

lib/std/Progress.zig+46-19
...@@ -192,32 +192,28 @@ pub fn refresh(self: *Progress) void {...@@ -192,32 +192,28 @@ pub fn refresh(self: *Progress) void {
192 return self.refreshWithHeldLock();192 return self.refreshWithHeldLock();
193}193}
194194
195fn refreshWithHeldLock(self: *Progress) void {195fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void {
196 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;196 const file = p.terminal orelse return;
197 if (is_dumb and self.dont_print_on_dumb) return;197 var end = end_ptr.*;
198198 if (p.columns_written > 0) {
199 const file = self.terminal orelse return;
200
201 var end: usize = 0;
202 if (self.columns_written > 0) {
203 // restore the cursor position by moving the cursor199 // restore the cursor position by moving the cursor
204 // `columns_written` cells to the left, then clear the rest of the200 // `columns_written` cells to the left, then clear the rest of the
205 // line201 // line
206 if (self.supports_ansi_escape_codes) {202 if (p.supports_ansi_escape_codes) {
207 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;203 end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len;
208 end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;204 end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
209 } else if (builtin.os.tag == .windows) winapi: {205 } else if (builtin.os.tag == .windows) winapi: {
210 std.debug.assert(self.is_windows_terminal);206 std.debug.assert(p.is_windows_terminal);
211207
212 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;208 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
213 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {209 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
214 // stop trying to write to this file210 // stop trying to write to this file
215 self.terminal = null;211 p.terminal = null;
216 break :winapi;212 break :winapi;
217 }213 }
218214
219 var cursor_pos = windows.COORD{215 var cursor_pos = windows.COORD{
220 .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),216 .X = info.dwCursorPosition.X - @intCast(windows.SHORT, p.columns_written),
221 .Y = info.dwCursorPosition.Y,217 .Y = info.dwCursorPosition.Y,
222 };218 };
223219
...@@ -235,7 +231,7 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -235,7 +231,7 @@ fn refreshWithHeldLock(self: *Progress) void {
235 &written,231 &written,
236 ) != windows.TRUE) {232 ) != windows.TRUE) {
237 // stop trying to write to this file233 // stop trying to write to this file
238 self.terminal = null;234 p.terminal = null;
239 break :winapi;235 break :winapi;
240 }236 }
241 if (windows.kernel32.FillConsoleOutputCharacterW(237 if (windows.kernel32.FillConsoleOutputCharacterW(
...@@ -246,22 +242,33 @@ fn refreshWithHeldLock(self: *Progress) void {...@@ -246,22 +242,33 @@ fn refreshWithHeldLock(self: *Progress) void {
246 &written,242 &written,
247 ) != windows.TRUE) {243 ) != windows.TRUE) {
248 // stop trying to write to this file244 // stop trying to write to this file
249 self.terminal = null;245 p.terminal = null;
250 break :winapi;246 break :winapi;
251 }247 }
252 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) {248 if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) {
253 // stop trying to write to this file249 // stop trying to write to this file
254 self.terminal = null;250 p.terminal = null;
255 break :winapi;251 break :winapi;
256 }252 }
257 } else {253 } else {
258 // we are in a "dumb" terminal like in acme or writing to a file254 // we are in a "dumb" terminal like in acme or writing to a file
259 self.output_buffer[end] = '\n';255 p.output_buffer[end] = '\n';
260 end += 1;256 end += 1;
261 }257 }
262258
263 self.columns_written = 0;259 p.columns_written = 0;
264 }260 }
261 end_ptr.* = end;
262}
263
264fn refreshWithHeldLock(self: *Progress) void {
265 const is_dumb = !self.supports_ansi_escape_codes and !self.is_windows_terminal;
266 if (is_dumb and self.dont_print_on_dumb) return;
267
268 const file = self.terminal orelse return;
269
270 var end: usize = 0;
271 clearWithHeldLock(self, &end);
265272
266 if (!self.done) {273 if (!self.done) {
267 var need_ellipse = false;274 var need_ellipse = false;
...@@ -318,6 +325,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {...@@ -318,6 +325,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
318 self.columns_written = 0;325 self.columns_written = 0;
319}326}
320327
328/// Allows the caller to freely write to stderr until unlock_stderr() is called.
329/// During the lock, the progress information is cleared from the terminal.
330pub fn lock_stderr(p: *Progress) void {
331 p.update_mutex.lock();
332 if (p.terminal) |file| {
333 var end: usize = 0;
334 clearWithHeldLock(p, &end);
335 _ = file.write(p.output_buffer[0..end]) catch {
336 // stop trying to write to this file
337 p.terminal = null;
338 };
339 }
340 std.debug.getStderrMutex().lock();
341}
342
343pub fn unlock_stderr(p: *Progress) void {
344 std.debug.getStderrMutex().unlock();
345 p.update_mutex.unlock();
346}
347
321fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {348fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
322 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {349 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
323 const amt = written.len;350 const amt = written.len;