1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
const std = @import("std");
const builtin = @import("builtin");
const File = std.fs.File;
const process = std.process;
const windows = std.os.windows;
const native_os = builtin.os.tag;
/// Deprecated in favor of `Config.detect`.
pub fn detectConfig(file: File) Config {
return .detect(file);
}
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,
};
/// Provides simple functionality for manipulating the terminal in some way,
/// such as coloring text, etc.
pub const Config = union(enum) {
no_color,
escape_codes,
windows_api: if (native_os == .windows) WindowsContext else void,
/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
/// This includes feature checks for ANSI escape codes and the Windows console API, as well as
/// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
/// Will attempt to enable ANSI escape code support if necessary/possible.
pub fn detect(file: File) Config {
const force_color: ?bool = if (builtin.os.tag == .wasi)
null // wasi does not support environment variables
else if (process.hasNonEmptyEnvVarConstant("NO_COLOR"))
false
else if (process.hasNonEmptyEnvVarConstant("CLICOLOR_FORCE"))
true
else
null;
if (force_color == false) return .no_color;
if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;
if (native_os == .windows and file.isTty()) {
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) == windows.FALSE) {
return if (force_color == true) .escape_codes else .no_color;
}
return .{ .windows_api = .{
.handle = file.handle,
.reset_attributes = info.wAttributes,
} };
}
return if (force_color == true) .escape_codes else .no_color;
}
pub const WindowsContext = struct {
handle: File.Handle,
reset_attributes: u16,
};
pub const SetColorError = std.os.windows.SetConsoleTextAttributeError || std.Io.Writer.Error;
pub fn setColor(conf: Config, w: *std.Io.Writer, color: Color) SetColorError!void {
nosuspend switch (conf) {
.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 w.writeAll(color_string);
},
.windows_api => |ctx| if (native_os == .windows) {
const attributes = 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 w.flush();
try windows.SetConsoleTextAttribute(ctx.handle, attributes);
} else {
unreachable;
},
};
}
};
|