Skip to content

Commit 7fc3fb9

Browse files
authored
Merge pull request #20172 from squeek502/windows-vt-enable
Add `File.getOrEnableAnsiEscapeSupport` and use it
2 parents 7f6ec51 + 337f09e commit 7fc3fb9

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

lib/std/Progress.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ pub fn start(options: Options) Node {
377377
}
378378
const stderr = std.io.getStdErr();
379379
global_progress.terminal = stderr;
380-
if (stderr.supportsAnsiEscapeCodes()) {
380+
if (stderr.getOrEnableAnsiEscapeSupport()) {
381381
global_progress.terminal_mode = .ansi_escape_codes;
382382
} else if (is_windows and stderr.isTty()) {
383383
global_progress.terminal_mode = TerminalMode{ .windows_api = .{

lib/std/fs/File.zig

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ pub fn sync(self: File) SyncError!void {
188188
}
189189

190190
/// Test whether the file refers to a terminal.
191-
/// See also `supportsAnsiEscapeCodes`.
191+
/// See also `getOrEnableAnsiEscapeSupport` and `supportsAnsiEscapeCodes`.
192192
pub fn isTty(self: File) bool {
193193
return posix.isatty(self.handle);
194194
}
@@ -245,7 +245,47 @@ pub fn isCygwinPty(file: File) bool {
245245
std.mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
246246
}
247247

248-
/// Test whether ANSI escape codes will be treated as such.
248+
/// Returns whether or not ANSI escape codes will be treated as such,
249+
/// and attempts to enable support for ANSI escape codes if necessary
250+
/// (on Windows).
251+
///
252+
/// Returns `true` if ANSI escape codes are supported or support was
253+
/// successfully enabled. Returns false if ANSI escape codes are not
254+
/// supported or support was unable to be enabled.
255+
///
256+
/// See also `supportsAnsiEscapeCodes`.
257+
pub fn getOrEnableAnsiEscapeSupport(self: File) bool {
258+
if (builtin.os.tag == .windows) {
259+
var original_console_mode: windows.DWORD = 0;
260+
261+
// For Windows Terminal, VT Sequences processing is enabled by default.
262+
if (windows.kernel32.GetConsoleMode(self.handle, &original_console_mode) != 0) {
263+
if (original_console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true;
264+
265+
// For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.
266+
// https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/
267+
// Use Microsoft's recommended way to enable virtual terminal processing.
268+
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
269+
var requested_console_modes: windows.DWORD = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | windows.DISABLE_NEWLINE_AUTO_RETURN;
270+
var console_mode = original_console_mode | requested_console_modes;
271+
if (windows.kernel32.SetConsoleMode(self.handle, console_mode) != 0) return true;
272+
273+
// An application receiving ERROR_INVALID_PARAMETER with one of the newer console mode
274+
// flags in the bit field should gracefully degrade behavior and try again.
275+
requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
276+
console_mode = original_console_mode | requested_console_modes;
277+
if (windows.kernel32.SetConsoleMode(self.handle, console_mode) != 0) return true;
278+
}
279+
280+
return self.isCygwinPty();
281+
}
282+
return self.supportsAnsiEscapeCodes();
283+
}
284+
285+
/// Test whether ANSI escape codes will be treated as such without
286+
/// attempting to enable support for ANSI escape codes.
287+
///
288+
/// See also `getOrEnableAnsiEscapeSupport`.
249289
pub fn supportsAnsiEscapeCodes(self: File) bool {
250290
if (builtin.os.tag == .windows) {
251291
var console_mode: windows.DWORD = 0;

lib/std/io/tty.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const native_os = builtin.os.tag;
88
/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
99
/// This includes feature checks for ANSI escape codes and the Windows console API, as well as
1010
/// respecting the `NO_COLOR` and `CLICOLOR_FORCE` environment variables to override the default.
11+
/// Will attempt to enable ANSI escape code support if necessary/possible.
1112
pub fn detectConfig(file: File) Config {
1213
const force_color: ?bool = if (builtin.os.tag == .wasi)
1314
null // wasi does not support environment variables
@@ -20,7 +21,7 @@ pub fn detectConfig(file: File) Config {
2021

2122
if (force_color == false) return .no_color;
2223

23-
if (file.supportsAnsiEscapeCodes()) return .escape_codes;
24+
if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;
2425

2526
if (native_os == .windows and file.isTty()) {
2627
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;

0 commit comments

Comments
 (0)