Skip to content

Add File.getOrEnableAnsiEscapeSupport and use it #20172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/std/Progress.zig
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ pub fn start(options: Options) Node {
}
const stderr = std.io.getStdErr();
global_progress.terminal = stderr;
if (stderr.supportsAnsiEscapeCodes()) {
if (stderr.getOrEnableAnsiEscapeSupport()) {
global_progress.terminal_mode = .ansi_escape_codes;
} else if (is_windows and stderr.isTty()) {
global_progress.terminal_mode = TerminalMode{ .windows_api = .{
Expand Down
44 changes: 42 additions & 2 deletions lib/std/fs/File.zig
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ pub fn sync(self: File) SyncError!void {
}

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

/// Test whether ANSI escape codes will be treated as such.
/// Returns whether or not ANSI escape codes will be treated as such,
/// and attempts to enable support for ANSI escape codes if necessary
/// (on Windows).
///
/// Returns `true` if ANSI escape codes are supported or support was
/// successfully enabled. Returns false if ANSI escape codes are not
/// supported or support was unable to be enabled.
///
/// See also `supportsAnsiEscapeCodes`.
pub fn getOrEnableAnsiEscapeSupport(self: File) bool {
if (builtin.os.tag == .windows) {
var original_console_mode: windows.DWORD = 0;

// For Windows Terminal, VT Sequences processing is enabled by default.
if (windows.kernel32.GetConsoleMode(self.handle, &original_console_mode) != 0) {
if (original_console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true;

// For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.
// https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/
// Use Microsoft's recommended way to enable virtual terminal processing.
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
var requested_console_modes: windows.DWORD = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | windows.DISABLE_NEWLINE_AUTO_RETURN;
var console_mode = original_console_mode | requested_console_modes;
if (windows.kernel32.SetConsoleMode(self.handle, console_mode) != 0) return true;

// An application receiving ERROR_INVALID_PARAMETER with one of the newer console mode
// flags in the bit field should gracefully degrade behavior and try again.
requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
console_mode = original_console_mode | requested_console_modes;
if (windows.kernel32.SetConsoleMode(self.handle, console_mode) != 0) return true;
}

return self.isCygwinPty();
}
return self.supportsAnsiEscapeCodes();
}

/// Test whether ANSI escape codes will be treated as such without
/// attempting to enable support for ANSI escape codes.
///
/// See also `getOrEnableAnsiEscapeSupport`.
pub fn supportsAnsiEscapeCodes(self: File) bool {
if (builtin.os.tag == .windows) {
var console_mode: windows.DWORD = 0;
Expand Down
3 changes: 2 additions & 1 deletion lib/std/io/tty.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const native_os = builtin.os.tag;
/// 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 detectConfig(file: File) Config {
const force_color: ?bool = if (builtin.os.tag == .wasi)
null // wasi does not support environment variables
Expand All @@ -20,7 +21,7 @@ pub fn detectConfig(file: File) Config {

if (force_color == false) return .no_color;

if (file.supportsAnsiEscapeCodes()) return .escape_codes;
if (file.getOrEnableAnsiEscapeSupport()) return .escape_codes;

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