clr/init.ps1

# adds ANSI escape support for Windows PowerShell
if ($PSEdition -eq "Desktop")
{
    # older terminals require manually enabling support for ANSI
    function Enable-ANSIEscapes
    {
        # Enable ANSI / VT100 16-color escape sequences:
        # Original discovery blog post:
        # http://stknohg.hatenablog.jp/entry/2016/02/22/195644
        # Esc sequence support documentation
        # https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx

        # This doesn't do anything if the type is already added, so don't worry
        # about doing this every single time, I guess
        Add-Type -MemberDefinition @"
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetStdHandle(int handle);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool GetConsoleMode(IntPtr handle, out int mode);
"@
 -Namespace Win32 -Name NativeMethods

        # GetStdHandle: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683231(v=vs.85).aspx
        # -11 is the code for STDOUT (-10 is STDIN, -12 is STDERR)
        $Handle = [Win32.NativeMethods]::GetStdHandle(-11)

        # GetConsoleMode: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
        # get the console "mode" --- contains info about how to handle
        # wrapping, etc. $Mode is set by reference by GetConsoleMode
        $Mode = 0
        [Win32.NativeMethods]::GetConsoleMode($Handle, [ref]$Mode)
        # the mode is a bitmask so we binary or with 0x0004
        # (ENABLE_VIRTUAL_TERMINAL_PROCESSING)

        # SetConsoleMode: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
        return [Win32.NativeMethods]::SetConsoleMode($Handle, $Mode -bor 4)
    }

    class ForegroundStyle
    {
        [string]$Black = "$([char]0x1b)[30m"
        [string]$Red = "$([char]0x1b)[31m"
        [string]$Green = "$([char]0x1b)[32m"
        [string]$Yellow = "$([char]0x1b)[33m"
        [string]$Blue = "$([char]0x1b)[34m"
        [string]$Magenta = "$([char]0x1b)[35m"
        [string]$Cyan = "$([char]0x1b)[36m"
        [string]$White = "$([char]0x1b)[37m"
        [string]$BrightBlack = "$([char]0x1b)[90m"
        [string]$BrightRed = "$([char]0x1b)[91m"
        [string]$BrightGreen = "$([char]0x1b)[92m"
        [string]$BrightYellow = "$([char]0x1b)[93m"
        [string]$BrightBlue = "$([char]0x1b)[94m"
        [string]$BrightMagenta = "$([char]0x1b)[95m"
        [string]$BrightCyan = "$([char]0x1b)[96m"
        [string]$BrightWhite = "$([char]0x1b)[97m"
    }

    class BackgroundStyle
    {
        [string]$Black = "$([char]0x1b)[40m"
        [string]$Red = "$([char]0x1b)[41m"
        [string]$Green = "$([char]0x1b)[42m"
        [string]$Yellow = "$([char]0x1b)[43m"
        [string]$Blue = "$([char]0x1b)[44m"
        [string]$Magenta = "$([char]0x1b)[45m"
        [string]$Cyan = "$([char]0x1b)[46m"
        [string]$White = "$([char]0x1b)[47m"
        [string]$BrightBlack = "$([char]0x1b)[100m"
        [string]$BrightRed = "$([char]0x1b)[101m"
        [string]$BrightGreen = "$([char]0x1b)[102m"
        [string]$BrightYellow = "$([char]0x1b)[103m"
        [string]$BrightBlue = "$([char]0x1b)[104m"
        [string]$BrightMagenta = "$([char]0x1b)[105m"
        [string]$BrightCyan = "$([char]0x1b)[106m"
        [string]$BrightWhite = "$([char]0x1b)[107m"
    }

    class PSFormattingStyle
    {
        [string]$FormatAccent = "$([char]0x1b)[32;1m"
        [string]$TableHeader = "$([char]0x1b)[32;1m"
        [string]$ErrorAccent = "$([char]0x1b)[36;1m"
        [string]$Error = "$([char]0x1b)[31;1m"
        [string]$Warning = "$([char]0x1b)[33;1m"
        [string]$Verbose = "$([char]0x1b)[33;1m"
        [string]$Debug = "$([char]0x1b)[33;1m"
    }

    class PSProgressStyle
    {
        [string]$ProgressStyle = "$([char]0x1b)[33;1m"
        [int]$MaxWidth = 120
        [int]$View = 0
        [bool]$UseOSCIndicator = $false
    }

    class PSStyleFileInfo
    {
        [string]$Directory = "$([char]0x1b)[44;1m"
        [string]$SymbolicLink = "$([char]0x1b)[36;1m"
        [string]$Executable = "$([char]0x1b)[32;1m"
        [string[]]$Extensions = @(
            ".zip",
            ".tgz",
            ".gz",
            ".tar",
            ".nupkg",
            ".cab",
            ".7z",
            ".ps1",
            ".psd1",
            ".psm1",
            ".ps1xml"
        )
    }

    class PSStyle
    {
        [int]$OutputRendering = 0
        $Formatting = [PSFormattingStyle]::new()
        $Background = [BackgroundStyle]::new()
        $Foreground = [ForegroundStyle]::new()
        $FileInfo = [PSStyleFileInfo]::new()
        [string]$Blink = "$([char]0x1b)[5m"
        [string]$BlinkOff = "$([char]0x1b)[25m"
        [string]$Bold = "$([char]0x1b)[1m"
        [string]$BoldOff = "$([char]0x1b)[22m"
        [string]$Hidden = "$([char]0x1b)[8m"
        [string]$HiddenOff = "$([char]0x1b)[28m"
        [string]$Reverse = "$([char]0x1b)[7m"
        [string]$ReverseOff = "$([char]0x1b)[27m"
        [string]$Italic = "$([char]0x1b)[3m"
        [string]$ItalicOff = "$([char]0x1b)[23m"
        [string]$Underline = "$([char]0x1b)[4m"
        [string]$UnderlineOff = "$([char]0x1b)[24m"
        [string]$Strikethrough = "$([char]0x1b)[9m"
        [string]$StrikethroughOff = "$([char]0x1b)[29m"
        [string]$Reset = "$([char]0x1b)[0m"
    }

    function Test-Ansi
    {
        [OutputType([bool])]
        param()

        return $host.Name -like "*ISE*"
    }

    function Get-PSStyle
    {
        [OutputType([PSStyle])]
        param()

        if (!(Test-Ansi))
        {
            Write-Host "$($host.Name) does not support ANSI escapes." -ForegroundColor Yellow
        }

        return [PSStyle]::new()
    }

    function Test-PSStyle
    {
        param(
            [Parameter(Position = 0)]
            [string]$TestMessage = "Test Message"
        )

        function Show-TestMessage
        {
            param(
                [string]$Header,
                [string]$Name,
                [string]$AnsiEscape,
                [string]$Message
            )

            $isFg = $Header -like "*Foreground*"

            if ($isFg)
            {
                $opt1 = $PSStyle.Background.Black
                $opt2 = $PSStyle.Background.White
            }
            else
            {
                $opt1 = $PSStyle.Foreground.White
                $opt2 = $PSStyle.Foreground.Black
            }

            $testString = "$AnsiEscape$($opt1)$Message$($PSStyle.Reset) $AnsiEscape$($opt2)$Message$($PSStyle.Reset)"

            $text = [string]::Format("{0}.{1,-13}: {2}", $Header, $Name, $testString)
            Write-Host $text
        }

        if ([string]::IsNullOrWhiteSpace($TestMessage))
        {
            $TestMessage = "Test Message"
        }

        $colors = $PSStyle.Foreground | Get-Member -MemberType Property | Where-Object Name -NotLike "Bright*" | Select-Object -ExpandProperty Name
        $colorType = @("Foreground", "Background")

        foreach ($styleType in $colorType)
        {
            # foreground colors
            foreach ($color in $colors)
            {
                $format = $PSStyle.$styleType
                $colorStyle = $format."$color"
                $brightColorStyle = $format."Bright$color"

                Show-TestMessage -Header $styleType -Name $color -AnsiEscape $colorStyle -Message $TestMessage
                Show-TestMessage -Header $styleType -Name "Bright$color" -AnsiEscape $brightColorStyle -Message $TestMessage
            }
        }
    }

    $PSStyle = [PSStyle]::new()

    Enable-ANSIEscapes
}