mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 11:13:08 +00:00
Merge pull request #8174 from LemonBoy/progress-line-wrap
std: Better handling of line-wrapping in Progress
This commit is contained in:
commit
f950489ed9
@ -59,10 +59,6 @@ done: bool = true,
|
|||||||
/// while it was still being accessed by the `refresh` function.
|
/// while it was still being accessed by the `refresh` function.
|
||||||
update_lock: std.Thread.Mutex = .{},
|
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
|
/// Represents one unit of progress. Each node can have children nodes, or
|
||||||
/// one can use integers with `update`.
|
/// one can use integers with `update`.
|
||||||
pub const Node = struct {
|
pub const Node = struct {
|
||||||
@ -159,7 +155,6 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*
|
|||||||
.unprotected_estimated_total_items = estimated_total_items,
|
.unprotected_estimated_total_items = estimated_total_items,
|
||||||
.unprotected_completed_items = 0,
|
.unprotected_completed_items = 0,
|
||||||
};
|
};
|
||||||
self.columns_written = 0;
|
|
||||||
self.prev_refresh_timestamp = 0;
|
self.prev_refresh_timestamp = 0;
|
||||||
self.timer = try std.time.Timer.start();
|
self.timer = try std.time.Timer.start();
|
||||||
self.done = false;
|
self.done = false;
|
||||||
@ -187,64 +182,65 @@ pub fn refresh(self: *Progress) void {
|
|||||||
return self.refreshWithHeldLock();
|
return self.refreshWithHeldLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ED -- Clear screen
|
||||||
|
const ED = "\x1b[J";
|
||||||
|
// DECSC -- Save cursor position
|
||||||
|
const DECSC = "\x1b[s";
|
||||||
|
// DECRC -- Restore cursor position
|
||||||
|
const DECRC = "\x1b[u";
|
||||||
|
|
||||||
fn refreshWithHeldLock(self: *Progress) void {
|
fn refreshWithHeldLock(self: *Progress) void {
|
||||||
const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows);
|
const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows);
|
||||||
if (is_dumb and self.dont_print_on_dumb) return;
|
if (is_dumb and self.dont_print_on_dumb) return;
|
||||||
const file = self.terminal orelse return;
|
const file = self.terminal orelse return;
|
||||||
|
|
||||||
const prev_columns_written = self.columns_written;
|
|
||||||
var end: usize = 0;
|
var end: usize = 0;
|
||||||
if (self.columns_written > 0) {
|
// Save the cursor position and clear the part of the screen below.
|
||||||
// restore the cursor position by moving the cursor
|
// Clearing only the line is not enough as the terminal may wrap the line
|
||||||
// `columns_written` cells to the left, then clear the rest of the
|
// when it becomes too long.
|
||||||
// line
|
var saved_cursor_pos: windows.COORD = undefined;
|
||||||
if (self.supports_ansi_escape_codes) {
|
if (self.supports_ansi_escape_codes) {
|
||||||
end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
|
const seq_before = DECSC ++ ED;
|
||||||
end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
|
std.mem.copy(u8, self.output_buffer[end..], seq_before);
|
||||||
} else if (std.builtin.os.tag == .windows) winapi: {
|
end += seq_before.len;
|
||||||
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
} else if (std.builtin.os.tag == .windows) winapi: {
|
||||||
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
|
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
||||||
unreachable;
|
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
|
||||||
|
unreachable;
|
||||||
|
|
||||||
var cursor_pos = windows.COORD{
|
saved_cursor_pos = info.dwCursorPosition;
|
||||||
.X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
|
|
||||||
.Y = info.dwCursorPosition.Y,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (cursor_pos.X < 0)
|
const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top + 1);
|
||||||
cursor_pos.X = 0;
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
|
var written: windows.DWORD = undefined;
|
||||||
|
if (windows.kernel32.FillConsoleOutputAttribute(
|
||||||
var written: windows.DWORD = undefined;
|
file.handle,
|
||||||
if (windows.kernel32.FillConsoleOutputAttribute(
|
info.wAttributes,
|
||||||
file.handle,
|
fill_chars,
|
||||||
info.wAttributes,
|
saved_cursor_pos,
|
||||||
fill_chars,
|
&written,
|
||||||
cursor_pos,
|
) != windows.TRUE) {
|
||||||
&written,
|
// Stop trying to write to this file.
|
||||||
) != windows.TRUE) {
|
self.terminal = null;
|
||||||
// Stop trying to write to this file.
|
break :winapi;
|
||||||
self.terminal = null;
|
}
|
||||||
break :winapi;
|
if (windows.kernel32.FillConsoleOutputCharacterA(
|
||||||
}
|
file.handle,
|
||||||
if (windows.kernel32.FillConsoleOutputCharacterA(
|
' ',
|
||||||
file.handle,
|
fill_chars,
|
||||||
' ',
|
saved_cursor_pos,
|
||||||
fill_chars,
|
&written,
|
||||||
cursor_pos,
|
) != windows.TRUE) {
|
||||||
&written,
|
unreachable;
|
||||||
) != 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) {
|
if (!self.done) {
|
||||||
@ -279,10 +275,26 @@ 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 (std.builtin.os.tag != .windows) {
|
||||||
|
self.output_buffer[end] = '\n';
|
||||||
|
end += 1;
|
||||||
|
}
|
||||||
|
|
||||||
_ = file.write(self.output_buffer[0..end]) catch |e| {
|
_ = file.write(self.output_buffer[0..end]) catch |e| {
|
||||||
// Stop trying to write to this file once it errors.
|
// Stop trying to write to this file once it errors.
|
||||||
self.terminal = null;
|
self.terminal = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (std.builtin.os.tag == .windows) {
|
||||||
|
if (windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos) != windows.TRUE)
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
self.prev_refresh_timestamp = self.timer.read();
|
self.prev_refresh_timestamp = self.timer.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,17 +305,14 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
|
|||||||
self.terminal = null;
|
self.terminal = null;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
self.columns_written = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
|
fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
|
||||||
if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
|
if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
|
||||||
const amt = written.len;
|
const amt = written.len;
|
||||||
end.* += amt;
|
end.* += amt;
|
||||||
self.columns_written += amt;
|
|
||||||
} else |err| switch (err) {
|
} else |err| switch (err) {
|
||||||
error.NoSpaceLeft => {
|
error.NoSpaceLeft => {
|
||||||
self.columns_written += self.output_buffer.len - end.*;
|
|
||||||
end.* = self.output_buffer.len;
|
end.* = self.output_buffer.len;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -311,7 +320,6 @@ 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;
|
const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
|
||||||
if (end.* > max_end) {
|
if (end.* > max_end) {
|
||||||
const suffix = "... ";
|
const suffix = "... ";
|
||||||
self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
|
|
||||||
std.mem.copy(u8, self.output_buffer[max_end..], suffix);
|
std.mem.copy(u8, self.output_buffer[max_end..], suffix);
|
||||||
end.* = max_end + suffix.len;
|
end.* = max_end + suffix.len;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user