diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-17 15:47:33 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:09 -0800 |
| commit | aa57793b680b3da05f1d888b4df15807905e57c8 (patch) | |
| tree | d88a1c6f56796942c9b21aee4bbb88e72045d298 /lib/std/Io/File/Writer.zig | |
| parent | 97f106f949891870433bfcc6b7cf4c2a6709402e (diff) | |
| download | zig-aa57793b680b3da05f1d888b4df15807905e57c8.tar.gz zig-aa57793b680b3da05f1d888b4df15807905e57c8.zip | |
std: rework locking stderr
Diffstat (limited to 'lib/std/Io/File/Writer.zig')
| -rw-r--r-- | lib/std/Io/File/Writer.zig | 250 |
1 files changed, 3 insertions, 247 deletions
diff --git a/lib/std/Io/File/Writer.zig b/lib/std/Io/File/Writer.zig index 03e4ad8a15..1d0494c674 100644 --- a/lib/std/Io/File/Writer.zig +++ b/lib/std/Io/File/Writer.zig @@ -18,172 +18,7 @@ write_file_err: ?WriteFileError = null, seek_err: ?SeekError = null, interface: Io.Writer, -pub const Mode = union(enum) { - /// Uses `Io.VTable.fileWriteFileStreaming` if possible. Not a terminal. - /// `setColor` does nothing. - streaming, - /// Uses `Io.VTable.fileWriteFilePositional` if possible. Not a terminal. - /// `setColor` does nothing. - positional, - /// Avoids `Io.VTable.fileWriteFileStreaming`. Not a terminal. `setColor` - /// does nothing. - streaming_simple, - /// Avoids `Io.VTable.fileWriteFilePositional`. Not a terminal. `setColor` - /// does nothing. - positional_simple, - /// It's a terminal. Writes are escaped so as to strip escape sequences. - /// Color is enabled. - terminal_escaped, - /// It's a terminal. Colors are enabled via calling - /// SetConsoleTextAttribute. Writes are not escaped. - terminal_winapi: TerminalWinapi, - /// Indicates writing cannot continue because of a seek failure. - failure, - - pub fn toStreaming(m: @This()) @This() { - return switch (m) { - .positional, .streaming => .streaming, - .positional_simple, .streaming_simple => .streaming_simple, - inline else => |_, x| x, - }; - } - - pub fn toSimple(m: @This()) @This() { - return switch (m) { - .positional, .positional_simple => .positional_simple, - .streaming, .streaming_simple => .streaming_simple, - inline else => |_, x| x, - }; - } - - pub fn toUnescaped(m: @This()) @This() { - return switch (m) { - .terminal_escaped => .streaming_simple, - inline else => |_, x| x, - }; - } - - pub const TerminalWinapi = if (!is_windows) noreturn else struct { - handle: File.Handle, - reset_attributes: u16, - }; - - /// Detect suitable TTY configuration options for the given file (commonly - /// stdout/stderr). - /// - /// Will attempt to enable ANSI escape code support if necessary/possible. - pub fn detect(io: Io, file: File, want_color: bool, fallback: Mode) Io.Cancelable!Mode { - if (!want_color) return if (try file.isTty(io)) .terminal_escaped else fallback; - - if (file.enableAnsiEscapeCodes(io)) |_| { - return .terminal_escaped; - } else |err| switch (err) { - error.Canceled => return error.Canceled, - error.NotTerminalDevice, error.Unexpected => {}, - } - - if (is_windows and file.isTty(io)) { - const windows = std.os.windows; - var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; - if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.FALSE) { - return .{ .terminal_winapi = .{ - .handle = file.handle, - .reset_attributes = info.wAttributes, - } }; - } - return .terminal_escaped; - } - - return fallback; - } - - pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || Io.Writer.Error; - - pub fn setColor(mode: Mode, io_w: *Io.Writer, color: Color) SetColorError!void { - switch (mode) { - .streaming, .positional, .streaming_simple, .positional_simple, .failure => return, - .terminal_escaped => { - const color_string = switch (color) { - .black => "\x1b[30m", - .red => "\x1b[31m", - .green => "\x1b[32m", - .yellow => "\x1b[33m", - .blue => "\x1b[34m", - .magenta => "\x1b[35m", - .cyan => "\x1b[36m", - .white => "\x1b[37m", - .bright_black => "\x1b[90m", - .bright_red => "\x1b[91m", - .bright_green => "\x1b[92m", - .bright_yellow => "\x1b[93m", - .bright_blue => "\x1b[94m", - .bright_magenta => "\x1b[95m", - .bright_cyan => "\x1b[96m", - .bright_white => "\x1b[97m", - .bold => "\x1b[1m", - .dim => "\x1b[2m", - .reset => "\x1b[0m", - }; - try io_w.writeAll(color_string); - }, - .terminal_winapi => |ctx| { - const windows = std.os.windows; - const attributes: windows.WORD = switch (color) { - .black => 0, - .red => windows.FOREGROUND_RED, - .green => windows.FOREGROUND_GREEN, - .yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN, - .blue => windows.FOREGROUND_BLUE, - .magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE, - .cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, - .white => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE, - .bright_black => windows.FOREGROUND_INTENSITY, - .bright_red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY, - .bright_green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, - .bright_yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY, - .bright_blue => windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_magenta => windows.FOREGROUND_RED | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - .bright_white, .bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY, - // "dim" is not supported using basic character attributes, but let's still make it do *something*. - // This matches the old behavior of TTY.Color before the bright variants were added. - .dim => windows.FOREGROUND_INTENSITY, - .reset => ctx.reset_attributes, - }; - try io_w.flush(); - try windows.SetConsoleTextAttribute(ctx.handle, attributes); - }, - } - } - - fn DecorateArgs(comptime Args: type) type { - const fields = @typeInfo(Args).@"struct".fields; - var new_fields: [fields.len]type = undefined; - for (fields, &new_fields) |old, *new| { - if (old.type == std.debug.FormatStackTrace) { - new.* = std.debug.FormatStackTrace.Decorated; - } else { - new.* = old.type; - } - } - return @Tuple(&new_fields); - } - - pub fn decorateArgs(file_writer_mode: std.Io.File.Writer.Mode, args: anytype) DecorateArgs(@TypeOf(args)) { - var new_args: DecorateArgs(@TypeOf(args)) = undefined; - inline for (args, &new_args) |old, *new| { - if (@TypeOf(old) == std.debug.FormatStackTrace) { - new.* = .{ - .stack_trace = old.stack_trace, - .file_writer_mode = file_writer_mode, - }; - } else { - new.* = old; - } - } - return new_args; - } -}; +pub const Mode = File.Reader.Mode; pub const Error = error{ DiskQuota, @@ -277,8 +112,7 @@ pub fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); switch (w.mode) { .positional, .positional_simple => return drainPositional(w, data, splat), - .streaming, .streaming_simple, .terminal_winapi => return drainStreaming(w, data, splat), - .terminal_escaped => return drainEscaping(w, data, splat), + .streaming, .streaming_simple => return drainStreaming(w, data, splat), .failure => return error.WriteFailed, } } @@ -319,38 +153,13 @@ fn drainStreaming(w: *Writer, data: []const []const u8, splat: usize) Io.Writer. return w.interface.consume(n); } -fn findTerminalEscape(buffer: []const u8) ?usize { - return std.mem.findScalar(u8, buffer, 0x1b); -} - -fn drainEscaping(w: *Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize { - const io = w.io; - const header = w.interface.buffered(); - if (findTerminalEscape(header)) |i| { - _ = i; - // TODO strip terminal escape sequences here - } - for (data) |d| { - if (findTerminalEscape(d)) |i| { - _ = i; - // TODO strip terminal escape sequences here - } - } - const n = io.vtable.fileWriteStreaming(io.userdata, w.file, header, data, splat) catch |err| { - w.err = err; - return error.WriteFailed; - }; - w.pos += n; - return w.interface.consume(n); -} - pub fn sendFile(io_w: *Io.Writer, file_reader: *Io.File.Reader, limit: Io.Limit) Io.Writer.FileError!usize { const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); switch (w.mode) { .positional => return sendFilePositional(w, file_reader, limit), .positional_simple => return error.Unimplemented, .streaming => return sendFileStreaming(w, file_reader, limit), - .streaming_simple, .terminal_escaped, .terminal_winapi => return error.Unimplemented, + .streaming_simple => return error.Unimplemented, .failure => return error.WriteFailed, } } @@ -454,60 +263,7 @@ pub fn end(w: *Writer) EndError!void { .streaming, .streaming_simple, - .terminal_escaped, - .terminal_winapi, .failure, => {}, } } - -pub const Color = enum { - black, - red, - green, - yellow, - blue, - magenta, - cyan, - white, - bright_black, - bright_red, - bright_green, - bright_yellow, - bright_blue, - bright_magenta, - bright_cyan, - bright_white, - dim, - bold, - reset, -}; - -pub fn setColor(w: *Writer, color: Color) Io.Writer.Error!void { - return w.mode.setColor(&w.interface, color) catch |err| switch (err) { - error.WriteFailed => |e| return e, - else => |e| w.err = e, - }; -} - -pub fn disableEscape(w: *Writer) Mode { - const prev = w.mode; - w.mode = w.mode.toUnescaped(); - return prev; -} - -pub fn restoreEscape(w: *Writer, mode: Mode) void { - w.mode = mode; -} - -pub fn writeAllUnescaped(w: *Writer, bytes: []const u8) Io.Writer.Error!void { - const prev_mode = w.disableEscape(); - defer w.restoreEscape(prev_mode); - return w.interface.writeAll(bytes); -} - -pub fn printUnescaped(w: *Writer, comptime fmt: []const u8, args: anytype) Io.Writer.Error!void { - const prev_mode = w.disableEscape(); - defer w.restoreEscape(prev_mode); - return w.interface.print(fmt, args); -} |
