Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Downloadable files for bradwilson.io/blog/prompt/powershell
######## POSH-GIT
# ... Import-Module for posh-git here ...
# Background colors
$GitPromptSettings.AfterStash.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.AfterStatus.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BeforeIndex.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BeforeStash.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BeforeStatus.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BranchAheadStatusSymbol.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BranchBehindAndAheadStatusSymbol.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BranchBehindStatusSymbol.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BranchColor.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BranchGoneStatusSymbol.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.BranchIdenticalStatusSymbol.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.DefaultColor.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.DelimStatus.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.ErrorColor.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.IndexColor.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.LocalDefaultStatusSymbol.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.LocalStagedStatusSymbol.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.LocalWorkingStatusSymbol.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.StashColor.BackgroundColor = [ConsoleColor]::DarkBlue
$GitPromptSettings.WorkingColor.BackgroundColor = [ConsoleColor]::DarkBlue
# Foreground colors
$GitPromptSettings.AfterStatus.ForegroundColor = [ConsoleColor]::Blue
$GitPromptSettings.BeforeStatus.ForegroundColor = [ConsoleColor]::Blue
$GitPromptSettings.BranchColor.ForegroundColor = [ConsoleColor]::White
$GitPromptSettings.BranchGoneStatusSymbol.ForegroundColor = [ConsoleColor]::Blue
$GitPromptSettings.BranchIdenticalStatusSymbol.ForegroundColor = [ConsoleColor]::Blue
$GitPromptSettings.DefaultColor.ForegroundColor = [ConsoleColor]::Gray
$GitPromptSettings.DelimStatus.ForegroundColor = [ConsoleColor]::Blue
$GitPromptSettings.IndexColor.ForegroundColor = [ConsoleColor]::Cyan
$GitPromptSettings.WorkingColor.ForegroundColor = [ConsoleColor]::Yellow
# Prompt shape
$GitPromptSettings.AfterStatus.Text = " "
$GitPromptSettings.BeforeStatus.Text = ""
$GitPromptSettings.BranchAheadStatusSymbol.Text = ""
$GitPromptSettings.BranchBehindStatusSymbol.Text = ""
$GitPromptSettings.BranchGoneStatusSymbol.Text = ""
$GitPromptSettings.BranchBehindAndAheadStatusSymbol.Text = ""
$GitPromptSettings.BranchIdenticalStatusSymbol.Text = ""
$GitPromptSettings.BranchUntrackedText = ""
$GitPromptSettings.DelimStatus.Text = ""
$GitPromptSettings.LocalStagedStatusSymbol.Text = ""
$GitPromptSettings.LocalWorkingStatusSymbol.Text = ""
$GitPromptSettings.EnableStashStatus = $false
$GitPromptSettings.ShowStatusWhenZero = $false
######## PROMPT
set-content Function:prompt {
# Start with a blank line, for breathing room :)
Write-Host ""
# Reset the foreground color to default
$Host.UI.RawUI.ForegroundColor = $GitPromptSettings.DefaultColor.ForegroundColor
# Write ERR for any PowerShell errors
if ($Error.Count -ne 0) {
Write-Host " " -NoNewLine
Write-Host "  ERR " -NoNewLine -BackgroundColor DarkRed -ForegroundColor Yellow
$Error.Clear()
}
# Write non-zero exit code from last launched process
if ($LASTEXITCODE -ne "") {
Write-Host " " -NoNewLine
Write-Host "$LASTEXITCODE " -NoNewLine -BackgroundColor DarkRed -ForegroundColor Yellow
$LASTEXITCODE = ""
}
# Write any custom prompt environment (f.e., from vs2017.ps1)
if (get-content variable:\PromptEnvironment -ErrorAction Ignore) {
Write-Host " " -NoNewLine
Write-Host $PromptEnvironment -NoNewLine -BackgroundColor DarkMagenta -ForegroundColor White
}
# Write the current kubectl context
if ((Get-Command "kubectl" -ErrorAction Ignore) -ne $null) {
$currentContext = (& kubectl config current-context 2> $null)
if ($Error.Count -eq 0) {
Write-Host " " -NoNewLine
Write-Host "" -NoNewLine -BackgroundColor DarkGray -ForegroundColor Green
Write-Host " $currentContext " -NoNewLine -BackgroundColor DarkGray -ForegroundColor White
}
else {
$Error.Clear()
}
}
# Write the current public cloud Azure CLI subscription
# NOTE: You will need sed from somewhere (for example, from Git for Windows)
if (Test-Path ~/.azure/clouds.config) {
$currentSub = & sed -nr "/^\[AzureCloud\]/ { :l /^subscription[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" ~/.azure/clouds.config
if ($null -ne $currentSub) {
$currentAccount = (Get-Content ~/.azure/azureProfile.json | ConvertFrom-Json).subscriptions | Where-Object { $_.id -eq $currentSub }
if ($null -ne $currentAccount) {
Write-Host " " -NoNewLine
Write-Host "" -NoNewLine -BackgroundColor DarkCyan -ForegroundColor Yellow
Write-Host " $($currentAccount.name) " -NoNewLine -BackgroundColor DarkCyan -ForegroundColor White
}
}
}
# Write the current Git information
if ((Get-Command "Get-GitDirectory" -ErrorAction Ignore) -ne $null) {
if (Get-GitDirectory -ne $null) {
Write-Host (Write-VcsStatus) -NoNewLine
}
}
# Write the current directory, with home folder normalized to ~
$currentPath = (get-location).Path.replace($home, "~")
$idx = $currentPath.IndexOf("::")
if ($idx -gt -1) { $currentPath = $currentPath.Substring($idx + 2) }
Write-Host " " -NoNewLine
Write-Host "" -NoNewLine -BackgroundColor DarkGreen -ForegroundColor Yellow
Write-Host " $currentPath " -NoNewLine -BackgroundColor DarkGreen -ForegroundColor White
# Reset LASTEXITCODE so we don't show it over and over again
$global:LASTEXITCODE = 0
# Write one + for each level of the pushd stack
if ((get-location -stack).Count -gt 0) {
Write-Host " " -NoNewLine
Write-Host (("+" * ((get-location -stack).Count))) -NoNewLine -ForegroundColor Cyan
}
# Newline
Write-Host ""
# Determine if the user is admin, so we color the prompt green or red
$isAdmin = $false
$isDesktop = ($PSVersionTable.PSEdition -eq "Desktop")
if ($isDesktop -or $IsWindows) {
$windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'System.Security.Principal.WindowsPrincipal' $windowsIdentity
$isAdmin = $windowsPrincipal.IsInRole("Administrators") -eq 1
} else {
$isAdmin = ((& id -u) -eq 0)
}
if ($isAdmin) { $color = "Red"; }
else { $color = "Green"; }
# Write PS> for desktop PowerShell, pwsh> for PowerShell Core
if ($isDesktop) {
Write-Host " PS>" -NoNewLine -ForegroundColor $color
}
else {
Write-Host " pwsh>" -NoNewLine -ForegroundColor $color
}
# Always have to return something or else we get the default prompt
return " "
}
param(
[string]$edition,
[string]$version = "2017",
[string]$basePath = "",
[switch]$noWeb = $false
)
function append-path {
param(
[string[]][Parameter(Mandatory = $true)]$pathsToAdd
)
$local:separator = ";"
if (($PSVersionTable.PSVersion.Major -gt 5) -and ($PSVersionTable.Platform -eq "Unix")) {
$local:separator = ":"
}
$env:PATH = $env:PATH + $local:separator + ($pathsToAdd -join $local:separator)
}
# Only support one prompt environment at a time
if ($PromptEnvironment -ne $null) {
write-host "error: Prompt is already in a custom environment." -ForegroundColor Red
exit 1
}
# Look in the standard installation location unless they override
if ($basePath -eq "") {
$basePath = join-path (join-path ${env:ProgramFiles(x86)} "Microsoft Visual Studio") $version
}
# Test to see if it's installed
if ((test-path $basePath) -eq $false) {
write-warning "Visual Studio $version is not installed in '$basePath'."
exit 1
}
# If edition wasn't specified, see what's there, and bail out if there is more than 1
if ($edition -eq "") {
$editions = (get-childitem $basePath | where-object { $_.PSIsContainer })
if ($editions.Count -eq 0) {
write-warning "Visual Studio $version is not installed."
exit 1
}
if ($editions.Count -gt 1) {
write-warning "Multiple editions of Visual Studio $version are installed. Please specify one of the editions ($($editions -join ', ')) with the -edition switch."
exit 1
}
$edition = $editions[0]
}
# Find VsDevCmd.bat
$path = join-path (join-path (join-path $basePath $edition) "Common7") "Tools"
if ((test-path $path) -eq $false) {
write-warning "Visual Studio $version edition '$edition' could not be found."
exit 1
}
$cmdPath = join-path $path "VsDevCmd.bat"
if ((test-path $cmdPath) -eq $false) {
write-warning "File not found: $cmdPath"
exit 1
}
# Run VsDevCmd.bat and then dump all environment variables, so we can
# overwrite ours with theirs
$tempFile = [IO.Path]::GetTempFileName()
cmd /c " `"$cmdPath`" && set > `"$tempFile`" "
Get-Content $tempFile | %{
if ($_ -match "^(.*?)=(.*)$") {
Set-Content "env:\$($matches[1])" $matches[2]
}
}
# Optionally add the external web tools unless skipped
if ($noWeb -eq $false) {
$path = join-path (join-path (join-path $basePath $edition) "Web") "External"
if (test-path $path) {
append-path $path
} else {
write-warning "Path $path not found; specify -noWeb to skip searching for web tools"
}
}
# Set the prompt environment variable (printed in our prompt function)
$global:PromptEnvironment = "$version "
param(
[string]$edition,
[string]$basePath = "",
[switch]$noWeb = $false
)
$folder = Split-Path $PSCommandPath
$script = Join-Path $folder "vs.ps1"
& $script -edition:$edition -version:"2017" -basePath:$basePath -noWeb:$noWeb
param(
[string]$edition,
[string]$basePath = "",
[switch]$noWeb = $false
)
$folder = Split-Path $PSCommandPath
$script = Join-Path $folder "vs.ps1"
& $script -edition:$edition -version:"2019" -basePath:$basePath -noWeb:$noWeb
param(
[string]$edition,
[string]$basePath = "",
[switch]$noWeb = $false
)
$folder = Split-Path $PSCommandPath
$script = Join-Path $folder "vs.ps1"
& $script -edition:$edition -version:"Preview" -basePath:$basePath -noWeb:$noWeb
@tillig

This comment has been minimized.

Copy link

commented Mar 13, 2019

First, thanks for this and the great blog article on it. I'm in the process of adopting some of this myself, though not using ConEmu - just plain PowerShell.

I noticed a couple of things that might help reduce the custom code you have to maintain, if you're interested.

  • The block that reduces the path such that the home is normalized to ~ - you can use the function from posh-git that does it for you: $currentPath = $GitPromptSettings.DefaultPromptPath.Expand().Text
  • There's a VSSetup PowerShell module that can be used to locate VS installs for 2017 (and, I think, 2019). It might be easier to use that than manually probe paths. Admittedly, I've only tried this on Windows.
  • The PowerShell Community Extensions (pscx) module has Import-VisualStudioVars that makes use of the VSSetup module and has support for the earlier versions of VS based on environment variables. That might also help.

And something for the blog article, maybe: if you're not using ConEmu, it appears the Windows system hijacks DarkMagenta to mean "transparent" so the VS environment bit ends up showing with no background.

@tillig

This comment has been minimized.

Copy link

commented Mar 14, 2019

Potential enhancement for showing the current dotnet SDK version:

  # Write the current dotnet version.
  if ((Get-Command "dotnet" -ErrorAction Ignore) -ne $null) {
    $dotnetVersion = (& dotnet --version 2> $null)
    Write-Host " " -NoNewLine
    Write-Host "" -NoNewLine -BackgroundColor Gray -ForegroundColor Blue
    Write-Host " $dotnetVersion " -NoNewLine -BackgroundColor Gray -ForegroundColor Black
  }

Also, I found I could fairly consistently shave about 40 - 100ms off the rendering of the prompt by running the external commands (kubectl, sed, dotnet, etc.) in parallel using PowerShell runspaces. It makes the script more complicated since you have to separate the retrieval of the data from the rendering of the data, but the prompt feels a little faster. Like:

  $dotnetScript = {
    # Get the current dotnet version.
    $dotnetVersion = $null
    if ((Get-Command "dotnet" -ErrorAction Ignore) -ne $null) {
      $dotnetVersion = (& dotnet --version 2> $null)
    }
    return $dotnetVersion
  }

  $kubectlScript = {
    # Get the current kubectl context.
    $currentContext = $null
    if ((Get-Command "kubectl" -ErrorAction Ignore) -ne $null) {
      $currentContext = (& kubectl config current-context 2> $null)
    }
    return $currentContext
  }

  $azureScript = {
    # Get the current public cloud Azure CLI subscription.
    # NOTE: You will need sed from somewhere (for example, from Git for Windows).
    $currentAccount = $null
    $cloudConfigPath = Resolve-Path "~/.azure/clouds.config"
    if ((Test-Path $cloudConfigPath) -and ((Get-Command "sed" -ErrorAction Ignore) -ne $null)) {
      $currentSub = & sed -nr "/^\[AzureCloud\]/ { :l /^subscription[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" "$($cloudConfigPath.Path)"
      if ($null -ne $currentSub) {
        $currentAccount = (Get-Content ~/.azure/azureProfile.json | ConvertFrom-Json).subscriptions | Where-Object { $_.id -eq $currentSub } | Select-Object -ExpandProperty Name
      }
    }
    return $currentAccount
  }

  # Create the set of jobs to run, each in a runspace. Not using RunspacePool
  # because there aren't a lot of jobs and it's really hard to set the current
  # working directory in a pool.
  $scripts = @{ "azure" = $azureScript; "dotnet" = $dotnetScript; "kubectl" = $kubectlScript }
  $jobs = @()

  foreach ($key in $scripts.Keys) {
    $thread = [powershell]::Create().AddScript($scripts[$key])
    $runspace = [RunspaceFactory]::CreateRunspace()
    $runspace.Open()
    $runspace.SessionStateProxy.Path.SetLocation($pwd.Path) | Out-Null
    $thread.Runspace = $runspace
    $handle = $thread.BeginInvoke()
    $jobs += @{ "Handle" = $handle; "Thread" = $thread; "Name" = $key }
  }

  while (@($jobs | Where-Object {$_.Handle.IsCompleted -ne $True}).count -gt 0) {
      Start-Sleep -Milliseconds 1
  }

  $scriptReturnValues = @{}
  foreach ($job in $jobs) {
    $scriptReturnValues.Add($job.Name, $job.Thread.EndInvoke($job.Handle))
    $job.Thread.Runspace.Close()
    $job.Thread.Dispose()
  }

  if ($scriptReturnValues["dotnet"] -ne $null) {
    Write-Host " " -NoNewLine
    Write-Host "" -NoNewLine -BackgroundColor Gray -ForegroundColor Blue
    Write-Host " $($scriptReturnValues['dotnet']) " -NoNewLine -BackgroundColor Gray -ForegroundColor Black
  }

  if ($scriptReturnValues["kubectl"] -ne $null) {
    Write-Host " " -NoNewLine
    Write-Host "" -NoNewLine -BackgroundColor DarkCyan -ForegroundColor Green
    Write-Host " $($scriptReturnValues['kubectl']) " -NoNewLine -BackgroundColor DarkCyan -ForegroundColor White
  }

  if ($scriptReturnValues["azure"] -ne $null) {
    Write-Host " " -NoNewLine
    Write-Host "" -NoNewLine -BackgroundColor DarkCyan -ForegroundColor Green
    Write-Host " $($scriptReturnValues['azure']) " -NoNewLine -BackgroundColor DarkCyan -ForegroundColor White
  }

I'm not using ConEmu so my glyphs aren't Nerd Font but you get the idea. I also can't guarantee I'm 100% handling all error conditions there so YMMV.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.