PowerLineClasses.psm1
class PowerLineBlock { [Nullable[ConsoleColor]]$BackgroundColor [Nullable[ConsoleColor]]$ForegroundColor [Object]$Content [bool]$Clear = $false PowerLineBlock() {} PowerLineBlock([hashtable]$values) { foreach($key in $values.Keys) { if("bg" -eq $key -or "BackgroundColor" -match "^$key") { $this.BackgroundColor = $values.$key } elseif("fg" -eq $key -or "ForegroundColor" -match "^$key") { $this.ForegroundColor = $values.$key } elseif("fg" -eq $key -or "ForegroundColor" -match "^$key") { $this.ForegroundColor = $values.$key } elseif("text" -match "^$key" -or "Content" -match "^$key") { $this.Content = $values.$key } elseif("Clear" -match "^$key") { $this.Clear = $values.$key } else { throw "Unknown key '$key' in hashtable. Allowed values are BackgroundColor, ForegroundColor, Content, and Clear" } } } [string] GetText() { if($this.Content -is [scriptblock]) { return & $this.Content } else { return $this.Content } } [string] ToString() { return $( if($this.BackgroundColor) { [PowerLineBlock]::EscapeCodes.bg."$($this.BackgroundColor)" } else { [PowerLineBlock]::EscapeCodes.bg.Clear } ) + $( if($this.ForegroundColor) { [PowerLineBlock]::EscapeCodes.fg."$($this.ForegroundColor)" } else { [PowerLineBlock]::EscapeCodes.fg.Clear } ) + $this.GetText() + $( if($this.Clear) { [PowerLineBlock]::EscapeCodes.bg.Clear [PowerLineBlock]::EscapeCodes.fg.Clear } ) } static [PowerLineBlock] $Column = [PowerLineBlockCache][PowerLineBlock]@{Content="`t"} static [hashtable] $EscapeCodes = @{ ESC = ([char]27) + "[" CSI = [char]155 Clear = ([char]27) + "[0m" fg = @{ Clear = ([char]27) + "[39m" Black = ([char]27) + "[30m"; DarkGray = ([char]27) + "[90m" DarkRed = ([char]27) + "[31m"; Red = ([char]27) + "[91m" DarkGreen = ([char]27) + "[32m"; Green = ([char]27) + "[92m" DarkYellow = ([char]27) + "[33m"; Yellow = ([char]27) + "[93m" DarkBlue = ([char]27) + "[34m"; Blue = ([char]27) + "[94m" DarkMagenta = ([char]27) + "[35m"; Magenta = ([char]27) + "[95m" DarkCyan = ([char]27) + "[36m"; Cyan = ([char]27) + "[96m" Gray = ([char]27) + "[37m"; White = ([char]27) + "[97m" } bg = @{ Clear = ([char]27) + "[49m" Black = ([char]27) + "[40m"; DarkGray = ([char]27) + "[100m" DarkRed = ([char]27) + "[41m"; Red = ([char]27) + "[101m" DarkGreen = ([char]27) + "[42m"; Green = ([char]27) + "[102m" DarkYellow = ([char]27) + "[43m"; Yellow = ([char]27) + "[103m" DarkBlue = ([char]27) + "[44m"; Blue = ([char]27) + "[104m" DarkMagenta = ([char]27) + "[45m"; Magenta = ([char]27) + "[105m" DarkCyan = ([char]27) + "[46m"; Cyan = ([char]27) + "[106m" Gray = ([char]27) + "[47m"; White = ([char]27) + "[107m" } } } class PowerLineBlockCache : PowerLineBlock { [string]$Content [int]$Length PowerLineBlockCache([PowerLineBlock] $output) { $this.BackgroundColor = $output.BackgroundColor $this.ForegroundColor = $output.ForegroundColor $this.Content = $output.GetText() $this.Length = $this.Content.Length } } class PowerLineBlock { [Nullable[ConsoleColor]]$BackgroundColor [Nullable[ConsoleColor]]$ForegroundColor [Object]$Content [bool]$Clear = $false PowerLineBlock() {} PowerLineBlock([hashtable]$values) { foreach($key in $values.Keys) { if("bg" -eq $key -or "BackgroundColor" -match "^$key") { $this.BackgroundColor = $values.$key } elseif("fg" -eq $key -or "ForegroundColor" -match "^$key") { $this.ForegroundColor = $values.$key } elseif("fg" -eq $key -or "ForegroundColor" -match "^$key") { $this.ForegroundColor = $values.$key } elseif("text" -match "^$key" -or "Content" -match "^$key") { $this.Content = $values.$key } elseif("Clear" -match "^$key") { $this.Clear = $values.$key } else { throw "Unknown key '$key' in hashtable. Allowed values are BackgroundColor, ForegroundColor, Content, and Clear" } } } [string] GetText() { if($this.Content -is [scriptblock]) { return & $this.Content } else { return $this.Content } } [string] ToString() { return $( if($this.BackgroundColor) { [PowerLineBlock]::EscapeCodes.bg."$($this.BackgroundColor)" } else { [PowerLineBlock]::EscapeCodes.bg.Clear } ) + $( if($this.ForegroundColor) { [PowerLineBlock]::EscapeCodes.fg."$($this.ForegroundColor)" } else { [PowerLineBlock]::EscapeCodes.fg.Clear } ) + $this.GetText() + $( if($this.Clear) { [PowerLineBlock]::EscapeCodes.bg.Clear [PowerLineBlock]::EscapeCodes.fg.Clear } ) } static [PowerLineBlock] $Column = [PowerLineBlockCache][PowerLineBlock]@{Content="`t"} static [hashtable] $EscapeCodes = @{ ESC = ([char]27) + "[" CSI = [char]155 Clear = ([char]27) + "[0m" fg = @{ Clear = ([char]27) + "[39m" Black = ([char]27) + "[30m"; DarkGray = ([char]27) + "[90m" DarkRed = ([char]27) + "[31m"; Red = ([char]27) + "[91m" DarkGreen = ([char]27) + "[32m"; Green = ([char]27) + "[92m" DarkYellow = ([char]27) + "[33m"; Yellow = ([char]27) + "[93m" DarkBlue = ([char]27) + "[34m"; Blue = ([char]27) + "[94m" DarkMagenta = ([char]27) + "[35m"; Magenta = ([char]27) + "[95m" DarkCyan = ([char]27) + "[36m"; Cyan = ([char]27) + "[96m" Gray = ([char]27) + "[37m"; White = ([char]27) + "[97m" } bg = @{ Clear = ([char]27) + "[49m" Black = ([char]27) + "[40m"; DarkGray = ([char]27) + "[100m" DarkRed = ([char]27) + "[41m"; Red = ([char]27) + "[101m" DarkGreen = ([char]27) + "[42m"; Green = ([char]27) + "[102m" DarkYellow = ([char]27) + "[43m"; Yellow = ([char]27) + "[103m" DarkBlue = ([char]27) + "[44m"; Blue = ([char]27) + "[104m" DarkMagenta = ([char]27) + "[45m"; Magenta = ([char]27) + "[105m" DarkCyan = ([char]27) + "[46m"; Cyan = ([char]27) + "[106m" Gray = ([char]27) + "[47m"; White = ([char]27) + "[107m" } } } class PowerLineBlockCache : PowerLineBlock { [string]$Content [int]$Length PowerLineBlockCache([PowerLineBlock] $output) { $this.BackgroundColor = $output.BackgroundColor $this.ForegroundColor = $output.ForegroundColor $this.Content = $output.GetText() $this.Length = $this.Content.Length } } class PowerLine : System.Collections.Generic.List[PowerLineBlock] { static [char]$LeftCap = [char]0xe0b0 # right-pointing arrow static [char]$RightCap = [char]0xe0b2 # left-pointing arrow static [char]$LeftSep = [char]0xe0b1 # left open > static [char]$RightSep = [char]0xe0b3 # right open < static [char]$Branch = [char]0xe0a0 # Branch symbol static [char]$LOCK = [char]0xe0a2 # Padlock static [char]$GEAR = [char]0x26ef # The settings icon, I use it for debug static [char]$POWER = [char]0x26a1 # The Power lightning-bolt icon [bool]$IsPromptLine = $false PowerLine() {} PowerLine([PowerLineBlock[]]$Blocks) : base($Blocks) { } PowerLine([PowerLineBlock[]]$Blocks, [bool]$PromptLine) : base($Blocks) { $this.IsPromptLine = $PromptLine } [string] ToString() { # Initialize variables ... $width = [Console]::BufferWidth $leftLength = 0 $rightLength = 0 # Precalculate all the text and remove empty blocks $Output = ([PowerLineBlockCache[]]@($this)) | Where Length # Output each block with appropriate separators and caps return $(for($l=0; $l -lt $Output.Length; $l++) { $block = $Output[$l] if([PowerLineBlock]::Column -eq $block) { # the length of the second column $rightLength = ($(for($r=$l+1; $r -lt $Output.Length; $r++) { $Output[$r].length + 1 }) | Measure-Object -Sum).Sum $space = $width - $rightLength if($leftLength) { # Output a cap on the left if there's output there # Use the Background of the previous block as the foreground [PowerLineBlock]@{ ForegroundColor = ($Output[($l-1)]).BackgroundColor Content = [PowerLine]::LeftCap Clear = $true } } if($this.IsPromptLine) { "$([PowerLineBlock]::EscapeCodes.ESC)s" } "$([PowerLineBlock]::EscapeCodes.ESC)${space}G" # the right cap uses the background of the next block as it's foreground [PowerLineBlock]@{ ForegroundColor = ($Output[($l+1)]).BackgroundColor Content = [PowerLine]::RightCap } } else { if($leftLength -eq 0 -and $rightLength -eq 0) { # On a new line, recalculate the length of the "left-aligned" line $leftLength = ($(for($r=$l; $r -lt $Output.Length -and $Output[$r] -ne [PowerLineBlock]::NewLine -and $Output[$r] -ne [PowerLineBlock]::Column; $r++) { $Output[$r].length + 1 }) | Measure-Object -Sum).Sum } $block # the actual output if($Output[($l+1)] -ne [PowerLineBlock]::NewLine -and $Output[($l+1)] -ne [PowerLineBlock]::Column) { # if the next block is the sambe background color, use a > if($block.BackgroundColor -eq $Output[($l+1)].BackgroundColor) { if($rightLength) { [PowerLine]::RightSep } else { [PowerLine]::LeftSep } } else { # Otherwise output a cap [PowerLineBlock]@{ ForegroundColor = $block.BackgroundColor BackgroundColor = $Output[($l+1)].BackgroundColor Content = if($rightLength) { [PowerLine]::RightCap } else { [PowerLine]::LeftCap } } } } } } # Output a cap on the left if we didn't already if(!$rightLength -and $leftLength) { [PowerLineBlock]@{ ForegroundColor = ($Output[($l-1)]).BackgroundColor Content = [PowerLine]::LeftCap Clear = $true } } [PowerLineBlock]::EscapeCodes.fg.Clear [PowerLineBlock]::EscapeCodes.bg.Clear # Anchor here if we didn't already if($this.IsPromptLine -and !$rightLength) { "$([PowerLineBlock]::EscapeCodes.ESC)s" }) -join "" } } class PowerLinePrompt : System.Collections.Generic.List[PowerLine] { [bool]$SetTitle = $true [bool]$SetCwd = $true [int]$PrefixLines = 0 PowerLinePrompt() { } PowerLinePrompt([Object[]]$PowerLines) { $this.AddRange([PowerLine[]]@($PowerLines)) } PowerLinePrompt([PowerLine[]]$PowerLines, [int]$PrefixLines) { $this.AddRange($PowerLines) $this.PrefixLines = $PrefixLines } [string] ToString() { return $( # Like output on the previous line(s) if($this.PrefixLines -ne 0) { "$([PowerLineBlock]::EscapeCodes.ESC)1A" * [Math]::Abs($this.PrefixLines) } @($this) -join "`n" # RECALL LOCATION if(@($this).IsPromptLine -contains $true) { "$([PowerLineBlock]::EscapeCodes.ESC)u" } [PowerLineBlock]::EscapeCodes.fg.Default ) -join "" } } |