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.
This commit is contained in:
Andrew Kelley 2023-02-16 15:03:05 -07:00
parent 8d38472293
commit 7ebaa05bb1

View File

@ -192,32 +192,28 @@ pub fn refresh(self: *Progress) void {
return self.refreshWithHeldLock();
}
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;
const file = self.terminal orelse return;
var end: usize = 0;
if (self.columns_written > 0) {
fn clearWithHeldLock(p: *Progress, end_ptr: *usize) void {
const file = p.terminal orelse return;
var end = end_ptr.*;
if (p.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;
if (p.supports_ansi_escape_codes) {
end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[{d}D", .{p.columns_written}) catch unreachable).len;
end += (std.fmt.bufPrint(p.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
} else if (builtin.os.tag == .windows) winapi: {
std.debug.assert(self.is_windows_terminal);
std.debug.assert(p.is_windows_terminal);
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
// stop trying to write to this file
self.terminal = null;
p.terminal = null;
break :winapi;
}
var cursor_pos = windows.COORD{
.X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
.X = info.dwCursorPosition.X - @intCast(windows.SHORT, p.columns_written),
.Y = info.dwCursorPosition.Y,
};
@ -235,7 +231,7 @@ fn refreshWithHeldLock(self: *Progress) void {
&written,
) != windows.TRUE) {
// stop trying to write to this file
self.terminal = null;
p.terminal = null;
break :winapi;
}
if (windows.kernel32.FillConsoleOutputCharacterW(
@ -246,22 +242,33 @@ fn refreshWithHeldLock(self: *Progress) void {
&written,
) != windows.TRUE) {
// stop trying to write to this file
self.terminal = null;
p.terminal = null;
break :winapi;
}
if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) {
// stop trying to write to this file
self.terminal = null;
p.terminal = null;
break :winapi;
}
} else {
// we are in a "dumb" terminal like in acme or writing to a file
self.output_buffer[end] = '\n';
p.output_buffer[end] = '\n';
end += 1;
}
self.columns_written = 0;
p.columns_written = 0;
}
end_ptr.* = end;
}
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;
const file = self.terminal orelse return;
var end: usize = 0;
clearWithHeldLock(self, &end);
if (!self.done) {
var need_ellipse = false;
@ -318,6 +325,26 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
self.columns_written = 0;
}
/// Allows the caller to freely write to stderr until unlock_stderr() is called.
/// During the lock, the progress information is cleared from the terminal.
pub fn lock_stderr(p: *Progress) void {
p.update_mutex.lock();
if (p.terminal) |file| {
var end: usize = 0;
clearWithHeldLock(p, &end);
_ = file.write(p.output_buffer[0..end]) catch {
// stop trying to write to this file
p.terminal = null;
};
}
std.debug.getStderrMutex().lock();
}
pub fn unlock_stderr(p: *Progress) void {
std.debug.getStderrMutex().unlock();
p.update_mutex.unlock();
}
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;