authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2022-12-14 20:01:11-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-15 03:49:26-05:00
log88d927a511922efd82c4ec862c8e2aa83df84391
tree16449de5465989e1e3f8ae7894b5544cd9491109
parent83e0e23f8aa2d90495bbf9c3649aa68f7c55c926

std.debug.TTY: Fix colors not resetting on Windows

This fixes a regression introduced in #12298 where colors would never reset in a Windows console because the attributes would be queried on every `setColor` call, and then try to 'reset' the attributes to what it just queried (i.e. it was essentially doing a complicated no-op on .Reset). This fixes the problem while (I think) keeping with the spirit of the changes in #12298--that is, `TTY.Config` is not specifically tied to stderr like it was before #12298. To that end, detectTTYConfig now takes a `File` and that's what gets used to query the initial attributes to reset to. (for context, before #12298, the first `setColor` call is where the reset attributes would get queried and it would always use stderr to do it)

4 files changed, 35 insertions(+), 26 deletions(-)

lib/std/builtin.zig+1-1
...@@ -51,7 +51,7 @@ pub const StackTrace = struct {...@@ -51,7 +51,7 @@ pub const StackTrace = struct {
51 const debug_info = std.debug.getSelfDebugInfo() catch |err| {51 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
52 return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});52 return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
53 };53 };
54 const tty_config = std.debug.detectTTYConfig();54 const tty_config = std.debug.detectTTYConfig(std.io.getStdErr());
55 try writer.writeAll("\n");55 try writer.writeAll("\n");
56 std.debug.writeStackTrace(self, writer, arena.allocator(), debug_info, tty_config) catch |err| {56 std.debug.writeStackTrace(self, writer, arena.allocator(), debug_info, tty_config) catch |err| {
57 try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)});57 try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)});
lib/std/debug.zig+26-17
...@@ -109,17 +109,24 @@ pub fn getSelfDebugInfo() !*DebugInfo {...@@ -109,17 +109,24 @@ pub fn getSelfDebugInfo() !*DebugInfo {
109 }109 }
110}110}
111111
112pub fn detectTTYConfig() TTY.Config {112pub fn detectTTYConfig(file: std.fs.File) TTY.Config {
113 if (process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {113 if (process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
114 return .escape_codes;114 return .escape_codes;
115 } else if (process.hasEnvVarConstant("NO_COLOR")) {115 } else if (process.hasEnvVarConstant("NO_COLOR")) {
116 return .no_color;116 return .no_color;
117 } else {117 } else {
118 const stderr_file = io.getStdErr();118 if (file.supportsAnsiEscapeCodes()) {
119 if (stderr_file.supportsAnsiEscapeCodes()) {
120 return .escape_codes;119 return .escape_codes;
121 } else if (native_os == .windows and stderr_file.isTty()) {120 } else if (native_os == .windows and file.isTty()) {
122 return .{ .windows_api = stderr_file };121 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
122 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
123 // TODO: Should this return an error instead?
124 return .no_color;
125 }
126 return .{ .windows_api = .{
127 .handle = file.handle,
128 .reset_attributes = info.wAttributes,
129 } };
123 } else {130 } else {
124 return .no_color;131 return .no_color;
125 }132 }
...@@ -146,7 +153,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -146,7 +153,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
146 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;153 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
147 return;154 return;
148 };155 };
149 writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| {156 writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {
150 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;157 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
151 return;158 return;
152 };159 };
...@@ -174,7 +181,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {...@@ -174,7 +181,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
174 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;181 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
175 return;182 return;
176 };183 };
177 const tty_config = detectTTYConfig();184 const tty_config = detectTTYConfig(io.getStdErr());
178 printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;185 printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;
179 var it = StackIterator.init(null, bp);186 var it = StackIterator.init(null, bp);
180 while (it.next()) |return_address| {187 while (it.next()) |return_address| {
...@@ -257,7 +264,7 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {...@@ -257,7 +264,7 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
257 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;264 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
258 return;265 return;
259 };266 };
260 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| {267 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig(io.getStdErr())) catch |err| {
261 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;268 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
262 return;269 return;
263 };270 };
...@@ -600,7 +607,12 @@ pub const TTY = struct {...@@ -600,7 +607,12 @@ pub const TTY = struct {
600 pub const Config = union(enum) {607 pub const Config = union(enum) {
601 no_color,608 no_color,
602 escape_codes,609 escape_codes,
603 windows_api: File,610 windows_api: if (native_os == .windows) WindowsContext else void,
611
612 pub const WindowsContext = struct {
613 handle: File.Handle,
614 reset_attributes: u16,
615 };
604616
605 pub fn setColor(conf: Config, out_stream: anytype, color: Color) !void {617 pub fn setColor(conf: Config, out_stream: anytype, color: Color) !void {
606 nosuspend switch (conf) {618 nosuspend switch (conf) {
...@@ -617,19 +629,16 @@ pub const TTY = struct {...@@ -617,19 +629,16 @@ pub const TTY = struct {
617 };629 };
618 try out_stream.writeAll(color_string);630 try out_stream.writeAll(color_string);
619 },631 },
620 .windows_api => |file| if (native_os == .windows) {632 .windows_api => |ctx| if (native_os == .windows) {
621 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
622 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
623 return error.FailedRetrievingTerminalInfo;
624 const attributes = switch (color) {633 const attributes = switch (color) {
625 .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY,634 .Red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY,
626 .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,635 .Green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
627 .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,636 .Cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
628 .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,637 .White, .Bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
629 .Dim => windows.FOREGROUND_INTENSITY,638 .Dim => windows.FOREGROUND_INTENSITY,
630 .Reset => info.wAttributes,639 .Reset => ctx.reset_attributes,
631 };640 };
632 try windows.SetConsoleTextAttribute(file.handle, attributes);641 try windows.SetConsoleTextAttribute(ctx.handle, attributes);
633 } else {642 } else {
634 unreachable;643 unreachable;
635 },644 },
...@@ -2005,7 +2014,7 @@ test "#4353: std.debug should manage resources correctly" {...@@ -2005,7 +2014,7 @@ test "#4353: std.debug should manage resources correctly" {
2005 const writer = std.io.null_writer;2014 const writer = std.io.null_writer;
2006 var di = try openSelfDebugInfo(testing.allocator);2015 var di = try openSelfDebugInfo(testing.allocator);
2007 defer di.deinit();2016 defer di.deinit();
2008 try printSourceAtAddress(&di, writer, showMyTrace(), detectTTYConfig());2017 try printSourceAtAddress(&di, writer, showMyTrace(), detectTTYConfig(std.io.getStdErr()));
2009}2018}
20102019
2011noinline fn showMyTrace() usize {2020noinline fn showMyTrace() usize {
...@@ -2065,7 +2074,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize...@@ -2065,7 +2074,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
2065 pub fn dump(t: @This()) void {2074 pub fn dump(t: @This()) void {
2066 if (!enabled) return;2075 if (!enabled) return;
20672076
2068 const tty_config = detectTTYConfig();2077 const tty_config = detectTTYConfig(std.io.getStdErr());
2069 const stderr = io.getStdErr().writer();2078 const stderr = io.getStdErr().writer();
2070 const end = @min(t.index, size);2079 const end = @min(t.index, size);
2071 const debug_info = getSelfDebugInfo() catch |err| {2080 const debug_info = getSelfDebugInfo() catch |err| {
lib/std/testing.zig+1-1
...@@ -312,7 +312,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const...@@ -312,7 +312,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
312 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];312 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];
313 const actual_truncated = window_start + actual_window.len < actual.len;313 const actual_truncated = window_start + actual_window.len < actual.len;
314314
315 const ttyconf = std.debug.detectTTYConfig();315 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());
316 var differ = if (T == u8) BytesDiffer{316 var differ = if (T == u8) BytesDiffer{
317 .expected = expected_window,317 .expected = expected_window,
318 .actual = actual_window,318 .actual = actual_window,
src/main.zig+7-7
...@@ -3431,7 +3431,7 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void...@@ -3431,7 +3431,7 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void
34313431
3432 if (errors.list.len != 0) {3432 if (errors.list.len != 0) {
3433 const ttyconf: std.debug.TTY.Config = switch (comp.color) {3433 const ttyconf: std.debug.TTY.Config = switch (comp.color) {
3434 .auto => std.debug.detectTTYConfig(),3434 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
3435 .on => .escape_codes,3435 .on => .escape_codes,
3436 .off => .no_color,3436 .off => .no_color,
3437 };3437 };
...@@ -4252,7 +4252,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -4252,7 +4252,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
42524252
4253 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);4253 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);
4254 const ttyconf: std.debug.TTY.Config = switch (color) {4254 const ttyconf: std.debug.TTY.Config = switch (color) {
4255 .auto => std.debug.detectTTYConfig(),4255 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
4256 .on => .escape_codes,4256 .on => .escape_codes,
4257 .off => .no_color,4257 .off => .no_color,
4258 };4258 };
...@@ -4465,7 +4465,7 @@ fn fmtPathFile(...@@ -4465,7 +4465,7 @@ fn fmtPathFile(
44654465
4466 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);4466 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);
4467 const ttyconf: std.debug.TTY.Config = switch (fmt.color) {4467 const ttyconf: std.debug.TTY.Config = switch (fmt.color) {
4468 .auto => std.debug.detectTTYConfig(),4468 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
4469 .on => .escape_codes,4469 .on => .escape_codes,
4470 .off => .no_color,4470 .off => .no_color,
4471 };4471 };
...@@ -4586,7 +4586,7 @@ fn printErrsMsgToStdErr(...@@ -4586,7 +4586,7 @@ fn printErrsMsgToStdErr(
4586 };4586 };
45874587
4588 const ttyconf: std.debug.TTY.Config = switch (color) {4588 const ttyconf: std.debug.TTY.Config = switch (color) {
4589 .auto => std.debug.detectTTYConfig(),4589 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
4590 .on => .escape_codes,4590 .on => .escape_codes,
4591 .off => .no_color,4591 .off => .no_color,
4592 };4592 };
...@@ -5176,7 +5176,7 @@ pub fn cmdAstCheck(...@@ -5176,7 +5176,7 @@ pub fn cmdAstCheck(
5176 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);5176 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
5177 try Compilation.AllErrors.addZir(arena, &errors, &file);5177 try Compilation.AllErrors.addZir(arena, &errors, &file);
5178 const ttyconf: std.debug.TTY.Config = switch (color) {5178 const ttyconf: std.debug.TTY.Config = switch (color) {
5179 .auto => std.debug.detectTTYConfig(),5179 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
5180 .on => .escape_codes,5180 .on => .escape_codes,
5181 .off => .no_color,5181 .off => .no_color,
5182 };5182 };
...@@ -5301,7 +5301,7 @@ pub fn cmdChangelist(...@@ -5301,7 +5301,7 @@ pub fn cmdChangelist(
5301 if (file.zir.hasCompileErrors()) {5301 if (file.zir.hasCompileErrors()) {
5302 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);5302 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
5303 try Compilation.AllErrors.addZir(arena, &errors, &file);5303 try Compilation.AllErrors.addZir(arena, &errors, &file);
5304 const ttyconf = std.debug.detectTTYConfig();5304 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());
5305 for (errors.items) |full_err_msg| {5305 for (errors.items) |full_err_msg| {
5306 full_err_msg.renderToStdErr(ttyconf);5306 full_err_msg.renderToStdErr(ttyconf);
5307 }5307 }
...@@ -5340,7 +5340,7 @@ pub fn cmdChangelist(...@@ -5340,7 +5340,7 @@ pub fn cmdChangelist(
5340 if (file.zir.hasCompileErrors()) {5340 if (file.zir.hasCompileErrors()) {
5341 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);5341 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
5342 try Compilation.AllErrors.addZir(arena, &errors, &file);5342 try Compilation.AllErrors.addZir(arena, &errors, &file);
5343 const ttyconf = std.debug.detectTTYConfig();5343 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());
5344 for (errors.items) |full_err_msg| {5344 for (errors.items) |full_err_msg| {
5345 full_err_msg.renderToStdErr(ttyconf);5345 full_err_msg.renderToStdErr(ttyconf);
5346 }5346 }