From 543f5209c0372b5ff0ce65fdf74aefc6c1f5ba84 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 24 Aug 2022 11:11:18 -0700 Subject: [PATCH] Support mapping foreground/background `ConsoleColor` values to VT escape sequences (#17938) --- .../commands/utility/MatchString.cs | 4 +- .../PowerShellCore_format_ps1xml.cs | 28 +--- .../FormatAndOutput/common/PSStyle.cs | 130 ++++++++++++++++++ .../utils/VTUtils.cs | 2 + 4 files changed, 139 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs index b6b8cc9bfb..b9964d92bc 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs @@ -315,8 +315,8 @@ namespace Microsoft.PowerShell.Commands /// The matched line with matched text inverted. private string EmphasizeLine() { - string invertColorsVT100 = VTUtility.GetEscapeSequence(VTUtility.VT.Inverse); - string resetVT100 = VTUtility.GetEscapeSequence(VTUtility.VT.Reset); + string invertColorsVT100 = PSStyle.Instance.Reverse; + string resetVT100 = PSStyle.Instance.Reset; char[] chars = new char[(_matchIndexes.Count * (invertColorsVT100.Length + resetVT100.Length)) + Line.Length]; int lineIndex = 0; diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index f4b9d03489..4ab1fa8dd7 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -816,9 +816,7 @@ namespace System.Management.Automation.Runspaces $errorColor = $psstyle.Formatting.Error } - $resetColor = [System.Management.Automation.VTUtility]::GetEscapeSequence( - [System.Management.Automation.VTUtility+VT]::Reset - ) + $resetColor = $PSStyle.Reset } function Get-VT100Color([ConsoleColor] $color) { @@ -826,7 +824,7 @@ namespace System.Management.Automation.Runspaces return '' } - return [System.Management.Automation.VTUtility]::GetEscapeSequence($color) + return [System.Management.Automation.PSStyle]::MapForegroundColorToEscapeSequence($color) } function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) { @@ -1070,9 +1068,7 @@ namespace System.Management.Automation.Runspaces $resetColor = '' if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) { - $resetColor = [System.Management.Automation.VTUtility]::GetEscapeSequence( - [System.Management.Automation.VTUtility+VT]::Reset - ) + $resetColor = $PSStyle.Reset } function Get-VT100Color([ConsoleColor] $color) { @@ -1080,21 +1076,7 @@ namespace System.Management.Automation.Runspaces return '' } - return [System.Management.Automation.VTUtility]::GetEscapeSequence($color) - } - - # return length of string sans VT100 codes - function Get-RawStringLength($string) { - $vtCodes = ""`e[0m"", ""`e[2;30m"", ""`e[2;31m"", ""`e[2;32m"", ""`e[2;33m"", ""`e[2;34m"", - ""`e[2;35m"", ""`e[2;36m"", ""`e[2;37m"", ""`e[1;30m"", ""`e[1;31m"", ""`e[1;32m"", - ""`e[1;33m"", ""`e[1;34m"", ""`e[1;35m"", ""`e[1;36m"", ""`e[1;37m"" - - $newString = $string - foreach ($vtCode in $vtCodes) { - $newString = $newString.Replace($vtCode, '') - } - - return $newString.Length + return [System.Management.Automation.PSStyle]::MapForegroundColorToEscapeSequence($color) } # returns a string cut to last whitespace @@ -1217,7 +1199,7 @@ namespace System.Management.Automation.Runspaces # if rendering line information, break up the message if it's wider than the console if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') { - $prefixLength = Get-RawStringLength -string $prefix + $prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength $prefixVtLength = $prefix.Length - $prefixLength # replace newlines in message so it lines up correct diff --git a/src/System.Management.Automation/FormatAndOutput/common/PSStyle.cs b/src/System.Management.Automation/FormatAndOutput/common/PSStyle.cs index 3a5dcb36f0..a217063410 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/PSStyle.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/PSStyle.cs @@ -155,6 +155,16 @@ namespace System.Management.Automation return FromRgb(red, green, blue); } + + /// + /// Return the VT escape sequence for a foreground color. + /// + /// The foreground color to be mapped from. + /// The VT escape sequence representing the foreground color. + public string FromConsoleColor(ConsoleColor color) + { + return MapForegroundColorToEscapeSequence(color); + } } /// @@ -270,6 +280,16 @@ namespace System.Management.Automation return FromRgb(red, green, blue); } + + /// + /// Return the VT escape sequence for a background color. + /// + /// The background color to be mapped from. + /// The VT escape sequence representing the background color. + public string FromConsoleColor(ConsoleColor color) + { + return MapBackgroundColorToEscapeSequence(color); + } } /// @@ -722,6 +742,116 @@ namespace System.Management.Automation return s_psstyle; } } + + /// + /// The map of background console colors to escape sequences. + /// + private static readonly string[] BackgroundColorMap = + { + "\x1b[40m", // Black + "\x1b[44m", // DarkBlue + "\x1b[42m", // DarkGreen + "\x1b[46m", // DarkCyan + "\x1b[41m", // DarkRed + "\x1b[45m", // DarkMagenta + "\x1b[43m", // DarkYellow + "\x1b[47m", // Gray + "\x1b[100m", // DarkGray + "\x1b[104m", // Blue + "\x1b[102m", // Green + "\x1b[106m", // Cyan + "\x1b[101m", // Red + "\x1b[105m", // Magenta + "\x1b[103m", // Yellow + "\x1b[107m", // White + }; + + /// + /// The map of foreground console colors to escape sequences. + /// + private static readonly string[] ForegroundColorMap = + { + "\x1b[30m", // Black + "\x1b[34m", // DarkBlue + "\x1b[32m", // DarkGreen + "\x1b[36m", // DarkCyan + "\x1b[31m", // DarkRed + "\x1b[35m", // DarkMagenta + "\x1b[33m", // DarkYellow + "\x1b[37m", // Gray + "\x1b[90m", // DarkGray + "\x1b[94m", // Blue + "\x1b[92m", // Green + "\x1b[96m", // Cyan + "\x1b[91m", // Red + "\x1b[95m", // Magenta + "\x1b[93m", // Yellow + "\x1b[97m", // White + }; + + /// + /// Return the VT escape sequence for a ConsoleColor. + /// + /// The to be mapped from. + /// Whether or not it's a background color. + /// The VT escape sequence representing the color. + internal static string MapColorToEscapeSequence(ConsoleColor color, bool isBackground) + { + int index = (int)color; + if (index < 0 || index >= ForegroundColorMap.Length) + { + throw new ArgumentOutOfRangeException(paramName: nameof(color)); + } + + return (isBackground ? BackgroundColorMap : ForegroundColorMap)[index]; + } + + /// + /// Return the VT escape sequence for a foreground color. + /// + /// The foreground color to be mapped from. + /// The VT escape sequence representing the foreground color. + public static string MapForegroundColorToEscapeSequence(ConsoleColor foregroundColor) + => MapColorToEscapeSequence(foregroundColor, isBackground: false); + + /// + /// Return the VT escape sequence for a background color. + /// + /// The background color to be mapped from. + /// The VT escape sequence representing the background color. + public static string MapBackgroundColorToEscapeSequence(ConsoleColor backgroundColor) + => MapColorToEscapeSequence(backgroundColor, isBackground: true); + + /// + /// Return the VT escape sequence for a pair of foreground and background colors. + /// + /// The foreground color of the color pair. + /// The background color of the color pair. + /// The VT escape sequence representing the foreground and background color pair. + public static string MapColorPairToEscapeSequence(ConsoleColor foregroundColor, ConsoleColor backgroundColor) + { + int foreIndex = (int)foregroundColor; + int backIndex = (int)backgroundColor; + + if (foreIndex < 0 || foreIndex >= ForegroundColorMap.Length) + { + throw new ArgumentOutOfRangeException(paramName: nameof(foregroundColor)); + } + + if (backIndex < 0 || backIndex >= ForegroundColorMap.Length) + { + throw new ArgumentOutOfRangeException(paramName: nameof(backgroundColor)); + } + + string foreground = ForegroundColorMap[foreIndex]; + string background = BackgroundColorMap[backIndex]; + + return string.Concat( + foreground.AsSpan(start: 0, length: foreground.Length - 1), + ";".AsSpan(), + background.AsSpan(start: 2)); + } } + #endregion PSStyle } diff --git a/src/System.Management.Automation/utils/VTUtils.cs b/src/System.Management.Automation/utils/VTUtils.cs index c91ce24e19..3a22256f05 100644 --- a/src/System.Management.Automation/utils/VTUtils.cs +++ b/src/System.Management.Automation/utils/VTUtils.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Collections.Generic; namespace System.Management.Automation @@ -8,6 +9,7 @@ namespace System.Management.Automation /// /// Class to help with VT escape sequences. /// + [Obsolete("This class is deprecated. Use 'System.Management.Automation.PSStyle' instead.", error: true)] public sealed class VTUtility { ///