Last active
March 21, 2024 07:24
-
-
Save bradwilson/d965fa17fd3b3eee168270122e0f7da4 to your computer and use it in GitHub Desktop.
Downloadable files for bradwilson.io/blog/prompt/powershell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
######## POSH-GIT | |
# ... Import-Module for posh-git here ... | |
# Background colors | |
$GitPromptSettings.AfterStash.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.AfterStatus.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BeforeIndex.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BeforeStash.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BeforeStatus.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BranchAheadStatusSymbol.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BranchBehindAndAheadStatusSymbol.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BranchBehindStatusSymbol.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BranchColor.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BranchGoneStatusSymbol.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.BranchIdenticalStatusSymbol.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.DefaultColor.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.DelimStatus.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.ErrorColor.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.IndexColor.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.LocalDefaultStatusSymbol.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.LocalStagedStatusSymbol.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.LocalWorkingStatusSymbol.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.StashColor.BackgroundColor = 0x5F5FAF | |
$GitPromptSettings.WorkingColor.BackgroundColor = 0x5F5FAF | |
# Foreground colors | |
$GitPromptSettings.AfterStash.ForegroundColor = 0xF49797 | |
$GitPromptSettings.AfterStatus.ForegroundColor = 0x729FCF | |
$GitPromptSettings.BeforeStash.ForegroundColor = 0xF49797 | |
$GitPromptSettings.BeforeStatus.ForegroundColor = 0x729FCF | |
$GitPromptSettings.BranchAheadStatusSymbol.ForegroundColor = 0x8AE234 | |
$GitPromptSettings.BranchBehindAndAheadStatusSymbol.ForegroundColor = 0xFCE94F | |
$GitPromptSettings.BranchBehindStatusSymbol.ForegroundColor = 0xF49797 | |
$GitPromptSettings.BranchColor.ForegroundColor = 0xFBFBFB | |
$GitPromptSettings.BranchGoneStatusSymbol.ForegroundColor = 0x729FCF | |
$GitPromptSettings.BranchIdenticalStatusSymbol.ForegroundColor = 0x729FCF | |
$GitPromptSettings.DefaultColor.ForegroundColor = 0xB5BBAE | |
$GitPromptSettings.DelimStatus.ForegroundColor = 0x729FCF | |
$GitPromptSettings.ErrorColor.ForegroundColor = 0xF49797 | |
$GitPromptSettings.IndexColor.ForegroundColor = 0x2EC3C3 | |
$GitPromptSettings.StashColor.ForegroundColor = 0xF49797 | |
$GitPromptSettings.WorkingColor.ForegroundColor = 0xFCE94F | |
# 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.PathStatusSeparator = "" | |
$GitPromptSettings.EnableStashStatus = $false | |
$GitPromptSettings.ShowStatusWhenZero = $false | |
######## INI FILE PARSER | |
function parseIniFile { | |
[CmdletBinding()] | |
param( | |
[Parameter(Position = 0)] | |
[String] $Inputfile | |
) | |
if ($Inputfile -eq "") { | |
Write-Error "Ini File Parser: No file specified or selected to parse." | |
Break | |
} | |
else { | |
$ContentFile = Get-Content $Inputfile | |
# commented Section | |
$COMMENT_CHARACTERS = ";" | |
# match section header | |
$HEADER_REGEX = "\[+[A-Z0-9._ %<>/#+-]+\]" | |
$OccurenceOfComment = 0 | |
$ContentComment = $ContentFile | Where-Object { ($_ -match "^\s*$COMMENT_CHARACTERS") -or ($_ -match "^$COMMENT_CHARACTERS") } | ForEach-Object { | |
[PSCustomObject]@{ Comment = $_ ; | |
Index = [Array]::IndexOf($ContentFile, $_) | |
} | |
$OccurenceOfComment++ | |
} | |
$COMMENT_INI = @() | |
foreach ($COMMENT_ELEMENT in $ContentComment) { | |
$COMMENT_OBJ = New-Object PSObject | |
$COMMENT_OBJ | Add-Member -type NoteProperty -name Index -value $COMMENT_ELEMENT.Index | |
$COMMENT_OBJ | Add-Member -type NoteProperty -name Comment -value $COMMENT_ELEMENT.Comment | |
$COMMENT_INI += $COMMENT_OBJ | |
} | |
$CONTENT_USEFUL = $ContentFile | Where-Object { ($_ -notmatch "^\s*$COMMENT_CHARACTERS") -or ($_ -notmatch "^$COMMENT_CHARACTERS") } | |
$ALL_SECTION_HASHTABLE = $CONTENT_USEFUL | Where-Object { $_ -match $HEADER_REGEX } | ForEach-Object { [PSCustomObject]@{ Section = $_ ; Index = [Array]::IndexOf($CONTENT_USEFUL, $_) } } | |
#$ContentUncomment | Select-String -AllMatches $HEADER_REGEX | Select-Object -ExpandProperty Matches | |
$SECTION_INI = @() | |
foreach ($SECTION_ELEMENT in $ALL_SECTION_HASHTABLE) { | |
$SECTION_OBJ = New-Object PSObject | |
$SECTION_OBJ | Add-Member -type NoteProperty -name Index -value $SECTION_ELEMENT.Index | |
$SECTION_OBJ | Add-Member -type NoteProperty -name Section -value $SECTION_ELEMENT.Section | |
$SECTION_INI += $SECTION_OBJ | |
} | |
$INI_FILE_CONTENT = @() | |
$NBR_OF_SECTION = $SECTION_INI.count | |
$NBR_MAX_LINE = $CONTENT_USEFUL.count | |
#********************************************* | |
# select each lines and value of each section | |
#********************************************* | |
for ($i = 1; $i -le $NBR_OF_SECTION ; $i++) { | |
if ($i -ne $NBR_OF_SECTION) { | |
if (($SECTION_INI[$i - 1].Index + 1) -eq ($SECTION_INI[$i].Index )) { | |
$CONVERTED_OBJ = @() #There is nothing between the two section | |
} | |
else { | |
$SECTION_STRING = $CONTENT_USEFUL | Select-Object -Index (($SECTION_INI[$i - 1].Index + 1)..($SECTION_INI[$i].Index - 1)) | Out-String | |
$CONVERTED_OBJ = convertfrom-stringdata -stringdata $SECTION_STRING | |
} | |
} | |
else { | |
if (($SECTION_INI[$i - 1].Index + 1) -eq $NBR_MAX_LINE) { | |
$CONVERTED_OBJ = @() #There is nothing between the two section | |
} | |
else { | |
$SECTION_STRING = $CONTENT_USEFUL | Select-Object -Index (($SECTION_INI[$i - 1].Index + 1)..($NBR_MAX_LINE - 1)) | Out-String | |
$CONVERTED_OBJ = convertfrom-stringdata -stringdata $SECTION_STRING | |
} | |
} | |
$CURRENT_SECTION = New-Object PSObject | |
$CURRENT_SECTION | Add-Member -Type NoteProperty -Name Section -Value $SECTION_INI[$i - 1].Section | |
$CURRENT_SECTION | Add-Member -Type NoteProperty -Name Content -Value $CONVERTED_OBJ | |
$INI_FILE_CONTENT += $CURRENT_SECTION | |
} | |
return $INI_FILE_CONTENT | |
} | |
} | |
######## FIND A FILE/PATTERN HERE OR IN ANY PARENT FOLDER | |
function findHereOrParent { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory = $true)] | |
[string[]] $filePaths | |
) | |
try { | |
$currentFolder = Get-Location | |
while ($currentFolder -ne "") { | |
foreach ($filePath in $filePaths) { | |
if ((Get-ChildItem -ErrorAction Ignore -LiteralPath $currentFolder -Filter $filePath).Count -gt 0) { | |
return $true | |
} | |
} | |
$currentFolder = Split-Path $currentFolder | |
} | |
} | |
catch { | |
$Error.Clear() | |
} | |
return $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 = "Gray" | |
Write-Host " " -NoNewLine | |
# Write ERR for any PowerShell errors | |
if ($Error.Count -ne 0) { | |
Write-Host "$([char]27)[38;5;131;40m$([char]27)[38;5;227;48;5;131m ERR $([char]27)[38;5;131;40m" -NoNewLine | |
$Error.Clear() | |
} | |
# Write Ctrl+C separately | |
if ($LASTEXITCODE -eq -1073741510) { | |
Write-Host "$([char]27)[38;5;131;40m$([char]27)[38;5;227;48;5;131m Ctrl-C $([char]27)[38;5;131;40m" -NoNewLine | |
$LASTEXITCODE = "" | |
} | |
# Write non-zero exit code from last launched process | |
if ($LASTEXITCODE -ne "") { | |
Write-Host "$([char]27)[38;5;131;40m$([char]27)[38;5;227;48;5;131m $LASTEXITCODE $([char]27)[38;5;131;40m" -NoNewLine | |
$LASTEXITCODE = "" | |
} | |
# Write any custom prompt environment (f.e., from vs2017.ps1) | |
if (get-content variable:\PromptEnvironment -ErrorAction Ignore) { | |
Write-Host "$([char]27)[38;5;183;40m$([char]27)[38;5;54;48;5;183m$PromptEnvironment$([char]27)[38;5;183;40m" -NoNewLine | |
} | |
# Write Go version | |
if (($null -ne (Get-Command "go" -ErrorAction Ignore)) -and (findHereOrParent go.mod)) { | |
$goVersion = (& go version).split()[2].substring(2) | |
Write-Host "$([char]27)[38;5;80;48;5;0m$([char]27)[38;5;16;48;5;80m $goVersion $([char]27)[38;5;80;40m" -NoNewLine | |
} | |
# Write Rust version | |
if (($null -ne (Get-Command "rustc" -ErrorAction Ignore)) -and (findHereOrParent Cargo.toml)) { | |
$rustVersion = (& rustc --version).Split()[1] | |
Write-Host "$([char]27)[38;5;172;40m$([char]27)[38;5;231;48;5;172m $rustVersion $([char]27)[38;5;172;40m" -NoNewLine | |
} | |
# Write .NET SDK version | |
if (($null -ne (Get-Command "dotnet" -ErrorAction Ignore)) -and (findHereOrParent *.sln,*.csproj)) { | |
$dotNetVersion = (& dotnet --version) | |
Write-Host "$([char]27)[38;5;54;40m$([char]27)[38;5;254;48;5;54m $dotNetVersion $([char]27)[38;5;54;40m" -NoNewLine | |
} | |
# Write the current kubectl context | |
if ($null -ne (Get-Command "kubectl" -ErrorAction Ignore)) { | |
$currentContext = (& kubectl config current-context 2> $null) | |
if (($Error.Count -eq 0) -and ($currentContext -ne $null)) { | |
Write-Host "$([char]27)[38;5;242;40m$([char]27)[38;5;112;48;5;242m $([char]27)[38;5;254m$currentContext $([char]27)[38;5;242;40m" -NoNewLine | |
} | |
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) { | |
$cloudsConfig = parseIniFile ~/.azure/clouds.config | |
$azureCloud = $cloudsConfig | Where-Object { $_.Section -eq "[AzureCloud]" } | |
if ($null -ne $azureCloud) { | |
$currentSub = $azureCloud.Content.subscription | |
if ($null -ne $currentSub) { | |
$currentAccount = (Get-Content ~/.azure/azureProfile.json | ConvertFrom-Json).subscriptions | Where-Object { $_.id -eq $currentSub } | |
if ($null -ne $currentAccount) { | |
Write-Host "$([char]27)[38;5;30;40m$([char]27)[38;5;227;48;5;30m $([char]27)[38;5;254m$($currentAccount.name) $([char]27)[38;5;30;40m" -NoNewLine | |
} | |
} | |
} | |
} | |
# Write the current Git information | |
if ($null -ne (Get-Command "Get-GitDirectory" -ErrorAction Ignore)) { | |
if (Get-GitDirectory -ne $null) { | |
Write-Host "$([char]27)[38;5;61;40m$(Write-VcsStatus)$([char]27)[38;5;61;40m" -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) } | |
$host.UI.RawUI.WindowTitle = $currentPath | |
Write-Host "$([char]27)[38;5;28;40m$([char]27)[38;5;227;48;5;28m $([char]27)[38;5;254m$currentPath " -NoNewline | |
# 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 ("$([char]27)[38;5;227;48;5;28m" + ("«" * ((get-location -stack).Count)) + " ") -NoNewLine | |
} | |
# Newline | |
Write-Host "$([char]27)[38;5;28;40m$([char]27)[30;40m $([char]27)[0m" | |
# 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 " " | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
param( | |
[string]$edition, | |
[string]$version = "2022", | |
[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 locations unless they override | |
if ($basePath -eq "") { | |
$basePath = join-path (join-path ${env:ProgramFiles(x86)} "Microsoft Visual Studio") $version | |
if ((test-path $basePath) -eq $false) { | |
$basePath = join-path (join-path ${env:ProgramFiles} "Microsoft Visual Studio") $version | |
} | |
} | |
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 | Foreach-Object { "'" + $_.Name + "'" }) -join ', ')) with the -edition switch." | |
exit 1 | |
} | |
$edition = $editions[0].Name | |
} | |
# 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 " |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
param( | |
[string]$edition, | |
[string]$basePath = "", | |
[switch]$noWeb = $false | |
) | |
$folder = Split-Path $PSCommandPath | |
$script = Join-Path $folder "vs.ps1" | |
& $script -edition:$edition -version:"2022" -basePath:$basePath -noWeb:$noWeb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very nice! I had to add a small tweak to line 51 of
vs.ps1
from$edition = $editions[0]
to$edition = $editions[0].Name
. From some contexts the value was the full path so it'd end up withC:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\Tools\C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\Tools
and bail as the path would't exist.