diff --git a/CHANGELOG.md b/CHANGELOG.md index f63522d3eb1..997ae349008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ * `dart:io`: Added functions `File.lastAccessed`, `File.lastAccessedSync`, `File.setLastModified`, `File.setLastModifiedSync`, `File.setLastAccessed`, and `File.setLastAccessedSync`. -* `dart:io`: Added `Platform.ansiSupported`. +* `dart:io`: Added `{Stdin,Stdout}.supportsAnsiEscapes`. ### Dart VM * Calls to `print()` and `Stdout.write*()` now correctly print unicode diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc index b39e44e9fb3..8f3c79a9a31 100644 --- a/runtime/bin/io_natives.cc +++ b/runtime/bin/io_natives.cc @@ -92,7 +92,6 @@ namespace bin { V(Platform_Environment, 0) \ V(Platform_ExecutableArguments, 0) \ V(Platform_GetVersion, 0) \ - V(Platform_AnsiSupported, 0) \ V(Process_Start, 11) \ V(Process_Wait, 5) \ V(Process_KillPid, 2) \ @@ -148,7 +147,9 @@ namespace bin { V(Stdin_SetEchoMode, 1) \ V(Stdin_GetLineMode, 0) \ V(Stdin_SetLineMode, 1) \ + V(Stdin_AnsiSupported, 0) \ V(Stdout_GetTerminalSize, 1) \ + V(Stdout_AnsiSupported, 1) \ V(StringToSystemEncoding, 1) \ V(SystemEncodingToString, 1) \ V(X509_Subject, 1) \ diff --git a/runtime/bin/platform.cc b/runtime/bin/platform.cc index dc1ebc459d3..e8b7849147f 100644 --- a/runtime/bin/platform.cc +++ b/runtime/bin/platform.cc @@ -109,11 +109,6 @@ void FUNCTION_NAME(Platform_GetVersion)(Dart_NativeArguments args) { Dart_SetReturnValue(args, Dart_NewStringFromCString(Dart_VersionString())); } - -void FUNCTION_NAME(Platform_AnsiSupported)(Dart_NativeArguments args) { - Dart_SetBooleanReturnValue(args, Platform::AnsiSupported()); -} - } // namespace bin } // namespace dart diff --git a/runtime/bin/platform.h b/runtime/bin/platform.h index f5906d2ca00..9dc7e7a21a6 100644 --- a/runtime/bin/platform.h +++ b/runtime/bin/platform.h @@ -83,8 +83,6 @@ class Platform { static int GetScriptIndex() { return script_index_; } static char** GetArgv() { return argv_; } - static bool AnsiSupported(); - static DART_NORETURN void Exit(int exit_code); private: diff --git a/runtime/bin/platform_android.cc b/runtime/bin/platform_android.cc index 10371c60f88..b81ce71e195 100644 --- a/runtime/bin/platform_android.cc +++ b/runtime/bin/platform_android.cc @@ -85,11 +85,6 @@ const char* Platform::ResolveExecutablePath() { } -bool Platform::AnsiSupported() { - return true; -} - - void Platform::Exit(int exit_code) { exit(exit_code); } diff --git a/runtime/bin/platform_fuchsia.cc b/runtime/bin/platform_fuchsia.cc index ea3115a11ac..3b1a1c190ce 100644 --- a/runtime/bin/platform_fuchsia.cc +++ b/runtime/bin/platform_fuchsia.cc @@ -109,11 +109,6 @@ const char* Platform::ResolveExecutablePath() { } -bool Platform::AnsiSupported() { - return true; -} - - void Platform::Exit(int exit_code) { exit(exit_code); } diff --git a/runtime/bin/platform_linux.cc b/runtime/bin/platform_linux.cc index 810dec4c457..b68f4921c7e 100644 --- a/runtime/bin/platform_linux.cc +++ b/runtime/bin/platform_linux.cc @@ -110,11 +110,6 @@ const char* Platform::ResolveExecutablePath() { } -bool Platform::AnsiSupported() { - return true; -} - - void Platform::Exit(int exit_code) { exit(exit_code); } diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc index 787069a4978..0088956c210 100644 --- a/runtime/bin/platform_macos.cc +++ b/runtime/bin/platform_macos.cc @@ -154,11 +154,6 @@ const char* Platform::ResolveExecutablePath() { } -bool Platform::AnsiSupported() { - return true; -} - - void Platform::Exit(int exit_code) { exit(exit_code); } diff --git a/runtime/bin/platform_patch.dart b/runtime/bin/platform_patch.dart index 5896fbdac24..c4b348114e2 100644 --- a/runtime/bin/platform_patch.dart +++ b/runtime/bin/platform_patch.dart @@ -21,8 +21,6 @@ native "Platform_ExecutableArguments"; @patch static String _version() native "Platform_GetVersion"; - @patch static bool _ansiSupported() - native "Platform_AnsiSupported"; @patch static String _packageRoot() => VMLibraryHooks.packageRootString; diff --git a/runtime/bin/platform_unsupported.cc b/runtime/bin/platform_unsupported.cc index 520d9f2ed7b..64f62ef7e9b 100644 --- a/runtime/bin/platform_unsupported.cc +++ b/runtime/bin/platform_unsupported.cc @@ -64,12 +64,6 @@ void FUNCTION_NAME(Platform_GetVersion)(Dart_NativeArguments args) { "Platform is not supported on this platform")); } - -void FUNCTION_NAME(Platform_AnsiSupported)(Dart_NativeArguments args) { - Dart_ThrowException(DartUtils::NewInternalError( - "Platform is not supported on this platform")); -} - } // namespace bin } // namespace dart diff --git a/runtime/bin/platform_win.cc b/runtime/bin/platform_win.cc index 14f1a1f2458..53293024268 100644 --- a/runtime/bin/platform_win.cc +++ b/runtime/bin/platform_win.cc @@ -83,23 +83,18 @@ class PlatformWin { SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); - ansi_supported_ = true; + // Try to set the bits for ANSI support, but swallow any failures. HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); DWORD out_mode; if ((out != INVALID_HANDLE_VALUE) && GetConsoleMode(out, &out_mode)) { const DWORD request = out_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING; - ansi_supported_ = ansi_supported_ && SetConsoleMode(out, request); - } else { - ansi_supported_ = false; + SetConsoleMode(out, request); } - HANDLE in = GetStdHandle(STD_INPUT_HANDLE); DWORD in_mode; if ((in != INVALID_HANDLE_VALUE) && GetConsoleMode(in, &in_mode)) { const DWORD request = in_mode | ENABLE_VIRTUAL_TERMINAL_INPUT; - ansi_supported_ = ansi_supported_ && SetConsoleMode(in, request); - } else { - ansi_supported_ = false; + SetConsoleMode(in, request); } } @@ -108,13 +103,10 @@ class PlatformWin { RestoreConsoleLocked(); } - static bool ansi_supported() { return ansi_supported_; } - private: static Mutex* platform_win_mutex_; static int saved_output_cp_; static int saved_input_cp_; - static bool ansi_supported_; static void RestoreConsoleLocked() { // STD_OUTPUT_HANDLE and STD_INPUT_HANDLE may have been closed or @@ -179,7 +171,6 @@ class PlatformWin { int PlatformWin::saved_output_cp_ = -1; int PlatformWin::saved_input_cp_ = -1; Mutex* PlatformWin::platform_win_mutex_ = NULL; -bool PlatformWin::ansi_supported_ = false; bool Platform::Initialize() { PlatformWin::InitOnce(); @@ -276,11 +267,6 @@ const char* Platform::ResolveExecutablePath() { } -bool Platform::AnsiSupported() { - return PlatformWin::ansi_supported(); -} - - void Platform::Exit(int exit_code) { // TODO(zra): Remove once VM shuts down cleanly. ::dart::private_flag_windows_run_tls_destructors = false; diff --git a/runtime/bin/stdio.cc b/runtime/bin/stdio.cc index aa1c7c2288c..fec14b1c170 100644 --- a/runtime/bin/stdio.cc +++ b/runtime/bin/stdio.cc @@ -69,6 +69,16 @@ void FUNCTION_NAME(Stdin_SetLineMode)(Dart_NativeArguments args) { } +void FUNCTION_NAME(Stdin_AnsiSupported)(Dart_NativeArguments args) { + bool supported = false; + if (Stdin::AnsiSupported(&supported)) { + Dart_SetBooleanReturnValue(args, supported); + } else { + Dart_SetReturnValue(args, DartUtils::NewDartOSError()); + } +} + + void FUNCTION_NAME(Stdout_GetTerminalSize)(Dart_NativeArguments args) { if (!Dart_IsInteger(Dart_GetNativeArgument(args, 0))) { OSError os_error(-1, "Invalid argument", OSError::kUnknown); @@ -92,6 +102,26 @@ void FUNCTION_NAME(Stdout_GetTerminalSize)(Dart_NativeArguments args) { } } + +void FUNCTION_NAME(Stdout_AnsiSupported)(Dart_NativeArguments args) { + if (!Dart_IsInteger(Dart_GetNativeArgument(args, 0))) { + OSError os_error(-1, "Invalid argument", OSError::kUnknown); + Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); + return; + } + intptr_t fd = DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 0)); + if ((fd != 1) && (fd != 2)) { + Dart_SetReturnValue(args, Dart_NewApiError("Terminal fd must be 1 or 2")); + return; + } + bool supported = false; + if (Stdout::AnsiSupported(fd, &supported)) { + Dart_SetBooleanReturnValue(args, supported); + } else { + Dart_SetReturnValue(args, DartUtils::NewDartOSError()); + } +} + } // namespace bin } // namespace dart diff --git a/runtime/bin/stdio.h b/runtime/bin/stdio.h index 56b591499b6..e55c317aa1d 100644 --- a/runtime/bin/stdio.h +++ b/runtime/bin/stdio.h @@ -27,6 +27,8 @@ class Stdin { static bool GetLineMode(bool* enabled); static bool SetLineMode(bool enabled); + static bool AnsiSupported(bool* supported); + private: DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(Stdin); @@ -36,6 +38,7 @@ class Stdin { class Stdout { public: static bool GetTerminalSize(intptr_t fd, int size[2]); + static bool AnsiSupported(intptr_t fd, bool* supported); private: DISALLOW_ALLOCATION(); diff --git a/runtime/bin/stdio_android.cc b/runtime/bin/stdio_android.cc index 8b264f81496..e6b62501dab 100644 --- a/runtime/bin/stdio_android.cc +++ b/runtime/bin/stdio_android.cc @@ -83,6 +83,21 @@ bool Stdin::SetLineMode(bool enabled) { } +static bool TermHasXTerm() { + const char* term = getenv("TERM"); + if (term == NULL) { + return false; + } + return strstr(term, "xterm") != NULL; +} + + +bool Stdin::AnsiSupported(bool* supported) { + *supported = isatty(STDIN_FILENO) && TermHasXTerm(); + return true; +} + + bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { struct winsize w; int status = NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w)); @@ -94,6 +109,12 @@ bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { return false; } + +bool Stdout::AnsiSupported(intptr_t fd, bool* supported) { + *supported = isatty(fd) && TermHasXTerm(); + return true; +} + } // namespace bin } // namespace dart diff --git a/runtime/bin/stdio_fuchsia.cc b/runtime/bin/stdio_fuchsia.cc index 07febe1df26..c36e06f6927 100644 --- a/runtime/bin/stdio_fuchsia.cc +++ b/runtime/bin/stdio_fuchsia.cc @@ -42,11 +42,23 @@ bool Stdin::SetLineMode(bool enabled) { } +bool Stdin::AnsiSupported(bool* supported) { + UNIMPLEMENTED(); + return false; +} + + bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { UNIMPLEMENTED(); return false; } + +bool Stdout::AnsiSupported(intptr_t fd, bool* supported) { + UNIMPLEMENTED(); + return false; +} + } // namespace bin } // namespace dart diff --git a/runtime/bin/stdio_linux.cc b/runtime/bin/stdio_linux.cc index 8199f201d71..d3aab28ae31 100644 --- a/runtime/bin/stdio_linux.cc +++ b/runtime/bin/stdio_linux.cc @@ -83,6 +83,21 @@ bool Stdin::SetLineMode(bool enabled) { } +static bool TermHasXTerm() { + const char* term = getenv("TERM"); + if (term == NULL) { + return false; + } + return strstr(term, "xterm") != NULL; +} + + +bool Stdin::AnsiSupported(bool* supported) { + *supported = isatty(STDIN_FILENO) && TermHasXTerm(); + return true; +} + + bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { struct winsize w; int status = NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w)); @@ -94,6 +109,12 @@ bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { return false; } + +bool Stdout::AnsiSupported(intptr_t fd, bool* supported) { + *supported = isatty(fd) && TermHasXTerm(); + return true; +} + } // namespace bin } // namespace dart diff --git a/runtime/bin/stdio_macos.cc b/runtime/bin/stdio_macos.cc index 5ed3d7e4e11..37ac894583d 100644 --- a/runtime/bin/stdio_macos.cc +++ b/runtime/bin/stdio_macos.cc @@ -83,6 +83,21 @@ bool Stdin::SetLineMode(bool enabled) { } +static bool TermHasXTerm() { + const char* term = getenv("TERM"); + if (term == NULL) { + return false; + } + return strstr(term, "xterm") != NULL; +} + + +bool Stdin::AnsiSupported(bool* supported) { + *supported = isatty(STDIN_FILENO) && TermHasXTerm(); + return true; +} + + bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { struct winsize w; int status = NO_RETRY_EXPECTED(ioctl(fd, TIOCGWINSZ, &w)); @@ -94,6 +109,12 @@ bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { return false; } + +bool Stdout::AnsiSupported(intptr_t fd, bool* supported) { + *supported = isatty(fd) && TermHasXTerm(); + return true; +} + } // namespace bin } // namespace dart diff --git a/runtime/bin/stdio_patch.dart b/runtime/bin/stdio_patch.dart index e537dc87212..d33fbc2123d 100644 --- a/runtime/bin/stdio_patch.dart +++ b/runtime/bin/stdio_patch.dart @@ -83,11 +83,20 @@ } } + @patch bool get supportsAnsiEscapes { + var result = _supportsAnsiEscapes(); + if (result is OSError) { + throw new StdinException("Error determining ANSI support", result); + } + return result; + } + static _echoMode() native "Stdin_GetEchoMode"; static _setEchoMode(bool enabled) native "Stdin_SetEchoMode"; static _lineMode() native "Stdin_GetLineMode"; static _setLineMode(bool enabled) native "Stdin_SetLineMode"; static _readByte() native "Stdin_ReadByte"; + static _supportsAnsiEscapes() native "Stdin_AnsiSupported"; } @patch class Stdout { @@ -112,6 +121,16 @@ } static _getTerminalSize(int fd) native "Stdout_GetTerminalSize"; + + @patch static bool _supportsAnsiEscapes(int fd) { + var result = _getAnsiSupported(fd); + if (result is! bool) { + throw new StdoutException("Error determining ANSI support", result); + } + return result; + } + + static _getAnsiSupported(int fd) native "Stdout_AnsiSupported"; } diff --git a/runtime/bin/stdio_unsupported.cc b/runtime/bin/stdio_unsupported.cc index 1482f919bbb..d4c9d0693ca 100644 --- a/runtime/bin/stdio_unsupported.cc +++ b/runtime/bin/stdio_unsupported.cc @@ -41,11 +41,23 @@ void FUNCTION_NAME(Stdin_SetLineMode)(Dart_NativeArguments args) { } +void FUNCTION_NAME(Stdin_AnsiSupported)(Dart_NativeArguments args) { + Dart_ThrowException( + DartUtils::NewDartArgumentError("Stdin unsupported on this platform")); +} + + void FUNCTION_NAME(Stdout_GetTerminalSize)(Dart_NativeArguments args) { Dart_ThrowException( DartUtils::NewDartArgumentError("Stdout unsupported on this platform")); } + +void FUNCTION_NAME(Stdout_AnsiSupported)(Dart_NativeArguments args) { + Dart_ThrowException( + DartUtils::NewDartArgumentError("Stdout unsupported on this platform")); +} + } // namespace bin } // namespace dart diff --git a/runtime/bin/stdio_win.cc b/runtime/bin/stdio_win.cc index 8f2a72cf23d..40df3a707be 100644 --- a/runtime/bin/stdio_win.cc +++ b/runtime/bin/stdio_win.cc @@ -9,6 +9,16 @@ #include "bin/stdio.h" +// These are not always defined in the header files. See: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx +#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT +#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 +#endif + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + namespace dart { namespace bin { @@ -77,6 +87,23 @@ bool Stdin::SetLineMode(bool enabled) { } +bool Stdin::AnsiSupported(bool* supported) { + ASSERT(supported != NULL); + HANDLE h = GetStdHandle(STD_INPUT_HANDLE); + if (h == INVALID_HANDLE_VALUE) { + *supported = false; + return false; + } + DWORD mode; + if (!GetConsoleMode(h, &mode)) { + *supported = false; + return false; + } + *supported = (mode & ENABLE_VIRTUAL_TERMINAL_INPUT) != 0; + return true; +} + + bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { HANDLE h; if (fd == 1) { @@ -93,6 +120,28 @@ bool Stdout::GetTerminalSize(intptr_t fd, int size[2]) { return true; } + +bool Stdout::AnsiSupported(intptr_t fd, bool* supported) { + ASSERT(supported != NULL); + HANDLE h; + if (fd == 1) { + h = GetStdHandle(STD_OUTPUT_HANDLE); + } else { + h = GetStdHandle(STD_ERROR_HANDLE); + } + if (h == INVALID_HANDLE_VALUE) { + *supported = false; + return false; + } + DWORD mode; + if (!GetConsoleMode(h, &mode)) { + *supported = false; + return false; + } + *supported = (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0; + return true; +} + } // namespace bin } // namespace dart diff --git a/sdk/lib/_internal/js_runtime/lib/io_patch.dart b/sdk/lib/_internal/js_runtime/lib/io_patch.dart index d6647d7a37b..46f8404ab2d 100644 --- a/sdk/lib/_internal/js_runtime/lib/io_patch.dart +++ b/sdk/lib/_internal/js_runtime/lib/io_patch.dart @@ -250,11 +250,6 @@ class _Platform { static String _version() { throw new UnsupportedError("Platform._version"); } - - @patch - static bool _ansiSupported() { - throw new UnsupportedError("Platform._ansiSupported"); - } } @patch @@ -550,6 +545,11 @@ class Stdin { void set lineMode(bool enabled) { throw new UnsupportedError("Stdin.lineMode"); } + + @patch + bool get supportsAnsiEscapes { + throw new UnsupportedError("Stdin.supportsAnsiEscapes"); + } } @patch @@ -568,6 +568,11 @@ class Stdout { int _terminalLines(int fd) { throw new UnsupportedError("Stdout.terminalLines"); } + + @patch + static bool _supportsAnsiEscapes(int fd) { + throw new UnsupportedError("Stdout.supportsAnsiEscapes"); + } } @patch diff --git a/sdk/lib/io/platform.dart b/sdk/lib/io/platform.dart index 482898c8d01..b087b053c32 100644 --- a/sdk/lib/io/platform.dart +++ b/sdk/lib/io/platform.dart @@ -71,7 +71,6 @@ class Platform { static final _operatingSystem = _Platform.operatingSystem; static final _localHostname = _Platform.localHostname; static final _version = _Platform.version; - static final _ansiSupported = _Platform.ansiSupported; /** * Get the number of processors of the machine. @@ -125,14 +124,6 @@ class Platform { */ static final bool isFuchsia = (_operatingSystem == "fuchsia"); - /** - * When stdio is connected to a terminal, whether ANSI codes are supported. - * - * This value is hard-coded to `true`, except on Windows where only more - * recent versions of Windows 10 support the codes. - */ - static final bool ansiSupported = _ansiSupported; - /** * Get the environment for this process. * diff --git a/sdk/lib/io/platform_impl.dart b/sdk/lib/io/platform_impl.dart index 98ad5c1816d..5f68b7dbdef 100644 --- a/sdk/lib/io/platform_impl.dart +++ b/sdk/lib/io/platform_impl.dart @@ -11,7 +11,6 @@ class _Platform { external static _localHostname(); external static _executable(); external static _resolvedExecutable(); - external static bool _ansiSupported(); /** * Retrieve the entries of the process environment. @@ -46,7 +45,6 @@ class _Platform { static int get numberOfProcessors => _numberOfProcessors(); static String get pathSeparator => _pathSeparator(); static String get operatingSystem => _operatingSystem(); - static bool get ansiSupported => _ansiSupported(); static Uri script; static String get localHostname { diff --git a/sdk/lib/io/stdio.dart b/sdk/lib/io/stdio.dart index 29ea48d2a43..8f1c6256dcc 100644 --- a/sdk/lib/io/stdio.dart +++ b/sdk/lib/io/stdio.dart @@ -136,6 +136,30 @@ class Stdin extends _StdStream implements Stream> { */ external void set lineMode(bool enabled); + /** + * Whether connected to a terminal that supports ANSI escape sequences. + * + * Not all terminals are recognized, and not all recognized terminals can + * report whether they support ANSI escape sequences, so this value is a + * best-effort attempt at detecting the support. + * + * The actual escape sequence support may differ between terminals, + * with some terminals supporting more escape sequences than others, + * and some terminals even differing in behavior for the same escape + * sequence. + * + * The ANSI color selection is generally supported. + * + * Currently, a `TERM` environment variable containing the string `xterm` + * will be taken as evidence that ANSI escape sequences are supported. + * On Windows, only versions of Windows 10 after v.1511 + * ("TH2", OS build 10586) will be detected as supporting the output of + * ANSI escape sequences, and only versions after v.1607 ("Anniversery + * Update", OS build 14393) will be detected as supporting the input of + * ANSI escape sequences. + */ + external bool get supportsAnsiEscapes; + /** * Synchronously read a byte from stdin. This call will block until a byte is * available. @@ -178,7 +202,7 @@ class Stdout extends _StdFileSink implements IOSink { */ int get terminalColumns => _terminalColumns(_fd); - /** + /* * Get the number of lines of the terminal. * * If no terminal is attached to stdout, a [StdoutException] is thrown. See @@ -186,9 +210,34 @@ class Stdout extends _StdFileSink implements IOSink { */ int get terminalLines => _terminalLines(_fd); + /** + * Whether connected to a terminal that supports ANSI escape sequences. + * + * Not all terminals are recognized, and not all recognized terminals can + * report whether they support ANSI escape sequences, so this value is a + * best-effort attempt at detecting the support. + * + * The actual escape sequence support may differ between terminals, + * with some terminals supporting more escape sequences than others, + * and some terminals even differing in behavior for the same escape + * sequence. + * + * The ANSI color selection is generally supported. + * + * Currently, a `TERM` environment variable containing the string `xterm` + * will be taken as evidence that ANSI escape sequences are supported. + * On Windows, only versions of Windows 10 after v.1511 + * ("TH2", OS build 10586) will be detected as supporting the output of + * ANSI escape sequences, and only versions after v.1607 ("Anniversery + * Update", OS build 14393) will be detected as supporting the input of + * ANSI escape sequences. + */ + bool get supportsAnsiEscapes => _supportsAnsiEscapes(_fd); + external bool _hasTerminal(int fd); external int _terminalColumns(int fd); external int _terminalLines(int fd); + external static bool _supportsAnsiEscapes(int fd); /** * Get a non-blocking `IOSink`. diff --git a/tests/standalone/io/ansi_supported_test.dart b/tests/standalone/io/ansi_supported_test.dart index a0f46690eb0..643ae7a95b7 100644 --- a/tests/standalone/io/ansi_supported_test.dart +++ b/tests/standalone/io/ansi_supported_test.dart @@ -6,18 +6,30 @@ import 'dart:io'; import "package:expect/expect.dart"; -main() { +testStdout(Stdout s) { try { - Platform.ansiSupported; - } catch (e, s) { - Expect.fail("Platform.ansiSupported threw: $e\n$s\n"); + s.supportsAnsiEscapes; + } catch (e, st) { + Expect.fail("$s.supportsAnsiEscapes threw: $e\n$st\n"); } - Expect.isNotNull(Platform.ansiSupported); - Expect.isTrue(Platform.ansiSupported is bool); - if (stdout.hasTerminal && Platform.ansiSupported) { - stdout.writeln('\x1b[31mThis text has a red foreground using SGR.31.'); - stdout.writeln('\x1b[39mThis text has restored the foreground color.'); + Expect.isNotNull(s.supportsAnsiEscapes); + Expect.isTrue(s.supportsAnsiEscapes is bool); + if (s.supportsAnsiEscapes) { + s.writeln('\x1b[31mThis text has a red foreground using SGR.31.'); + s.writeln('\x1b[39mThis text has restored the foreground color.'); } else { - stdout.writeln('ANSI codes not supported on this platform'); + s.writeln('ANSI escape codes are not supported on this platform'); } } + +main() { + testStdout(stdout); + testStdout(stderr); + try { + stdin.supportsAnsiEscapes; + } catch (e, st) { + Expect.fail("stdin.supportsAnsiEscapes threw: $e\n$st\n"); + } + Expect.isNotNull(stdin.supportsAnsiEscapes); + Expect.isTrue(stdin.supportsAnsiEscapes is bool); +}