mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 04:24:33 +01:00
144 lines
5.4 KiB
Zig
144 lines
5.4 KiB
Zig
/// Abstraction for writing to a stream that might support terminal escape
|
|
/// codes.
|
|
const Terminal = @This();
|
|
|
|
const builtin = @import("builtin");
|
|
const is_windows = builtin.os.tag == .windows;
|
|
|
|
const std = @import("std");
|
|
const Io = std.Io;
|
|
const File = std.Io.File;
|
|
|
|
writer: *Io.Writer,
|
|
mode: Mode,
|
|
|
|
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 const Mode = union(enum) {
|
|
no_color,
|
|
escape_codes,
|
|
windows_api: WindowsApi,
|
|
|
|
pub const WindowsApi = if (!is_windows) noreturn else struct {
|
|
io: Io,
|
|
file: File,
|
|
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.
|
|
///
|
|
/// * `NO_COLOR` indicates whether "NO_COLOR" environment variable is
|
|
/// present and non-empty.
|
|
/// * `CLICOLOR_FORCE` indicates whether "CLICOLOR_FORCE" environment
|
|
/// variable is present and non-empty.
|
|
pub fn detect(io: Io, file: File, NO_COLOR: bool, CLICOLOR_FORCE: bool) Io.Cancelable!Mode {
|
|
const force_color: ?bool = if (NO_COLOR) false else if (CLICOLOR_FORCE) true else null;
|
|
if (force_color == false) return .no_color;
|
|
|
|
if (file.enableAnsiEscapeCodes(io)) |_| {
|
|
return .escape_codes;
|
|
} else |err| switch (err) {
|
|
error.Canceled => return error.Canceled,
|
|
error.NotTerminalDevice, error.Unexpected => {},
|
|
}
|
|
|
|
if (is_windows and try file.isTty(io)) {
|
|
var get_console_info = std.os.windows.CONSOLE.USER_IO.GET_SCREEN_BUFFER_INFO;
|
|
switch (try get_console_info.operate(io, file)) {
|
|
.SUCCESS => return .{ .windows_api = .{
|
|
.io = io,
|
|
.file = file,
|
|
.reset_attributes = get_console_info.Data.wAttributes,
|
|
} },
|
|
else => {},
|
|
}
|
|
}
|
|
return if (force_color == true) .escape_codes else .no_color;
|
|
}
|
|
};
|
|
|
|
pub const SetColorError = Io.Cancelable || Io.UnexpectedError || Io.Writer.Error;
|
|
|
|
pub fn setColor(t: Terminal, color: Color) SetColorError!void {
|
|
switch (t.mode) {
|
|
.no_color => return,
|
|
.escape_codes => {
|
|
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 t.writer.writeAll(color_string);
|
|
},
|
|
.windows_api => |wa| {
|
|
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 => wa.reset_attributes,
|
|
};
|
|
try t.writer.flush();
|
|
var set_text_attribute = windows.CONSOLE.USER_IO.SET_TEXT_ATTRIBUTE(attributes);
|
|
switch (try set_text_attribute.operate(wa.io, wa.file)) {
|
|
.SUCCESS => {},
|
|
else => |status| return windows.unexpectedStatus(status),
|
|
}
|
|
},
|
|
}
|
|
}
|