PowerLine.psm1

using namespace PoshCode
using namespace System.Management.Automation
using namespace System.Collections.Generic
using namespace PoshCode.Pansies
#Region '.\Classes\PowerLineTheme.ps1' 0
#using namespace PoshCode
class PowerLineTheme {
    [TerminalBlock[]]$Prompt
    [BlockCaps]$DefaultCaps
    [string]$DefaultSeparator
    [scriptblock]$Title
    [bool]$SetCurrentDirectory
    [bool]$HideErrors
    [string]$RepeatPrompt
    [RgbColor[]]$PSReadLineErrorColor
    [string]$PSReadLineContinuationPrompt
    [string]$PSReadLineContinuationPromptColor
    [string[]]$PSReadLinePromptText
    [int]$DefaultAddIndex = -1
}

Add-MetadataConverter @{
    [PowerLineTheme] = { "PSObject @{
    DefaultCaps = '$($_.DefaultCaps.Left)', '$($_.DefaultCaps.Right)'
    DefaultSeparator = '$($_.DefaultSeparator)'
    Prompt = @(
        $($_.Prompt.ToPsScript() -join "`n ")
    )
    PSReadLineContinuationPrompt = '$($_.PSReadLineContinuationPrompt)'
    PSReadLineContinuationPromptColor = '$($_.PSReadLineContinuationPromptColor)'
    PSReadLineErrorColor = '$($_.PSReadLineErrorColor -join "','")'
    HideErrors = $(if ($_.HideErrors) { '$true' } else { '$false' })
    RepeatPrompt = '$(if ($_.RepeatPrompt) { $_.RepeatPrompt } else { 'CachedPrompt' })'
    SetCurrentDirectory = $(if ($_.SetCurrentDirectory) { '$true' } else { '$false' })$(
    if (![string]::IsNullOrWhiteSpace($_.Title)) {
        "`n Title = ScriptBlock @'`n$($_.Title)`n'@"
    })$(
        if ($_.DefaultAddIndex -ge 0) {
        "`n DefaultAddIndex = $($_.DefaultAddIndex)"
    })
}"

    }
}
#EndRegion '.\Classes\PowerLineTheme.ps1' 39
#Region '.\Private\_init.ps1' 0
#!/usr/bin/env powershell
#using namespace System.Management.Automation
#using namespace System.Collections.Generic
#using namespace PoshCode.Pansies

[PoshCode.TerminalBlock]::DefaultCaps = "","$([char]0xE0B0)"

# Ensure the global prompt variable exists and is typed the way we expect
[System.Collections.Generic.List[PoshCode.TerminalBlock]]$Global:Prompt = [PoshCode.TerminalBlock[]]@(
    if (Test-Path Variable:Prompt) {
        $Prompt | ForEach-Object { [PoshCode.TerminalBlock]$_ }
    }
)
#EndRegion '.\Private\_init.ps1' 14
#Region '.\Private\WriteExceptions.ps1' 0
function WriteExceptions {
    [CmdletBinding()]
    param(
        # A dictionary mapping script blocks to the exceptions which threw them
        [System.Collections.Specialized.OrderedDictionary]$ScriptExceptions
    )
    $ErrorString = ""

    if ($PromptErrors.Count -gt 0) {
        $global:PromptErrors = [ordered]@{} + $ScriptExceptions
        Write-Warning "$($global:PromptErrors.Count) error(s) in prompt. Check `$PromptErrors for details. To ignore, Set-PowerLinePrompt -HideError"
        if ((Test-Path Variable:\PSStyle) -and $PSStyle.Formatting.Error) {
            foreach ($e in $ScriptExceptions.Values) {
                $ErrorString += $PSStyle.Formatting.Error + "$e" + $PSStyle.Reset + "`n"
            }
        } elseif (@($Host.PrivateData.PSTypeNames)[0] -eq "Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy") {
            foreach ($e in $ScriptExceptions.Values) {
                $ErrorString += [PoshCode.Pansies.Text]@{
                    ForegroundColor = $Host.PrivateData.ErrorForegroundColor
                    BackgroundColor = $Host.PrivateData.ErrorBackgroundColor
                    Object = $e
                }
                $ErrorString += "`n"
            }
        } else {
            foreach ($e in $ScriptExceptions) {
                $ErrorString += [PoshCode.Pansies.Text]@{
                    ForegroundColor = "Red"
                    BackgroundColor = "Black"
                    Object = $e
                }
                $ErrorString += "`n"
            }
        }
    }

    $ErrorString
}
#EndRegion '.\Private\WriteExceptions.ps1' 39
#Region '.\Public\Add-PowerLineBlock.ps1' 0
function Add-PowerLineBlock {
    <#
        .Synopsis
            Insert text or a ScriptBlock into the $Prompt
        .Description
            This function exists primarily to ensure that modules are able to modify the prompt easily without repeating themselves.
        .Example
            Add-PowerLineBlock { "`nI &hearts; PS" }
 
            Adds the classic "I ♥ PS" to your prompt on a new line. We actually recommend having a simple line in pure 16-color mode on the last line of your prompt, to ensures that PSReadLine won't mess up your colors. PSReadline overwrites your prompt line when you type -- and it can only handle 16 color mode.
        .Example
            Add-PowerLineBlock {
                New-TerminalBlock { Show-Elapsed } -ForegroundColor White -BackgroundColor DarkBlue -ErrorBackground DarkRed -ElevatedForegroundColor Yellow
            } -Index -2
 
            # This example uses Add-PowerLineBlock to insert a block into the prommpt _before_ the last block
            # It calls Show-Elapsed to show the duration of the last command as the text of the block
            # It uses New-TerminalBlock to control the color so that it's highlighted in red if there is an error, but otherwise in dark blue (or yellow if it's an elevated host).
    #>

    [CmdletBinding()]
    param(
        # The text, object, or scriptblock to show as output
        [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias("Text", "Content")]
        $InputObject,

        # The position to insert the InputObject at, by default, inserts in the same place as the last one
        [int]$Index = -1,

        # When set by a module, hooks the calling module to remove this block if the module is removed
        [Switch]$AutoRemove,

        # If set, adds the input to the prompt without checking if it's already there
        [Switch]$Force
    )
    process {

        Write-Debug "Add-PowerLineBlock $InputObject"
        if(!$PSBoundParameters.ContainsKey("Index")) {
            $Index = $Script:PowerLineConfig.DefaultAddIndex++
        }
        if ($InputObject -isnot [PoshCode.TerminalBlock]) {
            $InputObject = New-TerminalBlock $InputObject
        }

        # If force is true, it doesn't matter what Skip is. Otherwise, calculate Skip
        $Skip = $Force -or @($Global:Prompt).ForEach{ $_.Content.ToString().Trim() } -eq $InputObject.Content.ToString().Trim()

        if ($Force -or !$Skip) {
            if ($Index -eq -1 -or $Index -ge $Global:Prompt.Count) {
                Write-Verbose "Appending '$InputObject' to the end of the prompt"
                $Global:Prompt.Add($InputObject)
                $Index = $Global:Prompt.Count
            } elseif($Index -lt 0) {
                $Index = $Global:Prompt.Count - $Index
                Write-Verbose "Inserting '$InputObject' at $Index of the prompt"
                $Global:Prompt.Insert($Index, $InputObject)
            } else {
                Write-Verbose "Inserting '$InputObject' at $Index of the prompt"
                $Global:Prompt.Insert($Index, $InputObject)
            }
            $Script:PowerLineConfig.DefaultAddIndex = $Index + 1
        } else {
            Write-Verbose "Prompt already contained the InputObject block"
        }

        if ($AutoRemove) {
            if (($CallStack = Get-PSCallStack).Count -ge 2) {
                if ($Module = $CallStack[1].InvocationInfo.MyCommand.Module) {
                    $Module.OnRemove = { Remove-PowerLineBlock $InputObject }.GetNewClosure()
                }
            }
        }
    }
}
#EndRegion '.\Public\Add-PowerLineBlock.ps1' 76
#Region '.\Public\Export-PowerLinePrompt.ps1' 0
function Export-PowerLinePrompt {
    [CmdletBinding()]
    param()

    Get-PowerLineTheme | Export-Configuration -AsHashtable

}
#EndRegion '.\Public\Export-PowerLinePrompt.ps1' 8
#Region '.\Public\Get-PowerLineTheme.ps1' 0
function Get-PowerLineTheme {
    <#
        .SYNOPSIS
            Get the themeable PowerLine settings
    #>

    [CmdletBinding()]
    param()

    [PowerLineTheme]$Local:Configuration = $Script:PowerLineConfig

    # We use global:Prompt except when importing and exporting
    $Configuration.Prompt = [PoshCode.TerminalBlock[]]$global:Prompt

    if (Get-Command Get-PSReadLineOption) {
        $PSReadLineOptions = Get-PSReadLineOption
        # ContinuationPrompt can have colors in them
        $Configuration.PSReadLineContinuationPrompt = $PSReadLineOptions.ContinuationPrompt
        # If the ContinuationPrompt has color in it, this is irrelevant, but keep it anyway
        $Configuration.PSReadLineContinuationPromptColor = $PSReadLineOptions.ContinuationPromptColor
    }

    $Configuration
}
#EndRegion '.\Public\Get-PowerLineTheme.ps1' 24
#Region '.\Public\Remove-PowerLineBlock.ps1' 0
function Remove-PowerLineBlock {
    <#
        .Synopsis
            Remove text or a ScriptBlock from the $Prompt
        .Description
            This function exists primarily to ensure that modules are able to clean up the prompt easily when they're removed
        .Example
            Remove-PowerLineBlock -Index -1
 
            Removes the last block from the prompt
        .Example
            Show-ElapsedTime | Add-PowerLineBlock
 
            Show-ElapsedTime | Remove-PowerLineBlock
 
            Adds and then removes the Show-ElapsedTime block from the prompt
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidDefaultValueForMandatoryParameter', "Index",
        Justification = 'This rule should ignore parameters that are only mandatory in some parameter sets')]
    [CmdletBinding(DefaultParameterSetName="ByObject")]
    param(
        # The text, object, or scriptblock to show as output
        [Parameter(Position=0, Mandatory, ValueFromPipeline, ParameterSetName = "ByObject")]
        [Alias("Text")]
        $InputObject,

        [Parameter(Mandatory, ParameterSetName = "ByIndex")]
        [int]$Index = -1
    )
    process {
        # We always remove by index so we can adjust the DefaultAddIndex
        if ($PSCmdlet.ParameterSetName -eq "ByObject") {
            if ($InputObject -is [PoshCode.TerminalBlock]) {
                $Index = @($Global:Prompt).IndexOf($InputObject)
                if ($Index -lt 0) {
                    $InputObject = $InputObject.Content
                }
            }

            if ($Index -lt 0) {
                $Index = @($Global:Prompt).ForEach("Content").IndexOf($InputObject)
            }

            if ($Index -lt 0) {
                $InputString = $InputObject.ToString().Trim()
                $Index = @($Global:Prompt).ForEach{ $_.Content.ToString().Trim() }.IndexOf($InputString)
                if ($Index -ge 0) {
                    Write-Warning "Removing `$Prompt[$Index] from the prompt with partial match."
                }
            }
        } elseif ($Index -lt 0) {
            # We are ADDING the negative number to subract.
            $Index = $Global:Prompt.Count + $Index
        }

        if ($Index -ge 0) {
            $null = $Global:Prompt.RemoveAt($Index)
            if ($Index -lt $Script:PowerLineConfig.DefaultAddIndex) {
                $Script:PowerLineConfig.DefaultAddIndex--
            }
        } else {
            Write-Error "Could not find $InputObject to remove from the prompt."
        }
    }
}
#EndRegion '.\Public\Remove-PowerLineBlock.ps1' 66
#Region '.\Public\Set-PowerLinePrompt.ps1' 0
function Set-PowerLinePrompt {
    #.Synopsis
    # Set the default PowerLine prompt function which uses the $Prompt variable
    #.Description
    # Overwrites the current prompt function with one that uses the $Prompt variable
    # Note that this doesn't try to preserve any changes already made to the prompt by modules like ZLocation
    #.Example
    # Set-PowerLinePrompt -SetCurrentDirectory
    #
    # Sets the powerline prompt and activates and option supported by this prompt function to update the .Net environment with the current directory each time the prompt runs.
    #.Example
    # Set-PowerLinePrompt -SetCurrentDirectory -Title {
    # Show-Path -HomeString "~" -Depth 2 -GitDir
    # } -RestoreVirtualTerminal
    #
    # Turns on all the non-prompt features of PowerLine:
    # - Update the .net environment with the current directory each time the prompt runs
    # - Update the title with a short version of the path each time the prompt runs
    # - This legacy option calls a Windows api to enable VirtualTerminal processing on old versions of the Windows Console where this wasn't the default (if git is messing up your terminal, try this).
    #.Example
    # Set-PowerLinePrompt -PowerLineFont
    #
    # Sets the prompt using the default PowerLine characters. Note that you can still change the characters used to separate blocks in the PowerLine output after running this, by setting the Cap and Separator.
    #.Example
    # Set-PowerLinePrompt -NoBackground
    #
    # Sets the powerline prompt without the PowerLine effect. This disables background color on ALL current blocks, and switches the Cap and Separator to just a space. Remember that you can change the characters used to separate blocks in your prompt, by setting the Cap and Separator without affecting backgrounds.

    [Alias("Set-PowerLineTheme")]
    [CmdletBinding(DefaultParameterSetName = "Manual")]
    param(
        # One or more scriptblocks or TerminalBlocks you want to use as your new prompt
        [Parameter(Position = 0, ValueFromPipelineByPropertyName)]
        $Prompt,

        # Resets the prompt to use the default PowerLine characters as cap and separator
        [Parameter(ParameterSetName = "PowerLine", Mandatory)]
        [switch]$PowerLineFont,

        # PowerLine uses TerminalBlocks, and the DefaultCaps parameter sets [PoshCode.TerminalBlocks]::DefaultCaps
        # These are the cap character(s) that will be used (by default) on blocks
        # Pass two characters: the first for the left side, the second for the right side.
        [Parameter(ParameterSetName = "Manual", ValueFromPipelineByPropertyName)]
        [PoshCode.BlockCaps]$DefaultCaps,

        # PowerLine uses TerminalBlocks, and the DefaultSeparator parameter sets [PoshCode.TerminalBlocks]::DefaultSeparator
        # The separator character is used by some TerminalBlocks to separate multiple objects (like a path separator)
        [Parameter(ParameterSetName = "Manual", ValueFromPipelineByPropertyName)]
        [Alias("Separator")]
        [string]$DefaultSeparator,

        # Sets the powerline prompt without the PowerLine effect.
        # Disables background on ALL TerminalBlocks
        # Switches the Cap and Separator to just a space.
        [Parameter(ParameterSetName = "Reset", Mandatory)]
        [switch]$NoBackground,

        # If set, calls Export-PowerLinePrompt
        [Parameter()]
        [Switch]$Save,

        # A script which outputs a string used to update the Window Title each time the prompt is run
        [Parameter(ValueFromPipelineByPropertyName)]
        [scriptblock]$Title,

        # Keep the .Net Current Directory in sync with PowerShell's
        [Parameter(ValueFromPipelineByPropertyName)]
        [Alias("CurrentDirectory")]
        [switch]$SetCurrentDirectory,

        # Prevent errors in the prompt from being shown (like the normal PowerShell behavior)
        [Parameter(ValueFromPipelineByPropertyName)]
        [switch]$HideErrors,

        # How to render repeated prompts. A prompt is considered a repeat if it's run multiple times without a command,
        # such as when pressing enter repeatedly, or hitting Ctrl+C or Ctrl+L (any time the $MyInvcation.HistoryId does not change)
        #
        # By default, PowerLine uses "CachedPrompt" which repeats the whole prompt, but doesn't re-run the prompt blocks.
        #
        # You can choose to render only the last block, or the last line of the prompt, or to Recalculate the whole prompt.
        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateSet("LastBlock", "LastLine", "CachedPrompt", "Recalculate")]
        [string]$RepeatPrompt,

        # When there's a parse error, PSReadLine changes a part of the prompt based on it's PromptText configuration.
        # This setting causes PowerLine to update the PSReadLiine PromptText on each run.
        #
        # By default, if the last prompt block has a background color, it will be set to tomato red (otherwise, the foreground color)
        # If you pass just one color, that color will be used instead of tomato red.
        # If you pass a pair of colors, the first will be replaced with the second throughout the entire last line of the prompt
        #
        # To disable this feature, pass an empty array, and PowerLine will not change the PromptText
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor[]]$PSReadLineErrorColor,

        # When you type a command that requires a second line (like if you type | and hit enter)
        # This is the prompt text. Can be an empty string. Can be anything, really.
        [Parameter(ValueFromPipelineByPropertyName)]
        [AllowEmptyString()]
        [string]$PSReadLineContinuationPrompt,

        # Let's you set the fg/bg of the PSReadLine continuation prompt as escape sequences.
        # The easy way is to use PANSIES notation: "$fg:Red$Bg:White"
        [Parameter(ValueFromPipelineByPropertyName)]
        [AllowEmptyString()]
        [string]$PSReadLineContinuationPromptColor
    )
    begin {
        if ($null -eq $script:OldPrompt) {
            $script:OldPrompt = $function:global:prompt
            $MyInvocation.MyCommand.Module.OnRemove = {
                $function:global:prompt = $script:OldPrompt
            }
        }

        # Switches have a (non-null) default value, so we need to set them in case they were not passed explicitly
        $Configuration = Import-Configuration -ErrorAction SilentlyContinue | Update-Object @{
            HideErrors = $HideErrors
            SetCurrentDirectory = $SetCurrentDirectory
        }

        # Strip common parameters to avoid adding nonsense to the object
        foreach ($name in [System.Management.Automation.PSCmdlet]::CommonParameters + @("Save", "NoBackground", "PowerLineFont")) {
            $null = $PSBoundParameters.Remove($name)
        }
    }
    process {

        [PowerLineTheme]$Local:PowerLineConfig = $Configuration | Update-Object $PSBoundParameters

        # Set the default cap & separator before we cast prompt blocks
        if ($NoBackground) {
            $PowerLineConfig.DefaultCaps = [PoshCode.TerminalBlock]::DefaultCaps = "", " "
            $PowerLineConfig.DefaultSeparator = [PoshCode.TerminalBlock]::DefaultSeparator = "/"
        }

        # For backward compatibility:
        if ($PSBoundParameters.ContainsKey("PowerLineFont")) {
            if ($PowerLineFont) {
                # Make sure we're using the default PowerLine characters:
                [PoshCode.Pansies.Entities]::ExtendedCharacters['ColorSeparator'] = [char]0xe0b0
                [PoshCode.Pansies.Entities]::ExtendedCharacters['ReverseColorSeparator'] = [char]0xe0b2
                [PoshCode.Pansies.Entities]::ExtendedCharacters['Separator'] = [char]0xe0b1
                [PoshCode.Pansies.Entities]::ExtendedCharacters['ReverseSeparator'] = [char]0xe0b3
                [PoshCode.Pansies.Entities]::ExtendedCharacters['Branch'] = [char]0xE0A0
                [PoshCode.Pansies.Entities]::ExtendedCharacters['Gear'] = [char]0x26EF

                # [PoshCode.Pansies.Entities]::EnableNerdFonts = $true
            } else {
                # Use characters that at least work in Consolas and Lucida Console
                [PoshCode.Pansies.Entities]::ExtendedCharacters['ColorSeparator'] = [char]0x258C
                [PoshCode.Pansies.Entities]::ExtendedCharacters['ReverseColorSeparator'] = [char]0x2590
                [PoshCode.Pansies.Entities]::ExtendedCharacters['Separator'] = [char]0x25BA
                [PoshCode.Pansies.Entities]::ExtendedCharacters['ReverseSeparator'] = [char]0x25C4
                [PoshCode.Pansies.Entities]::ExtendedCharacters['Branch'] = [char]0x00A7
                [PoshCode.Pansies.Entities]::ExtendedCharacters['Gear'] = [char]0x263C
            }
            # Set the new Cap and Separator options too
            [PoshCode.TerminalBlock]::DefaultCaps = $PowerLineConfig.DefaultCaps = "", [PoshCode.Pansies.Entities]::ExtendedCharacters["ColorSeparator"]
            [PoshCode.TerminalBlock]::DefaultSeparator = $PowerLineConfig.Separator = [PoshCode.Pansies.Entities]::ExtendedCharacters["Separator"]
        }

        if ($PSBoundParameters.ContainsKey("DefaultCaps")) {
            [PoshCode.TerminalBlock]::DefaultCaps = $PowerLineConfig.DefaultCaps = $DefaultCaps
            [PoshCode.Pansies.Entities]::ExtendedCharacters["ColorSeparator"] = $DefaultCaps.Right
            [PoshCode.Pansies.Entities]::ExtendedCharacters["ReverseColorSeparator"] = $DefaultCaps.Left
        } elseif (!$PowerLineConfig.DefaultCaps) {
            # If there's nothing in the config, then default to Powerline style!
            [PoshCode.TerminalBlock]::DefaultCaps = $PowerLineConfig.DefaultCaps = "", [PoshCode.Pansies.Entities]::ExtendedCharacters["ColorSeparator"]
        } elseif ($PowerLineConfig.DefaultCaps) {
            [PoshCode.TerminalBlock]::DefaultCaps = $PowerLineConfig.DefaultCaps
        }

        if ($PSBoundParameters.ContainsKey("DefaultSeparator")) {
            [PoshCode.TerminalBlock]::DefaultSeparator = $PowerLineConfig.DefaultSeparator = $DefaultSeparator
        } elseif (!$PowerLineConfig.DefaultSeparator) {
            # If there's nothing in the config, then default to Powerline style!
            [PoshCode.TerminalBlock]::DefaultSeparator = $PowerLineConfig.DefaultSeparator = [PoshCode.Pansies.Entities]::ExtendedCharacters["DefaultSeparator"]
        } elseif ($PowerLineConfig.DefaultSeparator) {
            [PoshCode.TerminalBlock]::DefaultSeparator = $PowerLineConfig.DefaultSeparator
        }

        # These switches aren't stored in the config
        $null = $PSBoundParameters.Remove("Save")

        Write-Verbose "Setting global:Prompt"
        # We want to support modifying the global:prompt variable outside this function
        [PoshCode.TerminalBlock[]]$PowerLineConfig.Prompt = @(
            if ($PSBoundParameters.ContainsKey("Prompt")) {
                Write-Verbose "Setting global:Prompt from prompt parameter"
                $Local:Prompt
            # They didn't pass anything, and there's nothing set
            } elseif ($global:Prompt.Count -eq 0) {
                # If we found something in config
                if ($PowerLineConfig.Prompt.Count -gt 0) {
                    Write-Verbose "Setting global:Prompt from powerline config"
                    # If the config is already TerminalBlock, just use that:
                    if ($PowerLineConfig.Prompt -as [PoshCode.TerminalBlock[]]) {
                        $PowerLineConfig.Prompt
                    } else {
                        # Try to upgrade by casting through scriptblock
                        [ScriptBlock[]]@($PowerLineConfig.Prompt)
                    }
                } else {
                    Write-Verbose "Setting global:Prompt from default prompt"
                    # The default PowerLine Prompt
                    Show-HistoryId -DefaultBackgroundColor DarkGray -ErrorBackgroundColor Red
                    Show-Path -DefaultBackgroundColor White
                }
            } else {
                Write-Verbose "Setting global:Prompt from existing global:prompt"
                $global:Prompt
            }
        )
        if ($NoBackground) {
            foreach($block in $PowerLineConfig.Prompt){
                $block.BackgroundColor = $null
            }
        }

        [System.Collections.Generic.List[PoshCode.TerminalBlock]]$global:Prompt = $PowerLineConfig.Prompt

        if ($null -eq $PowerLineConfig.DefaultAddIndex) {
            $PowerLineConfig.DefaultAddIndex    = -1
        }

        $Script:PowerLineConfig = $PowerLineConfig

        if (Get-Module PSReadLine) {
            $Options = @{}
            if ($PowerLineConfig.PSReadLineContinuationPrompt) {
                $Options["ContinuationPrompt"] = $PowerLineConfig.PSReadLineContinuationPrompt
            }
            if ($PowerLineConfig.PSReadLineContinuationPromptColor) {
                $Options["Colors"] = @{
                    ContinuationPrompt = $PowerLineConfig.PSReadLineContinuationPromptColor
                }
            }
            if ($Options) {
                Write-Verbose "Updating PSReadLine prompt options: `n$($Options.PromptText -join "`n")`n`n$($Options["Colors"]["ContinuationPrompt"])$($Options["ContinuationPrompt"])"
                Set-PSReadLineOption @Options
            }
        }

        # Finally, update the prompt function
        $function:global:prompt = { Write-PowerlinePrompt }
        [PoshCode.Pansies.RgbColor]::ResetConsolePalette()

        # If they asked us to save, or if there's nothing saved yet
        if($Save -or ($PSBoundParameters.Count -and !(Test-Path (Join-Path (Get-StoragePath) Configuration.psd1)))) {
            Export-PowerLinePrompt
        }
    }
}
#EndRegion '.\Public\Set-PowerLinePrompt.ps1' 255
#Region '.\Public\Write-PowerlinePrompt.ps1' 0
function Write-PowerlinePrompt {
    [CmdletBinding()]
    [OutputType([string])]
    param()

    try {
        $PromptErrors = [ordered]@{}

        ### PowerLinePrompt Features:
        # Title
        if ($Script:PowerLineConfig.Title) {
            try {
                $Host.UI.RawUI.WindowTitle = [System.Management.Automation.LanguagePrimitives]::ConvertTo( (& $Script:PowerLineConfig.Title), [string] )
            } catch {
                $PromptErrors.Add("0 {$($Script:PowerLineConfig.Title)}", $_)
                Write-Error "Failed to set Title from scriptblock { $($Script:PowerLineConfig.Title) }"
            }
        }
        # SetCurrentDirectory (for dotnet)
        if ($Script:PowerLineConfig.SetCurrentDirectory) {
            try {
                # Make sure Windows & .Net know where we are
                # They can only handle the FileSystem, and not in .Net Core
                [System.IO.Directory]::SetCurrentDirectory( (Get-Location -PSProvider FileSystem).ProviderPath )
            } catch {
                $PromptErrors.Add("0 { SetCurrentDirectory }", $_)
                Write-Error "Failed to set CurrentDirectory to: (Get-Location -PSProvider FileSystem).ProviderPath"
            }
        }

        # RepeatPrompt (for speed)
        if ($MyInvocation.HistoryId -eq $Script:LastHistoryId) {
            if ($Script:PowerLineConfig.RepeatPrompt -eq "LastLine") {
                # Repeat only the last line
                $LastLine = 1 + $Prompt.FindLastIndex([Predicate[PoshCode.TerminalBlock]] { $args[0].Content -in "NewLine", "`n" })
                $local:Prompt = $Prompt.GetRange($LastLine, $Prompt.Count - $LastLine)
            } elseif ($Script:PowerLineConfig.RepeatPrompt -eq "LastBlock") {
                # Repeat only the last block
                $local:Prompt = $Prompt.GetRange($Prompt.Count - 1, 1)
            }
        }
        $CacheKey = $Script:LastHistoryId = $MyInvocation.HistoryId
        if ($Script:PowerLineConfig.RepeatPrompt -eq "Recalculate") {
            $CacheKey = $null
        }

        ### Invoke the prompt blocks (before we start outputting anything), to find out whether they have content
        $PromptErrors = [ordered]@{}
        for ($b = 0; $b -lt $Prompt.Count; $b++) {
            try {
                # ignore the original output (we'll fetch it from the cache with ToString to handle colors)
                $null = $Prompt[$b].Invoke($CacheKey)
                if ($Prompt[$b].HadErrors) {
                    foreach ($e in $Prompt[$b].Streams.Error) {
                        $PromptErrors.Add("$b { $($Prompt[$b].Content) }", $e)
                    }
                }
            } catch {
                $PromptErrors.Add("$b { $($Prompt[$b].Content) }", $_)
            }
        }

        [string]$PromptErrorString = if (-not $Script:PowerLineConfig.HideErrors) {
            WriteExceptions $PromptErrors
        }

        ### Output the prompt blocks, using the color of adjacent blocks for PowerLine's classic cap "overlap"
        $builder = [System.Text.StringBuilder]::new($PromptErrorString)
        # create the number of lines we need for output up front:
        $extraLineCount = $Prompt.Where{ $_.Content -eq "NewLine" }.Count
        $null = $builder.Append("`n" * $extraLineCount)
        $null = $builder.Append("$([char]27)M" * $extraLineCount)
        $rightAlign = $false

        # Add-Content -Value "BEFORE $($extraLineCount+1) line prompt: $EOL" -Path $HOME\PowerLine.log
        for ($b = 0; $b -lt $Prompt.Count; $b++) {
            $PreviousNeighbor = $NextNeighbor = $null
            $Block = $Prompt[$b]

            # Your previous neighbor is the previous non-empty block with the same alignment as you
            for ($p = $b - 1; $p -ge 0; $p--){
                $Prev = $Prompt[$p]
                if ($Prev.Content -is [PoshCode.SpecialBlock]) {
                    break;
                } elseif ($Prev.Cache) {
                    $PreviousNeighbor = $Prev
                    break;
                }
            }

            # Your next neighbor is the next non-empty block with the same alignment as you
            for ($n = $b + 1; $n -lt $Prompt.Count; $n++) {
                $Next = $Prompt[$n]
                if ($Next.Content -is [PoshCode.SpecialBlock]) {
                    break;
                } elseif ($Next.Cache) {
                    $NextNeighbor = $Next
                    break;
                }
            }

            # Don't render spacers, if they don't have a real (non-space) neighbors on the "next" side
            if ($Block.Content -in "Spacer" -and (!$NextNeighbor.Cache -or $NextNeighbor.Content -eq "Spacer")) {
                continue
            }

            $null = $builder.Append($Block.ToString($PreviousNeighbor.BackgroundColor, $NextNeighbor.BackgroundColor, $CacheKey))
        }

        if ($Colors = $PowerLineConfig.PSReadLineErrorColor) {
            $DefaultColor, $Replacement = if ($Colors.Count -eq 1) {
                $Prompt.BackgroundColor.Where({$_},"Last",1)
                $Colors[0]
            } else {
                $Colors[0]
                $Colors[-1]
            }

            if ($DefaultColor -and $Replacement) {
                $LastLine = $builder.ToString().Split("`n")[-1]
                Set-PSReadLineOption -PromptText @(
                    $LastLine
                    $LastLine -replace ([regex]::escape($DefaultColor.ToVt())), $Replacement.ToVt() -replace ([regex]::escape($DefaultColor.ToVt($true))), $Replacement.ToVt($true)
                )
            }
        }

        # At the end, output everything that's left
        $builder.ToString()
        # Add-Content -Value "return prompt:`n$($Builder.ToString())" -Path $HOME\PowerLine.log
        if ($global:LASTEXITCODE) {
            Write-Warning "LASTEXITCODE set in PowerLinePrompt: $global:LASTEXITCODE"
        }
    } catch {
        Write-Warning "Exception in Write-PowerLinePrompt`n$_"
        "${PWD}>"
    } finally {
        # Put back LASTEXITCODE so you don't have to turn off your prompt when things go wrong
        $global:LASTEXITCODE = [PoshCode.TerminalBlock]::LastExitCode
    }
}
#EndRegion '.\Public\Write-PowerlinePrompt.ps1' 142
#Region '.\postfix.ps1' 0
if (Get-Module EzTheme -ErrorAction SilentlyContinue) {
    Get-ModuleTheme | Set-PowerLineTheme
} else {
    Set-PowerLinePrompt
}
#EndRegion '.\postfix.ps1' 6